New failable based error handling. Labelled break/continue/next.

This commit is contained in:
Christoffer Lerno
2020-06-22 11:37:42 +02:00
committed by Christoffer Lerno
parent 10f715cec0
commit dc86c21210
40 changed files with 3727 additions and 2808 deletions

View File

@@ -58,8 +58,6 @@ void comment(void);
"void" { count(); return(VOID); }
"volatile" { count(); return(VOLATILE); }
"while" { count(); return(WHILE); }
"throw" { count(); return(THROW); }
"throws" { count(); return(THROWS); }
"func" { count(); return(FUNC); }
"nil" { count(); return(NIL); }
"next" { count(); return(NEXT); }

11
resources/c3ir.md Normal file
View File

@@ -0,0 +1,11 @@
```
block
{
z = add(x, y)
z = sub(x, y)
z = mult(x, y)
neg(z, x)
goto
```

View File

@@ -8,10 +8,10 @@ func int main()
}
}
func string@ bin(int x)
func string bin(int x)
{
int bits = (x == 0) ? 1 : log10(cast(x, double)) / log10(2);
string@ ret = string.make_repeat('0', bits);
string ret = str.make_repeat('0', bits);
for (int i = 0; i < bits; i++)
{
ret[bits - i - 1] = x & 1 ? '1' : '0';

View File

@@ -21,12 +21,7 @@ func void main()
{
string s = match.string;
printf("Enter a value for '%s': ", s.slice(1, s.size - 2));
string! word = strin.readln().strip();
catch (word)
{
// Exit on error.
return;
}
string word = strin.readln().strip() else return;
story = story.replace(s, word);
}

View File

@@ -110,7 +110,7 @@ struct Parser
/**
* @ensure const(input)
*/
func Parser.parse(Parser* p, char* input, char* diagMsg, Blocks* blocks) throws
func void! Parser.parse(Parser* p, char* input, char* diagMsg, Blocks* blocks)
{
p.tokenizer.init(input);
p.tok.init();
@@ -125,15 +125,9 @@ func Parser.parse(Parser* p, char* input, char* diagMsg, Blocks* blocks) throws
try p.consumeToken();
try p.parseTopLevel();
return true;
catch (error e)
{
return false;
}
}
func void Parser.parseTopLevel(Parser* p) throws ParseError
func void! Parser.parseTopLevel(Parser* p)
{
// key = value
// | [[array]]
@@ -150,7 +144,7 @@ func void Parser.parseTopLevel(Parser* p) throws ParseError
p.parseTableArray();
default:
sprintf(p.errorMsg, "syntax error %s", p.tok.loc.str());
throw ParseError.SYNTAX_ERROR;
return SyntaxError!;
}
}
}

View File

@@ -16,6 +16,7 @@ void yyerror(char *s);
%token ADD_MOD SUB_MOD MULT_MOD ADD_MOD_ASSIGN SUB_MOD_ASSIGN
%token MULT_MOD_ASSIGN NEG_MOD
%token XOR_ASSIGN OR_ASSIGN VAR NIL ELVIS HASH_IDENT NEXT
%token NOFAIL_ASSIGN
%token TYPEDEF MODULE IMPORT
%token CHAR SHORT INT LONG FLOAT DOUBLE CONST VOLATILE VOID
@@ -402,14 +403,25 @@ expression_statement
;
if_expr
: failable_type IDENT '=' initializer
| failable_type IDENT NOFAIL_ASSIGN expression
| expression
;
if_cond_expr
: if_expr
| if_cond_expr ',' if_expr
;
control_expression
: decl_expr_list
| decl_expr_list ';' decl_expr_list
;
selection_statement
: IF '(' control_expression ')' statement
| IF '(' control_expression ')' compound_statement ELSE statement
: IF '(' if_cond_expr ')' statement
| IF '(' if_cond_expr ')' compound_statement ELSE statement
| SWITCH '(' control_expression ')' compound_statement
;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,30 @@
module foo;
extern func void printf(char *hello, ...);
error MyError;
error YourError;
func void main()
{
int! i = 1;
catch (i)
{
printf("Caught\n");
return;
}
i = MyError!;
catch FOO: (i)
{
case YourError:
printf("YourError\n");
case MyError:
printf("MyError\n");
next FOO : YourError;
default:
printf("Any error\n");
}
do
{
printf("Test\n");
}
}

View File

@@ -782,6 +782,7 @@ int i = 0;
return 1;
}
func void test2(int* x, int y, int z)
{
*(&(&x)[0]);

View File

@@ -68,8 +68,8 @@ Decl *decl_new_with_type(Token name, DeclKind decl_type, Visibility visibility)
case DECL_STRUCT:
kind = TYPE_STRUCT;
break;
case DECL_ERROR:
kind = TYPE_ERROR;
case DECL_ERR:
kind = TYPE_ERRTYPE;
break;
case DECL_ENUM:
kind = TYPE_ENUM;
@@ -80,7 +80,6 @@ Decl *decl_new_with_type(Token name, DeclKind decl_type, Visibility visibility)
case DECL_POISONED:
case DECL_VAR:
case DECL_ENUM_CONSTANT:
case DECL_ERROR_CONSTANT:
case DECL_ARRAY_VALUE:
case DECL_IMPORT:
case DECL_MACRO:
@@ -90,6 +89,7 @@ Decl *decl_new_with_type(Token name, DeclKind decl_type, Visibility visibility)
case DECL_CT_ELIF:
case DECL_ATTRIBUTE:
case DECL_MEMBER:
case DECL_LABEL:
UNREACHABLE
}
Type *type = type_new(kind, name.string);
@@ -111,6 +111,12 @@ const char *decl_var_to_string(VarDeclKind kind)
return "local";
case VARDECL_PARAM:
return "param";
case VARDECL_ALIAS:
return "alias";
case VARDECL_LOCAL_CT:
return "$local";
case VARDECL_LOCAL_CT_TYPE:
return "$Local";
}
UNREACHABLE
}
@@ -148,7 +154,7 @@ Decl *struct_find_name(Decl *decl, const char* name)
Expr *expr_new(ExprKind kind, SourceRange start)
{
Expr *expr = malloc_arena(sizeof(Expr));
Expr *expr = CALLOCS(Expr);
expr->expr_kind = kind;
expr->span = start;
expr->type = NULL;
@@ -222,7 +228,7 @@ UnaryOp unary_op[TOKEN_LAST + 1] = {
[TOKEN_STAR] = UNARYOP_DEREF,
[TOKEN_AMP] = UNARYOP_ADDR,
[TOKEN_BIT_NOT] = UNARYOP_BITNEG,
[TOKEN_NOT] = UNARYOP_NOT,
[TOKEN_BANG] = UNARYOP_NOT,
[TOKEN_MINUS] = UNARYOP_NEG,
[TOKEN_MINUS_MOD] = UNARYOP_NEGMOD,
[TOKEN_PLUSPLUS] = UNARYOP_INC,
@@ -331,8 +337,8 @@ void fprint_type_recursive(FILE *file, Type *type, int indent)
case TYPE_ENUM:
DUMPF("(enum %s)", type->name);
return;
case TYPE_ERROR:
DUMPF("(error %s)", type->name);
case TYPE_ERRTYPE:
DUMPF("(errtype %s)", type->name);
return;
case TYPE_MEMBER:
DUMPF("(member %s", type->name);
@@ -381,8 +387,8 @@ void fprint_type_recursive(FILE *file, Type *type, int indent)
case TYPE_STRING:
DUMP("(string)");
return;
case TYPE_ERROR_UNION:
DUMP("(error-union)");
case TYPE_ERR_UNION:
DUMP("(any-error)");
return;
}
}
@@ -491,6 +497,20 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent)
if (!expr) return;
switch (expr->expr_kind)
{
case EXPR_FAIL_CHECK:
DUMP("(fail-check");
DUMPEXPC(expr);
DUMPEXPR(expr->fail_check_expr);
DUMPEND();
case EXPR_DECL_LIST:
DUMP("(decllist");
DUMPASTS(expr->dexpr_list_expr);
DUMPEND();
case EXPR_FAILABLE:
DUMP("(failable");
DUMPEXPC(expr);
DUMPEXPR(expr->failable_expr);
DUMPEND();
case EXPR_IDENTIFIER:
if (expr->identifier_expr.is_macro)
{
@@ -538,6 +558,16 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent)
DUMPEXPC(expr);
DUMPEXPR(expr->post_expr.expr);
DUMPEND();
case EXPR_CATCH:
DUMP("(catch");
DUMPEXPC(expr);
DUMPEXPR(expr->trycatch_expr);
DUMPEND();
case EXPR_TRY:
DUMP("(try");
DUMPEXPC(expr);
DUMPEXPR(expr->trycatch_expr);
DUMPEND();
case EXPR_TYPE_ACCESS:
DUMPF("(typeaccess .%s", expr->type_access.name.string);
DUMPEXPC(expr);
@@ -615,29 +645,26 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent)
DUMPEXPR(expr->subscript_expr.expr);
DUMPEXPC(expr->subscript_expr.index);
DUMPEND();
case EXPR_TRY:
switch (expr->try_expr.type)
case EXPR_GUARD:
DUMP("(guard");
DUMPEXPR(expr->guard_expr);
DUMPEND();
case EXPR_ELSE:
if (expr->else_expr.is_jump)
{
case TRY_EXPR_ELSE_JUMP:
DUMP("(try-else-jump");
DUMPEXPC(expr);
DUMPEXPR(expr->try_expr.expr);
DUMPAST(expr->try_expr.else_stmt);
DUMPEND();
case TRY_EXPR_ELSE_EXPR:
DUMP("(try-else");
DUMPEXPC(expr);
DUMPEXPR(expr->try_expr.expr);
DUMPEXPR(expr->try_expr.else_expr);
DUMPEND();
case TRY_STMT:
DUMP("(try-stmt");
DUMPAST(expr->try_expr.stmt);
DUMPEND();
case TRY_EXPR:
DUMP("(try-expr");
DUMPEXPR(expr->try_expr.expr);
DUMPEND();
DUMP("(else-jump");
DUMPEXPC(expr);
DUMPEXPR(expr->else_expr.expr);
DUMPAST(expr->else_expr.else_stmt);
DUMPEND();
}
else
{
DUMP("(else-expr");
DUMPEXPC(expr);
DUMPEXPR(expr->else_expr.expr);
DUMPEXPR(expr->else_expr.else_expr);
DUMPEND();
}
UNREACHABLE
case EXPR_CAST:
@@ -702,16 +729,6 @@ void fprint_func_signature(FILE *file, FunctionSignature *signature, int indent)
DUMP("(params");
DUMPDECLS(signature->params);
DUMPE();
if (signature->throw_any)
{
DUMP("(throws any)");
}
else
{
DUMP("(throws");
VECEACH(signature->throws, i) DUMPTI(signature->throws[i]);
DUMPE();
}
indent--;
} while (false);
DUMPEND();
@@ -731,10 +748,20 @@ void fprint_decl_recursive(FILE *file, Decl *decl, int indent)
case VARDECL_GLOBAL:
case VARDECL_LOCAL:
case VARDECL_PARAM:
case VARDECL_LOCAL_CT:
DUMPEXPR(decl->var.init_expr);
break;
case VARDECL_LOCAL_CT_TYPE:
DUMPTI(decl->var.type_info);
break;
case VARDECL_ALIAS:
DUMPDECL(decl->var.alias);
break;
}
DUMPEND();
case DECL_LABEL:
DUMPF("(label %s", decl->name);
DUMPEND();
case DECL_MACRO:
DUMPF("(macro %s", decl->name);
DUMPTI(decl->macro_decl.rtype);
@@ -771,9 +798,9 @@ void fprint_decl_recursive(FILE *file, Decl *decl, int indent)
DUMPTI(decl->enums.type_info);
DUMPDECLS(decl->enums.values);
DUMPEND();
case DECL_ERROR:
case DECL_ERR:
DUMPF("(error %s", decl->name);
DUMPDECLS(decl->error.error_constants);
DUMPDECLS(decl->strukt.members);
DUMPEND();
case DECL_ENUM_CONSTANT:
if (!decl->enum_constant.expr)
@@ -784,9 +811,6 @@ void fprint_decl_recursive(FILE *file, Decl *decl, int indent)
DUMPF("(enum-constant %s", decl->name);
DUMPEXPR(decl->enum_constant.expr);
DUMPEND();
case DECL_ERROR_CONSTANT:
DUMPF("(error-constant %s)", decl->name);
return;
case DECL_GENERIC:
DUMPF("(generic %s\n", decl->name);
indent++;
@@ -907,6 +931,11 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent)
if (!ast) return;
switch (ast->ast_kind)
{
case AST_TRY_STMT:
DUMP("(try");
DUMPEXPR(ast->try_stmt.decl_expr);
DUMPAST(ast->try_stmt.body);
DUMPEND();
case AST_COMPOUND_STMT:
if (!ast->compound_stmt.stmts)
{
@@ -916,9 +945,9 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent)
DUMP("(compound\n");
fprint_asts_recursive(file, ast->compound_stmt.stmts, indent + 1);
DUMPEND();
case AST_DECL_EXPR_LIST:
DUMP("(declexprlist");
DUMPASTS(ast->decl_expr_stmt);
case AST_DEFINE_STMT:
DUMP("(define");
DUMPDECL(ast->define_stmt);
DUMPEND();
case AST_DECLARE_STMT:
DUMP("(declare");
@@ -930,7 +959,7 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent)
DUMPEND();
case AST_WHILE_STMT:
DUMP("(while");
DUMPAST(ast->while_stmt.cond);
DUMPEXPR(ast->while_stmt.cond);
DUMPAST(ast->while_stmt.body);
DUMPEND();
case AST_SCOPED_STMT:
@@ -977,7 +1006,7 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent)
return;
case AST_FOR_STMT:
DUMP("(for");
DUMPAST(ast->for_stmt.init);
DUMPEXPR(ast->for_stmt.init);
DUMPEXPR(ast->for_stmt.cond);
if (ast->for_stmt.incr)
{
@@ -991,39 +1020,31 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent)
DUMPEND();
case AST_IF_STMT:
DUMP("(if");
DUMPAST(ast->if_stmt.cond);
DUMPEXPR(ast->if_stmt.cond);
DUMPAST(ast->if_stmt.then_body);
DUMPAST(ast->if_stmt.else_body);
DUMPEND();
case AST_SWITCH_STMT:
DUMP("(switch");
DUMPAST(ast->switch_stmt.cond);
DUMPEXPR(ast->switch_stmt.cond);
DUMPASTS(ast->switch_stmt.cases);
DUMPEND();
case AST_CASE_STMT:
DUMP("(case");
DUMPEXPR(ast->case_stmt.expr);
if (ast->case_stmt.is_type)
{
DUMPTI(ast->case_stmt.type_info);
}
else
{
DUMPEXPR(ast->case_stmt.expr);
}
DUMPAST(ast->case_stmt.body);
DUMPEND();
case AST_DEFER_STMT:
DUMP("(defer");
DUMPAST(ast->defer_stmt.body);
DUMPEND();
case AST_GENERIC_CASE_STMT:
DUMP("(generic-case");
indent++;
DUMP("(match");
VECEACH(ast->generic_case_stmt.types, i)
{
DUMPTI(ast->generic_case_stmt.types[i]);
}
DUMPE();
indent--;
DUMPAST(ast->generic_case_stmt.body);
DUMPEND();
case AST_GENERIC_DEFAULT_STMT:
DUMP("(generic-default");
DUMPAST(ast->generic_default_stmt);
DUMPEND();
case AST_POISONED:
DUMP("(ast-poisoned)");
return;
@@ -1033,7 +1054,7 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent)
DUMPEND();
case AST_CATCH_STMT:
DUMP("(catch");
DUMPDECL(ast->catch_stmt.error_param);
DUMPEXPR(ast->catch_stmt.catchable);
DUMPAST(ast->catch_stmt.body);
DUMPEND();
case AST_CT_IF_STMT:
@@ -1057,26 +1078,9 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent)
DUMPEXPR(ast->ct_switch_stmt.cond);
DUMPASTS(ast->ct_switch_stmt.body);
DUMPEND();
case AST_CT_CASE_STMT:
DUMP("(ct-case");
TODO
case AST_CT_DEFAULT_STMT:
DUMP("(ct-default");
DUMPAST(ast->ct_default_stmt);
DUMPEND();
case AST_GOTO_STMT:
DUMPF("(goto %s)", ast->goto_stmt.label_name);
return;
case AST_LABEL:
DUMPF("(label %s)", ast->label_stmt.name);
return;
case AST_NOP_STMT:
DUMP("(nop)");
return;
case AST_THROW_STMT:
DUMP("(throw");
DUMPEXPR(ast->throw_stmt.throw_value);
DUMPEND();
case AST_VOLATILE_STMT:
DUMP("(volatile");
DUMPAST(ast->volatile_stmt);

View File

@@ -101,6 +101,7 @@ void compiler_compile(BuildTarget *target)
Context *context = contexts[i];
llvm_codegen(context);
}
print_arena_status();
exit(EXIT_SUCCESS);
}

View File

@@ -1,4 +1,7 @@
#pragma once
#pragma clang diagnostic push
#pragma ide diagnostic ignored "bugprone-reserved-identifier"
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the GNU LGPLv3.0 license
// a copy of which can be found in the LICENSE file.
@@ -16,6 +19,7 @@ typedef uint32_t SourceLoc;
#define INVALID_RANGE ((SourceRange){ .loc = UINT32_MAX })
#define EMPTY_TOKEN ((Token) { .string = NULL })
#define MAX_LOCALS 0xFFFF
#define MAX_FLAGS 0xFFFF
#define MAX_SCOPE_DEPTH 0xFF
#define MAX_PATH 1024
#define MAX_DEFERS 0xFFFF
@@ -56,7 +60,6 @@ typedef struct
int len;
} string;
Decl *enum_constant;
Decl *error_constant;
};
// Valid type kinds:
// bool, ints, floats, string
@@ -232,12 +235,6 @@ typedef struct
};
} Attr;
typedef struct
{
Decl **error_constants;
void *start_value;
} ErrorDecl;
typedef struct
{
Path *path;
@@ -255,12 +252,18 @@ typedef struct
typedef struct _VarDecl
{
unsigned id : 16;
VarDeclKind kind : 3;
bool constant : 1;
bool failable : 1;
bool unwrap : 1;
TypeInfo *type_info;
Expr *init_expr;
union
{
Expr *init_expr;
Decl *alias;
};
void *backend_debug_ref;
void *failable_ref;
} VarDecl;
@@ -292,25 +295,15 @@ typedef struct
TypeInfo *type_info;
} EnumDecl;
typedef enum
{
ERROR_RETURN_NONE = 0,
ERROR_RETURN_ONE = 1,
ERROR_RETURN_MANY = 2,
ERROR_RETURN_ANY = 3,
} ErrorReturn;
typedef struct _FunctionSignature
{
CallConvention convention : 4;
bool variadic : 1;
bool has_default : 1;
bool throw_any : 1;
bool return_param : 1;
ErrorReturn error_return : 4;
bool failable : 1;
TypeInfo *rtype;
Decl** params;
TypeInfo** throws;
const char *mangled_signature;
} FunctionSignature;
@@ -362,6 +355,7 @@ typedef struct
typedef struct
{
bool failable : 1;
Decl **parameters;
TypeInfo *rtype; // May be null!
struct _Ast *body;
@@ -375,6 +369,17 @@ typedef struct
Path *path; // For redefinition
} GenericDecl;
typedef struct
{
Ast *defer;
void *break_target;
void *continue_target;
unsigned scope_id;
bool next_target : 1;
Ast *parent;
} LabelDecl;
typedef struct
{
unsigned index : 32;
@@ -422,7 +427,6 @@ typedef struct _Decl
};
union
{
ErrorDecl error;
StructDecl strukt;
EnumDecl enums;
};
@@ -430,6 +434,7 @@ typedef struct _Decl
ErrorConstantDecl error_constant;
ImportDecl import;
VarDecl var;
LabelDecl label;
EnumConstantDecl enum_constant;
FuncDecl func;
AttrDecl attr;
@@ -440,34 +445,23 @@ typedef struct _Decl
CtIfDecl ct_elif_decl;
Decl** ct_else_decl;
Expr *incr_array_decl;
TypeInfo *throws;
MemberDecl member_decl;
};
} Decl;
typedef enum
{
TRY_EXPR_ELSE_EXPR,
TRY_EXPR_ELSE_JUMP,
TRY_EXPR,
TRY_STMT,
} TryType;
typedef struct
{
TryType type;
union
{
Expr *expr;
Ast *stmt;
};
bool is_jump : 1;
Expr *expr;
union
{
Expr *else_expr;
Ast *else_stmt;
};
void *jump_target;
} ExprTry;
} ExprElse;
typedef struct
{
@@ -497,6 +491,8 @@ typedef struct
Expr *left;
Expr *right;
BinaryOp operator;
bool left_maybe : 1;
bool right_maybe : 1;
} ExprBinary;
typedef struct
@@ -505,6 +501,7 @@ typedef struct
UnaryOp operator;
} ExprUnary;
typedef struct
{
Expr* expr;
@@ -512,33 +509,7 @@ typedef struct
} ExprPostUnary;
typedef enum
{
CATCH_TRY_ELSE,
CATCH_REGULAR,
CATCH_RETURN_ANY,
CATCH_RETURN_MANY,
CATCH_RETURN_ONE
} CatchKind;
typedef struct
{
CatchKind kind;
union
{
Expr *try_else;
Ast *catch;
Decl *error;
};
Ast *defer;
} CatchInfo;
typedef struct
{
bool is_completely_handled;
Ast *defer;
CatchInfo *catches;
} ThrowInfo;
typedef struct
{
@@ -546,7 +517,6 @@ typedef struct
bool is_pointer_call : 1;
Expr *function;
Expr **arguments;
ThrowInfo *throw_info;
} ExprCall;
typedef struct
@@ -636,10 +606,17 @@ typedef struct
Expr* value;
} ExprDesignatedInit;
typedef struct
{
const char *name;
SourceRange span;
} Label;
struct _Expr
{
ExprKind expr_kind : 8;
ResolveStatus resolve_status : 3;
bool failable : 1;
SourceRange span;
Type *type;
union {
@@ -651,9 +628,12 @@ struct _Expr
ExprRange range_expr;
ExprStructValue struct_value_expr;
ExprTypeAccess type_access;
ExprTry try_expr;
Expr *guard_expr;
Expr *trycatch_expr;
ExprElse else_expr;
ExprBinary binary_expr;
ExprTernary ternary_expr;
Expr *fail_check_expr;
ExprUnary unary_expr;
ExprPostUnary post_expr;
ExprCall call_expr;
@@ -667,14 +647,11 @@ struct _Expr
ExprScope expr_scope;
ExprFuncBlock expr_block;
ExprMacroBlock macro_block;
Expr* failable_expr;
Ast** dexpr_list_expr;
};
};
typedef struct
{
} AstAttribute;
typedef struct
{
@@ -682,15 +659,6 @@ typedef struct
DeferList defer_list;
} AstCompoundStmt;
typedef struct
{
const char *name;
uint16_t last_goto;
bool is_used : 1;
Ast *defer;
struct _Ast *in_defer;
void *backend_value;
} AstLabelStmt;
typedef struct
{
@@ -700,79 +668,104 @@ typedef struct
typedef struct
{
Ast *decl;
Ast *cond;
Decl *label;
Expr *cond;
Ast *body;
void *break_block;
void *continue_block;
} AstWhileStmt;
typedef struct
{
Expr *expr;
Ast *body;
DeferList expr_defer;
DeferList body_defer;
union
{
struct
{
void *break_block;
void *continue_block;
};
struct
{
Decl *label;
Expr *expr;
Ast *body;
};
};
} AstDoStmt;
typedef struct
{
Ast *decl;
Ast *cond;
Decl *label;
Expr *cond;
Ast *then_body;
Ast *else_body;
void *break_block;
} AstIfStmt;
typedef struct
{
Expr *expr; // NULL == DEFAULT
bool is_type;
union
{
TypeInfo *type_info;
Expr *expr;
};
Ast *body;
void *backend_value;
void *backend_block;
} AstCaseStmt;
typedef struct
{
Ast *decl;
Ast *cond;
Decl *label;
Expr *cond;
Ast **cases;
void *retry_block;
void *exit_block;
void *retry_var;
Ast *defer;
} AstSwitchStmt;
typedef struct
{
Ast *init;
Decl *label;
Expr *init;
Expr *cond;
Expr *incr;
Ast *body;
void *continue_block;
void *exit_block;
} AstForStmt;
typedef struct
{
const char *label_name;
Ast *label;
DeferList defer;
union
{
struct _Ast *in_defer;
};
} AstGotoStmt;
typedef struct _AstDeferStmt
{
bool emit_boolean : 1;
Ast *body; // Compound statement
Ast *prev_defer;
void *bool_var;
} AstDeferStmt;
typedef struct _AstCatchStmt
typedef struct
{
Decl *error_param;
struct _Ast *body;
bool is_switch : 1;
bool has_err_var : 1;
Decl *label;
union
{
Expr *catchable;
Decl *err_var;
};
union
{
Ast *body;
Ast **cases;
};
void *block;
Ast *defer;
} AstCatchStmt;
typedef struct _AstCtIfStmt
{
Expr *expr;
@@ -781,12 +774,6 @@ typedef struct _AstCtIfStmt
} AstCtIfStmt;
typedef struct _AstGenericCaseStmt
{
TypeInfo **types;
struct _Ast *body;
} AstGenericCaseStmt;
typedef struct
{
Ast *stmt;
@@ -799,11 +786,6 @@ typedef struct
Ast **body;
} AstCtSwitchStmt;
typedef struct
{
TypeInfo **types;
Ast *body;
} AstCtCaseStmt;
typedef struct
{
@@ -815,38 +797,67 @@ typedef struct
typedef struct
{
bool is_label;
union
{
Label label;
Ast *ast;
};
DeferList defers;
} AstContinueBreakStmt;
typedef struct
{
Ast *prev;
DeferList defers;
union
{
struct
{
Label label;
bool is_type;
union
{
Expr *target;
TypeInfo *type_info;
};
};
struct
{
Ast *case_switch_stmt;
Expr *switch_expr;
};
};
} AstNextStmt;
typedef struct
{
Expr *throw_value;
Ast *defer;
} AstThrowStmt;
typedef struct
{
Token name;
Expr *expr;
Token alias;
Token constraints;
Token token;
} AsmOperand;
typedef struct
{
Expr *decl_expr;
Ast *body;
} AstTryStmt;
typedef struct
{
AsmOperand *inputs;
AsmOperand *outputs;
Token *clobbers;
Token *labels;
} AsmParams;
typedef struct
{
bool is_volatile : 1;
bool is_inline : 1;
bool is_goto : 1;
Expr *asm_template;
AsmOperand** output_operands;
AsmOperand** input_operands;
AsmOperand** clobbers;
Token* labels;
AsmParams *params;
Token *instructions;
} AstAsmStmt;
typedef struct _Ast
@@ -855,42 +866,35 @@ typedef struct _Ast
AstKind ast_kind : 8;
union
{
AstAttribute attribute;
AstAsmStmt asm_stmt;
AstCompoundStmt compound_stmt;
Decl *declare_stmt;
Expr *expr_stmt;
AstThrowStmt throw_stmt;
Ast *volatile_stmt;
AstLabelStmt label_stmt;
AstReturnStmt return_stmt;
AstWhileStmt while_stmt;
AstDoStmt do_stmt;
AstIfStmt if_stmt;
AstDeferStmt defer_stmt;
AstSwitchStmt switch_stmt;
AstCaseStmt case_stmt;
AstCtSwitchStmt ct_switch_stmt;
AstCtCaseStmt ct_case_stmt;
AstContinueBreakStmt continue_stmt;
AstContinueBreakStmt break_stmt;
Ast* ct_default_stmt;
AstNextStmt next_stmt;
AstCatchStmt catch_stmt;
AstGotoStmt goto_stmt;
AstForStmt for_stmt;
AstCtIfStmt ct_if_stmt;
AstCtIfStmt ct_elif_stmt;
Ast *ct_else_stmt;
AstCtForStmt ct_for_stmt;
AstGenericCaseStmt generic_case_stmt;
Ast *generic_default_stmt;
Ast** decl_expr_stmt;
AstScopedStmt scoped_stmt;
AstAsmStmt asm_stmt; // 24
AstCompoundStmt compound_stmt; // 16
Decl *declare_stmt; // 8
Expr *expr_stmt; // 8
AstTryStmt try_stmt;
Decl *define_stmt; // 8
Ast *volatile_stmt; // 8
AstReturnStmt return_stmt; // 16
AstWhileStmt while_stmt; // 24
AstDoStmt do_stmt; // 32
AstIfStmt if_stmt; // 32
AstDeferStmt defer_stmt; // 32
AstSwitchStmt switch_stmt; // 24
AstCaseStmt case_stmt; // 32
AstCtSwitchStmt ct_switch_stmt; // 16
AstContinueBreakStmt contbreak_stmt; // 8
AstNextStmt next_stmt; // 16
AstCatchStmt catch_stmt; // 32
AstForStmt for_stmt; // 32
AstCtIfStmt ct_if_stmt; // 24
AstCtIfStmt ct_elif_stmt; // 24
Ast *ct_else_stmt; // 8
AstCtForStmt ct_for_stmt; // 64
AstScopedStmt scoped_stmt; // 16
};
} Ast;
typedef struct _Module
{
Path *name;
@@ -909,14 +913,17 @@ typedef struct _Module
} Module;
typedef struct _DynamicScope
{
unsigned scope_id;
ScopeFlags flags;
ScopeFlags flags_created;
unsigned throws;
Decl **local_decl_start;
DeferList defers;
Ast *current_defer;
ExitType exit;
const char* label;
} DynamicScope;
@@ -928,37 +935,21 @@ typedef struct
uint16_t source_file;
File *current_file;
SourceLoc last_in_range;
Token tok;
Token next_tok;
} Lexer;
typedef enum
{
THROW_TYPE_CALL_ANY,
THROW_TYPE_CALL_THROW_MANY,
THROW_TYPE_CALL_THROW_ONE,
} ThrowType;
typedef struct
{
SourceRange span;
ThrowType kind : 4;
ThrowInfo *throw_info;
TypeInfo **throws;
} Throw;
typedef struct _Context
{
unsigned numbering;
unsigned current_block;
BuildTarget *target;
Path *module_name;
Token* module_parameters;
File* file;
Decl** imports;
Decl *specified_imports;
Module *module;
STable local_symbols;
Decl **enums;
Decl **error_types;
Decl **types;
Decl **functions;
Decl **methods;
@@ -966,31 +957,30 @@ typedef struct _Context
Decl **ct_ifs;
Ast **defers;
Decl *active_function_for_analysis;
Decl *active_type_for_analysis;
Decl **last_local;
Ast **labels;
Ast **gotos;
Token *comments;
Token *lead_comment;
Token *trailing_comment;
Token *next_lead_comment;
unsigned scope_id;
Ast *break_target;
Ast *break_defer;
Ast *continue_target;
Ast *continue_defer;
Ast *next_target;
Ast *next_switch;
Ast *next_defer;
DynamicScope *current_scope;
struct
{
Type *expected_block_type;
Ast **returns;
bool expr_failable_return;
// Reusable returns cache.
Ast **returns_cache;
};
Decl *evaluating_macro;
// Error handling
struct
{
Ast **throw;
Throw *error_calls;
int try_nesting;
};
Type *rtype;
bool failable_return;
int in_volatile_section;
struct
{
@@ -1000,7 +990,8 @@ typedef struct _Context
int macro_counter;
int macro_nesting;
};
Decl *locals[MAX_LOCALS];
Decl* locals[MAX_LOCALS];
Decl **last_local;
DynamicScope scopes[MAX_SCOPE_DEPTH];
char path_scratch[MAX_PATH];
struct {
@@ -1056,7 +1047,7 @@ extern Type *type_byte, *type_ushort, *type_uint, *type_ulong, *type_usize;
extern Type *type_compint, *type_compfloat;
extern Type *type_c_short, *type_c_int, *type_c_long, *type_c_longlong;
extern Type *type_c_ushort, *type_c_uint, *type_c_ulong, *type_c_ulonglong;
extern Type *type_typeid, *type_error_union, *type_error_base;
extern Type *type_typeid, *type_error;
extern const char *attribute_list[NUMBER_OF_ATTRIBUTES];
@@ -1072,13 +1063,13 @@ static inline bool ast_poison(Ast *ast) { ast->ast_kind = AST_POISONED; return f
static inline Ast *new_ast(AstKind kind, SourceRange range)
{
Ast *ast = malloc_arena(sizeof(Ast));
memset(ast, 0, sizeof(Ast));
Ast *ast = CALLOCS(Ast);
ast->span = range;
ast->ast_kind = kind;
return ast;
}
static inline Ast *extend_ast(Ast *ast, Token token)
{
ast->span.end_loc = token.span.end_loc;
@@ -1140,14 +1131,15 @@ bool cast_implicit(Expr *expr, Type *to_type);
bool cast(Expr *expr, Type *to_type, CastType cast_type);
bool cast_binary_arithmetic(Expr *left, Expr *right, const char *action);
CastKind cast_to_bool_kind(Type *type);
bool cast_to_runtime(Expr *expr);
void cast_to_smallest_runtime(Expr *expr);
bool cast_implicitly_to_runtime(Expr *expr);
void llvm_codegen(Context *context);
void llvm_codegen_setup();
bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr);
bool sema_expr_analyse_assign_right_side(Context *context, Expr *expr, Type *left_type, Expr *right, ExprFailableStatus lhs_is_failable);
bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr, bool may_be_failable);
bool sema_analyse_expr(Context *context, Type *to, Expr *expr);
bool sema_analyse_decl(Context *context, Decl *decl);
@@ -1171,10 +1163,21 @@ Decl *decl_new_with_type(Token name, DeclKind decl_type, Visibility visibility);
Decl *decl_new_var(Token name, TypeInfo *type, VarDeclKind kind, Visibility visibility);
void decl_set_external_name(Decl *decl);
const char *decl_var_to_string(VarDeclKind kind);
static inline Decl *decl_raw(Decl *decl)
{
if (decl->decl_kind != DECL_VAR || decl->var.kind != VARDECL_ALIAS) return decl;
decl = decl->var.alias;
assert(decl->decl_kind != DECL_VAR || decl->var.kind != VARDECL_ALIAS);
return decl;
}
static inline bool decl_ok(Decl *decl) { return decl->decl_kind != DECL_POISONED; }
static inline bool decl_ok(Decl *decl) { return !decl || decl->decl_kind != DECL_POISONED; }
static inline bool decl_poison(Decl *decl) { decl->decl_kind = DECL_POISONED; decl->resolve_status = RESOLVE_DONE; return false; }
static inline bool decl_is_struct_type(Decl *decl) { return decl->decl_kind == DECL_UNION || decl->decl_kind == DECL_STRUCT; }
static inline bool decl_is_struct_type(Decl *decl)
{
DeclKind kind = decl->decl_kind;
return kind == DECL_UNION | kind == DECL_STRUCT | kind == DECL_ERR;
}
static inline DeclKind decl_from_token(TokenType type)
{
if (type == TOKEN_STRUCT) return DECL_STRUCT;
@@ -1216,6 +1219,8 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent);
#pragma mark --- Lexer functions
Token lexer_scan_asm(Lexer *lexer);
Token lexer_scan_asm_constraint(Lexer *lexer);
Token lexer_scan_token(Lexer *lexer);
Token lexer_scan_ident_test(Lexer *lexer, const char *scan);
void lexer_init_for_test(Lexer *lexer, const char *text, size_t len);
@@ -1232,14 +1237,17 @@ Path *path_find_parent_path(Path *path);
const char *resolve_status_to_string(ResolveStatus status);
#define SEMA_TOKEN_ERROR(_tok, ...) sema_error_range(_tok.span, __VA_ARGS__)
#define SEMA_ERROR(_node, ...) sema_error_range(_node->span, __VA_ARGS__)
#define SEMA_PREV(_node, ...) sema_prev_at_range(_node->span, __VA_ARGS__)
#define SEMA_TOKEN_ERROR(_tok, ...) sema_error_range((_tok).span, __VA_ARGS__)
#define SEMA_ERROR(_node, ...) sema_error_range((_node)->span, __VA_ARGS__)
#define SEMA_PREV(_node, ...) sema_prev_at_range((_node)->span, __VA_ARGS__)
void sema_analysis_pass_process_imports(Context *context);
void sema_analysis_pass_conditional_compilation(Context *context);
void sema_analysis_pass_decls(Context *context);
bool sema_add_local(Context *context, Decl *decl);
bool sema_unwrap_var(Context *context, Decl *decl);
bool sema_rewrap_var(Context *context, Decl *decl);
bool sema_analyse_statement(Context *context, Ast *statement);
Decl *sema_resolve_symbol(Context *context, const char *symbol, Path *path, Decl **ambiguous_other_decl);
bool sema_resolve_type_info(Context *context, TypeInfo *type_info);
@@ -1277,19 +1285,10 @@ void *target_data_layout();
void *target_machine();
void *target_target();
bool throw_completely_caught(TypeInfo *throw, CatchInfo *catches);
static inline Throw throw_new_union(SourceRange range, ThrowType type, ThrowInfo *info)
{
return (Throw) { .kind = THROW_TYPE_CALL_ANY, .span = range, .throw_info = info };
}
static inline Throw throw_new(SourceRange range, ThrowType type, ThrowInfo *info, TypeInfo **throws)
{
return (Throw) { .kind = type, .span = range, .throw_info = info, .throws = throws };
}
#define TOKEN_MAX_LENGTH 0xFFFF
#define TOK2VARSTR(_token) _token.span.length, _token.start
bool token_is_type(TokenType type);
bool token_is_any_type(TokenType type);
bool token_is_symbol(TokenType type);
const char *token_type_to_string(TokenType type);
static inline Token wrap(const char *string)
@@ -1308,11 +1307,10 @@ Type *type_unsigned_int_by_bitsize(unsigned bytesize);
bool type_is_subtype(Type *type, Type *possible_subtype);
Type *type_find_common_ancestor(Type *left, Type *right);
const char *type_to_error_string(Type *type);
size_t type_size(Type *canonical);
unsigned int type_abi_alignment(Type *canonical);
size_t type_size(Type *type);
unsigned int type_abi_alignment(Type *type);
void type_append_signature_name(Type *type, char *dst, size_t *offset);
Type *type_find_max_type(Type *type, Type *other);
static inline bool type_is_builtin(TypeKind kind) { return kind >= TYPE_VOID && kind <= TYPE_FXX; }
static inline bool type_kind_is_signed(TypeKind kind) { return kind >= TYPE_I8 && kind <= TYPE_I64; }
static inline bool type_kind_is_unsigned(TypeKind kind) { return kind >= TYPE_U8 && kind <= TYPE_U64; }
@@ -1327,7 +1325,6 @@ static inline Type *type_reduced(Type *type)
{
Type *canonical = type->canonical;
if (canonical->type_kind == TYPE_ENUM) return canonical->decl->enums.type_info->type->canonical;
if (canonical->type_kind == TYPE_ERROR) return type_error_base;
return canonical;
}
@@ -1382,6 +1379,24 @@ static inline bool type_info_poison(TypeInfo *type)
return false;
}
static inline bool type_is_ct(Type *type)
{
while (1)
{
switch (type->type_kind)
{
case TYPE_FXX:
case TYPE_IXX:
return true;
case TYPE_TYPEDEF:
type = type->canonical;
break;
default:
return false;
}
}
}
static inline bool type_is_float(Type *type)
{
assert(type == type->canonical);
@@ -1390,8 +1405,7 @@ static inline bool type_is_float(Type *type)
static inline TypeInfo *type_info_new(TypeInfoKind kind, SourceRange range)
{
TypeInfo *type_info = malloc_arena(sizeof(TypeInfo));
memset(type_info, 0, sizeof(TypeInfo));
TypeInfo *type_info = CALLOCS(TypeInfo);
type_info->kind = kind;
type_info->span = range;
type_info->resolve_status = RESOLVE_NOT_DONE;
@@ -1400,8 +1414,7 @@ static inline TypeInfo *type_info_new(TypeInfoKind kind, SourceRange range)
static inline TypeInfo *type_info_new_base(Type *type, SourceRange range)
{
TypeInfo *type_info = malloc_arena(sizeof(TypeInfo));
memset(type_info, 0, sizeof(TypeInfo));
TypeInfo *type_info = CALLOCS(TypeInfo);
type_info->kind = TYPE_INFO_IDENTIFIER;
type_info->resolve_status = RESOLVE_DONE;
type_info->type = type;
@@ -1429,7 +1442,7 @@ static inline bool type_convert_will_trunc(Type *destination, Type *source)
static inline bool type_is_numeric(Type *type)
{
assert(type == type->canonical);
if (type->type_kind == TYPE_TYPEDEF) type = type->canonical;
return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_FXX;
}
@@ -1463,4 +1476,5 @@ static inline void advance_and_verify(Context *context, TokenType token_type)
{
assert(context->tok.type == token_type);
advance(context);
}
}
#pragma clang diagnostic pop

View File

@@ -113,6 +113,7 @@ void context_register_global_decl(Context *context, Decl *decl)
case DECL_STRUCT:
case DECL_UNION:
case DECL_TYPEDEF:
case DECL_ERR:
vec_add(context->types, decl);
decl_set_external_name(decl);
break;
@@ -120,18 +121,14 @@ void context_register_global_decl(Context *context, Decl *decl)
vec_add(context->enums, decl);
decl_set_external_name(decl);
break;
case DECL_ERROR:
vec_add(context->error_types, decl);
decl_set_external_name(decl);
break;
case DECL_ENUM_CONSTANT:
case DECL_ERROR_CONSTANT:
case DECL_ARRAY_VALUE:
case DECL_IMPORT:
case DECL_CT_ELSE:
case DECL_CT_ELIF:
case DECL_ATTRIBUTE:
case DECL_MEMBER:
case DECL_LABEL:
UNREACHABLE
break;
case DECL_CT_IF:
@@ -208,10 +205,6 @@ void context_print_ast(Context *context, FILE *file)
{
fprint_decl(file, context->types[i]);
}
VECEACH(context->error_types, i)
{
fprint_decl(file, context->error_types[i]);
}
VECEACH(context->functions, i)
{
fprint_decl(file, context->functions[i]);

View File

@@ -4,24 +4,6 @@
// Only include this from compiler_common.h
typedef enum
{
ASSIGNOP_ERROR,
ASSIGNOP_ASSIGN,
ASSIGNOP_MULT_ASSIGN,
ASSIGNOP_ADD_ASSIGN,
ASSIGNOP_SUB_ASSIGN,
ASSIGNOP_DIV_ASSIGN,
ASSIGNOP_MOD_ASSIGN,
ASSIGNOP_AND_ASSIGN,
ASSIGNOP_OR_ASSIGN,
ASSIGNOP_BIT_AND_ASSIGN,
ASSIGNOP_BIT_OR_ASSIGN,
ASSIGNOP_BIT_XOR_ASSIGN,
ASSIGNOP_SHR_ASSIGN,
ASSIGNOP_SHL_ASSIGN,
} AssignOp;
typedef enum
{
BINARYOP_ERROR,
@@ -74,29 +56,23 @@ typedef enum
AST_CATCH_STMT,
AST_COMPOUND_STMT,
AST_CONTINUE_STMT,
AST_DEFINE_STMT,
AST_CT_IF_STMT,
AST_CT_ELIF_STMT,
AST_CT_ELSE_STMT,
AST_CT_FOR_STMT,
AST_CT_SWITCH_STMT,
AST_CT_DEFAULT_STMT,
AST_CT_CASE_STMT,
AST_DECLARE_STMT,
AST_DEFAULT_STMT,
AST_DEFER_STMT,
AST_DO_STMT,
AST_EXPR_STMT,
AST_TRY_STMT,
AST_FOR_STMT,
AST_GENERIC_CASE_STMT,
AST_GENERIC_DEFAULT_STMT,
AST_GOTO_STMT,
AST_IF_STMT,
AST_LABEL,
AST_NOP_STMT,
AST_RETURN_STMT,
AST_DECL_EXPR_LIST,
AST_SWITCH_STMT,
AST_THROW_STMT,
AST_NEXT_STMT,
AST_VOLATILE_STMT,
AST_WHILE_STMT,
@@ -113,9 +89,9 @@ typedef enum
typedef enum
{
CAST_ERROR,
CAST_ERREU,
CAST_EUERR,
CAST_EUBOOL,
CAST_EUER,
CAST_EREU,
CAST_XIERR,
CAST_PTRPTR,
CAST_PTRXI,
@@ -140,34 +116,9 @@ typedef enum
CAST_ENUMSI,
CAST_APTSA,
CAST_SAPTR,
/*
CAST_NONE,
CAST_INLINE,
CAST_FAILED,
CAST_SUBTYPE,
CAST_VARRPTR,
CAST_STRPTR,
CAST_ARRPTR,
CAST_INTPTR,
CAST_PTRINT,
CAST_PTRBOOL,
CAST_FPFP,
CAST_FPUI,
CAST_FPSI,
CAST_FPBOOL,
CAST_UIFP,
CAST_UIUI,
CAST_UISI,
CAST_SIFP,
CAST_SISI,
CAST_SIUI,
CAST_INTBOOL,
CAST_BOOLFP,
CAST_BOOLINT,
CAST_RVALUE,*/
} CastKind;
typedef enum _CastType
typedef enum
{
CAST_TYPE_EXPLICIT,
CAST_TYPE_IMPLICIT,
@@ -181,14 +132,14 @@ typedef enum
DECL_POISONED = 0,
DECL_FUNC,
DECL_VAR,
DECL_LABEL,
DECL_ENUM_CONSTANT,
DECL_TYPEDEF,
DECL_STRUCT,
DECL_MEMBER,
DECL_UNION,
DECL_ENUM,
DECL_ERROR,
DECL_ERROR_CONSTANT,
DECL_ERR,
DECL_ARRAY_VALUE,
DECL_IMPORT,
DECL_MACRO,
@@ -203,18 +154,19 @@ typedef enum
typedef enum
{
EXIT_NONE = 0,
EXIT_BREAK,
EXIT_NEXT,
EXIT_GOTO,
EXIT_CONTINUE,
EXIT_THROW,
EXIT_RETURN,
EXIT_BREAK = 1 << 1,
EXIT_NEXT = 1 << 2,
EXIT_CONTINUE = 1 << 5,
EXIT_RETURN = 1 << 6,
} ExitType;
typedef enum
{
EXPR_POISONED,
EXPR_GUARD,
EXPR_TRY,
EXPR_CATCH,
EXPR_ELSE,
EXPR_CONST,
EXPR_BINARY,
EXPR_TERNARY,
@@ -237,16 +189,24 @@ typedef enum
EXPR_RANGE,
EXPR_DESIGNATED_INITIALIZER,
EXPR_COMPOUND_LITERAL,
EXPR_FAILABLE,
EXPR_FAIL_CHECK,
EXPR_DECL_LIST
} ExprKind;
typedef enum
{
FAILABLE_NO,
FAILABLE_YES,
FAILABLE_UNWRAPPED
} ExprFailableStatus;
typedef enum
{
PREC_NONE,
PREC_ASSIGNMENT, // =, *=, /=, %=, +=, etc
PREC_TRY, // try
PREC_TRY_ELSE, // try and else
PREC_TERNARY, // ?:
PREC_RANGE, // ...
PREC_LOGICAL, // && ||
@@ -262,12 +222,10 @@ typedef enum
typedef enum
{
SCOPE_NONE = 0,
SCOPE_BREAK = 1 << 0,
SCOPE_CONTINUE = 1 << 1,
SCOPE_NEXT = 1 << 2,
SCOPE_DEFER = 1 << 3,
SCOPE_EXPR_BLOCK = 1 << 4,
SCOPE_MACRO = 1 << 5
SCOPE_DEFER = 1 << 4,
SCOPE_EXPR_BLOCK = 1 << 5,
SCOPE_MACRO = 1 << 6,
SCOPE_COND = 1 << 7,
} ScopeFlags;
typedef enum
@@ -314,7 +272,7 @@ typedef enum
TOKEN_LPAREN, // (
TOKEN_MINUS, // -
TOKEN_MOD, // %
TOKEN_NOT, // !
TOKEN_BANG, // !
TOKEN_OR, // |
TOKEN_PLUS, // +
TOKEN_QUESTION, // ?
@@ -350,6 +308,7 @@ typedef enum
TOKEN_SCOPE, // ::
TOKEN_SHR, // >>
TOKEN_SHL, // <<
TOKEN_BANGBANG, // !!
// Three or more
TOKEN_ELLIPSIS, // ...
@@ -390,11 +349,14 @@ typedef enum
// Literals.
TOKEN_IDENT, // Any normal ident.
TOKEN_CONST_IDENT, // Any purely upper case ident,
TOKEN_TYPE_IDENT, // Any ident on the format FooBar or __FooBar
// Asm
TOKEN_ASM_STRING,
TOKEN_ASM_CONSTRAINT,
// We want to parse $foo separately.
// Otherwise we allow things like "# foo" which would be pretty bad.
TOKEN_CT_IDENT, // $foobar
@@ -419,18 +381,18 @@ typedef enum
TOKEN_CATCH,
TOKEN_CONST,
TOKEN_CONTINUE,
TOKEN_DEFINE,
TOKEN_DEFAULT,
TOKEN_DEFER,
TOKEN_DO,
TOKEN_ELSE,
TOKEN_ENUM,
TOKEN_ERROR_TYPE,
TOKEN_ERR,
TOKEN_EXTERN,
TOKEN_FALSE,
TOKEN_FOR,
TOKEN_FUNC,
TOKEN_GENERIC,
TOKEN_GOTO,
TOKEN_IF,
TOKEN_IMPORT,
TOKEN_IN,
@@ -443,8 +405,6 @@ typedef enum
TOKEN_RETURN,
TOKEN_STRUCT,
TOKEN_SWITCH,
TOKEN_THROW,
TOKEN_THROWS,
TOKEN_TRUE,
TOKEN_TRY,
TOKEN_TYPEDEF,
@@ -454,7 +414,6 @@ typedef enum
TOKEN_VOLATILE,
TOKEN_WHILE,
TOKEN_TYPEOF,
TOKEN_ERRSET,
TOKEN_CT_CASE, // $case
TOKEN_CT_DEFAULT, // $default
@@ -498,11 +457,11 @@ typedef enum
TYPE_FXX,
TYPE_POINTER,
TYPE_ENUM,
TYPE_ERROR,
TYPE_FUNC,
TYPE_STRUCT,
TYPE_UNION,
TYPE_ERROR_UNION,
TYPE_ERRTYPE,
TYPE_ERR_UNION,
TYPE_TYPEDEF,
TYPE_STRING,
TYPE_ARRAY,
@@ -547,6 +506,9 @@ typedef enum
VARDECL_GLOBAL = 1,
VARDECL_LOCAL = 2,
VARDECL_PARAM = 3,
VARDECL_LOCAL_CT = 4,
VARDECL_LOCAL_CT_TYPE = 5,
VARDECL_ALIAS = 6,
} VarDeclKind;
typedef enum

View File

@@ -161,7 +161,7 @@ static inline Token parse_nested_comment(Lexer *lexer)
}
next(lexer);
}
if (reached_end(lexer))
if (nesting > 0)
{
return error_token(lexer, "Missing '+/' to end the nested comment.");
}
@@ -470,6 +470,102 @@ static inline Token scan_string(Lexer *lexer)
#pragma mark --- Lexer public functions
Token lexer_scan_asm_constraint(Lexer *lexer)
{
// Skip the whitespace.
skip_whitespace(lexer);
// Point start to the first non-whitespace character.
lexer->lexing_start = lexer->current;
if (reached_end(lexer))
{
return make_token(lexer, TOKEN_EOF, "\n");
}
// Move past '+=&'
char c;
while (1)
{
c = next(lexer);
if (c == '+' || c == '=' || c == '&') continue;
break;
}
while (1)
{
if (is_letter(c) || is_digit(c))
{
c = next(lexer);
continue;
}
if (c != ' ')
{
return error_token(lexer, "Invalid asm constraint");
}
break;
}
return make_token(lexer, TOKEN_ASM_CONSTRAINT, strcopy(lexer->lexing_start, lexer->current - lexer->lexing_start + 1));
}
Token lexer_scan_asm(Lexer *lexer)
{
// Skip the whitespace.
skip_whitespace(lexer);
// Point start to the first non-whitespace character.
lexer->lexing_start = lexer->current;
if (reached_end(lexer))
{
return make_token(lexer, TOKEN_EOF, "\n");
}
int bracket = 0;
const char* last_non_whitespace = lexer->lexing_start;
while (1)
{
char c = next(lexer);
switch (c)
{
case '\n':
break;
case ';':
while (!reached_end(lexer) && next(lexer) != '\n');
break;
case '\t':
case '\r':
case '\f':
case ' ':
continue;
case '{':
bracket++;
last_non_whitespace = lexer->current - 1;
continue;
case '}':
if (--bracket >= 0)
{
// Matched bracket.
last_non_whitespace = lexer->current - 1;
continue;
}
// Non matched right bracket.
// If this is the first non whitespace this is an end of asm.
if (lexer->lexing_start == lexer->current - 1)
{
return make_token(lexer, TOKEN_RBRACE, "}");
}
// Otherwise we need to return the previous as a token.
break;
default:
last_non_whitespace = lexer->current - 1;
continue;
}
return make_token(lexer, TOKEN_ASM_STRING, strcopy(lexer->lexing_start, last_non_whitespace - lexer->lexing_start + 1));
}
}
Token lexer_scan_token(Lexer *lexer)
{
// Now skip the whitespace.
@@ -520,7 +616,8 @@ Token lexer_scan_token(Lexer *lexer)
case ':':
return match(lexer, ':') ? make_token(lexer, TOKEN_SCOPE, "::") : make_token(lexer, TOKEN_COLON, ":");
case '!':
return match(lexer, '=') ? make_token(lexer, TOKEN_NOT_EQUAL, "!=") : make_token(lexer, TOKEN_NOT, "!");
if (match(lexer, '!')) return make_token(lexer, TOKEN_BANGBANG, "!!");
return match(lexer, '=') ? make_token(lexer, TOKEN_NOT_EQUAL, "!=") : make_token(lexer, TOKEN_BANG, "!");
case '/':
if (match(lexer, '/')) return parse_line_comment(lexer);
if (match(lexer, '*')) return parse_multiline_comment(lexer);

View File

@@ -52,10 +52,9 @@ LLVMValueRef gencontext_emit_memclear_size_align(GenContext *context, LLVMValueR
LLVMValueRef gencontext_emit_memclear(GenContext *context, LLVMValueRef ref, Type *type)
{
Type *canonical = type->canonical;
// TODO avoid bitcast on those that do not need them.
return gencontext_emit_memclear_size_align(context, ref, type_size(canonical),
type_abi_alignment(canonical), true);
return gencontext_emit_memclear_size_align(context, ref, type_size(type),
type_abi_alignment(type), true);
}
@@ -74,7 +73,7 @@ static void gencontext_emit_global_variable_definition(GenContext *context, Decl
}
else
{
LLVMSetInitializer(decl->ref, LLVMConstInt(llvm_type(type_bool), 0, false));
LLVMSetInitializer(decl->ref, LLVMConstNull(llvm_type(decl->type)));
}
// If read only: LLVMSetGlobalConstant(decl->var.backend_ref, 1);
@@ -116,8 +115,7 @@ static void gencontext_verify_ir(GenContext *context)
{
char *error = NULL;
assert(context->module);
LLVMVerifyModule(context->module, LLVMPrintMessageAction, &error);
if (error)
if (LLVMVerifyModule(context->module, LLVMPrintMessageAction, &error))
{
if (*error)
{
@@ -137,7 +135,7 @@ void gencontext_emit_object_file(GenContext *context)
// Generate .o or .obj file
char *filename = strformat("%.*s.o", (int)strlen(context->ast_context->file->name) - 3, context->ast_context->file->name);
if (LLVMTargetMachineEmitToFile(target_machine(), context->module, filename, LLVMObjectFile, &err) != 0)
if (LLVMTargetMachineEmitToFile(target_machine(), context->module, filename, LLVMObjectFile, &err))
{
error_exit("Could not emit object file: %s", err);
}
@@ -147,7 +145,7 @@ void gencontext_print_llvm_ir(GenContext *context)
{
char *err = NULL;
char *filename = strformat("%.*s.ll", (int)strlen(context->ast_context->file->name) - 3, context->ast_context->file->name);
if (LLVMPrintModuleToFile(context->module, filename, &err) != 0)
if (LLVMPrintModuleToFile(context->module, filename, &err))
{
error_exit("Could not emit ir to file: %s", err);
}
@@ -239,26 +237,25 @@ void llvm_codegen_setup()
intrinsics_setup = true;
}
void gencontext_emit_struct_decl(GenContext *context, Decl *decl)
void gencontext_emit_introspection_type(GenContext *context, Decl *decl)
{
llvm_type(decl->type);
LLVMValueRef global_name = LLVMAddGlobal(context->module, llvm_type(type_bool), decl->name);
LLVMSetLinkage(global_name, LLVMInternalLinkage);
LLVMValueRef global_name = LLVMAddGlobal(context->module, llvm_type(type_byte), decl->name);
LLVMSetGlobalConstant(global_name, 1);
LLVMSetInitializer(global_name, LLVMConstInt(llvm_type(type_bool), 1, false));
LLVMSetInitializer(global_name, LLVMConstInt(llvm_type(type_byte), 1, false));
decl->type->backend_typeid = LLVMBuildPtrToInt(context->builder, global_name, llvm_type(type_typeid), "");
decl->type->backend_typeid = global_name;
switch (decl->visibility)
{
case VISIBLE_MODULE:
LLVMSetVisibility(global_name, LLVMProtectedVisibility);
break;
case VISIBLE_PUBLIC:
LLVMSetLinkage(global_name, LLVMLinkOnceODRLinkage);
LLVMSetVisibility(global_name, LLVMDefaultVisibility);
break;
case VISIBLE_EXTERN:
case VISIBLE_LOCAL:
LLVMSetVisibility(global_name, LLVMHiddenVisibility);
LLVMSetLinkage(global_name, LLVMLinkerPrivateLinkage);
break;
}
}
@@ -275,35 +272,6 @@ static inline uint32_t upper_power_of_two(uint32_t v)
return v;
}
void gencontext_emit_error_decl(GenContext *context, Decl *decl)
{
unsigned slots = vec_size(decl->error.error_constants) + 1;
LLVMTypeRef reserved_type = LLVMArrayType(llvm_type(type_char), slots);
char *buffer = strcat_arena(decl->external_name, "_DOMAIN");
LLVMValueRef global_name = LLVMAddGlobal(context->module, reserved_type, buffer);
LLVMSetLinkage(global_name, LLVMExternalLinkage);
LLVMSetGlobalConstant(global_name, 1);
LLVMSetInitializer(global_name, LLVMConstAllOnes(reserved_type));
decl->error.start_value = LLVMBuildPtrToInt(context->builder, global_name, llvm_type(type_usize), "");
uint32_t min_align = upper_power_of_two(slots);
uint32_t pointer_align = type_abi_alignment(type_voidptr);
LLVMSetAlignment(global_name, pointer_align > min_align ? pointer_align : min_align);
LLVMSetVisibility(global_name, LLVMDefaultVisibility);
switch (decl->visibility)
{
case VISIBLE_MODULE:
LLVMSetVisibility(global_name, LLVMProtectedVisibility);
break;
case VISIBLE_PUBLIC:
LLVMSetVisibility(global_name, LLVMDefaultVisibility);
break;
case VISIBLE_EXTERN:
case VISIBLE_LOCAL:
LLVMSetVisibility(global_name, LLVMHiddenVisibility);
break;
}
}
static void gencontext_emit_decl(GenContext *context, Decl *decl)
{
@@ -324,17 +292,12 @@ static void gencontext_emit_decl(GenContext *context, Decl *decl)
break;;
case DECL_STRUCT:
case DECL_UNION:
gencontext_emit_struct_decl(context, decl);
case DECL_ERR:
gencontext_emit_introspection_type(context, decl);
break;
case DECL_ENUM:
// TODO
break;
case DECL_ERROR:
UNREACHABLE;
break;;
case DECL_ERROR_CONSTANT:
//TODO
break;;
case DECL_ARRAY_VALUE:
case DECL_IMPORT:
case DECL_MACRO:
@@ -344,6 +307,7 @@ static void gencontext_emit_decl(GenContext *context, Decl *decl)
case DECL_CT_ELIF:
case DECL_ATTRIBUTE:
case DECL_MEMBER:
case DECL_LABEL:
UNREACHABLE
}
}
@@ -371,10 +335,6 @@ void llvm_codegen(Context *context)
{
gencontext_emit_decl(&gen_context, context->types[i]);
}
VECEACH(context->error_types, i)
{
gencontext_emit_error_decl(&gen_context, context->error_types[i]);
}
VECEACH(context->vars, i)
{
gencontext_emit_global_variable_definition(&gen_context, context->vars[i]);
@@ -429,6 +389,8 @@ void llvm_codegen(Context *context)
// Serialize the LLVM IR, if requested
if (build_options.emit_llvm) gencontext_print_llvm_ir(&gen_context);
gencontext_verify_ir(&gen_context);
if (build_options.emit_bitcode) gencontext_emit_object_file(&gen_context);
gencontext_end_module(&gen_context);

View File

@@ -18,10 +18,10 @@ static inline LLVMMetadataRef gencontext_create_debug_type_from_decl(GenContext
case DECL_CT_ELSE:
case DECL_CT_ELIF:
case DECL_VAR:
case DECL_ERROR_CONSTANT:
case DECL_ARRAY_VALUE:
case DECL_IMPORT:
case DECL_MEMBER:
case DECL_LABEL:
UNREACHABLE;
case DECL_FUNC:
{
@@ -45,7 +45,7 @@ static inline LLVMMetadataRef gencontext_create_debug_type_from_decl(GenContext
TODO
case DECL_ENUM:
TODO
case DECL_ERROR:
case DECL_ERR:
TODO
}
UNREACHABLE
@@ -134,7 +134,7 @@ LLVMMetadataRef gencontext_get_debug_type(GenContext *context, Type *type)
return type->backend_debug_type = LLVMDIBuilderCreatePointerType(context->debug.builder, type->pointer->backend_debug_type, type_size(type->canonical->pointer), 0, 0, type->name, strlen(type->name));
case TYPE_ENUM:
TODO
case TYPE_ERROR:
case TYPE_ERRTYPE:
TODO
case TYPE_FUNC:
// TODO
@@ -168,7 +168,7 @@ LLVMMetadataRef gencontext_get_debug_type(GenContext *context, Type *type)
TODO
case TYPE_SUBARRAY:
TODO
case TYPE_ERROR_UNION:
case TYPE_ERR_UNION:
TODO
}
UNREACHABLE

View File

@@ -130,11 +130,17 @@ static LLVMValueRef gencontext_emit_member_addr(GenContext *context, LLVMValueRe
value = gencontext_emit_member_addr(context, value, parent, current_parent);
}
if (current_parent->type->canonical->type_kind == TYPE_UNION)
switch (current_parent->type->canonical->type_kind)
{
return LLVMBuildBitCast(context->builder, value, LLVMPointerType(llvm_type(member->type), 0), member->name);
case TYPE_UNION:
return LLVMBuildBitCast(context->builder, value, LLVMPointerType(llvm_type(member->type), 0), member->name);
case TYPE_ERRTYPE:
return LLVMBuildStructGEP2(context->builder, llvm_type(current_parent->type), value, member->member_decl.index + 1, member->name);
case TYPE_STRUCT:
return LLVMBuildStructGEP2(context->builder, llvm_type(current_parent->type), value, member->member_decl.index, member->name);
default:
UNREACHABLE
}
return LLVMBuildStructGEP2(context->builder, llvm_type(current_parent->type), value, member->member_decl.index, member->name);
}
@@ -168,15 +174,15 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr)
{
case EXPR_RANGE:
TODO
case EXPR_EXPR_BLOCK:
TODO
case EXPR_DESIGNATED_INITIALIZER:
// Should only appear when generating designated initializers.
UNREACHABLE
case EXPR_MACRO_BLOCK:
TODO
case EXPR_FAIL_CHECK:
UNREACHABLE
case EXPR_IDENTIFIER:
return expr->identifier_expr.decl->ref;
return decl_ref(expr->identifier_expr.decl);
case EXPR_UNARY:
assert(expr->unary_expr.operator == UNARYOP_DEREF);
return gencontext_emit_expr(context, expr->unary_expr.expr);
@@ -193,7 +199,7 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr)
case EXPR_CONST:
case EXPR_TYPEID:
case EXPR_POISONED:
case EXPR_TRY:
case EXPR_GUARD:
case EXPR_BINARY:
case EXPR_TERNARY:
case EXPR_POST_UNARY:
@@ -203,18 +209,17 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr)
case EXPR_EXPRESSION_LIST:
case EXPR_CAST:
case EXPR_TYPEOF:
case EXPR_FAILABLE:
case EXPR_CATCH:
case EXPR_TRY:
case EXPR_EXPR_BLOCK:
case EXPR_DECL_LIST:
case EXPR_ELSE:
UNREACHABLE
}
UNREACHABLE
}
static inline LLVMValueRef gencontext_emit_error_cast(GenContext *context, LLVMValueRef value, Type *type)
{
LLVMValueRef global = type->decl->error.start_value;
LLVMValueRef extend = LLVMBuildZExtOrBitCast(context->builder, value, llvm_type(type_usize), "");
return LLVMBuildAdd(context->builder, global, extend, "");
}
LLVMValueRef gencontext_emit_arr_to_subarray_cast(GenContext *context, LLVMValueRef value, Type *to_type, Type *from_type)
{
size_t size = from_type->pointer->array.len;
@@ -230,6 +235,13 @@ LLVMValueRef gencontext_emit_subarray_to_ptr_cast(GenContext *context, LLVMValue
return LLVMBuildExtractValue(context->builder, value, 0, "");
}
LLVMValueRef gencontext_emit_value_bitcast(GenContext *context, LLVMValueRef value, Type *to_type, Type *from_type)
{
LLVMValueRef ptr = gencontext_emit_alloca(context, llvm_type(from_type), "");
LLVMBuildStore(context->builder, value, ptr);
LLVMValueRef ptr_cast = gencontext_emit_bitcast(context, ptr, type_get_ptr(to_type));
return gencontext_emit_load(context, to_type, ptr_cast);
}
LLVMValueRef gencontext_emit_cast(GenContext *context, CastKind cast_kind, LLVMValueRef value, Type *to_type, Type *from_type)
{
switch (cast_kind)
@@ -253,14 +265,13 @@ LLVMValueRef gencontext_emit_cast(GenContext *context, CastKind cast_kind, LLVMV
TODO
case CAST_STRPTR:
TODO
case CAST_ERREU:
return gencontext_emit_error_cast(context, value, from_type);
case CAST_EUERR:
TODO
case CAST_EREU:
case CAST_EUER:
return gencontext_emit_value_bitcast(context, value, to_type, from_type);
case CAST_EUBOOL:
return LLVMBuildICmp(context->builder, LLVMIntNE, value, llvm_int(type_usize, 0), "eubool");
return LLVMBuildICmp(context->builder, LLVMIntNE, value, gencontext_emit_no_error_union(context), "eubool");
case CAST_PTRBOOL:
return LLVMBuildICmp(context->builder, LLVMIntNE, value, LLVMConstPointerNull(llvm_type(to_type->canonical->pointer)), "ptrbool");
return LLVMBuildICmp(context->builder, LLVMIntNE, value, LLVMConstPointerNull(llvm_type(to_type->canonical)), "ptrbool");
case CAST_BOOLINT:
return LLVMBuildTrunc(context->builder, value, llvm_type(to_type), "boolsi");
case CAST_FPBOOL:
@@ -330,6 +341,14 @@ static inline LLVMValueRef gencontext_emit_initializer_list_expr_addr(GenContext
gencontext_emit_memclear(context, ref, canonical);
}
bool is_error = expr->type->canonical->type_kind == TYPE_ERRTYPE;
if (is_error)
{
LLVMValueRef err_type = LLVMBuildStructGEP2(context->builder, type, ref, 0, "");
LLVMBuildStore(context->builder, expr->type->canonical->backend_typeid, err_type);
}
if (expr->expr_initializer.init_type == INITIALIZER_ZERO)
{
return ref;
@@ -338,6 +357,7 @@ static inline LLVMValueRef gencontext_emit_initializer_list_expr_addr(GenContext
Expr **elements = expr->expr_initializer.initializer_expr;
bool is_union = expr->type->canonical->type_kind == TYPE_UNION;
if (expr->expr_initializer.init_type == INITIALIZER_NORMAL)
{
if (is_union)
@@ -352,7 +372,7 @@ static inline LLVMValueRef gencontext_emit_initializer_list_expr_addr(GenContext
{
Expr *element = elements[i];
LLVMValueRef init_value = gencontext_emit_expr(context, element);
LLVMValueRef subref = LLVMBuildStructGEP2(context->builder, type, ref, i, "");
LLVMValueRef subref = LLVMBuildStructGEP2(context->builder, type, ref, i + (int)is_error, "");
LLVMBuildStore(context->builder, init_value, subref);
}
return ref;
@@ -360,6 +380,7 @@ static inline LLVMValueRef gencontext_emit_initializer_list_expr_addr(GenContext
VECEACH(elements, i)
{
if (is_error) TODO
Expr *element = elements[i];
DesignatedPath *path = element->designated_init_expr.path;
LLVMValueRef sub_value = gencontext_emit_expr(context, element->designated_init_expr.value);
@@ -455,8 +476,16 @@ LLVMValueRef gencontext_emit_unary_expr(GenContext *context, Expr *expr)
case UNARYOP_ERROR:
FATAL_ERROR("Illegal unary op %s", expr->unary_expr.operator);
case UNARYOP_NOT:
return LLVMBuildXor(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr),
llvm_int(type_bool, 1), "not");
if (type_is_float(type))
{
LLVMValueRef val = gencontext_emit_expr(context, expr->unary_expr.expr);
return LLVMBuildFCmp(context->builder, LLVMRealUNE, val, LLVMConstNull(llvm_type(type)), "");
}
else
{
LLVMValueRef val = gencontext_emit_expr(context, expr->unary_expr.expr);
return LLVMBuildICmp(context->builder, LLVMIntEQ, val, LLVMConstNull(llvm_type(type)), "");
}
case UNARYOP_BITNEG:
return LLVMBuildNot(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "bnot");
case UNARYOP_NEGMOD:
@@ -813,15 +842,6 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM
UNREACHABLE
}
LLVMBasicBlockRef gencontext_get_try_target(GenContext *context, Expr *try_expr)
{
if (!try_expr->try_expr.jump_target)
{
try_expr->try_expr.jump_target = gencontext_create_free_block(context, "tryjump");
}
return try_expr->try_expr.jump_target;
}
LLVMValueRef gencontext_emit_post_unary_expr(GenContext *context, Expr *expr)
{
@@ -838,41 +858,45 @@ LLVMValueRef gencontext_emit_typeid(GenContext *context, Expr *expr)
return expr->typeid_expr->type->backend_typeid;
}
LLVMValueRef gencontext_emit_try_expr(GenContext *context, Expr *expr)
LLVMValueRef gencontext_emit_trycatch_expr(GenContext *context, Expr *expr)
{
if (expr->try_expr.type == TRY_STMT)
{
gencontext_emit_stmt(context, expr->try_expr.stmt);
return NULL;
}
if (expr->try_expr.type == TRY_EXPR_ELSE_EXPR)
{
LLVMBasicBlockRef else_block = gencontext_get_try_target(context, expr);
LLVMBasicBlockRef after_catch = gencontext_create_free_block(context, "aftercatch");
LLVMValueRef res = gencontext_emit_alloca(context, llvm_type(expr->try_expr.else_expr->type), "catch");
LLVMValueRef normal_res = gencontext_emit_expr(context, expr->try_expr.expr);
LLVMBuildStore(context->builder, normal_res, res);
gencontext_emit_br(context, after_catch);
gencontext_emit_block(context, else_block);
LLVMValueRef catch_value = gencontext_emit_expr(context, expr->try_expr.else_expr);
LLVMBuildStore(context->builder, catch_value, res);
gencontext_emit_br(context, after_catch);
gencontext_emit_block(context, after_catch);
return gencontext_emit_load(context, expr->try_expr.else_expr->type, res);
}
if (expr->try_expr.type == TRY_EXPR_ELSE_JUMP)
{
LLVMBasicBlockRef else_block = gencontext_get_try_target(context, expr);
LLVMValueRef val = gencontext_emit_expr(context, expr->try_expr.expr);
LLVMBasicBlockRef after_catch = gencontext_create_free_block(context, "aftercatch");
gencontext_emit_br(context, after_catch);
gencontext_emit_block(context, else_block);
gencontext_emit_stmt(context, expr->try_expr.else_stmt);
gencontext_emit_br(context, after_catch);
gencontext_emit_block(context, after_catch);
return val;
}
return gencontext_emit_expr(context, expr->try_expr.expr);
LLVMBasicBlockRef error_block = gencontext_create_free_block(context, "error_block");
LLVMBasicBlockRef no_err_block = gencontext_create_free_block(context, "noerr_block");
LLVMBasicBlockRef phi_block = gencontext_create_free_block(context, "phi_block");
// Store catch/error var
PUSH_ERROR();
// Set the catch/error var
context->error_var = NULL;
context->catch_block = error_block;
gencontext_emit_expr(context, expr->trycatch_expr);
// Restore.
POP_ERROR();
// Emit success and jump to phi.
gencontext_emit_br(context, no_err_block);
gencontext_emit_block(context, no_err_block);
gencontext_emit_br(context, phi_block);
// Emit error and jump to phi
gencontext_emit_block(context, error_block);
gencontext_emit_br(context, phi_block);
gencontext_emit_block(context, phi_block);
LLVMValueRef phi = LLVMBuildPhi(context->builder, llvm_type(expr->type), "val");
LLVMValueRef lhs = llvm_int(type_bool, expr->expr_kind == EXPR_TRY ? 1 : 0);
LLVMValueRef rhs = llvm_int(type_bool, expr->expr_kind == EXPR_TRY ? 0 : 1);
LLVMValueRef logic_values[2] = { lhs, rhs };
LLVMBasicBlockRef blocks[2] = { no_err_block, error_block };
LLVMAddIncoming(phi, logic_values, blocks, 2);
return phi;
}
static LLVMValueRef gencontext_emit_binary_expr(GenContext *context, Expr *expr)
@@ -890,7 +914,12 @@ static LLVMValueRef gencontext_emit_binary_expr(GenContext *context, Expr *expr)
if (binary_op == BINARYOP_ASSIGN)
{
LLVMValueRef addr = gencontext_emit_address(context, expr->binary_expr.left);
return gencontext_emit_assign_expr(context, addr, expr->binary_expr.right);
LLVMValueRef failable_ref = NULL;
if (expr->binary_expr.left->expr_kind == EXPR_IDENTIFIER)
{
failable_ref = decl_failable_ref(expr->binary_expr.left->identifier_expr.decl);
}
return gencontext_emit_assign_expr(context, addr, expr->binary_expr.right, failable_ref);
}
return gencontext_emit_binary(context, expr, NULL, binary_op);
}
@@ -915,14 +944,17 @@ LLVMValueRef gencontext_emit_elvis_expr(GenContext *context, Expr *expr)
gencontext_emit_block(context, rhs_block);
LLVMValueRef rhs = gencontext_emit_expr(context, expr->ternary_expr.else_expr);
LLVMBasicBlockRef end_block = context->current_block;
gencontext_emit_br(context, phi_block);
// Generate phi
gencontext_emit_block(context, phi_block);
LLVMValueRef phi = LLVMBuildPhi(context->builder, llvm_type(expr->type), "val");
LLVMValueRef logic_values[2] = { lhs, rhs };
LLVMBasicBlockRef blocks[2] = { current_block, rhs_block };
LLVMBasicBlockRef blocks[2] = { current_block, end_block };
LLVMAddIncoming(phi, logic_values, blocks, 2);
return phi;
@@ -944,10 +976,12 @@ LLVMValueRef gencontext_emit_ternary_expr(GenContext *context, Expr *expr)
gencontext_emit_block(context, lhs_block);
LLVMValueRef lhs = gencontext_emit_expr(context, expr->ternary_expr.then_expr);
LLVMBasicBlockRef lhs_exit = context->current_block;
gencontext_emit_br(context, phi_block);
gencontext_emit_block(context, rhs_block);
LLVMValueRef rhs = gencontext_emit_expr(context, expr->ternary_expr.else_expr);
LLVMBasicBlockRef rhs_exit = context->current_block;
gencontext_emit_br(context, phi_block);
// Generate phi
@@ -955,7 +989,7 @@ LLVMValueRef gencontext_emit_ternary_expr(GenContext *context, Expr *expr)
LLVMValueRef phi = LLVMBuildPhi(context->builder, llvm_type(expr->type), "val");
LLVMValueRef logic_values[2] = { lhs, rhs };
LLVMBasicBlockRef blocks[2] = { lhs_block, rhs_block };
LLVMBasicBlockRef blocks[2] = { lhs_exit, rhs_exit };
LLVMAddIncoming(phi, logic_values, blocks, 2);
return phi;
@@ -993,271 +1027,12 @@ LLVMValueRef gencontext_emit_const_expr(GenContext *context, Expr *expr)
0));
return global_name;
}
case TYPE_ERROR:
return llvm_int(type_error_base, expr->const_expr.error_constant->error_constant.value);
case TYPE_ERRTYPE:
TODO
default:
UNREACHABLE
}
}
/*
static inline void gencontext_emit_throw_union_branch(GenContext *context, CatchInfo *catches, LLVMValueRef value)
{
LLVMBasicBlockRef after_block = gencontext_create_free_block(context, "throwafter");
type_error_union
value = LLVMBuildExtractValue(context->builder, value, 1, "");
LLVMValueRef comparison = LLVMBuildICmp(context->builder, LLVMIntNE, llvm_int(error_type, 0), value, "checkerr");
VECEACH(catches, i)
{
LLVMBasicBlockRef ret_err_block = gencontext_create_free_block(context, "ret_err");
gencontext_emit_cond_br(context, comparison, ret_err_block, after_block);
gencontext_emit_block(context, ret_err_block);
if (catches->kind == CATCH_TRY_ELSE)
{
}
}
if (error_type == type_error_union)
size_t catch_index = context->catch_stack_index;
while (catch_index > 0)
{
catch_index--;
// TODO check error
Catch *current_catch = &context->catch_stack[catch_index];
if (!current_catch->decl)
{
LLVMValueRef zero = llvm_int(type_reduced(type_error), 0);
// TODO emit defers.
if (error_type == type_error_union)
{
}
LLVMValueRef comparison = LLVMBuildICmp(context->builder, LLVMIntNE, zero, value, "checkerr");
gencontext_emit_cond_br(context, comparison, current_catch->catch_block, after_block);
gencontext_emit_block(context, after_block);
return;
}
}
// Fix defers gencontext_emit_defer(context, ast->throw_stmt.defers.start, ast->throw_stmt.defers.end);
if (error_type == type_error_union)
{
gencontext_emit_load(context, type_error_union, context->error_out);
TODO
}
LLVMBuildRet(context->builder, value);
context->current_block = NULL;
context->current_block_is_target = false;
gencontext_emit_block(context, after_block);
}
*/
static inline void gencontext_emit_throw_branch(GenContext *context, LLVMValueRef value, TypeInfo** errors, ThrowInfo *throw_info, ErrorReturn error_return)
{
Type *call_error_type;
switch (error_return)
{
case ERROR_RETURN_NONE:
// If there is no error return, this is a no-op.
return;
case ERROR_RETURN_ONE:
call_error_type = type_error_base;
break;
case ERROR_RETURN_MANY:
case ERROR_RETURN_ANY:
call_error_type = type_error_union;
break;;
}
LLVMBasicBlockRef after_block = gencontext_create_free_block(context, "throwafter");
LLVMValueRef comparison = LLVMBuildICmp(context->builder, LLVMIntNE, llvm_int(call_error_type, 0), value, "checkerr");
unsigned catch_count = vec_size(throw_info->catches);
assert(throw_info->is_completely_handled && catch_count);
// Special handling for a single catch.
if (catch_count == 1)
{
CatchInfo *catch = &throw_info->catches[0];
switch (catch->kind)
{
case CATCH_RETURN_ONE:
{
LLVMBasicBlockRef else_block = gencontext_create_free_block(context, "erret_one");
gencontext_emit_cond_br(context, comparison, else_block, after_block);
gencontext_emit_block(context, else_block);
gencontext_emit_defer(context, throw_info->defer, NULL);
gencontext_emit_return_value(context, value);
gencontext_emit_block(context, after_block);
return;
}
case CATCH_RETURN_MANY:
{
LLVMBasicBlockRef else_block = gencontext_create_free_block(context, "erret_many");
gencontext_emit_cond_br(context, comparison, else_block, after_block);
gencontext_emit_block(context, else_block);
if (call_error_type == type_error_base)
{
value = gencontext_emit_cast(context, CAST_ERREU, value, type_error_union, call_error_type);
}
gencontext_emit_defer(context, throw_info->defer, NULL);
gencontext_emit_return_value(context, value);
gencontext_emit_block(context, after_block);
return;
}
case CATCH_TRY_ELSE:
{
LLVMBasicBlockRef else_block = gencontext_get_try_target(context, catch->try_else);
if (throw_info->defer != catch->defer)
{
LLVMBasicBlockRef defer_block = gencontext_create_free_block(context, "defer");
gencontext_emit_cond_br(context, comparison, defer_block, after_block);
gencontext_emit_defer(context, throw_info->defer, catch->defer);
gencontext_emit_br(context, else_block);
}
else
{
gencontext_emit_cond_br(context, comparison, else_block, after_block);
}
gencontext_emit_block(context, after_block);
return;
}
case CATCH_RETURN_ANY:
{
LLVMBasicBlockRef else_block = gencontext_create_free_block(context, "erret_any");
gencontext_emit_cond_br(context, comparison, else_block, after_block);
gencontext_emit_block(context, else_block);
if (call_error_type == type_error_base)
{
value = gencontext_emit_cast(context, CAST_ERREU, value, type_error_union, errors[0]->type);
}
gencontext_emit_defer(context, throw_info->defer, NULL);
gencontext_emit_return_value(context, value);
gencontext_emit_block(context, after_block);
return;
}
case CATCH_REGULAR:
{
gencontext_generate_catch_block_if_needed(context, catch->catch);
LLVMBasicBlockRef else_block = gencontext_create_free_block(context, "catch_regular");
gencontext_emit_cond_br(context, comparison, else_block, after_block);
gencontext_emit_block(context, else_block);
Decl *error_param = catch->catch->catch_stmt.error_param;
if (call_error_type == type_error_base)
{
if (error_param->type == type_error_union)
{
value = gencontext_emit_cast(context, CAST_ERREU, value, type_error_union, errors[0]->type);
}
}
LLVMBuildStore(context->builder, value, error_param->ref);
gencontext_emit_defer(context, throw_info->defer, catch->defer);
gencontext_emit_br(context, catch->catch->catch_stmt.block);
gencontext_emit_block(context, after_block);
return;
}
}
UNREACHABLE
}
// Here we handle multiple branches.
LLVMBasicBlockRef err_handling_block = gencontext_create_free_block(context, "errhandlingblock");
gencontext_emit_cond_br(context, comparison, err_handling_block, after_block);
gencontext_emit_block(context, err_handling_block);
err_handling_block = NULL;
assert(error_return != ERROR_RETURN_ONE && "Should already be handled.");
// Below here we can assume we're handling error unions.
VECEACH(throw_info->catches, i)
{
if (err_handling_block == NULL)
{
err_handling_block = gencontext_create_free_block(context, "thrownext");
}
CatchInfo *catch = &throw_info->catches[i];
switch (catch->kind)
{
case CATCH_RETURN_ONE:
{
// This is always the last catch, so we can assume that we have the correct error
// type already.
LLVMValueRef negated = LLVMBuildNeg(context->builder, catch->error->error.start_value, "");
LLVMValueRef final_value = LLVMBuildAnd(context->builder, negated, value, "");
gencontext_emit_defer(context, throw_info->defer, NULL);
gencontext_emit_return_value(context, final_value);
gencontext_emit_block(context, after_block);
assert(i == vec_size(throw_info->catches) - 1);
return;
}
case CATCH_RETURN_MANY:
case CATCH_RETURN_ANY:
{
// This is simple, just return our value.
gencontext_emit_defer(context, throw_info->defer, NULL);
gencontext_emit_return_value(context, value);
gencontext_emit_block(context, after_block);
assert(i == vec_size(throw_info->catches) - 1);
return;
}
case CATCH_TRY_ELSE:
{
// This should be the last catch.
gencontext_emit_defer(context, throw_info->defer, catch->defer);
LLVMBasicBlockRef else_block = gencontext_get_try_target(context, catch->try_else);
gencontext_emit_br(context, else_block);
gencontext_emit_block(context, after_block);
assert(i == vec_size(throw_info->catches) - 1);
return;
}
case CATCH_REGULAR:
{
gencontext_generate_catch_block_if_needed(context, catch->catch);
Decl *error_param = catch->catch->catch_stmt.error_param;
Type *error_type = error_param->type->canonical;
// The wildcard catch is always the last one.
if (error_type == type_error_union)
{
// Store the value, then jump
LLVMBuildStore(context->builder, value, error_param->ref);
gencontext_emit_defer(context, throw_info->defer, catch->defer);
gencontext_emit_br(context, catch->catch->catch_stmt.block);
gencontext_emit_block(context, after_block);
assert(i == vec_size(throw_info->catches) - 1);
return;
}
// Here we have a normal catch.
// wrapping(value - offset) < entries + 1 this handles both cases since wrapping will make
// values below the offset big.
Decl *error_decl = error_type->decl;
LLVMValueRef comp_value = LLVMBuildSub(context->builder, value, error_decl->error.start_value, "");
LLVMValueRef entries_value = llvm_int(type_error_union, vec_size(error_decl->error.error_constants) + 1);
LLVMValueRef match = LLVMBuildICmp(context->builder, LLVMIntULT, comp_value, entries_value, "regmatch");
LLVMBasicBlockRef match_block = gencontext_create_free_block(context, "match");
gencontext_emit_cond_br(context, match, match_block, err_handling_block);
gencontext_emit_block(context, match_block);
gencontext_emit_defer(context, throw_info->defer, catch->defer);
LLVMBuildStore(context->builder, comp_value, error_param->ref);
gencontext_emit_br(context, catch->catch->catch_stmt.block);
gencontext_emit_block(context, err_handling_block);
err_handling_block = NULL;
break;
}
}
}
gencontext_emit_br(context, after_block);
gencontext_emit_block(context, after_block);
}
LLVMValueRef gencontext_emit_call_expr(GenContext *context, Expr *expr)
{
@@ -1305,9 +1080,9 @@ LLVMValueRef gencontext_emit_call_expr(GenContext *context, Expr *expr)
values[param_index++] = gencontext_emit_expr(context, expr->call_expr.arguments[i]);
}
LLVMValueRef call = LLVMBuildCall2(context->builder, func_type, func, values, args, "call");
LLVMValueRef call = LLVMBuildCall2(context->builder, func_type, func, values, args, "");
gencontext_emit_throw_branch(context, call, signature->throws, expr->call_expr.throw_info, signature->error_return);
//gencontext_emit_throw_branch(context, call, signature->throws, expr->call_expr.throw_info, signature->error_return);
// If we used a return param, then load that info here.
if (return_param)
@@ -1390,7 +1165,7 @@ static inline LLVMValueRef gencontext_emit_macro_block(GenContext *context, Expr
Decl *decl = expr->macro_block.params[i];
decl->ref = gencontext_emit_alloca(context, llvm_type(decl->type), decl->name);
LLVMValueRef value = gencontext_emit_expr(context, expr->macro_block.args[i]);
LLVMBuildStore(context->builder, value, decl->ref);
gencontext_emit_store(context, decl, value);
}
VECEACH(stmts, i)
@@ -1416,23 +1191,119 @@ LLVMValueRef gencontext_emit_call_intrinsic(GenContext *context, unsigned intrin
return LLVMBuildCall2(context->builder, type, decl, values, arg_count, "");
}
LLVMValueRef gencontext_emit_assign_expr(GenContext *context, LLVMValueRef ref, Expr *expr)
LLVMValueRef gencontext_emit_assign_expr(GenContext *context, LLVMValueRef ref, Expr *expr, LLVMValueRef failable_ref)
{
LLVMBasicBlockRef assign_block = NULL;
PUSH_ERROR();
if (failable_ref)
{
assign_block = gencontext_create_free_block(context, "after_assign");
context->error_var = failable_ref;
context->catch_block = assign_block;
}
LLVMValueRef value;
switch (expr->expr_kind)
{
case EXPR_INITIALIZER_LIST:
return gencontext_emit_load(context,
value = gencontext_emit_load(context,
expr->type,
gencontext_emit_initializer_list_expr_addr(context, expr, ref));
default:
value = gencontext_emit_expr(context, expr);
LLVMBuildStore(context->builder, value, ref);
break;
}
LLVMValueRef value = gencontext_emit_expr(context, expr);
LLVMBuildStore(context->builder, value, ref);
if (failable_ref)
{
LLVMBuildStore(context->builder, gencontext_emit_no_error_union(context), failable_ref);
}
POP_ERROR();
if (failable_ref)
{
gencontext_emit_br(context, assign_block);
gencontext_emit_block(context, assign_block);
}
return value;
}
static inline LLVMValueRef gencontext_emit_identifier_rvalue(GenContext *context, Decl *decl)
{
if (decl->decl_kind != DECL_VAR || !decl->var.failable)
{
return gencontext_emit_load(context, decl->type, decl_ref(decl));
}
assert(context->catch_block);
LLVMBasicBlockRef after_block = gencontext_create_free_block(context, "after_check");
LLVMValueRef error_value = gencontext_emit_load(context, type_error, decl_failable_ref(decl));
LLVMValueRef comp = LLVMBuildICmp(context->builder, LLVMIntEQ, error_value,
gencontext_emit_no_error_union(context), "");
if (context->error_var)
{
LLVMBasicBlockRef error_block = gencontext_create_free_block(context, "error");
gencontext_emit_cond_br(context, comp, after_block, error_block);
gencontext_emit_block(context, error_block);
LLVMBuildStore(context->builder, error_value, gencontext_emit_bitcast(context, context->error_var, type_get_ptr(type_error)));
gencontext_emit_br(context, context->catch_block);
}
else
{
gencontext_emit_cond_br(context, comp, after_block, context->catch_block);
}
gencontext_emit_block(context, after_block);
return gencontext_emit_load(context, decl->type, decl_ref(decl));
}
static inline LLVMValueRef gencontext_emit_failable(GenContext *context, Expr *expr)
{
Expr *fail = expr->failable_expr;
if (context->error_var)
{
assert(context->error_var);
LLVMValueRef value = gencontext_emit_expr(context, fail);
LLVMBuildStore(context->builder, value, gencontext_emit_bitcast(context, context->error_var, type_get_ptr(fail->type)));
}
gencontext_emit_br(context, context->catch_block);
LLVMBasicBlockRef ignored_block = gencontext_create_free_block(context, "");
gencontext_emit_block(context, ignored_block);
return LLVMGetUndef(llvm_type(expr->type));
}
LLVMValueRef gencontext_emit_check_failable(GenContext *context, Expr *expr)
{
PUSH_ERROR();
LLVMBasicBlockRef after_check_block = gencontext_create_free_block(context, "after_check");
context->error_var = NULL;
context->catch_block = after_check_block;
gencontext_emit_expr(context, expr->fail_check_expr);
POP_ERROR();
LLVMBasicBlockRef phi_block = gencontext_create_free_block(context, "checkphi");
LLVMBasicBlockRef normal_block = context->current_block;
gencontext_emit_br(context, phi_block);
gencontext_emit_block(context, after_check_block);
gencontext_emit_br(context, phi_block);
gencontext_emit_block(context, phi_block);
LLVMValueRef phi = LLVMBuildPhi(context->builder, llvm_type(type_bool), "val");
LLVMValueRef logic_values[2] = { llvm_int(type_bool, 0), llvm_int(type_bool, 1) };
LLVMBasicBlockRef blocks[2] = { after_check_block, normal_block };
LLVMAddIncoming(phi, logic_values, blocks, 2);
return phi;
}
LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr)
{
NESTED_RETRY:
@@ -1440,10 +1311,20 @@ NESTED_RETRY:
{
case EXPR_RANGE:
case EXPR_POISONED:
case EXPR_DECL_LIST:
UNREACHABLE
case EXPR_DESIGNATED_INITIALIZER:
// Should only appear when generating designated initializers.
UNREACHABLE
case EXPR_FAIL_CHECK:
return gencontext_emit_check_failable(context, expr);
case EXPR_FAILABLE:
return gencontext_emit_failable(context, expr);
case EXPR_TRY:
case EXPR_CATCH:
return gencontext_emit_trycatch_expr(context, expr);
case EXPR_ELSE:
TODO
case EXPR_MACRO_BLOCK:
return gencontext_emit_macro_block(context, expr);
case EXPR_COMPOUND_LITERAL:
@@ -1465,8 +1346,8 @@ NESTED_RETRY:
return gencontext_emit_ternary_expr(context, expr);
case EXPR_POST_UNARY:
return gencontext_emit_post_unary_expr(context, expr);
case EXPR_TRY:
return gencontext_emit_try_expr(context, expr);
case EXPR_GUARD:
return gencontext_emit_trycatch_expr(context, expr);
case EXPR_TYPEID:
return gencontext_emit_typeid(context, expr);
case EXPR_TYPE_ACCESS:
@@ -1474,6 +1355,7 @@ NESTED_RETRY:
// These are folded in the semantic analysis step.
UNREACHABLE
case EXPR_IDENTIFIER:
return gencontext_emit_identifier_rvalue(context, expr->identifier_expr.decl);
case EXPR_SUBSCRIPT:
case EXPR_ACCESS:
return gencontext_emit_load(context, expr->type, gencontext_emit_address(context, expr));

View File

@@ -8,7 +8,7 @@
bool gencontext_check_block_branch_emit(GenContext *context)
{
assert(context->current_block);
if (!context->current_block) return false;
// If it's not used, we can delete the previous block and skip the branch.
// Unless it is the entry block or a label target for jumps
// These empty blocks will occur when doing branches.
@@ -76,25 +76,24 @@ static inline void gencontext_emit_parameter(GenContext *context, Decl *decl, un
// Allocate room on stack and copy.
decl->ref = gencontext_emit_alloca(context, llvm_type(decl->type), decl->name);
LLVMBuildStore(context->builder, LLVMGetParam(context->function, index), decl->ref);
gencontext_emit_store(context, decl, LLVMGetParam(context->function, index));
}
void gencontext_emit_implicit_return(GenContext *context)
{
switch (context->cur_func_decl->func.function_signature.error_return)
if (context->cur_func_decl->func.function_signature.failable)
{
case ERROR_RETURN_NONE:
LLVMBuildRetVoid(context->builder);
return;
case ERROR_RETURN_ANY:
case ERROR_RETURN_MANY:
LLVMBuildRet(context->builder, llvm_int(type_usize, 0));
return;
case ERROR_RETURN_ONE:
LLVMBuildRet(context->builder, llvm_int(type_error_base, 0));
return;
LLVMBuildRet(context->builder, gencontext_emit_no_error_union(context));
}
else
{
if (context->cur_func_decl->func.function_signature.rtype->type != type_void && !context->cur_func_decl->func.function_signature.return_param)
{
LLVMBuildUnreachable(context->builder);
return;
}
LLVMBuildRetVoid(context->builder);
}
UNREACHABLE
}
void gencontext_emit_function_body(GenContext *context, Decl *decl)
@@ -105,6 +104,9 @@ void gencontext_emit_function_body(GenContext *context, Decl *decl)
LLVMValueRef prev_function = context->function;
LLVMBuilderRef prev_builder = context->builder;
context->error_var = NULL;
context->catch_block = NULL;
context->function = decl->ref;
context->cur_func_decl = decl;
@@ -136,12 +138,6 @@ void gencontext_emit_function_body(GenContext *context, Decl *decl)
gencontext_emit_parameter(context, decl->func.function_signature.params[i], arg++);
}
VECEACH(decl->func.labels, i)
{
Ast *label = decl->func.labels[i];
label->label_stmt.backend_value = gencontext_create_free_block(context, label->label_stmt.name);
}
VECEACH(decl->func.body->compound_stmt.stmts, i)
{
gencontext_emit_stmt(context, decl->func.body->compound_stmt.stmts[i]);
@@ -183,7 +179,7 @@ void gencontext_emit_function_decl(GenContext *context, Decl *decl)
decl->ref = function;
if (decl->func.function_signature.return_param)
{
if (decl->func.function_signature.error_return == ERROR_RETURN_NONE)
if (!decl->func.function_signature.failable)
{
gencontext_add_attribute(context, function, sret_attribute, 1);
}
@@ -290,15 +286,12 @@ void gencontext_emit_extern_decl(GenContext *context, Decl *decl)
TODO
case DECL_STRUCT:
case DECL_UNION:
case DECL_ERR:
llvm_type(decl->type);
TODO // Fix typeid
break;
case DECL_ENUM:
TODO
case DECL_ERROR:
TODO
case DECL_ERROR_CONSTANT:
TODO
case DECL_MEMBER:
case DECL_ARRAY_VALUE:
case DECL_IMPORT:
@@ -308,6 +301,7 @@ void gencontext_emit_extern_decl(GenContext *context, Decl *decl)
case DECL_CT_ELSE:
case DECL_CT_ELIF:
case DECL_ATTRIBUTE:
case DECL_LABEL:
UNREACHABLE
}
}

View File

@@ -18,6 +18,7 @@
#include <llvm-c/Transforms/Scalar.h>
#include <llvm-c/Transforms/IPO.h>
#include <llvm-c/Transforms/Utils.h>
#include <llvm-c/Comdat.h>
#include "dwarf.h"
typedef struct
@@ -54,6 +55,9 @@ typedef struct
LLVMValueRef alloca_point;
LLVMBuilderRef builder;
LLVMBasicBlockRef current_block;
LLVMBasicBlockRef catch_block;
LLVMValueRef error_var;
Decl *cur_code_decl;
Decl *cur_func_decl;
TypeInfo *current_return_type;
@@ -66,8 +70,6 @@ typedef struct
Ast **defer_stack;
DebugContext debug;
Context *ast_context;
BreakContinue break_continue_stack[BREAK_STACK_MAX];
size_t break_continue_stack_index;
LLVMValueRef return_out;
LLVMBasicBlockRef error_exit_block;
LLVMBasicBlockRef expr_block_exit;
@@ -118,9 +120,9 @@ LLVMValueRef gencontext_emit_call_intrinsic(GenContext *context, unsigned intrin
LLVMValueRef *values, unsigned arg_count);
void gencontext_emit_panic_on_true(GenContext *context, LLVMValueRef value, const char *panic_name);
void gencontext_emit_defer(GenContext *context, Ast *defer_start, Ast *defer_end);
LLVMBasicBlockRef gencontext_get_try_target(GenContext *context, Expr *try_expr);
LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr);
LLVMValueRef gencontext_emit_assign_expr(GenContext *context, LLVMValueRef ref, Expr *expr);
LLVMValueRef gencontext_emit_assign_expr(GenContext *context, LLVMValueRef ref, Expr *expr, LLVMValueRef failable);
LLVMMetadataRef gencontext_get_debug_type(GenContext *context, Type *type);
void gencontext_emit_debug_location(GenContext *context, SourceRange location);
LLVMMetadataRef gencontext_create_builtin_debug_type(GenContext *context, Type *builtin_type);
@@ -136,6 +138,13 @@ static inline LLVMBasicBlockRef gencontext_create_free_block(GenContext *context
{
return LLVMCreateBasicBlockInContext(context->context, name);
}
#define PUSH_ERROR() \
LLVMBasicBlockRef _old_catch = context->catch_block; \
LLVMValueRef _old_error_var = context->error_var
#define POP_ERROR() \
context->catch_block = _old_catch; \
context->error_var = _old_error_var
void gencontext_emit_function_body(GenContext *context, Decl *decl);
void gencontext_emit_implicit_return(GenContext *context);
void gencontext_emit_function_decl(GenContext *context, Decl *decl);
@@ -147,12 +156,34 @@ static inline LLVMValueRef gencontext_emit_load(GenContext *context, Type *type,
assert(gencontext_get_llvm_type(context, type) == LLVMGetElementType(LLVMTypeOf(value)));
return LLVMBuildLoad2(context->builder, gencontext_get_llvm_type(context, type), value, "");
}
static inline void gencontext_emit_return_value(GenContext *context, LLVMValueRef value)
{
LLVMBuildRet(context->builder, value);
context->current_block = NULL;
context->current_block_is_target = false;
}
static inline LLVMValueRef decl_failable_ref(Decl *decl)
{
assert(decl->decl_kind == DECL_VAR);
if (decl->var.kind == VARDECL_ALIAS) return decl_failable_ref(decl->var.alias);
return decl->var.failable_ref;
}
static inline LLVMValueRef decl_ref(Decl *decl)
{
assert(decl->decl_kind == DECL_VAR);
if (decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_ALIAS) return decl_ref(decl->var.alias);
return decl->ref;
}
static inline void gencontext_emit_store(GenContext *context, Decl *decl, LLVMValueRef value)
{
LLVMBuildStore(context->builder, value, decl_ref(decl));
}
LLVMValueRef gencontext_emit_cast(GenContext *context, CastKind cast_kind, LLVMValueRef value, Type *to_type, Type *from_type);
static inline bool gencontext_func_pass_return_by_param(GenContext *context, Type *first_param_type) { return false; };
static inline bool gencontext_func_pass_param_by_reference(GenContext *context, Type *param_type) { return false; }
@@ -230,12 +261,18 @@ static inline LLVMCallConv llvm_call_convention_from_call(CallABI abi)
#define llvm_type(type) gencontext_get_llvm_type(context, type)
#define llvm_debug_type(type) gencontext_get_debug_type(context, type)
static inline LLVMValueRef gencontext_emit_no_error_union(GenContext *context)
{
return LLVMConstInt(llvm_type(type_error), 0, false);
}
static inline LLVMValueRef gencontext_emit_const_int(GenContext *context, Type *type, uint64_t val)
{
type = type->canonical;
if (type == type_error_union) type = type_usize->canonical;
if (type == type_error) type = type_usize->canonical;
assert(type_is_any_integer(type) || type->type_kind == TYPE_BOOL);
return LLVMConstInt(llvm_type(type), val, type_is_signed_integer(type));
}
#define llvm_int(_type, _val) gencontext_emit_const_int(context, _type, _val)
LLVMValueRef gencontext_emit_typeid(GenContext *context, Expr *expr);

View File

@@ -4,9 +4,7 @@
#include "llvm_codegen_internal.h"
static void gencontext_pop_break_continue(GenContext *context);
static void gencontext_push_break_continue(GenContext *context, LLVMBasicBlockRef break_block, LLVMBasicBlockRef continue_block,
LLVMBasicBlockRef next_block);
void gencontext_emit_try_stmt(GenContext *context, Ast *pAst);
void gencontext_emit_compound_stmt(GenContext *context, Ast *ast)
{
@@ -24,6 +22,11 @@ static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast)
Decl *decl = ast->declare_stmt;
decl->ref = gencontext_emit_alloca(context, llvm_type(type_reduced(decl->type)), decl->name);
if (decl->var.failable)
{
decl->var.failable_ref = gencontext_emit_alloca(context, llvm_type(type_error), decl->name);
LLVMBuildStore(context->builder, gencontext_emit_no_error_union(context), decl->var.failable_ref);
}
// TODO NRVO
// TODO debug info
/*
@@ -42,31 +45,39 @@ static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast)
*/
if (decl->var.init_expr)
{
gencontext_emit_assign_expr(context, decl->ref, decl->var.init_expr);
return decl->ref;
gencontext_emit_assign_expr(context, decl->ref, decl->var.init_expr, decl->var.failable_ref);
}
return decl->ref;
}
void gencontext_emit_decl_expr_list_ignore_result(GenContext *context, Ast *ast)
void gencontext_emit_decl_expr_list_ignore_result(GenContext *context, Expr *expr)
{
assert(ast->ast_kind == AST_DECL_EXPR_LIST);
VECEACH(ast->decl_expr_stmt, i)
assert(expr->expr_kind == EXPR_DECL_LIST);
VECEACH(expr->dexpr_list_expr, i)
{
gencontext_emit_stmt(context, ast->decl_expr_stmt[i]);
gencontext_emit_stmt(context, expr->dexpr_list_expr[i]);
}
}
LLVMValueRef gencontext_emit_decl_expr_list(GenContext *context, Ast *ast, bool bool_cast)
LLVMValueRef gencontext_emit_decl_expr_list(GenContext *context, Expr *expr, bool bool_cast)
{
assert(ast->ast_kind == AST_DECL_EXPR_LIST);
size_t size = vec_size(ast->decl_expr_stmt);
assert(expr->expr_kind == EXPR_DECL_LIST);
size_t size = vec_size(expr->dexpr_list_expr);
size_t last_index = size - 1;
for (size_t i = 0; i < last_index; i++)
{
gencontext_emit_stmt(context, ast->decl_expr_stmt[i]);
Ast *ast = expr->dexpr_list_expr[i];
if (ast->ast_kind == AST_DECLARE_STMT)
{
gencontext_emit_decl(context, ast);
}
else
{
assert(ast->ast_kind == AST_EXPR_STMT);
gencontext_emit_expr(context, ast->expr_stmt);
}
}
Ast *last = ast->decl_expr_stmt[last_index];
Ast *last = expr->dexpr_list_expr[last_index];
LLVMValueRef result;
Type *type;
switch (last->ast_kind)
@@ -77,7 +88,14 @@ LLVMValueRef gencontext_emit_decl_expr_list(GenContext *context, Ast *ast, bool
break;
case AST_DECLARE_STMT:
type = last->declare_stmt->var.type_info->type;
result = gencontext_emit_load(context, type, gencontext_emit_decl(context, last));
{
LLVMValueRef value = gencontext_emit_decl(context, last);
if (bool_cast && last->declare_stmt->var.unwrap)
{
return llvm_int(type_bool, 1);
}
result = gencontext_emit_load(context, type, value);
}
break;
default:
UNREACHABLE
@@ -106,7 +124,23 @@ static inline void gencontext_emit_return(GenContext *context, Ast *ast)
// Ensure we are on a branch that is non empty.
if (!gencontext_check_block_branch_emit(context)) return;
bool in_expression_block = context->expr_block_exit != NULL;
PUSH_ERROR();
LLVMBasicBlockRef error_return_block = NULL;
LLVMValueRef error_out = NULL;
if (!in_expression_block && context->cur_func_decl->func.function_signature.failable)
{
error_return_block = gencontext_create_free_block(context, "errretblock");
error_out = gencontext_emit_alloca(context, llvm_type(type_error), "reterr");
context->error_var = error_out;
context->catch_block = error_return_block;
}
LLVMValueRef ret_value = ast->return_stmt.expr ? gencontext_emit_expr(context, ast->return_stmt.expr) : NULL;
POP_ERROR();
gencontext_emit_defer(context, ast->return_stmt.defer, NULL);
// Are we in an expression block?
@@ -137,38 +171,20 @@ static inline void gencontext_emit_return(GenContext *context, Ast *ast)
}
}
context->current_block = NULL;
if (error_return_block && LLVMGetFirstUse(LLVMBasicBlockAsValue(error_return_block)))
{
gencontext_emit_block(context, error_return_block);
LLVMBuildRet(context->builder, gencontext_emit_load(context, type_error, error_out));
context->current_block = NULL;
}
LLVMBasicBlockRef post_ret_block = gencontext_create_free_block(context, "ret");
gencontext_emit_block(context, post_ret_block);
}
static inline void gencontext_emit_throw(GenContext *context, Ast *ast)
{
// Ensure we are on a branch that is non empty.
if (!gencontext_check_block_branch_emit(context)) return;
LLVMValueRef error_val = gencontext_emit_expr(context, ast->throw_stmt.throw_value);
gencontext_emit_defer(context, ast->throw_stmt.defer, NULL);
// In the case that the throw actually contains a single error, but the function is throwing an error union,
// we must insert a conversion.
if (context->cur_func_decl->func.function_signature.error_return != ERROR_RETURN_ONE &&
ast->throw_stmt.throw_value->type->type_kind == TYPE_ERROR)
{
error_val = gencontext_emit_cast(context, CAST_ERREU, error_val, type_error_union, ast->throw_stmt.throw_value->type);
}
gencontext_emit_return_value(context, error_val);
LLVMBasicBlockRef post_throw_block = gencontext_create_free_block(context, "throw");
gencontext_emit_block(context, post_throw_block);
}
void gencontext_emit_if(GenContext *context, Ast *ast)
{
if (ast->if_stmt.decl) gencontext_emit_decl_expr_list_ignore_result(context, ast->if_stmt.decl);
// We need at least the exit block and the "then" block.
LLVMBasicBlockRef exit_block = LLVMCreateBasicBlockInContext(context->context, "if.exit");
LLVMBasicBlockRef then_block = LLVMCreateBasicBlockInContext(context->context, "if.then");
@@ -179,20 +195,61 @@ void gencontext_emit_if(GenContext *context, Ast *ast)
{
else_block = LLVMCreateBasicBlockInContext(context->context, "if.else");
}
else
{
else_block = exit_block;
}
ast->if_stmt.break_block = exit_block;
// Output boolean value and switch.
PUSH_ERROR();
context->catch_block = else_block ?: exit_block;
context->error_var = NULL;
LLVMValueRef value = gencontext_emit_decl_expr_list(context, ast->if_stmt.cond, true);
gencontext_emit_cond_br(context, value, then_block, else_block ? else_block : exit_block);
if (LLVMIsConstant(value))
{
unsigned long v = LLVMConstIntGetZExtValue(value);
if (v)
{
gencontext_emit_br(context, then_block);
else_block = NULL;
}
else
{
gencontext_emit_br(context, else_block);
then_block = NULL;
}
}
else
{
gencontext_emit_cond_br(context, value, then_block, else_block);
}
POP_ERROR();
Decl *label = ast->if_stmt.label;
if (label)
{
label->label.break_target = exit_block;
}
// Emit the 'then' code.
gencontext_emit_block(context, then_block);
gencontext_emit_stmt(context, ast->if_stmt.then_body);
if (then_block)
{
gencontext_emit_block(context, then_block);
gencontext_emit_stmt(context, ast->if_stmt.then_body);
// Jump to exit.
gencontext_emit_br(context, exit_block);
}
// Jump to exit.
gencontext_emit_br(context, exit_block);
// Emit the 'else' branch if present.
if (else_block)
if (else_block && else_block != exit_block)
{
gencontext_emit_block(context, else_block);
gencontext_emit_stmt(context, ast->if_stmt.else_body);
@@ -204,33 +261,6 @@ void gencontext_emit_if(GenContext *context, Ast *ast)
}
static void gencontext_push_break_continue(GenContext *context, LLVMBasicBlockRef break_block,
LLVMBasicBlockRef continue_block, LLVMBasicBlockRef next_block)
{
size_t index = context->break_continue_stack_index++;
if (index == BREAK_STACK_MAX - 1)
{
error_exit("Exhausted break/continue stack - exceeded %d entries.", BREAK_STACK_MAX);
}
if (!index)
{
context->break_continue_stack[index].continue_block = continue_block;
context->break_continue_stack[index].break_block = break_block;
context->break_continue_stack[index].next_block = next_block;
return;
}
context->break_continue_stack[index].continue_block = continue_block ? continue_block : context->break_continue_stack[index - 1].continue_block;
context->break_continue_stack[index].next_block = next_block ? next_block : context->break_continue_stack[index - 1].next_block;
context->break_continue_stack[index].break_block = break_block ? break_block : context->break_continue_stack[index - 1].break_block;
}
static void gencontext_pop_break_continue(GenContext *context)
{
assert(context->break_continue_stack_index);
context->break_continue_stack_index--;
}
void gencontext_emit_for_stmt(GenContext *context, Ast *ast)
{
// First, emit all inits.
@@ -252,11 +282,10 @@ void gencontext_emit_for_stmt(GenContext *context, Ast *ast)
// For continue:
// 1. If there is inc, jump to the condition
// 2. If there is no condition, jump to the body.
gencontext_push_break_continue(context,
exit_block,
inc_block ? inc_block : (cond_block ? cond_block : body_block),
NULL);
LLVMBasicBlockRef continue_block = inc_block ? inc_block : (cond_block ? cond_block : body_block);
ast->for_stmt.continue_block = continue_block;
ast->for_stmt.exit_block = exit_block;
LLVMValueRef value = NULL;
if (cond_block)
@@ -305,31 +334,27 @@ void gencontext_emit_for_stmt(GenContext *context, Ast *ast)
// And insert exit block
gencontext_emit_block(context, exit_block);
gencontext_pop_break_continue(context);
}
void gencontext_emit_while_stmt(GenContext *context, Ast *ast)
{
// First, emit all inits.
if (ast->while_stmt.decl) gencontext_emit_decl_expr_list_ignore_result(context, ast->while_stmt.decl);
LLVMBasicBlockRef exit_block = gencontext_create_free_block(context, "while.exit");
LLVMBasicBlockRef begin_block = gencontext_create_free_block(context, "while.begin");
LLVMBasicBlockRef body_block = ast->while_stmt.body->compound_stmt.stmts ? gencontext_create_free_block(context, "while.body") : NULL;
gencontext_push_break_continue(context, exit_block, begin_block, NULL);
ast->while_stmt.continue_block = begin_block;
ast->while_stmt.break_block = exit_block;
// Emit cond
gencontext_emit_br(context, begin_block);
gencontext_emit_block(context, begin_block);
DeferList defers = { NULL, NULL };
Ast *cond = ast->while_stmt.cond;
if (cond->ast_kind == AST_SCOPED_STMT)
Expr *cond = ast->while_stmt.cond;
if (cond->expr_kind == EXPR_SCOPED_EXPR)
{
defers = cond->scoped_stmt.defers;
cond = cond->scoped_stmt.stmt;
defers = cond->expr_scope.defers;
cond = cond->expr_scope.expr;
}
LLVMValueRef value = gencontext_emit_decl_expr_list(context, cond, true);
// If we have a body, conditionally jump to it.
@@ -363,91 +388,170 @@ void gencontext_emit_while_stmt(GenContext *context, Ast *ast)
// Emit defers
gencontext_emit_defer(context, defers.start, defers.end);
gencontext_pop_break_continue(context);
}
void gencontext_emit_do_stmt(GenContext *context, Ast *ast)
{
LLVMBasicBlockRef exit_block = gencontext_create_free_block(context, "do.exit");
LLVMBasicBlockRef cond_block = ast->do_stmt.expr ? gencontext_create_free_block(context, "do.cond") : NULL;
LLVMBasicBlockRef body_block = ast->do_stmt.body ? gencontext_create_free_block(context, "do.body") : NULL;
// A loop must either have a body or an inc.
// This type do-while for loop is forbidden:
// do { } while (1);
assert((cond_block || body_block) && "Do has no body and no cond.");
LLVMBasicBlockRef body_block = gencontext_create_free_block(context, "do.body");
// Break is simple it always jumps out.
// For continue: if there is no condition, jump to the body.
gencontext_push_break_continue(context, exit_block, cond_block ? cond_block : body_block, NULL);
// For continue: if there is no condition, exit.
LLVMBasicBlockRef cont_block = cond_block ? cond_block : exit_block;
if (body_block)
{
// Emit the body
gencontext_emit_br(context, body_block);
gencontext_emit_block(context, body_block);
gencontext_emit_stmt(context, ast->do_stmt.body);
}
Expr *do_expr = ast->do_stmt.expr;
Ast *do_body = ast->do_stmt.body;
// Overwrite:
ast->do_stmt.break_block = exit_block;
ast->do_stmt.continue_block = cont_block;
// Emit the body
gencontext_emit_br(context, body_block);
gencontext_emit_block(context, body_block);
gencontext_emit_stmt(context, do_body);
if (cond_block)
{
gencontext_emit_br(context, cond_block);
gencontext_emit_block(context, cond_block);
LLVMValueRef value = gencontext_emit_expr(context, ast->do_stmt.expr);
gencontext_emit_cond_br(context, value, body_block ? body_block : cond_block, exit_block);
LLVMValueRef value = gencontext_emit_expr(context, do_expr);
if (LLVMIsConstant(value))
{
unsigned long v = LLVMConstIntGetZExtValue(value);
gencontext_emit_br(context, v ? body_block : exit_block);
}
else
{
gencontext_emit_cond_br(context, value, body_block, exit_block);
}
}
else
{
// Branch to the beginning of the block
gencontext_emit_br(context, body_block);
// Branch to the exit
gencontext_emit_br(context, exit_block);
}
// Emit the exit block.
gencontext_emit_block(context, exit_block);
gencontext_pop_break_continue(context);
}
void gencontext_emit_label(GenContext *context, Ast *ast)
{
gencontext_emit_br(context, ast->label_stmt.backend_value);
gencontext_emit_block(context, ast->label_stmt.backend_value);
context->current_block_is_target = true;
}
void gencontext_emit_switch(GenContext *context, Ast *ast)
void gencontext_emit_if_switch_body(GenContext *context, LLVMValueRef switch_value, Ast **cases)
{
// TODO check defer correctness
if (ast->switch_stmt.decl) gencontext_emit_decl_expr_list_ignore_result(context, ast->switch_stmt.decl);
LLVMValueRef switch_value = gencontext_emit_decl_expr_list(context, ast->switch_stmt.cond, false);
size_t cases = vec_size(ast->switch_stmt.cases);
if (!cases)
size_t case_count = vec_size(cases);
if (!case_count)
{
// No body or default is empty, just exit after the value.
return;
}
Ast *default_case = NULL;
for (unsigned i = 0; i < cases; i++)
for (unsigned i = 0; i < case_count; i++)
{
Ast *case_stmt = ast->switch_stmt.cases[i];
Ast *case_stmt = cases[i];
if (!case_stmt->case_stmt.expr)
{
if (case_stmt->case_stmt.body)
{
case_stmt->case_stmt.backend_value = gencontext_create_free_block(context, "switch.default");
case_stmt->case_stmt.backend_block = gencontext_create_free_block(context, "switch.default");
}
default_case = case_stmt;
}
else if (case_stmt->case_stmt.body)
{
case_stmt->case_stmt.backend_value = gencontext_create_free_block(context, "switch.case");
case_stmt->case_stmt.backend_block = gencontext_create_free_block(context, "switch.case");
}
}
LLVMBasicBlockRef exit_block = gencontext_create_free_block(context, "switch.exit");
for (unsigned i = 0; i < case_count; i++)
{
Ast *case_stmt = cases[i];
if (case_stmt == default_case) continue;
LLVMValueRef case_value;
LLVMBasicBlockRef case_block = case_stmt->case_stmt.backend_block;
// TODO handle string.
if (case_stmt->case_stmt.is_type)
{
case_value = case_stmt->case_stmt.type_info->type->backend_typeid;
}
else
{
case_value = gencontext_emit_expr(context, case_stmt->case_stmt.expr);
}
LLVMValueRef cmp = LLVMBuildICmp(context->builder, LLVMIntNE, switch_value, case_value, "");
LLVMBasicBlockRef next_block = gencontext_create_free_block(context, "");
gencontext_emit_cond_br(context, cmp, next_block, case_block);
gencontext_emit_block(context, next_block);
}
if (default_case)
{
gencontext_emit_br(context, default_case->case_stmt.backend_block);
}
else
{
gencontext_emit_br(context, exit_block);
}
for (unsigned i = 0; i < case_count; i++)
{
Ast *case_stmt = cases[i];
LLVMBasicBlockRef case_block = case_stmt->case_stmt.backend_block;
gencontext_emit_block(context, case_block);
if (!case_stmt->case_stmt.body)
{
gencontext_emit_br(context, i == case_count - 1 ? exit_block : cases[i + 1]->case_stmt.backend_block);
continue;
}
// IMPORTANT!
context->current_block_is_target = true;
gencontext_emit_stmt(context, case_stmt->case_stmt.body);
gencontext_emit_br(context, exit_block);
}
gencontext_emit_block(context, exit_block);
}
void gencontext_emit_switch_body(GenContext *context, LLVMValueRef switch_value, Ast *switch_ast)
{
Ast **cases = switch_ast->switch_stmt.cases;
size_t case_count = vec_size(cases);
if (!case_count)
{
// No body or default is empty, just exit after the value.
return;
}
Ast *default_case = NULL;
for (unsigned i = 0; i < case_count; i++)
{
Ast *case_stmt = cases[i];
if (!case_stmt->case_stmt.expr)
{
if (case_stmt->case_stmt.body)
{
case_stmt->case_stmt.backend_block = gencontext_create_free_block(context, "switch.default");
}
default_case = case_stmt;
}
else if (case_stmt->case_stmt.body)
{
case_stmt->case_stmt.backend_block = gencontext_create_free_block(context, "switch.case");
}
}
LLVMBasicBlockRef exit_block = gencontext_create_free_block(context, "switch.exit");
LLVMBasicBlockRef switch_block = gencontext_create_free_block(context, "switch.entry");
switch_ast->switch_stmt.retry_block = switch_block;
switch_ast->switch_stmt.exit_block = exit_block;
// We will now treat the fallthrough cases:
// switch (i)
@@ -458,29 +562,46 @@ void gencontext_emit_switch(GenContext *context, Ast *ast)
// default:
// }
LLVMBasicBlockRef next_block = exit_block;
for (unsigned i = cases; i > 0; i--)
for (unsigned i = case_count; i > 0; i--)
{
Ast *case_stmt = ast->switch_stmt.cases[i - 1];
if (case_stmt->case_stmt.backend_value)
Ast *case_stmt = cases[i - 1];
if (case_stmt->case_stmt.backend_block)
{
next_block = case_stmt->case_stmt.backend_value;
next_block = case_stmt->case_stmt.backend_block;
continue;
}
case_stmt->case_stmt.backend_value = next_block;
case_stmt->case_stmt.backend_block = next_block;
}
gencontext_push_break_continue(context, exit_block, NULL, NULL);
LLVMValueRef switch_stmt = LLVMBuildSwitch(context->builder, switch_value, default_case ? default_case->case_stmt.backend_value : exit_block, cases);
Type *switch_type = switch_ast->switch_stmt.cond->type;
LLVMValueRef switch_var = gencontext_emit_alloca(context, llvm_type(switch_type), "");
switch_ast->switch_stmt.retry_var = switch_var;
LLVMBuildStore(context->builder, switch_value, switch_var);
gencontext_emit_br(context, switch_block);
gencontext_emit_block(context, switch_block);
LLVMValueRef switch_load = gencontext_emit_load(context, switch_type, switch_var);
LLVMValueRef switch_stmt = LLVMBuildSwitch(context->builder, switch_load, default_case ? default_case->case_stmt.backend_block : exit_block, case_count);
context->current_block = NULL;
for (unsigned i = 0; i < cases; i++)
for (unsigned i = 0; i < case_count; i++)
{
Ast *case_stmt = ast->switch_stmt.cases[i];
LLVMBasicBlockRef block = case_stmt->case_stmt.backend_value;
Ast *case_stmt = cases[i];
LLVMBasicBlockRef block = case_stmt->case_stmt.backend_block;
if (case_stmt != default_case)
{
LLVMValueRef case_value = gencontext_emit_expr(context, case_stmt->case_stmt.expr);
LLVMValueRef case_value;
if (case_stmt->case_stmt.is_type)
{
case_value = case_stmt->case_stmt.type_info->type->backend_typeid;
}
else
{
case_value = gencontext_emit_expr(context, case_stmt->case_stmt.expr);
}
LLVMAddCase(switch_stmt, case_value, block);
}
@@ -490,23 +611,17 @@ void gencontext_emit_switch(GenContext *context, Ast *ast)
gencontext_emit_block(context, block);
// IMPORTANT!
context->current_block_is_target = true;
gencontext_push_break_continue(context, NULL, NULL, i < cases - 1 ? ast->switch_stmt.cases[i + 1]->case_stmt.backend_value : exit_block);
gencontext_emit_stmt(context, case_stmt->case_stmt.body);
gencontext_pop_break_continue(context);
gencontext_emit_br(context, exit_block);
}
gencontext_pop_break_continue(context);
gencontext_emit_block(context, exit_block);
}
LLVMValueRef gencontext_get_defer_bool(GenContext *context, Ast *defer)
void gencontext_emit_switch(GenContext *context, Ast *ast)
{
assert(defer->ast_kind == AST_DEFER_STMT && defer->defer_stmt.emit_boolean);
if (!defer->defer_stmt.bool_var)
{
defer->defer_stmt.bool_var = gencontext_emit_alloca(context, llvm_type(type_bool), "defer");
}
return defer->defer_stmt.bool_var;
LLVMValueRef switch_value = gencontext_emit_decl_expr_list(context, ast->switch_stmt.cond, false);
return gencontext_emit_switch_body(context, switch_value, ast);
}
void gencontext_emit_defer(GenContext *context, Ast *defer_start, Ast *defer_end)
@@ -515,70 +630,78 @@ void gencontext_emit_defer(GenContext *context, Ast *defer_start, Ast *defer_end
Ast *defer = defer_start;
while (defer && defer != defer_end)
{
if (defer->defer_stmt.emit_boolean)
{
// We need at least the exit block and the "then" block.
LLVMBasicBlockRef exit_block = LLVMCreateBasicBlockInContext(context->context, "skip.defer");
LLVMBasicBlockRef defer_block = LLVMCreateBasicBlockInContext(context->context, "do.defer");
LLVMValueRef value = gencontext_emit_load(context, type_bool, gencontext_get_defer_bool(context, defer));
gencontext_emit_cond_br(context, value, defer_block, exit_block);
// Emit the defer.
gencontext_emit_block(context, defer_block);
gencontext_emit_stmt(context, defer->defer_stmt.body);
// Jump to exit.
gencontext_emit_br(context, exit_block);
// And now we just emit the exit block.
gencontext_emit_block(context, exit_block);
}
else
{
gencontext_emit_stmt(context, defer->defer_stmt.body);
}
gencontext_emit_stmt(context, defer->defer_stmt.body);
defer = defer->defer_stmt.prev_defer;
}
}
void gencontext_emit_goto(GenContext *context, Ast *ast)
{
gencontext_emit_defer(context, ast->goto_stmt.defer.start, ast->goto_stmt.defer.end);
Ast *defer = ast->goto_stmt.label->label_stmt.defer;
while (defer != ast->goto_stmt.defer.end)
{
LLVMBuildStore(context->builder, LLVMConstInt(llvm_type(type_bool), 0, false),
gencontext_get_defer_bool(context, defer));
defer = defer->defer_stmt.prev_defer;
}
gencontext_emit_jmp(context, ast->goto_stmt.label->label_stmt.backend_value);
}
void gencontext_emit_break(GenContext *context, Ast *ast)
{
gencontext_emit_defer(context, ast->break_stmt.defers.start, ast->break_stmt.defers.end);
assert(context->break_continue_stack_index);
gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].break_block);
gencontext_emit_defer(context, ast->contbreak_stmt.defers.start, ast->contbreak_stmt.defers.end);
Ast *jump_target = ast->contbreak_stmt.ast;
LLVMBasicBlockRef jump;
switch (jump_target->ast_kind)
{
case AST_IF_STMT:
jump = jump_target->if_stmt.break_block;
break;
case AST_WHILE_STMT:
jump = jump_target->while_stmt.break_block;
break;
case AST_FOR_STMT:
jump = jump_target->for_stmt.exit_block;
break;
case AST_DO_STMT:
jump = jump_target->do_stmt.break_block;
break;
case AST_SWITCH_STMT:
jump = jump_target->switch_stmt.exit_block;
break;
default:
UNREACHABLE
}
gencontext_emit_jmp(context, jump);
}
void gencontext_emit_continue(GenContext *context, Ast *ast)
{
gencontext_emit_defer(context, ast->continue_stmt.defers.start, ast->continue_stmt.defers.end);
assert(context->break_continue_stack_index);
gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].continue_block);
{ gencontext_emit_defer(context, ast->contbreak_stmt.defers.start, ast->contbreak_stmt.defers.end);
Ast *jump_target = ast->contbreak_stmt.ast;
LLVMBasicBlockRef jump;
switch (jump_target->ast_kind)
{
case AST_IF_STMT:
case AST_SWITCH_STMT:
UNREACHABLE
break;
case AST_WHILE_STMT:
jump = jump_target->while_stmt.continue_block;
break;
case AST_DO_STMT:
jump = jump_target->do_stmt.continue_block;
break;
case AST_FOR_STMT:
jump = jump_target->for_stmt.continue_block;
break;
default:
UNREACHABLE
}
gencontext_emit_jmp(context, jump);
}
void gencontext_emit_next_stmt(GenContext *context, Ast *ast)
{
Ast *jump_target = ast->next_stmt.case_switch_stmt;
if (jump_target->ast_kind != AST_SWITCH_STMT)
{
gencontext_emit_defer(context, ast->next_stmt.defers.start, ast->next_stmt.defers.end);
gencontext_emit_jmp(context, jump_target->case_stmt.backend_block);
return;
}
LLVMValueRef value = gencontext_emit_expr(context, ast->next_stmt.switch_expr);
LLVMBuildStore(context->builder, value, jump_target->switch_stmt.retry_var);
gencontext_emit_defer(context, ast->next_stmt.defers.start, ast->next_stmt.defers.end);
assert(context->break_continue_stack_index);
gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].next_block);
gencontext_emit_jmp(context, jump_target->switch_stmt.retry_block);
}
void gencontext_emit_scoped_stmt(GenContext *context, Ast *ast)
@@ -594,6 +717,8 @@ void gencontext_generate_catch_block_if_needed(GenContext *context, Ast *ast)
block = gencontext_create_free_block(context, "catchblock");
ast->catch_stmt.block = block;
LLVMTypeRef type;
TODO
#if 0
if (ast->catch_stmt.error_param->type == type_error_union)
{
type = llvm_type(type_error_union);
@@ -603,15 +728,124 @@ void gencontext_generate_catch_block_if_needed(GenContext *context, Ast *ast)
type = llvm_type(type_error_base);
}
ast->catch_stmt.error_param->ref = gencontext_emit_alloca(context, type, "");
#endif
}
void gencontext_emit_try_stmt(GenContext *context, Ast *ast)
{
// Create after try block
LLVMBasicBlockRef after_try = gencontext_create_free_block(context, "after_try");
// Store catch/error var
PUSH_ERROR();
// Set the catch/error var
context->error_var = NULL;
context->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;
VECEACH(decl_expr, i)
{
Ast *dexpr = decl_expr[i];
switch (dexpr->ast_kind)
{
case AST_EXPR_STMT:
gencontext_emit_expr(context, dexpr->expr_stmt);
break;
case AST_DECLARE_STMT:
gencontext_emit_decl(context, dexpr);
break;
default:
UNREACHABLE
}
}
// Restore.
POP_ERROR();
// Emit the statement
gencontext_emit_stmt(context, ast->try_stmt.body);
// Jump to after.
gencontext_emit_br(context, after_try);
gencontext_emit_block(context, after_try);
}
void gencontext_emit_expr_stmt(GenContext *context, Ast *ast)
{
if (ast->expr_stmt->failable)
{
PUSH_ERROR();
LLVMBasicBlockRef discard_fail = gencontext_create_free_block(context, "voiderr");
context->catch_block = discard_fail;
context->error_var = NULL;
gencontext_emit_expr(context, ast->expr_stmt);
gencontext_emit_br(context, discard_fail);
gencontext_emit_block(context, discard_fail);
POP_ERROR();
return;
}
gencontext_emit_expr(context, ast->expr_stmt);
}
void gencontext_emit_catch_stmt(GenContext *context, Ast *ast)
{
gencontext_generate_catch_block_if_needed(context, ast);
Expr *catch_expr;
LLVMValueRef error_result = NULL;
if (ast->catch_stmt.has_err_var)
{
Decl *error_var = ast->catch_stmt.err_var;
assert(error_var->type->canonical == type_error);
error_result = gencontext_emit_alloca(context, llvm_type(type_error), error_var->name);
error_var->ref = error_result;
catch_expr = error_var->var.init_expr;
}
else
{
if (ast->catch_stmt.is_switch)
{
error_result = gencontext_emit_alloca(context, llvm_type(type_error), "catchval");
}
catch_expr = ast->catch_stmt.catchable;
}
// Create catch block.
LLVMBasicBlockRef catch_block = gencontext_create_free_block(context, "catch");
// Store catch/error var
PUSH_ERROR();
// Set the catch/error var
context->error_var = error_result;
context->catch_block = catch_block;
// Emit the catch, which will create jumps like we want them.
gencontext_emit_expr(context, catch_expr);
// Restore.
POP_ERROR();
// Create the bloch after and jump to it.
LLVMBasicBlockRef after_catch = gencontext_create_free_block(context, "after_catch");
gencontext_emit_br(context, after_catch);
gencontext_emit_block(context, ast->catch_stmt.block);
gencontext_emit_stmt(context, ast->catch_stmt.body);
// Emit catch.
gencontext_emit_block(context, catch_block);
if (ast->catch_stmt.is_switch)
{
LLVMValueRef ref = gencontext_emit_bitcast(context, error_result, type_get_ptr(type_typeid));
gencontext_emit_if_switch_body(context, gencontext_emit_load(context, type_typeid, ref), ast->catch_stmt.cases);
}
else
{
gencontext_emit_stmt(context, ast->catch_stmt.body);
}
// Jump to after.
gencontext_emit_br(context, after_catch);
gencontext_emit_block(context, after_catch);
}
@@ -630,15 +864,23 @@ void gencontext_emit_panic_on_true(GenContext *context, LLVMValueRef value, cons
void gencontext_emit_stmt(GenContext *context, Ast *ast)
{
if (context->catch_block == NULL)
{
context->catch_block = gencontext_create_free_block(context, "stmt_catch");
}
switch (ast->ast_kind)
{
case AST_POISONED:
case AST_DEFINE_STMT:
UNREACHABLE
case AST_TRY_STMT:
gencontext_emit_try_stmt(context, ast);
break;
case AST_SCOPED_STMT:
gencontext_emit_scoped_stmt(context, ast);
break;
case AST_EXPR_STMT:
gencontext_emit_expr(context, ast->expr_stmt);
gencontext_emit_expr_stmt(context, ast);
break;
case AST_DECLARE_STMT:
gencontext_emit_decl(context, ast);
@@ -671,20 +913,11 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast)
gencontext_emit_next_stmt(context, ast);
break;
case AST_DEFER_STMT:
if (ast->defer_stmt.emit_boolean)
{
LLVMBuildStore(context->builder, LLVMConstInt(llvm_type(type_bool), 1, false),
gencontext_get_defer_bool(context, ast));
}
break;
case AST_NOP_STMT:
break;
case AST_CATCH_STMT:
gencontext_emit_catch_stmt(context, ast);
break;
case AST_THROW_STMT:
gencontext_emit_throw(context, ast);
break;
case AST_ASM_STMT:
TODO
case AST_CT_IF_STMT:
@@ -692,18 +925,7 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast)
case AST_CT_ELSE_STMT:
case AST_CT_FOR_STMT:
case AST_CT_SWITCH_STMT:
case AST_CT_DEFAULT_STMT:
case AST_CT_CASE_STMT:
case AST_GENERIC_CASE_STMT:
case AST_GENERIC_DEFAULT_STMT:
case AST_DECL_EXPR_LIST:
UNREACHABLE
case AST_LABEL:
gencontext_emit_label(context, ast);
break;
case AST_GOTO_STMT:
gencontext_emit_goto(context, ast);
break;
case AST_SWITCH_STMT:
gencontext_emit_switch(context, ast);
break;
@@ -714,3 +936,4 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast)
TODO
}
}

View File

@@ -4,7 +4,7 @@
#include "llvm_codegen_internal.h"
LLVMTypeRef llvm_get_type(LLVMContextRef context, Type *type);
LLVMTypeRef llvm_get_type(LLVMContextRef context, Type *any_type);
static inline LLVMTypeRef llvm_type_from_decl(LLVMContextRef context, Decl *decl)
{
@@ -20,10 +20,10 @@ static inline LLVMTypeRef llvm_type_from_decl(LLVMContextRef context, Decl *decl
case DECL_CT_ELSE:
case DECL_CT_ELIF:
case DECL_VAR:
case DECL_ERROR_CONSTANT:
case DECL_ARRAY_VALUE:
case DECL_IMPORT:
case DECL_MEMBER:
case DECL_LABEL:
UNREACHABLE;
case DECL_FUNC:
{
@@ -79,19 +79,31 @@ static inline LLVMTypeRef llvm_type_from_decl(LLVMContextRef context, Decl *decl
}
case DECL_ENUM:
return llvm_get_type(context, decl->type);
case DECL_ERROR:
TODO
/*
if (!context->error_type)
case DECL_ERR:
{
LLVMTypeRef *types = NULL;
vec_add(types, llvm_get_type(context, type_typeid));
unsigned size = type_size(type_typeid);
VECEACH(decl->strukt.members, i)
{
LLVMTypeRef domain_type = LLVMInt64TypeInContext(LLVMCONTEXT(context));
LLVMTypeRef pointer_type = BACKEND_TYPE(type_voidptr);
LLVMTypeRef error_type = LLVMStructCreateNamed(LLVMCONTEXT(context), "error");
LLVMTypeRef types[2] = { domain_type, pointer_type };
LLVMStructSetBody(error_type, types, 2, false);
context->error_type = error_type;
Type *type = decl->strukt.members[i]->type->canonical;
unsigned alignment = type_abi_alignment(type);
if (size % alignment != 0)
{
size += alignment - size % alignment;
}
size += type_size(type);
vec_add(types, llvm_get_type(context, type));
}
return context->error_type;*/
unsigned padding = type_size(type_error) - size;
if (padding > 0)
{
vec_add(types, LLVMIntTypeInContext(context, padding * 8));
}
LLVMTypeRef type = LLVMStructCreateNamed(context, decl->name);
LLVMStructSetBody(type, types, vec_size(types), false);
return type;
}
}
UNREACHABLE
}
@@ -138,92 +150,86 @@ LLVMTypeRef llvm_func_type(LLVMContextRef context, Type *type)
}
}
LLVMTypeRef ret_type;
switch (signature->error_return)
if (signature->failable)
{
case ERROR_RETURN_ANY:
case ERROR_RETURN_MANY:
ret_type = llvm_get_type(context, type_error_union);
break;
case ERROR_RETURN_ONE:
ret_type = llvm_get_type(context, type_error_base);
break;;
case ERROR_RETURN_NONE:
ret_type = signature->return_param ? llvm_get_type(context, type_void) : llvm_get_type(context, type->func.signature->rtype->type);
break;
default:
UNREACHABLE
ret_type = llvm_get_type(context, type_error);
}
else
{
ret_type = signature->return_param
? llvm_get_type(context, type_void)
: llvm_get_type(context, type->func.signature->rtype->type);
}
return LLVMFunctionType(ret_type, params, parameters, signature->variadic);
}
LLVMTypeRef llvm_get_type(LLVMContextRef context, Type *type)
LLVMTypeRef llvm_get_type(LLVMContextRef context, Type *any_type)
{
if (type->backend_type && LLVMGetTypeContext(type->backend_type) == context)
if (any_type->backend_type && LLVMGetTypeContext(any_type->backend_type) == context)
{
return type->backend_type;
return any_type->backend_type;
}
DEBUG_LOG("Generating type %s", type->name);
switch (type->type_kind)
DEBUG_LOG("Generating type %s", any_type->name);
switch (any_type->type_kind)
{
case TYPE_MEMBER:
case TYPE_POISONED:
UNREACHABLE
case TYPE_TYPEID:
return type->backend_type = LLVMIntTypeInContext(context, type->builtin.bitsize);
case TYPE_ERROR:
return type->backend_type = llvm_get_type(context, type_error_base);
return any_type->backend_type = LLVMIntTypeInContext(context, any_type->builtin.bitsize);
case TYPE_TYPEDEF:
return type->backend_type = llvm_get_type(context, type->canonical);
return any_type->backend_type = llvm_get_type(context, any_type->canonical);
case TYPE_ENUM:
return type->backend_type = llvm_get_type(context, type->decl->enums.type_info->type->canonical);
case TYPE_ERROR_UNION:
return type->backend_type = LLVMIntTypeInContext(context, type->builtin.bitsize);
return any_type->backend_type = llvm_get_type(context, any_type->decl->enums.type_info->type->canonical);
case TYPE_ERR_UNION:
return any_type->backend_type = LLVMIntTypeInContext(context, any_type->builtin.bitsize);
case TYPE_STRUCT:
case TYPE_UNION:
return type->backend_type = llvm_type_from_decl(context, type->decl);
case TYPE_ERRTYPE:
return any_type->backend_type = llvm_type_from_decl(context, any_type->decl);
case TYPE_FUNC:
return type->backend_type = llvm_func_type(context, type);
return any_type->backend_type = llvm_func_type(context, any_type);
case TYPE_VOID:
return type->backend_type = LLVMVoidTypeInContext(context);
return any_type->backend_type = LLVMVoidTypeInContext(context);
case TYPE_F64:
case TYPE_FXX:
return type->backend_type = LLVMDoubleTypeInContext(context);
return any_type->backend_type = LLVMDoubleTypeInContext(context);
case TYPE_F32:
return type->backend_type = LLVMFloatTypeInContext(context);
return any_type->backend_type = LLVMFloatTypeInContext(context);
case TYPE_U64:
case TYPE_I64:
return type->backend_type = LLVMIntTypeInContext(context, 64U);
return any_type->backend_type = LLVMIntTypeInContext(context, 64U);
case TYPE_U32:
case TYPE_I32:
case TYPE_IXX:
return type->backend_type = LLVMIntTypeInContext(context, 32U);
return any_type->backend_type = LLVMIntTypeInContext(context, 32U);
case TYPE_U16:
case TYPE_I16:
return type->backend_type = LLVMIntTypeInContext(context, 16U);
return any_type->backend_type = LLVMIntTypeInContext(context, 16U);
case TYPE_U8:
case TYPE_I8:
return type->backend_type = LLVMIntTypeInContext(context, 8U);
return any_type->backend_type = LLVMIntTypeInContext(context, 8U);
case TYPE_BOOL:
return type->backend_type = LLVMIntTypeInContext(context, 1U);
return any_type->backend_type = LLVMIntTypeInContext(context, 1U);
case TYPE_POINTER:
return type->backend_type = llvm_type_from_ptr(context, type);
return any_type->backend_type = llvm_type_from_ptr(context, any_type);
case TYPE_STRING:
// TODO
return type->backend_type = LLVMPointerType(llvm_get_type(context, type_char), 0);
return any_type->backend_type = LLVMPointerType(llvm_get_type(context, type_char), 0);
case TYPE_ARRAY:
return type->backend_type = llvm_type_from_array(context, type);
return any_type->backend_type = llvm_type_from_array(context, any_type);
case TYPE_SUBARRAY:
{
LLVMTypeRef base_type = llvm_get_type(context, type_get_ptr(type->array.base));
LLVMTypeRef base_type = llvm_get_type(context, type_get_ptr(any_type->array.base));
LLVMTypeRef size_type = llvm_get_type(context, type_usize);
LLVMTypeRef array_type = LLVMStructCreateNamed(context, type->name);
LLVMTypeRef array_type = LLVMStructCreateNamed(context, any_type->name);
LLVMTypeRef types[2] = { base_type, size_type };
LLVMStructSetBody(array_type, types, 2, false);
return type->backend_type = array_type;
return any_type->backend_type = array_type;
}
case TYPE_VARARRAY:
return type->backend_type = llvm_get_type(context, type_get_ptr(type->array.base));
return any_type->backend_type = llvm_get_type(context, type_get_ptr(any_type->array.base));
}
UNREACHABLE;
}

View File

@@ -64,9 +64,6 @@ void expr_const_fprint(FILE *__restrict file, ExprConst *expr)
case TYPE_ENUM:
fprintf(file, "%s", expr->enum_constant->name);
break;
case TYPE_ERROR:
fprintf(file, "%s", expr->error_constant->name);
break;
case TYPE_STRING:
fprintf(file, "%.*s", expr->string.len, expr->string.chars);
break;
@@ -252,9 +249,6 @@ const char *expr_const_to_error_string(const ExprConst *expr)
case TYPE_ENUM:
asprintf(&buff, "%s.%s", expr->enum_constant->type->name, expr->enum_constant->name);
return buff;
case TYPE_ERROR:
asprintf(&buff, "%s.%s", expr->error_constant->type->name, expr->error_constant->name);
return buff;
case TYPE_STRING:
asprintf(&buff, "\"%*.s\"", expr->string.len, expr->string.chars);
return buff;

View File

@@ -24,6 +24,11 @@ inline Expr *parse_precedence_with_left_side(Context *context, Expr *left_side,
{
if (!expr_ok(left_side)) return left_side;
ParseFn infix_rule = rules[context->tok.type].infix;
if (!infix_rule)
{
SEMA_TOKEN_ERROR(context->tok, "An expression was expected.");
return poisoned_expr;
}
left_side = infix_rule(context, left_side);
}
return left_side;
@@ -67,7 +72,7 @@ inline Expr* parse_constant_expr(Context *context)
* ;
*
*/
bool parse_param_list(Context *context, Expr ***result, bool allow_type)
bool parse_param_list(Context *context, Expr ***result, bool allow_type, TokenType param_end)
{
*result = NULL;
while (1)
@@ -94,6 +99,7 @@ bool parse_param_list(Context *context, Expr ***result, bool allow_type)
{
return true;
}
if (context->tok.type == param_end) return true;
}
}
@@ -128,7 +134,7 @@ static inline Expr* parse_non_assign_expr(Context *context)
Expr *parse_expression_list(Context *context)
{
Expr *expr_list = EXPR_NEW_TOKEN(EXPR_EXPRESSION_LIST, context->tok);
if (!parse_param_list(context, &expr_list->expression_list, false)) return poisoned_expr;
if (!parse_param_list(context, &expr_list->expression_list, false, TOKEN_INVALID_TOKEN)) return poisoned_expr;
return expr_list;
}
@@ -206,12 +212,42 @@ static Expr *parse_range_expr(Context *context, Expr *left_side)
return range;
}
static bool token_may_end_expression(TokenType type)
{
switch (type)
{
case TOKEN_RPAREN:
case TOKEN_RBRACE:
case TOKEN_RBRACKET:
case TOKEN_EOS:
case TOKEN_COMMA:
return true;
default:
return false;
}
}
static inline Expr *parse_check_failable(Context *context, Expr *left_side)
{
Expr *expr_unwrap = EXPR_NEW_EXPR(EXPR_FAIL_CHECK, left_side);
advance_and_verify(context, TOKEN_QUESTION);
expr_unwrap->fail_check_expr = left_side;
RANGE_EXTEND_PREV(expr_unwrap);
return expr_unwrap;
}
static Expr *parse_ternary_expr(Context *context, Expr *left_side)
{
assert(expr_ok(left_side));
if (context->tok.type == TOKEN_QUESTION && token_may_end_expression(context->next_tok.type))
{
return parse_check_failable(context, left_side);
}
Expr *expr_ternary = EXPR_NEW_EXPR(EXPR_TERNARY, left_side);
expr_ternary->ternary_expr.cond = left_side;
// Check for elvis
if (try_consume(context, TOKEN_ELVIS))
{
@@ -286,12 +322,20 @@ Expr *parse_initializer_list(Context *context)
CONSUME_OR(TOKEN_LBRACE, poisoned_expr);
if (!try_consume(context, TOKEN_RBRACE))
{
if (!parse_param_list(context, &initializer_list->expr_initializer.initializer_expr, false)) return poisoned_expr;
if (!parse_param_list(context, &initializer_list->expr_initializer.initializer_expr, false, TOKEN_RBRACE)) return poisoned_expr;
CONSUME_OR(TOKEN_RBRACE, poisoned_expr);
}
return initializer_list;
}
static Expr *parse_failable(Context *context, Expr *left_side)
{
Expr *failable = expr_new(EXPR_FAILABLE, left_side->span);
advance_and_verify(context, TOKEN_BANG);
failable->failable_expr = left_side;
RANGE_EXTEND_PREV(failable);
return failable;
}
static Expr *parse_binary(Context *context, Expr *left_side)
{
assert(left_side && expr_ok(left_side));
@@ -327,7 +371,7 @@ static Expr *parse_call_expr(Context *context, Expr *left)
advance_and_verify(context, TOKEN_LPAREN);
if (context->tok.type != TOKEN_RPAREN)
{
if (!parse_param_list(context, &params, 0)) return poisoned_expr;
if (!parse_param_list(context, &params, 0, TOKEN_RPAREN)) return poisoned_expr;
}
TRY_CONSUME_OR(TOKEN_RPAREN, "Expected the ending ')' here", poisoned_expr);
@@ -407,39 +451,50 @@ static Expr *parse_maybe_scope(Context *context, Expr *left)
static Expr *parse_try_expr(Context *context, Expr *left)
{
assert(!left && "Unexpected left hand side");
Expr *try_expr = EXPR_NEW_TOKEN(EXPR_TRY, context->tok);
advance_and_verify(context, TOKEN_TRY);
try_expr->try_expr.expr = TRY_EXPR_OR(parse_precedence(context, PREC_TRY + 1), poisoned_expr);
try_expr->try_expr.type = TRY_EXPR;
if (try_consume(context, TOKEN_ELSE))
{
switch (context->tok.type)
{
case TOKEN_GOTO:
case TOKEN_RETURN:
case TOKEN_BREAK:
case TOKEN_CONTINUE:
case TOKEN_THROW:
{
Ast *ast = TRY_AST_OR(parse_jump_stmt_no_eos(context), poisoned_expr);
try_expr->try_expr.type = TRY_EXPR_ELSE_JUMP;
try_expr->try_expr.else_stmt = ast;
if (context->tok.type != TOKEN_EOS)
{
SEMA_ERROR(ast, "try-else jump statement must end with a ';'");
return poisoned_expr;
}
break;
}
default:
try_expr->try_expr.type = TRY_EXPR_ELSE_EXPR;
try_expr->try_expr.else_expr = TRY_EXPR_OR(parse_precedence(context, PREC_ASSIGNMENT), poisoned_expr);
break;
}
}
Expr *try_expr = EXPR_NEW_TOKEN(context->tok.type == TOKEN_TRY ? EXPR_TRY : EXPR_CATCH, context->tok);
advance(context);
CONSUME_OR(TOKEN_LPAREN, poisoned_expr);
try_expr->trycatch_expr = TRY_EXPR_OR(parse_expr(context), poisoned_expr);
CONSUME_OR(TOKEN_RPAREN, poisoned_expr);
return try_expr;
}
static Expr *parse_bangbang_expr(Context *context, Expr *left)
{
Expr *guard_expr = EXPR_NEW_TOKEN(EXPR_GUARD, context->tok);
advance_and_verify(context, TOKEN_BANGBANG);
guard_expr->guard_expr = left;
return guard_expr;
}
static Expr *parse_else_expr(Context *context, Expr *left)
{
Expr *else_expr = EXPR_NEW_TOKEN(EXPR_ELSE, context->tok);
advance_and_verify(context, TOKEN_ELSE);
else_expr->else_expr.expr = left;
switch (context->tok.type)
{
case TOKEN_RETURN:
case TOKEN_BREAK:
case TOKEN_CONTINUE:
{
Ast *ast = TRY_AST_OR(parse_jump_stmt_no_eos(context), poisoned_expr);
else_expr->else_expr.is_jump = true;
else_expr->else_expr.else_stmt = ast;
if (context->tok.type != TOKEN_EOS)
{
SEMA_ERROR(ast, "An else jump statement must end with a ';'");
return poisoned_expr;
}
break;
}
default:
else_expr->else_expr.else_expr = TRY_EXPR_OR(parse_precedence(context, PREC_ASSIGNMENT), poisoned_expr);
break;
}
return else_expr;
}
static Expr *parse_integer(Context *context, Expr *left)
{
assert(!left && "Had left hand side");
@@ -809,6 +864,17 @@ Expr *parse_type_expression_with_path(Context *context, Path *path)
{
return parse_type_compound_literal_expr_after_type(context, type);
}
if (try_consume(context, TOKEN_BANG))
{
Expr *expr = expr_new(EXPR_COMPOUND_LITERAL, type->span);
expr->expr_compound_literal.type_info = type;
expr->expr_compound_literal.initializer = expr_new(EXPR_INITIALIZER_LIST, type->span);
Expr *failable = expr_new(EXPR_FAILABLE, expr->span);
failable->failable_expr = expr;
RANGE_EXTEND_PREV(failable);
return failable;
}
CONSUME_OR(TOKEN_DOT, poisoned_expr);
return parse_type_access_expr_after_type(context, type);
}
@@ -835,6 +901,7 @@ static Expr* parse_expr_block(Context *context, Expr *left)
}
ParseRule rules[TOKEN_EOF + 1] = {
[TOKEN_ELSE] = { NULL, parse_else_expr, PREC_TRY_ELSE },
[TOKEN_ELLIPSIS] = { NULL, parse_range_expr, PREC_RANGE },
[TOKEN_QUESTION] = { NULL, parse_ternary_expr, PREC_TERNARY },
[TOKEN_ELVIS] = { NULL, parse_ternary_expr, PREC_TERNARY },
@@ -844,7 +911,9 @@ ParseRule rules[TOKEN_EOF + 1] = {
[TOKEN_LPARBRA] = { parse_expr_block, NULL, PREC_NONE },
[TOKEN_CAST] = { parse_cast_expr, NULL, PREC_NONE },
[TOKEN_TYPEOF] = { parse_typeof_expr, NULL, PREC_NONE },
[TOKEN_TRY] = { parse_try_expr, NULL, PREC_TRY },
[TOKEN_TRY] = { parse_try_expr, NULL, PREC_NONE },
[TOKEN_CATCH] = { parse_try_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 },
[TOKEN_MINUS_MOD] = { parse_unary_expr, parse_binary, PREC_ADDITIVE },
@@ -855,7 +924,7 @@ ParseRule rules[TOKEN_EOF + 1] = {
[TOKEN_STAR] = { parse_unary_expr, parse_binary, PREC_MULTIPLICATIVE },
[TOKEN_MULT_MOD] = { NULL, parse_binary, PREC_MULTIPLICATIVE },
[TOKEN_DOT] = { NULL, parse_access_expr, PREC_CALL },
[TOKEN_NOT] = { parse_unary_expr, NULL, PREC_UNARY },
[TOKEN_BANG] = { parse_unary_expr, parse_failable, PREC_UNARY },
[TOKEN_BIT_NOT] = { parse_unary_expr, NULL, PREC_UNARY },
[TOKEN_BIT_XOR] = { NULL, parse_binary, PREC_BIT },
[TOKEN_BIT_OR] = { NULL, parse_binary, PREC_BIT },

View File

@@ -21,36 +21,153 @@ static inline Ast *parse_declaration_stmt(Context *context)
return decl_stmt;
}
/**
* control_expression
* : decl_or_expr_list
* | declaration_list ';' decl_or_expr_list
* ;
*/
static inline bool parse_control_expression(Context *context, Ast **decls, Ast **exprs)
static inline Decl *parse_optional_label(Context *context, Ast *parent)
{
*exprs = TRY_AST_OR(parse_decl_expr_list(context), false);
if (!try_consume(context, TOKEN_EOS))
if (context->tok.type != TOKEN_CONST_IDENT) return NULL;
Decl *decl = decl_new(DECL_LABEL, context->tok, VISIBLE_LOCAL);
decl->label.parent = parent;
advance_and_verify(context, TOKEN_CONST_IDENT);
if (!try_consume(context, TOKEN_COLON))
{
*decls = NULL;
return true;
SEMA_ERROR(decl, "The name must be followed by a ':', did you forget it?");
return poisoned_decl;
}
return decl;
}
*decls = *exprs;
static inline void parse_optional_label_target(Context *context, Label *label)
{
if (context->tok.type == TOKEN_CONST_IDENT)
{
label->span = context->tok.span;
label->name = context->tok.string;
advance_and_verify(context, TOKEN_CONST_IDENT);
}
}
*exprs = TRY_AST_OR(parse_decl_expr_list(context), false);
static inline bool parse_asm_param(Context *context, AsmOperand **list)
{
AsmOperand operand;
// Reset parser
context->lexer.current = context->tok.span.loc + context->lexer.file_begin;
operand.constraints = lexer_scan_asm_constraint(&context->lexer);
if (operand.constraints.type == TOKEN_INVALID_TOKEN) return false;
// Restore state
context->tok = lexer_scan_token(&context->lexer);
context->next_tok = lexer_scan_token(&context->lexer);
operand.expr = TRY_EXPR_OR(parse_expr(context), false);
if (try_consume(context, TOKEN_AS))
{
EXPECT_OR(TOKEN_IDENT, false);
operand.alias = context->tok;
advance(context);
}
vec_add(*list, operand);
return true;
}
static inline bool parse_asm_paramlist(Context *context, AsmOperand **list)
{
if (context->tok.type == TOKEN_EOS || context->tok.type == TOKEN_RPAREN) return true;
while (1)
{
if (!parse_asm_param(context, list)) return false;
if (context->tok.type == TOKEN_EOS || context->tok.type == TOKEN_RPAREN) return true;
CONSUME_OR(TOKEN_COMMA, false);
}
}
static inline bool parse_asm_params(Context *context, Ast *asm_ast)
{
// Might be empty.
if (try_consume(context, TOKEN_RPAREN)) return true;
AsmParams *params = malloc_arena(sizeof(AsmParams));
asm_ast->asm_stmt.params = params;
// Parse outputs
if (!parse_asm_paramlist(context, &params->inputs)) return false;
// Might not have any more params
if (try_consume(context, TOKEN_RPAREN)) return true;
// Consume the ';'
advance_and_verify(context, TOKEN_EOS);
// Parse inputs
if (!parse_asm_paramlist(context, &params->inputs)) return false;
// Might not have any more params
if (try_consume(context, TOKEN_RPAREN)) return true;
while (1)
{
EXPECT_OR(TOKEN_IDENT, false);
vec_add(params->clobbers, context->tok);
if (!try_consume(context, TOKEN_COMMA)) break;
}
// Might not have any more params
if (try_consume(context, TOKEN_RPAREN)) return true;
// Consume the ';'
CONSUME_OR(TOKEN_EOS, false);
while (1)
{
EXPECT_OR(TOKEN_IDENT, false);
vec_add(params->labels, context->tok);
if (!try_consume(context, TOKEN_COMMA)) break;
}
// Consume the ')'
CONSUME_OR(TOKEN_RPAREN, false);
return true;
}
/**
* asm { ... }
* @param context
* @return
*/
static inline Ast* parse_asm_stmt(Context *context)
{
// TODO
SEMA_TOKEN_ERROR(context->tok, "ASM not supported yet.");
return poisoned_ast;
Ast *ast = AST_NEW_TOKEN(AST_ASM_STMT, context->tok);
advance_and_verify(context, TOKEN_ASM);
if (try_consume(context, TOKEN_LPAREN))
{
if (!parse_asm_params(context, ast)) return poisoned_ast;
}
if (context->tok.type != TOKEN_LBRACE)
{
SEMA_TOKEN_ERROR(context->tok, "Expected '{' to start asm segment.");
return poisoned_ast;
}
context->lexer.current = context->next_tok.span.loc + context->lexer.file_begin;
while (1)
{
context->tok = lexer_scan_asm(&context->lexer);
TokenType type = context->tok.type;
if (type == TOKEN_RBRACE || type == TOKEN_EOF) break;
context->prev_tok_end = context->tok.span.end_loc;
vec_add(ast->asm_stmt.instructions, context->tok);
}
if (context->tok.type == TOKEN_EOF)
{
sema_error_at(context->prev_tok_end, "Unexpected end of file while parsing asm, did you forget a '}'?");
return poisoned_ast;
}
assert(context->tok.type == TOKEN_RBRACE);
context->tok = lexer_scan_token(&context->lexer);
context->next_tok = lexer_scan_token(&context->lexer);
return ast;
}
/**
* do_stmt
* : DO statement WHILE '(' expression ')' ';'
@@ -61,24 +178,42 @@ static inline Ast* parse_do_stmt(Context *context)
advance_and_verify(context, TOKEN_DO);
do_ast->do_stmt.label = TRY_DECL_OR(parse_optional_label(context, do_ast), poisoned_ast);
do_ast->do_stmt.body = TRY_AST(parse_stmt(context));
CONSUME_OR(TOKEN_WHILE, poisoned_ast);
CONSUME_OR(TOKEN_LPAREN, poisoned_ast);
do_ast->do_stmt.expr = TRY_EXPR_OR(parse_expr(context), poisoned_ast);
CONSUME_OR(TOKEN_RPAREN, poisoned_ast);
CONSUME_OR(TOKEN_EOS, poisoned_ast);
if (try_consume(context, TOKEN_WHILE))
{
CONSUME_OR(TOKEN_LPAREN, poisoned_ast);
do_ast->do_stmt.expr = TRY_EXPR_OR(parse_expr(context), poisoned_ast);
CONSUME_OR(TOKEN_RPAREN, poisoned_ast);
CONSUME_OR(TOKEN_EOS, poisoned_ast);
}
return do_ast;
}
static inline bool token_type_ends_case(TokenType type)
{
return type == TOKEN_CASE || type == TOKEN_DEFAULT || type == TOKEN_RBRACE;
}
static inline Ast *parse_case_stmts(Context *context)
{
if (token_type_ends_case(context->tok.type)) return NULL;
Ast *compound = AST_NEW_TOKEN(AST_COMPOUND_STMT, context->tok);
while (!token_type_ends_case(context->tok.type))
{
Ast *stmt = TRY_AST(parse_stmt(context));
vec_add(compound->compound_stmt.stmts, stmt);
}
return compound;
}
/**
* catch_stmt
* : CATCH '(' ERROR ident ')' statement
* | CATCH '(' type_expression ident ')' statement
* : CATCH '(' expression ')' catch_body
* | CATCH '(' expression ')' compound_stmt
* ;
*/
static inline Ast* parse_catch_stmt(Context *context)
@@ -86,25 +221,23 @@ 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);
catch_stmt->catch_stmt.label = TRY_DECL_OR(parse_optional_label(context, catch_stmt), false);
CONSUME_OR(TOKEN_LPAREN, poisoned_ast);
TypeInfo *type = NULL;
if (context->tok.type == TOKEN_ERROR_TYPE)
{
type = type_info_new_base(type_error_union, context->tok.span);
advance(context);
}
else
{
type = TRY_TYPE_OR(parse_type(context), poisoned_ast);
}
EXPECT_IDENT_FOR_OR("error parameter", poisoned_ast);
Decl *decl = decl_new_var(context->tok, type, VARDECL_LOCAL, VISIBLE_LOCAL);
advance(context);
catch_stmt->catch_stmt.error_param = decl;
catch_stmt->catch_stmt.catchable = TRY_EXPR_OR(parse_expr(context), poisoned_ast);
CONSUME_OR(TOKEN_RPAREN, poisoned_ast);
if (context->tok.type == TOKEN_LBRACE && (context->next_tok.type == TOKEN_CASE || context->next_tok.type == TOKEN_DEFAULT))
{
catch_stmt->catch_stmt.is_switch = true;
if (!parse_switch_body(context, &catch_stmt->catch_stmt.cases, TOKEN_CASE, TOKEN_DEFAULT)) return poisoned_ast;
return catch_stmt;
}
catch_stmt->catch_stmt.body = TRY_AST(parse_stmt(context));
return catch_stmt;
}
@@ -127,16 +260,31 @@ static inline Ast* parse_defer_stmt(Context *context)
static inline Ast* parse_while_stmt(Context *context)
{
Ast *while_ast = AST_NEW_TOKEN(AST_WHILE_STMT, context->tok);
advance_and_verify(context, TOKEN_WHILE);
while_ast->while_stmt.label = TRY_DECL_OR(parse_optional_label(context, while_ast), poisoned_ast);
CONSUME_OR(TOKEN_LPAREN, poisoned_ast);
if (!parse_control_expression(context, &while_ast->while_stmt.decl, &while_ast->while_stmt.cond)) return poisoned_ast;
while_ast->while_stmt.cond = TRY_EXPR_OR(parse_decl_expr_list(context), poisoned_ast);
CONSUME_OR(TOKEN_RPAREN, poisoned_ast);
while_ast->while_stmt.body = TRY_AST(parse_stmt(context));
return while_ast;
}
/**
* if_expr
* : failable_type IDENT '=' initializer
* | failable_type IDENT NOFAIL_ASSIGN expression
* | expression
* ;
*
* if_cond_expr
* : if_expr
* | if_cond_expr ',' if_expr
* ;
*
* if_stmt
* : IF '(' control_expression ')' statement
* | IF '(' control_expression ')' compound_statement ELSE compound_statement
@@ -146,8 +294,9 @@ static inline Ast* parse_if_stmt(Context *context)
{
Ast *if_ast = AST_NEW_TOKEN(AST_IF_STMT, context->tok);
advance_and_verify(context, TOKEN_IF);
if_ast->if_stmt.label = TRY_DECL_OR(parse_optional_label(context, if_ast), poisoned_ast);
CONSUME_OR(TOKEN_LPAREN, poisoned_ast);
if (!parse_control_expression(context, &if_ast->if_stmt.decl, &if_ast->if_stmt.cond)) return poisoned_ast;
if_ast->if_stmt.cond = TRY_EXPR_OR(parse_decl_expr_list(context), poisoned_ast);
CONSUME_OR(TOKEN_RPAREN, poisoned_ast);
Ast *stmt = TRY_AST(parse_stmt(context));
if_ast->if_stmt.then_body = stmt;
@@ -159,35 +308,43 @@ static inline Ast* parse_if_stmt(Context *context)
return if_ast;
}
static inline bool token_type_ends_case(TokenType type)
{
return type == TOKEN_CASE || type == TOKEN_DEFAULT || type == TOKEN_RBRACE;
}
static inline Ast *parse_case_stmts(Context *context)
static bool parse_type_or_expr(Context *context, TypeInfo **type_info, Expr **expr)
{
if (token_type_ends_case(context->tok.type)) return NULL;
Ast *compound = AST_NEW_TOKEN(AST_COMPOUND_STMT, context->tok);
while (!token_type_ends_case(context->tok.type))
if (parse_next_is_case_type(context))
{
Ast *stmt = TRY_AST(parse_stmt(context));
vec_add(compound->compound_stmt.stmts, stmt);
*type_info = TRY_TYPE_OR(parse_type(context), false);
return true;
}
return compound;
*expr = TRY_EXPR_OR(parse_constant_expr(context), false);;
return true;
}
/**
*
* case_stmt
* : CASE constant_expression ':' case_stmts
* | CASE constant_expression ELLIPSIS constant_expression ':' cast_stmts
* | CAST type ':' cast_stmts
* ;
*/
static inline Ast* parse_case_stmt(Context *context)
{
Ast *ast = AST_NEW_TOKEN(AST_CASE_STMT, context->tok);
advance(context);
Expr *expr = TRY_EXPR_OR(parse_constant_expr(context), poisoned_ast);
ast->case_stmt.expr = expr;
TypeInfo *type = NULL;
Expr *expr = NULL;
if (!parse_type_or_expr(context, &type, &expr)) return poisoned_ast;
if (type)
{
ast->case_stmt.is_type = true;
ast->case_stmt.type_info = type;
}
else
{
ast->case_stmt.expr = expr;
}
TRY_CONSUME(TOKEN_COLON, "Missing ':' after case");
extend_ast_with_prev_token(context, ast);
ast->case_stmt.body = TRY_AST(parse_case_stmts(context));
@@ -201,7 +358,7 @@ static inline Ast* parse_case_stmt(Context *context)
static inline Ast *parse_default_stmt(Context *context)
{
Ast *ast = AST_NEW_TOKEN(AST_DEFAULT_STMT, context->tok);
advance_and_verify(context, TOKEN_DEFAULT);
advance(context);
TRY_CONSUME_OR(TOKEN_COLON, "Expected ':' after 'default'.", poisoned_ast);
extend_ast_with_prev_token(context, ast);
ast->case_stmt.body = TRY_AST(parse_case_stmts(context));
@@ -217,45 +374,50 @@ static inline Ast *parse_default_stmt(Context *context)
* | default_stmt switch body
* ;
*/
static inline bool parse_switch_body(Context *context, Ast *switch_ast)
bool parse_switch_body(Context *context, Ast ***cases, TokenType case_type, TokenType default_type)
{
Ast *result;
switch (context->tok.type)
CONSUME_OR(TOKEN_LBRACE, false);
while (!try_consume(context, TOKEN_RBRACE))
{
case TOKEN_CASE:
Ast *result;
TokenType next = context->tok.type;
if (next == case_type)
{
result = TRY_AST_OR(parse_case_stmt(context), false);
break;
case TOKEN_DEFAULT:
}
else if (next == default_type)
{
result = TRY_AST_OR(parse_default_stmt(context), false);
break;
default:
}
else
{
SEMA_TOKEN_ERROR(context->tok, "A 'case' or 'default' would be needed here, '%.*s' is not allowed.", source_range_len(context->tok.span), context->tok.start);
return false;
}
vec_add((*cases), result);
}
vec_add(switch_ast->switch_stmt.cases, result);
return true;
}
/**
* switch
* : SWITCH '(' control_expression ')' '{' switch_body '}'
* : SWITCH '(' decl_expr_list ')' '{' switch_body '}'
*/
static inline Ast* parse_switch_stmt(Context *context)
{
Ast *switch_ast = AST_NEW_TOKEN(AST_SWITCH_STMT, context->tok);
advance_and_verify(context, TOKEN_SWITCH);
switch_ast->switch_stmt.label = TRY_DECL_OR(parse_optional_label(context, switch_ast), poisoned_ast);
CONSUME_OR(TOKEN_LPAREN, poisoned_ast);
if (!parse_control_expression(context, &switch_ast->switch_stmt.decl, &switch_ast->switch_stmt.cond)) return poisoned_ast;
switch_ast->switch_stmt.cond = TRY_EXPR_OR(parse_decl_expr_list(context), poisoned_ast);
CONSUME_OR(TOKEN_RPAREN, poisoned_ast);
CONSUME_OR(TOKEN_LBRACE, poisoned_ast);
while (!try_consume(context, TOKEN_RBRACE))
{
if (!parse_switch_body(context, switch_ast)) return poisoned_ast;
}
if (!parse_switch_body(context, &switch_ast->switch_stmt.cases, TOKEN_CASE, TOKEN_DEFAULT)) return poisoned_ast;
return switch_ast;
}
/**
* for_statement
* : FOR '(' decl_expr_list ';' expression ';' ')' statement
@@ -270,11 +432,12 @@ static inline Ast* parse_for_stmt(Context *context)
{
Ast *ast = AST_NEW_TOKEN(AST_FOR_STMT, context->tok);
advance_and_verify(context, TOKEN_FOR);
ast->for_stmt.label = TRY_DECL_OR(parse_optional_label(context, ast), poisoned_ast);
CONSUME_OR(TOKEN_LPAREN, poisoned_ast);
if (context->tok.type != TOKEN_EOS)
{
ast->for_stmt.init = TRY_AST(parse_decl_expr_list(context));
ast->for_stmt.init = TRY_EXPR_OR(parse_decl_expr_list(context), poisoned_ast);
}
else
{
@@ -302,20 +465,6 @@ static inline Ast* parse_for_stmt(Context *context)
return ast;
}
/**
* goto
* : GOTO ct_ident
* ;
*/
static inline Ast* parse_goto(Context *context)
{
Ast *ast = AST_NEW_TOKEN(AST_GOTO_STMT, context->tok);
advance_and_verify(context, TOKEN_GOTO);
ast->goto_stmt.label_name = context->tok.string;
if (!consume_const_name(context, "label")) return poisoned_ast;
return ast;
}
/**
* continue_stmt
* : CONTINUE
@@ -324,6 +473,8 @@ static inline Ast* parse_continue(Context *context)
{
Ast *ast = AST_NEW_TOKEN(AST_CONTINUE_STMT, context->tok);
advance_and_verify(context, TOKEN_CONTINUE);
parse_optional_label_target(context, &ast->contbreak_stmt.label);
if (ast->contbreak_stmt.label.name) ast->contbreak_stmt.is_label = true;
return ast;
}
@@ -331,11 +482,32 @@ static inline Ast* parse_continue(Context *context)
/**
* next
* : NEXT
* | NEXT expr
*/
static inline Ast* parse_next(Context *context)
{
Ast *ast = AST_NEW_TOKEN(AST_NEXT_STMT, context->tok);
advance_and_verify(context, TOKEN_NEXT);
if (context->tok.type != TOKEN_EOS)
{
if (context->tok.type == TOKEN_CONST_IDENT && context->next_tok.type == TOKEN_COLON)
{
parse_optional_label_target(context, &ast->next_stmt.label);
advance_and_verify(context, TOKEN_COLON);
}
TypeInfo *type = NULL;
Expr *expr = NULL;
if (!parse_type_or_expr(context, &type, &expr)) return poisoned_ast;
if (type)
{
ast->next_stmt.is_type = true;
ast->next_stmt.type_info = type;
}
else
{
ast->next_stmt.target = expr;
}
}
return ast;
}
@@ -347,12 +519,15 @@ static inline Ast* parse_break(Context *context)
{
Ast *ast = AST_NEW_TOKEN(AST_BREAK_STMT, context->tok);
advance_and_verify(context, TOKEN_BREAK);
parse_optional_label_target(context, &ast->contbreak_stmt.label);
if (ast->contbreak_stmt.label.name) ast->contbreak_stmt.is_label = true;
return ast;
}
/**
* expr_stmt
* : expression EOS
* ;
*/
static inline Ast *parse_expr_stmt(Context *context)
{
@@ -362,6 +537,63 @@ static inline Ast *parse_expr_stmt(Context *context)
return stmt;
}
/**
* try_stmt
* : try '(' decl_expr ')' statement
* ;
*/
static inline Ast *parse_try_stmt(Context *context)
{
Ast *stmt = AST_NEW_TOKEN(AST_TRY_STMT, context->tok);
advance_and_verify(context, TOKEN_TRY);
TRY_CONSUME(TOKEN_LPAREN, "Expected a '(' after 'try'.");
stmt->try_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));
return stmt;
}
/**
* define_stmt
* : define CT_IDENT '=' const_expr EOS
* | define CT_TYPE '=' const_expr EOS
* | define CT_IDENT EOS
* | define CT_TYPE EOS
* ;
*/
static inline Ast *parse_define_stmt(Context *context)
{
Ast *ast = AST_NEW_TOKEN(AST_DEFINE_STMT, context->tok);
advance_and_verify(context, TOKEN_DEFINE);
Decl *decl = decl_new_var(context->tok, NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL);
ast->define_stmt = decl;
switch (context->tok.type)
{
case TOKEN_CT_IDENT:
advance(context);
if (try_consume(context, TOKEN_EQ))
{
decl->var.init_expr = TRY_EXPR_OR(parse_expr(context), poisoned_ast);
}
break;
case TOKEN_CT_TYPE_IDENT:
advance(context);
if (try_consume(context, TOKEN_EQ))
{
decl->var.type_info = TRY_TYPE_OR(parse_type(context), poisoned_ast);
}
break;
default:
SEMA_TOKEN_ERROR(context->tok, "Expected a compile time variable name ('$Foo' or '$foo').");
return poisoned_ast;
}
TRY_CONSUME_EOS();
return ast;
}
/**
* ct_else_stmt
* : CT_ELSE compound_stmt
@@ -421,18 +653,6 @@ static inline Ast* parse_ct_if_stmt(Context *context)
return ast;
}
/**
* label_stmt
* : ct_label ':'
*/
static inline Ast *parse_label_stmt(Context *context)
{
Ast *ast = AST_NEW_TOKEN(AST_LABEL, context->tok);
ast->label_stmt.name = context->tok.string;
advance_and_verify(context, TOKEN_CONST_IDENT);
advance_and_verify(context, TOKEN_COLON);
return extend_ast_with_prev_token(context, ast);
}
/**
@@ -453,18 +673,7 @@ static inline Ast *parse_return(Context *context)
return ast;
}
/**
* throw
* : THROW expr
*/
static inline Ast *parse_throw(Context *context)
{
Ast *ast = AST_NEW_TOKEN(AST_THROW_STMT, context->tok);
advance_and_verify(context, TOKEN_THROW);
ast->throw_stmt.throw_value = TRY_EXPR_OR(parse_expr(context), poisoned_ast);
RANGE_EXTEND_PREV(ast);
return ast;
}
/**
* volatile_stmt
@@ -544,43 +753,7 @@ static inline Ast* parse_ct_switch_stmt(Context *context)
Ast *ast = AST_NEW_TOKEN(AST_CT_SWITCH_STMT, context->tok);
advance_and_verify(context, TOKEN_CT_SWITCH);
ast->ct_switch_stmt.cond = TRY_EXPR_OR(parse_paren_expr(context), poisoned_ast);
CONSUME_OR(TOKEN_LBRACE, poisoned_ast);
Ast **switch_statements = NULL;
Ast *stmt = poisoned_ast;
while (stmt)
{
switch (context->tok.type)
{
case TOKEN_CT_CASE:
stmt = AST_NEW_TOKEN(AST_CT_CASE_STMT, context->tok);
advance(context);
while (1)
{
TypeInfo *type = TRY_TYPE_OR(parse_type(context), poisoned_ast);
vec_add(stmt->ct_case_stmt.types, type);
if (!try_consume(context, TOKEN_COMMA)) break;
}
CONSUME_OR(TOKEN_COLON, poisoned_ast);
stmt->ct_case_stmt.body = TRY_AST_OR(parse_stmt(context), poisoned_ast);
vec_add(switch_statements, stmt);
break;
case TOKEN_CT_DEFAULT:
stmt = AST_NEW_TOKEN(AST_CT_CASE_STMT, context->tok);
advance(context);
CONSUME_OR(TOKEN_COLON, poisoned_ast);
stmt->ct_default_stmt = TRY_AST_OR(parse_stmt(context), poisoned_ast);
vec_add(switch_statements, stmt);
break;
case TOKEN_RBRACE:
stmt = NULL;
break;
default:
SEMA_TOKEN_ERROR(context->tok, "Expected $case or $default.");
return poisoned_ast;
}
}
CONSUME_OR(TOKEN_RBRACE, poisoned_ast);
ast->ct_switch_stmt.body = switch_statements;
if (!parse_switch_body(context, &ast->ct_switch_stmt.body, TOKEN_CT_CASE, TOKEN_CT_DEFAULT)) return poisoned_ast;
return ast;
}
@@ -590,6 +763,9 @@ Ast *parse_stmt(Context *context)
{
switch (context->tok.type)
{
case TOKEN_ASM_STRING:
case TOKEN_ASM_CONSTRAINT:
UNREACHABLE
case TOKEN_LBRACE:
return parse_compound_stmt(context);
case TOKEN_HALF:
@@ -622,7 +798,7 @@ Ast *parse_stmt(Context *context)
case TOKEN_TYPEID:
case TOKEN_CT_TYPE_IDENT:
case TOKEN_TYPE_IDENT:
case TOKEN_ERROR_TYPE:
case TOKEN_ERR:
case TOKEN_IDENT:
if (parse_next_is_decl(context))
{
@@ -632,14 +808,14 @@ Ast *parse_stmt(Context *context)
{
return parse_expr_stmt(context);
}
case TOKEN_TRY:
return parse_try_stmt(context);
case TOKEN_DEFINE:
return parse_define_stmt(context);
case TOKEN_LOCAL: // Local means declaration!
case TOKEN_CONST: // Const means declaration!
return parse_declaration_stmt(context);
case TOKEN_CONST_IDENT:
if (context->next_tok.type == TOKEN_COLON)
{
return parse_label_stmt(context);
}
return parse_expr_stmt(context);
case TOKEN_AT:
return parse_expr_stmt(context);
@@ -656,31 +832,12 @@ Ast *parse_stmt(Context *context)
return parse_defer_stmt(context);
case TOKEN_SWITCH:
return parse_switch_stmt(context);
case TOKEN_GOTO:
{
Ast *ast = TRY_AST(parse_goto(context));
RETURN_AFTER_EOS(ast);
}
case TOKEN_DO:
return parse_do_stmt(context);
case TOKEN_FOR:
return parse_for_stmt(context);
case TOKEN_CATCH:
return parse_catch_stmt(context);
case TOKEN_TRY:
if (is_valid_try_statement(context->next_tok.type))
{
Expr *try_expr = EXPR_NEW_TOKEN(EXPR_TRY, context->tok);
advance(context);
Ast *stmt = TRY_AST(parse_stmt(context));
try_expr->try_expr.type = TRY_STMT;
try_expr->try_expr.stmt = stmt;
RANGE_EXTEND_PREV(try_expr);
Ast *ast = AST_NEW(AST_EXPR_STMT, try_expr->span);
ast->expr_stmt = try_expr;
return ast;
}
return parse_expr_stmt(context);
case TOKEN_CONTINUE:
{
Ast *ast = TRY_AST(parse_continue(context));
@@ -712,11 +869,6 @@ Ast *parse_stmt(Context *context)
return parse_ct_switch_stmt(context);
case TOKEN_CT_FOR:
return parse_ct_for_stmt(context);
case TOKEN_THROW:
{
Ast *ast = TRY_AST(parse_throw(context));
RETURN_AFTER_EOS(ast);
}
case TOKEN_VOLATILE:
return parse_volatile_stmt(context);
case TOKEN_STAR:
@@ -727,7 +879,7 @@ Ast *parse_stmt(Context *context)
case TOKEN_BIT_XOR:
case TOKEN_LPAREN:
case TOKEN_MINUS:
case TOKEN_NOT:
case TOKEN_BANG:
case TOKEN_OR:
case TOKEN_PLUS:
case TOKEN_MINUSMINUS:
@@ -798,8 +950,6 @@ Ast *parse_stmt(Context *context)
case TOKEN_PUBLIC:
case TOKEN_EXTERN:
case TOKEN_STRUCT:
case TOKEN_THROWS:
case TOKEN_ERRSET:
case TOKEN_TYPEDEF:
case TOKEN_UNION:
case TOKEN_UNTIL:
@@ -817,6 +967,7 @@ Ast *parse_stmt(Context *context)
case TOKEN_CT_DEFAULT:
case TOKEN_RPARBRA:
case TOKEN_IN:
case TOKEN_BANGBANG:
SEMA_TOKEN_ERROR(context->tok, "Unexpected '%s' found when expecting a statement.", token_type_to_string(context->tok.type));
advance(context);
return poisoned_ast;
@@ -841,16 +992,12 @@ Ast *parse_jump_stmt_no_eos(Context *context)
{
switch (context->tok.type)
{
case TOKEN_GOTO:
return parse_goto(context);
case TOKEN_RETURN:
return parse_return(context);
case TOKEN_BREAK:
return parse_break(context);
case TOKEN_CONTINUE:
return parse_continue(context);
case TOKEN_THROW:
return parse_throw(context);
default:
UNREACHABLE
}

View File

@@ -190,7 +190,6 @@ static void recover_top_level(Context *context)
case TOKEN_STRUCT:
case TOKEN_IMPORT:
case TOKEN_UNION:
case TOKEN_ERRSET:
case TOKEN_MACRO:
case TOKEN_EXTERN:
return;
@@ -336,8 +335,8 @@ static inline TypeInfo *parse_base_type(Context *context)
type_info = type_info_new(TYPE_INFO_IDENTIFIER, context->tok.span);
type_info->unresolved.name_loc = context->tok;
break;
case TOKEN_ERROR_TYPE:
type_found = type_error_union;
case TOKEN_ERR:
type_found = type_error;
break;
case TOKEN_VOID:
type_found = type_void;
@@ -509,6 +508,7 @@ TypeInfo *parse_type(Context *context)
return type_info;
}
#pragma mark --- Decl parsing
/**
@@ -543,7 +543,6 @@ Decl *parse_decl_after_type(Context *context, bool local, TypeInfo *type)
advance_and_verify(context, TOKEN_EQ);
decl->var.init_expr = TRY_EXPR_OR(parse_initializer(context), poisoned_decl);
}
return decl;
}
@@ -561,8 +560,14 @@ Decl *parse_decl(Context *context)
TypeInfo *type_info = parse_type(context);
TypeInfo *type = TRY_TYPE_OR(type_info, poisoned_decl);
bool failable = try_consume(context, TOKEN_BANG);
Decl *decl = TRY_DECL_OR(parse_decl_after_type(context, local, type), poisoned_decl);
if (failable && decl->var.unwrap)
{
SEMA_ERROR(decl, "You cannot use unwrap with a failable variable.");
return poisoned_decl;
}
decl->var.failable = failable;
if (constant) decl->var.kind = VARDECL_CONST;
return decl;
@@ -591,10 +596,12 @@ static inline Decl *parse_const_declaration(Context *context, Visibility visibil
}
else
{
if (token_is_type(context->tok.type) || context->tok.type == TOKEN_CT_TYPE_IDENT || context->tok.type == TOKEN_TYPE_IDENT)
if (token_is_any_type(context->tok.type))
{
decl->var.type_info = TRY_TYPE_OR(parse_type(context), poisoned_decl);
}
decl->name = context->tok.string;
decl->name_span = context->tok.span;
if (!consume_const_name(context, "constant")) return poisoned_decl;
}
@@ -608,8 +615,8 @@ static inline Decl *parse_const_declaration(Context *context, Visibility visibil
/**
* global_declaration
* : type_expression IDENT ';'
* | type_expression IDENT '=' expression ';'
* : failable_type IDENT ';'
* | failable_type IDENT '=' expression ';'
* ;
*
* @param visibility
@@ -643,6 +650,11 @@ static inline Decl *parse_incremental_array(Context *context)
Token name = context->tok;
advance_and_verify(context, TOKEN_IDENT);
if (!try_consume(context, TOKEN_PLUS_ASSIGN))
{
SEMA_TOKEN_ERROR(name, "Did you miss a declaration before the variable name?");
return poisoned_decl;
}
CONSUME_OR(TOKEN_PLUS_ASSIGN, poisoned_decl);
Decl *decl = decl_new(DECL_ARRAY_VALUE, name, VISIBLE_LOCAL);
decl->incr_array_decl = TRY_EXPR_OR(parse_initializer(context), poisoned_decl);
@@ -661,34 +673,36 @@ static inline Decl *parse_incremental_array(Context *context)
*
* @return bool
*/
Ast *parse_decl_expr_list(Context *context)
Expr *parse_decl_expr_list(Context *context)
{
Ast *decl_expr = AST_NEW_TOKEN(AST_DECL_EXPR_LIST, context->tok);
decl_expr->decl_expr_stmt = NULL;
Expr *decl_expr = EXPR_NEW_TOKEN(EXPR_DECL_LIST, context->tok);
decl_expr->dexpr_list_expr = NULL;
while (1)
{
if (parse_next_is_decl(context))
{
Decl *decl = TRY_DECL_OR(parse_decl(context), poisoned_ast);
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->decl_expr_stmt, stmt);
vec_add(decl_expr->dexpr_list_expr, stmt);
}
else
{
Expr *expr = TRY_EXPR_OR(parse_expr(context), poisoned_ast);
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->decl_expr_stmt, stmt);
vec_add(decl_expr->dexpr_list_expr, stmt);
}
if (!try_consume(context, TOKEN_COMMA)) break;
}
return extend_ast_with_prev_token(context, decl_expr);
RANGE_EXTEND_PREV(decl_expr);
return decl_expr;
}
bool parse_next_is_decl(Context *context)
{
TokenType next_tok = context->next_tok.type;
switch (context->tok.type)
{
case TOKEN_VOID:
@@ -716,11 +730,11 @@ bool parse_next_is_decl(Context *context)
case TOKEN_C_ULONGLONG:
case TOKEN_TYPE_IDENT:
case TOKEN_CT_TYPE_IDENT:
case TOKEN_ERROR_TYPE:
case TOKEN_ERR:
case TOKEN_TYPEID:
return context->next_tok.type == TOKEN_STAR || context->next_tok.type == TOKEN_LBRACKET || context->next_tok.type == TOKEN_IDENT;
return (next_tok == TOKEN_BANG) | (next_tok == TOKEN_STAR) | (next_tok == TOKEN_LBRACKET) | (next_tok == TOKEN_IDENT);
case TOKEN_IDENT:
if (context->next_tok.type != TOKEN_SCOPE) return false;
if (next_tok != TOKEN_SCOPE) return false;
// We need a little lookahead to see if this is type or expression.
context_store_lexer_state(context);
do
@@ -737,6 +751,56 @@ bool parse_next_is_decl(Context *context)
bool parse_next_is_case_type(Context *context)
{
TokenType next_tok = context->next_tok.type;
switch (context->tok.type)
{
case TOKEN_VOID:
case TOKEN_BYTE:
case TOKEN_BOOL:
case TOKEN_CHAR:
case TOKEN_DOUBLE:
case TOKEN_FLOAT:
case TOKEN_INT:
case TOKEN_ISIZE:
case TOKEN_LONG:
case TOKEN_SHORT:
case TOKEN_UINT:
case TOKEN_ULONG:
case TOKEN_USHORT:
case TOKEN_USIZE:
case TOKEN_QUAD:
case TOKEN_C_SHORT:
case TOKEN_C_INT:
case TOKEN_C_LONG:
case TOKEN_C_LONGLONG:
case TOKEN_C_USHORT:
case TOKEN_C_UINT:
case TOKEN_C_ULONG:
case TOKEN_C_ULONGLONG:
case TOKEN_TYPE_IDENT:
case TOKEN_CT_TYPE_IDENT:
case TOKEN_ERR:
case TOKEN_TYPEID:
return (next_tok == TOKEN_STAR) | (next_tok == TOKEN_LBRACKET | next_tok == TOKEN_COLON | next_tok == TOKEN_EOS);
case TOKEN_IDENT:
if (next_tok != TOKEN_SCOPE) return false;
// We need a little lookahead to see if this is type or expression.
context_store_lexer_state(context);
do
{
advance(context); advance(context);
} while (context->tok.type == TOKEN_IDENT && context->next_tok.type == TOKEN_SCOPE);
bool is_type = context->tok.type == TOKEN_TYPE_IDENT;
context_restore_lexer_state(context);
return is_type;
default:
return false;
}
}
#pragma mark --- Parse parameters & throws & attributes
@@ -794,55 +858,6 @@ static inline bool parse_attributes(Context *context, Decl *parent_decl)
/**
* throw_declaration
* : THROWS
* | THROWS error_list
* ;
*
* opt_throw_declaration
* : throw_declaration
* |
* ;
*
*/
static inline bool parse_opt_throw_declaration(Context *context, Visibility visibility, FunctionSignature *signature)
{
if (context->tok.type == TOKEN_THROW)
{
SEMA_TOKEN_ERROR(context->tok, "Did you mean 'throws'?");
return false;
}
if (!try_consume(context, TOKEN_THROWS)) return true;
if (context->tok.type != TOKEN_TYPE_IDENT && context->tok.type != TOKEN_IDENT)
{
signature->throw_any = true;
return true;
}
TypeInfo **throws = NULL;
while (1)
{
TypeInfo *throw = parse_base_type(context);
if (!type_info_ok(throw)) return false;
vec_add(throws, throw);
if (!try_consume(context, TOKEN_COMMA)) break;
}
switch (context->tok.type)
{
case TOKEN_TYPE_IDENT:
SEMA_TOKEN_ERROR(context->tok, "Expected ',' between each error type.");
return false;
case TOKEN_IDENT:
case TOKEN_CONST_IDENT:
SEMA_TOKEN_ERROR(context->tok, "Expected an error type.");
return false;
default:
break;
}
signature->throws = throws;
return true;
}
/**
@@ -1093,7 +1108,7 @@ static inline Ast *parse_generics_statements(Context *context)
/**
* generics_declaration
* : GENERIC opt_path IDENT '(' macro_argument_list ')' '{' generics_body '}'
* | GENERIC type_expression opt_path IDENT '(' macro_argument_list ')' '{' generics_body '}'
* | GENERIC failable_type opt_path IDENT '(' macro_argument_list ')' '{' generics_body '}'
* ;
*
* opt_path
@@ -1134,45 +1149,47 @@ static inline Decl *parse_generics_declaration(Context *context, Visibility visi
}
CONSUME_OR(TOKEN_LBRACE, poisoned_decl);
Ast **cases = NULL;
while (!try_consume(context, TOKEN_RBRACE))
{
if (context->tok.type == TOKEN_CASE)
{
Ast *generic_case = AST_NEW_TOKEN(AST_GENERIC_CASE_STMT, context->tok);
advance_and_verify(context, TOKEN_CASE);
TypeInfo **types = NULL;
while (!try_consume(context, TOKEN_COLON))
{
TypeInfo *type = TRY_TYPE_OR(parse_type(context), poisoned_decl);
types = VECADD(types, type);
if (!try_consume(context, TOKEN_COMMA) && context->tok.type != TOKEN_COLON)
{
SEMA_TOKEN_ERROR(context->tok, "Expected ',' or ':'.");
return poisoned_decl;
}
}
generic_case->generic_case_stmt.types = types;
generic_case->generic_case_stmt.body = TRY_AST_OR(parse_generics_statements(context), poisoned_decl);
cases = VECADD(cases, generic_case);
continue;
}
if (context->tok.type == TOKEN_DEFAULT)
{
Ast *generic_case = AST_NEW_TOKEN(AST_GENERIC_DEFAULT_STMT, context->tok);
advance_and_verify(context, TOKEN_DEFAULT);
CONSUME_OR(TOKEN_COLON, poisoned_decl);
generic_case->generic_default_stmt = TRY_AST_OR(parse_generics_statements(context), poisoned_decl);
cases = VECADD(cases, generic_case);
continue;
}
SEMA_TOKEN_ERROR(context->tok, "Expected 'case' or 'default'.");
return poisoned_decl;
}
if (!parse_switch_body(context, &cases, TOKEN_CASE, TOKEN_DEFAULT)) return poisoned_decl;
decl->generic_decl.cases = cases;
decl->generic_decl.parameters = parameters;
return decl;
}
static inline Decl *parse_define(Context *context, Visibility visibility)
{
advance_and_verify(context, TOKEN_DEFINE);
TypeInfo *rtype = NULL;
if (context->tok.type != TOKEN_IDENT)
{
rtype = TRY_TYPE_OR(parse_type(context), poisoned_decl);
}
bool had_error;
Path *path = parse_path_prefix(context, &had_error);
if (had_error) return poisoned_decl;
Decl *decl = decl_new(DECL_GENERIC, context->tok, visibility);
decl->generic_decl.path = path;
if (!consume_ident(context, "generic function name")) return poisoned_decl;
decl->generic_decl.rtype = rtype;
Token *parameters = NULL;
CONSUME_OR(TOKEN_LPAREN, poisoned_decl);
while (!try_consume(context, TOKEN_RPAREN))
{
if (context->tok.type != TOKEN_IDENT)
{
SEMA_TOKEN_ERROR(context->tok, "Expected an identifier.");
return false;
}
parameters = VECADD(parameters, context->tok);
advance(context);
COMMA_RPAREN_OR(poisoned_decl);
}
CONSUME_OR(TOKEN_LBRACE, poisoned_decl);
Ast **cases = NULL;
if (!parse_switch_body(context, &cases, TOKEN_CASE, TOKEN_DEFAULT)) return poisoned_decl;
decl->generic_decl.cases = cases;
decl->generic_decl.parameters = parameters;
return decl;
}
static AttributeDomain TOKEN_TO_ATTR[TOKEN_EOF + 1] = {
@@ -1183,7 +1200,7 @@ static AttributeDomain TOKEN_TO_ATTR[TOKEN_EOF + 1] = {
[TOKEN_UNION] = ATTR_UNION,
[TOKEN_CONST] = ATTR_CONST,
[TOKEN_TYPEDEF] = ATTR_TYPEDEF,
[TOKEN_ERRSET] = ATTR_ERROR,
[TOKEN_ERR] = ATTR_ERROR,
};
/**
@@ -1246,8 +1263,8 @@ static inline Decl *parse_attribute_declaration(Context *context, Visibility vis
*/
/**
* func_typedef
* : FUNC type_expression opt_parameter_type_list
* | FUNC type_expression opt_parameter_type_list throw_declaration
* : FUNC failable_type opt_parameter_type_list
* | FUNC failable_type opt_parameter_type_list throw_declaration
* ;
*/
static inline bool parse_func_typedef(Context *context, Decl *decl, Visibility visibility)
@@ -1255,12 +1272,16 @@ static inline bool parse_func_typedef(Context *context, Decl *decl, Visibility v
decl->typedef_decl.is_func = true;
advance_and_verify(context, TOKEN_FUNC);
TypeInfo *type_info = TRY_TYPE_OR(parse_type(context), false);
if (try_consume(context, TOKEN_BANG))
{
decl->typedef_decl.function_signature.failable = true;
}
decl->typedef_decl.function_signature.rtype = type_info;
if (!parse_opt_parameter_type_list(context, visibility, &(decl->typedef_decl.function_signature), true))
{
return false;
}
return parse_opt_throw_declaration(context, visibility, &(decl->typedef_decl.function_signature));
return true;
}
@@ -1291,12 +1312,15 @@ static inline Decl *parse_macro_declaration(Context *context, Visibility visibil
advance_and_verify(context, TOKEN_MACRO);
TypeInfo *rtype = NULL;
bool failable = false;
if (context->tok.type != TOKEN_IDENT)
{
rtype = TRY_TYPE_OR(parse_type(context), poisoned_decl);
failable = try_consume(context, TOKEN_BANG);
}
Decl *decl = decl_new(DECL_MACRO, context->tok, visibility);
decl->macro_decl.rtype = rtype;
decl->macro_decl.failable = failable;
TRY_CONSUME_OR(TOKEN_IDENT, "Expected a macro name here", poisoned_decl);
CONSUME_OR(TOKEN_LPAREN, poisoned_decl);
@@ -1332,48 +1356,37 @@ static inline Decl *parse_macro_declaration(Context *context, Visibility visibil
/**
* error_declaration
* : ERROR TYPE_IDENT '{' error_list '}'
* : ERROR TYPE_IDENT ';'
* | ERROR TYPE_IDENT '{' error_data '}'
* ;
*
*/
static inline Decl *parse_error_declaration(Context *context, Visibility visibility)
{
advance_and_verify(context, TOKEN_ERRSET);
advance_and_verify(context, TOKEN_ERR);
Decl *error_decl = decl_new_with_type(context->tok, DECL_ERROR, visibility);
Decl *err_decl = decl_new_with_type(context->tok, DECL_ERR, visibility);
if (!consume_type_name(context, "error type")) return poisoned_decl;
CONSUME_OR(TOKEN_LBRACE, poisoned_decl);
while (context->tok.type == TOKEN_CONST_IDENT)
{
Decl *err_constant = decl_new(DECL_ERROR_CONSTANT, context->tok, error_decl->visibility);
err_constant->error_constant.parent = error_decl;
VECEACH(error_decl->error.error_constants, i)
{
Decl *other_constant = error_decl->error.error_constants[i];
if (other_constant->name == context->tok.string)
{
SEMA_TOKEN_ERROR(context->tok, "This error is declared twice.");
SEMA_PREV(other_constant, "The previous declaration was here.");
decl_poison(err_constant);
decl_poison(error_decl);
break;
}
}
error_decl->error.error_constants = VECADD(error_decl->error.error_constants, err_constant);
advance_and_verify(context, TOKEN_CONST_IDENT);
if (!try_consume(context, TOKEN_COMMA)) break;
}
if (context->tok.type == TOKEN_TYPE_IDENT || context->tok.type == TOKEN_IDENT)
{
SEMA_TOKEN_ERROR(context->tok, "Errors must be all upper case.");
return poisoned_decl;
}
CONSUME_OR(TOKEN_RBRACE, poisoned_decl);
return error_decl;
if (try_consume(context, TOKEN_LBRACE))
{
while (!try_consume(context, TOKEN_RBRACE))
{
TypeInfo *type = TRY_TYPE_OR(parse_type(context), poisoned_decl);
if (context->tok.type != TOKEN_IDENT)
{
SEMA_TOKEN_ERROR(context->tok, "Expected an identifier here.");
return poisoned_decl;
}
Decl *member = decl_new(DECL_MEMBER, context->tok, visibility);
advance(context);
add_struct_member(err_decl, err_decl, member, type);
TRY_CONSUME_EOS_OR(poisoned_decl);
}
return err_decl;
}
TRY_CONSUME_EOS_OR(poisoned_decl);
return err_decl;
}
/**
@@ -1457,7 +1470,7 @@ static inline Decl *parse_enum_declaration(Context *context, Visibility visibili
if (try_consume(context, TOKEN_LPAREN))
{
Expr **result = NULL;
if (!parse_param_list(context, &result, true)) return poisoned_decl;
if (!parse_param_list(context, &result, true, TOKEN_RPAREN)) return poisoned_decl;
enum_const->enum_constant.args = result;
CONSUME_OR(TOKEN_RPAREN, poisoned_decl);
}
@@ -1494,8 +1507,8 @@ static inline Decl *parse_enum_declaration(Context *context, Visibility visibili
* ;
*
* func_declaration
* : FUNC type_expression func_name '(' opt_parameter_type_list ')' opt_attributes
* | FUNC type_expression func_name '(' opt_parameter_type_list ')' throw_declaration opt_attributes
* : FUNC failable_type func_name '(' opt_parameter_type_list ')' opt_attributes
* | FUNC failable_type func_name '(' opt_parameter_type_list ')' throw_declaration opt_attributes
* ;
*
* @param visibility
@@ -1506,10 +1519,9 @@ static inline Decl *parse_func_definition(Context *context, Visibility visibilit
advance_and_verify(context, TOKEN_FUNC);
TypeInfo *return_type = TRY_TYPE_OR(parse_type(context), poisoned_decl);
Decl *func = decl_new(DECL_FUNC, context->tok, visibility);
func->func.function_signature.rtype = return_type;
func->func.function_signature.failable = try_consume(context, TOKEN_BANG);
SourceRange start = context->tok.span;
bool had_error;
Path *path = parse_path_prefix(context, &had_error);
@@ -1535,8 +1547,6 @@ static inline Decl *parse_func_definition(Context *context, Visibility visibilit
RANGE_EXTEND_PREV(func);
if (!parse_opt_parameter_type_list(context, visibility, &(func->func.function_signature), is_interface)) return poisoned_decl;
if (!parse_opt_throw_declaration(context, visibility, &(func->func.function_signature))) return poisoned_decl;
if (!parse_attributes(context, func)) return poisoned_decl;
// TODO remove
@@ -1671,6 +1681,8 @@ static inline Decl *parse_top_level(Context *context)
switch (context->tok.type)
{
case TOKEN_DEFINE:
return parse_define(context, visibility);
case TOKEN_ATTRIBUTE:
return parse_attribute_declaration(context, visibility);
case TOKEN_FUNC:
@@ -1689,7 +1701,7 @@ static inline Decl *parse_top_level(Context *context)
return parse_macro_declaration(context, visibility);
case TOKEN_ENUM:
return parse_enum_declaration(context, visibility);
case TOKEN_ERRSET:
case TOKEN_ERR:
return parse_error_declaration(context, visibility);
case TOKEN_TYPEDEF:
return parse_typedef_declaration(context, visibility);

View File

@@ -29,6 +29,11 @@ do { if (!try_consume(context, TOKEN_COMMA) && context->tok.type != TOKEN_RPAREN
SEMA_TOKEN_ERROR(context->tok, "Expected ',' or ')'"); return _res; } } while(0)
typedef enum
{
DECL_PARSE_NORMAL,
DECL_PARSE_UNWRAP
} DeclParse;
Ast *parse_stmt(Context *context);
Path *parse_path_prefix(Context *context, bool *had_error);
Expr *parse_type_expression_with_path(Context *context, Path *path);
@@ -38,15 +43,17 @@ Expr* parse_constant_expr(Context *context);
Expr *parse_initializer_list(Context *context);
Expr *parse_initializer(Context *context);
Decl *parse_decl(Context *context);
Ast *parse_decl_expr_list(Context *context);
Expr *parse_decl_expr_list(Context *context);
Ast* parse_compound_stmt(Context *context);
Ast *parse_jump_stmt_no_eos(Context *context);
bool parse_switch_body(Context *context, Ast ***cases, TokenType case_type, TokenType default_type);
Expr *parse_expression_list(Context *context);
Decl *parse_decl_after_type(Context *context, bool local, TypeInfo *type);
bool parse_param_list(Context *context, Expr ***result, bool allow_type);
bool parse_param_list(Context *context, Expr ***result, bool allow_type, TokenType end_type);
Expr *parse_type_compound_literal_expr_after_type(Context *context, TypeInfo *type_info);
Expr *parse_type_access_expr_after_type(Context *context, TypeInfo *type_info);
bool parse_next_is_decl(Context *context);
bool parse_next_is_case_type(Context *context);
void error_at_current(Context *context, const char* message, ...);
bool try_consume(Context *context, TokenType type);
bool consume(Context *context, TokenType type, const char *message, ...);

View File

@@ -15,11 +15,10 @@
static inline void insert_cast(Expr *expr, CastKind kind, Type *canonical)
{
Expr *inner = malloc_arena(sizeof(Expr));
assert(expr->resolve_status == RESOLVE_DONE);
assert(expr->type);
assert(canonical->canonical == canonical);
*inner = *expr;
Expr *inner = COPY(expr);
expr->expr_kind = EXPR_CAST;
expr->cast_expr.kind = kind;
expr->cast_expr.expr = inner;
@@ -226,6 +225,7 @@ bool bofp(Expr *left, Type *canonical, Type *type, CastType cast_type)
*/
bool xibo(Expr *left, Type *canonical, Type *type, CastType cast_type)
{
if (cast_type == CAST_TYPE_OPTIONAL_IMPLICIT) return true;
// if (cast_type >= CAST_TYPE_IMPLICIT_ASSIGN) EXIT_T_MISMATCH();
RETURN_NON_CONST_CAST(CAST_INTBOOL);
@@ -328,40 +328,6 @@ bool ixxen(Expr *left, Type *canonical, Type *type, CastType cast_type)
return ixxxi(left, canonical, type, cast_type);
}
/**
* Convert from compile time int to error value
*/
bool ixxer(Expr *left, Type *canonical, Type *type, CastType cast_type)
{
// Assigning zero = no value is always ok.
REQUIRE_EXPLICIT_CAST(cast_type);
if (left->expr_kind == EXPR_CONST)
{
if (bigint_cmp_zero(&left->const_expr.i) != CMP_GT)
{
SEMA_ERROR(left, "Cannot cast '%s' to an error value.", expr_const_to_error_string(&left->const_expr));
return false;
}
BigInt comp;
bigint_init_unsigned(&comp, vec_size(canonical->decl->error.error_constants));
if (bigint_cmp(&left->const_expr.i, &comp) == CMP_GT)
{
SEMA_ERROR(left, "Cannot cast '%s' to a valid '%s' error value.", expr_const_to_error_string(&left->const_expr), canonical->decl->name);
return false;
}
left->type = type;
return true;
}
assert(canonical->type_kind == TYPE_ERROR);
if (!ixxxi(left, type_error_base->canonical, type_error_base, cast_type)) return false;
insert_cast(left, CAST_XIERR, canonical);
return true;
}
/**
* Convert from compile time int to error union
*/
@@ -613,16 +579,6 @@ bool enxi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ
// 3. Dispatch to the right cast:
return xixi(left, enum_type_canonical, canonical, type, cast_type);
}
bool erxi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
{
TODO
}
bool ereu(Expr *left)
{
insert_cast(left, CAST_ERREU, type_error_union);
return true;
}
bool vava(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
{
@@ -648,17 +604,6 @@ bool vasa(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ
bool euxi(Expr *left, Type *canonical, Type *type, CastType cast_type)
{
if (cast_type == CAST_TYPE_OPTIONAL_IMPLICIT) return true;
if (cast_type == CAST_TYPE_IMPLICIT)
{
SEMA_ERROR(left, "Cannot implictly cast an error to '%s'.", type_to_error_string(type));
return false;
}
left->type = type;
return true;
}
bool xieu(Expr *left, Type *canonical, Type *type, CastType cast_type)
{
@@ -670,23 +615,6 @@ bool xierr(Expr *left, Type *canonical, Type *type, CastType cast_type)
TODO
}
/**
* Convert error union to error. This is always a required cast.
* @return false if an error was reported.
*/
bool euer(Expr *left, Type *canonical, Type *type, CastType cast_type)
{
TODO
if (cast_type == CAST_TYPE_OPTIONAL_IMPLICIT) return true;
if (cast_type == CAST_TYPE_IMPLICIT)
{
SEMA_ERROR(left, "Cannot implictly cast an error union back to '%s'.", type_to_error_string(type));
return false;
}
insert_cast(left, CAST_EUERR, canonical);
return true;
}
/**
* Convert error union to error. This is always a required cast.
@@ -694,11 +622,24 @@ bool euer(Expr *left, Type *canonical, Type *type, CastType cast_type)
*/
bool eubool(Expr *left, Type *canonical, Type *type, CastType cast_type)
{
TODO
insert_cast(left, CAST_EUBOOL, canonical);
return true;
}
bool euer(Expr *left, Type *canonical, Type *type, CastType cast_type)
{
REQUIRE_EXPLICIT_CAST(cast_type);
insert_cast(left, CAST_EUER, canonical);
return true;
}
bool ereu(Expr *left, Type *canonical, Type *type, CastType cast_type)
{
insert_cast(left, CAST_EREU, canonical);
return true;
}
bool ptva(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
{
TODO
@@ -733,10 +674,10 @@ bool vapt(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ
}
bool cast_to_runtime(Expr *expr)
bool cast_implicitly_to_runtime(Expr *expr)
{
Type *canonical = expr->type->canonical;
int success;
switch (canonical->type_kind)
{
case TYPE_IXX:
@@ -746,23 +687,6 @@ bool cast_to_runtime(Expr *expr)
default:
return true;
}
}
void cast_to_smallest_runtime(Expr *expr)
{
Type *canonical = expr->type->canonical;
int success;
switch (canonical->type_kind)
{
case TYPE_IXX:
success = cast(expr, type_long, CAST_TYPE_IMPLICIT);
break;
case TYPE_FXX:
success = cast(expr, type_double, CAST_TYPE_IMPLICIT);
break;
default:
return;
}
assert(success && "This should always work");
}
@@ -780,11 +704,11 @@ CastKind cast_to_bool_kind(Type *type)
return cast_to_bool_kind(type->canonical);
case TYPE_POISONED:
case TYPE_VOID:
case TYPE_ERROR_UNION:
case TYPE_ERR_UNION:
case TYPE_STRUCT:
case TYPE_UNION:
case TYPE_STRING:
case TYPE_ERROR:
case TYPE_ERRTYPE:
case TYPE_ENUM:
case TYPE_FUNC:
case TYPE_MEMBER:
@@ -834,10 +758,9 @@ bool cast(Expr *expr, Type *to_type, CastType cast_type)
if (type_is_integer(canonical)) return boxi(expr, canonical, to_type, cast_type);
if (type_is_float(canonical)) return bofp(expr, canonical, to_type, cast_type);
break;
case TYPE_ERROR_UNION:
case TYPE_ERR_UNION:
if (to_type->type_kind == TYPE_BOOL) return eubool(expr, canonical, to_type, cast_type);
if (type_is_integer(canonical)) return euxi(expr, canonical, to_type, cast_type);
if (to_type->type_kind == TYPE_ERROR) return euer(expr, canonical, to_type, cast_type);
if (to_type->type_kind == TYPE_ERRTYPE) return euer(expr, canonical, to_type, cast_type);
break;
case TYPE_IXX:
// Compile time integers may convert into ints, floats, bools
@@ -846,7 +769,6 @@ bool cast(Expr *expr, Type *to_type, CastType cast_type)
if (canonical == type_bool) return ixxbo(expr, to_type);
if (canonical->type_kind == TYPE_POINTER) return xipt(expr, from_type, canonical, to_type, cast_type);
if (canonical->type_kind == TYPE_ENUM) return ixxen(expr, canonical, to_type, cast_type);
if (canonical->type_kind == TYPE_ERROR) return ixxer(expr, canonical, to_type, cast_type);
break;
case TYPE_I8:
case TYPE_I16:
@@ -857,8 +779,6 @@ bool cast(Expr *expr, Type *to_type, CastType cast_type)
if (type_is_float(canonical)) return sifp(expr, canonical, to_type);
if (canonical == type_bool) return xibo(expr, canonical, to_type, cast_type);
if (canonical->type_kind == TYPE_POINTER) return xipt(expr, from_type, canonical, to_type, cast_type);
if (canonical->type_kind == TYPE_ERROR_UNION) return xieu(expr, canonical, to_type, cast_type);
if (canonical->type_kind == TYPE_ERROR) return xierr(expr, canonical, to_type, cast_type);
break;
case TYPE_U8:
case TYPE_U16:
@@ -887,9 +807,8 @@ bool cast(Expr *expr, Type *to_type, CastType cast_type)
case TYPE_ENUM:
if (type_is_integer(canonical)) return enxi(expr, from_type, canonical, to_type, cast_type);
break;
case TYPE_ERROR:
if (type_is_integer(canonical)) return erxi(expr, from_type, canonical, to_type, cast_type);
if (canonical == type_error_union) return ereu(expr);
case TYPE_ERRTYPE:
if (canonical->type_kind == TYPE_ERR_UNION) return ereu(expr, canonical, to_type, cast_type);
break;
case TYPE_FUNC:
SEMA_ERROR(expr, "The function call is missing (...), if you want to take the address of a function it must be prefixed with '&'.");

View File

@@ -6,37 +6,6 @@
#include "bigint.h"
static inline bool sema_analyse_error(Context *context __unused, Decl *decl)
{
Decl **constants = decl->error.error_constants;
unsigned size = vec_size(constants);
if (size > MAX_ERRORS)
{
SEMA_ERROR(decl, "More than %d errors declared in a single error type.", MAX_ERRORS);
return false;
}
bool success = true;
for (unsigned i = 0; i < size; i++)
{
Decl *constant = constants[i];
for (unsigned j = 0; j < i; j++)
{
if (constant->name == constants[j]->name)
{
SEMA_ERROR(constant, "Duplicate error names, please remove one of them.");
SEMA_PREV(constants[j], "The previous declaration was here.");
decl_poison(constant);
decl_poison(constants[j]);
success = false;
break;
}
}
constant->type = decl->type;
constant->error_constant.value = i + 1;
constant->resolve_status = RESOLVE_DONE;
}
return success;
}
static inline void sema_set_struct_size(Decl *decl)
@@ -142,7 +111,7 @@ static inline bool sema_analyse_function_param(Context *context, Decl *param, bo
if (param->var.init_expr)
{
Expr *expr = param->var.init_expr;
if (!sema_analyse_expr_of_required_type(context, param->type, expr)) return false;
if (!sema_analyse_expr_of_required_type(context, param->type, expr, false)) return false;
if (expr->expr_kind != EXPR_CONST)
{
SEMA_ERROR(expr, "Only constant expressions may be used as default values.");
@@ -162,6 +131,7 @@ static inline Type *sema_analyse_function_signature(Context *context, FunctionSi
if (all_ok)
{
type_append_signature_name(signature->rtype->type, buffer, &buffer_write_offset);
if (signature->failable) buffer[buffer_write_offset++] = '!';
buffer[buffer_write_offset++] = '(';
}
if (vec_size(signature->params) > MAX_PARAMS)
@@ -212,47 +182,13 @@ static inline Type *sema_analyse_function_signature(Context *context, FunctionSi
buffer[buffer_write_offset++] = '.';
}
buffer[buffer_write_offset++] = ')';
if (signature->throw_any)
{
assert(!signature->throws);
buffer[buffer_write_offset++] = '!';
}
if (vec_size(signature->throws))
{
buffer[buffer_write_offset++] = '!';
VECEACH(signature->throws, i)
{
TypeInfo *err_decl = signature->throws[i];
if (!sema_resolve_type_info(context, err_decl))
{
return false;
}
if (i > 0 && all_ok)
{
buffer[buffer_write_offset++] = '|';
}
type_append_signature_name(err_decl->type, buffer, &buffer_write_offset);
}
}
unsigned error_types = vec_size(signature->throws);
ErrorReturn error_return = ERROR_RETURN_NONE;
if (signature->throw_any)
{
error_return = ERROR_RETURN_ANY;
}
else if (error_types)
{
error_return = error_types > 1 ? ERROR_RETURN_MANY : ERROR_RETURN_ONE;
}
signature->error_return = error_return;
Type *return_type = signature->rtype->type->canonical;
signature->return_param = false;
if (return_type->type_kind != TYPE_VOID)
{
// TODO fix this number with ABI compatibility
if (signature->error_return != ERROR_RETURN_NONE || type_size(return_type) > 8 * 2)
if (signature->failable || type_size(return_type) > 8 * 2)
{
signature->return_param = true;
}
@@ -339,7 +275,7 @@ static inline bool sema_analyse_enum(Context *context, Decl *decl)
}
// We try to convert to the desired type.
if (!sema_analyse_expr_of_required_type(context, type, expr))
if (!sema_analyse_expr_of_required_type(context, type, expr, false))
{
success = false;
enum_value->resolve_status = RESOLVE_DONE;
@@ -608,7 +544,7 @@ static inline bool sema_analyse_global(Context *context, Decl *decl)
decl->type = decl->var.type_info->type;
if (decl->var.init_expr)
{
if (!sema_analyse_expr_of_required_type(context, decl->type, decl->var.init_expr)) return false;
if (!sema_analyse_expr_of_required_type(context, decl->type, decl->var.init_expr, false)) return false;
if (decl->var.init_expr->expr_kind != EXPR_CONST)
{
SEMA_ERROR(decl->var.init_expr, "The expression must be a constant value.");
@@ -671,6 +607,49 @@ static inline bool sema_analyse_generic(Context *context, Decl *decl)
}
static inline bool sema_analyse_error(Context *context __unused, Decl *decl)
{
Decl **members = decl->strukt.members;
unsigned member_count = vec_size(members);
bool success = true;
unsigned error_size = 0;
for (unsigned i = 0; i < member_count; i++)
{
Decl *member = members[i];
for (unsigned j = 0; j < i; j++)
{
if (member->name == members[j]->name)
{
SEMA_ERROR(member, "Duplicate error names, please remove one of them.");
SEMA_PREV(members[j], "The previous declaration was here.");
decl_poison(member);
decl_poison(members[j]);
success = false;
break;
}
}
sema_analyse_struct_member(context, member);
unsigned alignment = type_abi_alignment(member->type);
unsigned size = type_size(member->type);
if (error_size % alignment != 0)
{
error_size += alignment - (error_size % alignment);
}
error_size += size;
}
sema_set_struct_size(decl);
if (decl->strukt.size > type_size(type_usize))
{
SEMA_ERROR(decl, "Error type may exceed pointer size (%d bytes) it was %d bytes.", type_size(type_usize), error_size);
return false;
}
decl->strukt.abi_alignment = type_abi_alignment(type_voidptr);
decl->strukt.size = type_size(type_error);
return success;
}
bool sema_analyse_decl(Context *context, Decl *decl)
{
if (decl->resolve_status == RESOLVE_DONE) return decl_ok(decl);
@@ -715,7 +694,7 @@ bool sema_analyse_decl(Context *context, Decl *decl)
if (!sema_analyse_enum(context, decl)) return decl_poison(decl);
decl_set_external_name(decl);
break;
case DECL_ERROR:
case DECL_ERR:
if (!sema_analyse_error(context, decl)) return decl_poison(decl);
decl_set_external_name(decl);
break;
@@ -727,11 +706,11 @@ bool sema_analyse_decl(Context *context, Decl *decl)
case DECL_POISONED:
case DECL_IMPORT:
case DECL_ENUM_CONSTANT:
case DECL_ERROR_CONSTANT:
case DECL_ARRAY_VALUE:
case DECL_CT_ELSE:
case DECL_CT_ELIF:
case DECL_MEMBER:
case DECL_LABEL:
UNREACHABLE
case DECL_CT_IF:
// Handled elsewhere

File diff suppressed because it is too large Load Diff

View File

@@ -11,3 +11,20 @@ int sema_check_comp_time_bool(Context *context, Expr *expr);
bool sema_analyse_function_body(Context *context, Decl *func);
void context_pop_scope(Context *context);
void context_push_scope_with_flags(Context *context, ScopeFlags flags);
#define PUSH_X(ast, X) Ast *_old_##X##_defer = context->X##_defer; Ast *_old_##X = context->X##_target; context->X##_target = ast; context->X##_defer = context->current_scope->defers.start
#define POP_X(X) context->X##_target = _old_##X; context->X##_defer = _old_##X##_defer
#define PUSH_CONTINUE(ast) PUSH_X(ast, continue)
#define POP_CONTINUE() POP_X(continue)
#define PUSH_BREAK(ast) PUSH_X(ast, break)
#define POP_BREAK() POP_X(break)
#define PUSH_NEXT(ast, sast) PUSH_X(ast, next); Ast *_old_next_switch = context->next_switch; context->next_switch = sast
#define POP_NEXT() POP_X(next); context->next_switch = _old_next_switch
#define PUSH_BREAKCONT(ast) PUSH_CONTINUE(ast); PUSH_BREAK(ast)
#define POP_BREAKCONT() POP_CONTINUE(); POP_BREAK()
#define PUSH_FAILABLES() \
unsigned _prev_failables = context->failables_found; \
context->failables_found = 0;
#define POP_FAILABLES() \
({ unsigned _failables = context->failables_found; context->failables_found = _prev_failables; _failables; })

View File

@@ -117,6 +117,21 @@ Decl *sema_resolve_symbol(Context *context, const char *symbol, Path *path, Decl
return decl;
}
static inline bool sema_append_local(Context *context, Decl *decl)
{
Decl *** vars = &context->active_function_for_analysis->func.annotations->vars;
unsigned num_vars = vec_size(*vars);
if (num_vars == MAX_LOCALS - 1 || context->last_local == &context->locals[MAX_LOCALS - 1])
{
SEMA_ERROR(decl, "Reached the maximum number of locals.");
return false;
}
*vars = VECADD(*vars, decl);
context->last_local[0] = decl;
context->last_local++;
return true;
}
bool sema_add_local(Context *context, Decl *decl)
{
Decl *dummy;
@@ -128,29 +143,26 @@ bool sema_add_local(Context *context, Decl *decl)
decl_poison(other);
return false;
}
Decl *** vars = &context->active_function_for_analysis->func.annotations->vars;
unsigned num_vars = vec_size(*vars);
if (num_vars == MAX_LOCALS - 1 || context->last_local == &context->locals[MAX_LOCALS - 1])
{
SEMA_ERROR(decl, "Reached the maximum number of locals.");
return false;
}
decl->var.id = num_vars;
*vars = VECADD(*vars, decl);
context->last_local[0] = decl;
context->last_local++;
return true;
return sema_append_local(context, decl);
}
bool sema_unwrap_var(Context *context, Decl *decl)
{
Decl *alias = COPY(decl);
alias->var.kind = VARDECL_ALIAS;
alias->var.alias = decl;
alias->var.failable = false;
return sema_append_local(context, decl);
}
bool sema_rewrap_var(Context *context, Decl *decl)
{
assert(decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_ALIAS && decl->var.alias->var.failable);
return sema_append_local(context, decl->var.alias);
}
bool sema_add_macro_local(Context *context, Decl *decl)
{
if (context->last_local == &context->locals[MAX_LOCALS - 1])
{
SEMA_ERROR(decl, "Reached the maximum number of locals.");
return false;
}
context->last_local[0] = decl;
context->last_local++;
return true;
return sema_add_local(context, decl);
}

View File

@@ -110,10 +110,6 @@ void sema_analysis_pass_decls(Context *context)
{
sema_analyse_decl(context, context->types[i]);
}
VECEACH(context->error_types, i)
{
sema_analyse_decl(context, context->error_types[i]);
}
VECEACH(context->methods, i)
{
sema_analyse_decl(context, context->methods[i]);

File diff suppressed because it is too large Load Diff

View File

@@ -18,28 +18,6 @@ static inline bool sema_resolve_ptr_type(Context *context, TypeInfo *type_info)
return true;
}
bool throw_completely_caught(TypeInfo *throw, CatchInfo *catches)
{
VECEACH(catches, i)
{
CatchInfo *catch_info = &catches[i];
switch (catch_info->kind)
{
case CATCH_REGULAR:
if (throw->type == catch_info->catch->catch_stmt.error_param->type) return true;
break;
case CATCH_TRY_ELSE:
case CATCH_RETURN_ANY:
return true;
case CATCH_RETURN_MANY:
case CATCH_RETURN_ONE:
if (throw->type == catch_info->error->type) return true;
break;
}
}
return false;
}
static inline bool sema_resolve_array_type(Context *context, TypeInfo *type)
{
@@ -57,7 +35,10 @@ static inline bool sema_resolve_array_type(Context *context, TypeInfo *type)
type->type = type_get_subarray(type->array.base->type);
break;;
case TYPE_INFO_ARRAY:
if (!sema_analyse_expr_of_required_type(context, type_usize, type->array.len)) return type_info_poison(type);
if (!sema_analyse_expr_of_required_type(context,
type_usize,
type->array.len,
false)) return type_info_poison(type);
if (type->array.len->expr_kind != EXPR_CONST)
{
SEMA_ERROR(type->array.len, "Expected a constant value as array size.");
@@ -82,7 +63,6 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info)
type_info->unresolved.name_loc.string,
type_info->unresolved.path,
&ambiguous_decl);
if (!decl)
{
SEMA_TOKEN_ERROR(type_info->unresolved.name_loc, "Unknown type '%s'.", type_info->unresolved.name_loc.string);
@@ -109,7 +89,7 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info)
{
case DECL_STRUCT:
case DECL_UNION:
case DECL_ERROR:
case DECL_ERR:
case DECL_ENUM:
case DECL_TYPEDEF:
if (decl->resolve_status == RESOLVE_NOT_DONE)
@@ -125,11 +105,11 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info)
case DECL_FUNC:
case DECL_VAR:
case DECL_ENUM_CONSTANT:
case DECL_ERROR_CONSTANT:
case DECL_ARRAY_VALUE:
case DECL_IMPORT:
case DECL_MACRO:
case DECL_GENERIC:
case DECL_LABEL:
SEMA_TOKEN_ERROR(type_info->unresolved.name_loc, "This is not a type.");
return type_info_poison(type_info);
case DECL_CT_ELSE:
@@ -164,7 +144,8 @@ bool sema_resolve_type_shallow(Context *context, TypeInfo *type_info)
case TYPE_INFO_INC_ARRAY:
UNREACHABLE
case TYPE_INFO_IDENTIFIER:
return sema_resolve_type_identifier(context, type_info);
if (!sema_resolve_type_identifier(context, type_info)) return false;
break;
case TYPE_INFO_EXPRESSION:
if (!sema_analyse_expr(context, NULL, type_info->unresolved_type_expr))
{
@@ -174,9 +155,11 @@ bool sema_resolve_type_shallow(Context *context, TypeInfo *type_info)
case TYPE_INFO_SUBARRAY:
case TYPE_INFO_VARARRAY:
case TYPE_INFO_ARRAY:
return sema_resolve_array_type(context, type_info);
if (!sema_resolve_array_type(context, type_info)) return false;
break;
case TYPE_INFO_POINTER:
return sema_resolve_ptr_type(context, type_info);
if (!sema_resolve_ptr_type(context, type_info)) return false;
break;
}
UNREACHABLE
return true;
}

View File

@@ -52,7 +52,7 @@ const char *token_type_to_string(TokenType type)
return "-";
case TOKEN_MOD:
return "%";
case TOKEN_NOT:
case TOKEN_BANG:
return "!";
case TOKEN_PLUS:
return "+";
@@ -90,6 +90,8 @@ const char *token_type_to_string(TokenType type)
return ">=";
case TOKEN_LESS_EQ:
return "<=";
case TOKEN_LPARBRA:
return "({";
case TOKEN_MINUS_ASSIGN:
return "-=";
case TOKEN_MINUS_MOD:
@@ -112,16 +114,16 @@ const char *token_type_to_string(TokenType type)
return "+%";
case TOKEN_PLUSPLUS:
return "++";
case TOKEN_RPARBRA:
return "})";
case TOKEN_SCOPE:
return "::";
case TOKEN_SHL:
return "<<";
case TOKEN_SHR:
return ">>";
case TOKEN_LPARBRA:
return "({";
case TOKEN_RPARBRA:
return "})";
case TOKEN_BANGBANG:
return "!!";
// Three character tokens
case TOKEN_ELLIPSIS:
@@ -151,6 +153,12 @@ const char *token_type_to_string(TokenType type)
case TOKEN_TYPE_IDENT:
return "TYPE_IDENT";
// Asm
case TOKEN_ASM_STRING:
return "ASM_STRING";
case TOKEN_ASM_CONSTRAINT:
return "ASM_CONSTRAINT";
// Values
case TOKEN_STRING:
return "STRING";
@@ -190,6 +198,8 @@ const char *token_type_to_string(TokenType type)
return "default";
case TOKEN_DEFER:
return "defer";
case TOKEN_DEFINE:
return "define";
case TOKEN_DO:
return "do";
case TOKEN_ELSE:
@@ -198,10 +208,8 @@ const char *token_type_to_string(TokenType type)
return "enum";
case TOKEN_EXTERN:
return "extern";
case TOKEN_ERROR_TYPE:
case TOKEN_ERR:
return "error";
case TOKEN_ERRSET:
return "errset";
case TOKEN_FALSE:
return "false";
case TOKEN_FOR:
@@ -210,8 +218,6 @@ const char *token_type_to_string(TokenType type)
return "func";
case TOKEN_GENERIC:
return "generic";
case TOKEN_GOTO:
return "goto";
case TOKEN_IF:
return "if";
case TOKEN_IMPORT:
@@ -236,10 +242,6 @@ const char *token_type_to_string(TokenType type)
return "struct";
case TOKEN_SWITCH:
return "switch";
case TOKEN_THROW:
return "throw";
case TOKEN_THROWS:
return "throws";
case TOKEN_TRUE:
return "true";
case TOKEN_TRY:
@@ -362,3 +364,7 @@ bool token_is_type(TokenType type)
return type >= TOKEN_VOID && type <= TOKEN_C_ULONGLONG;
}
bool token_is_any_type(TokenType type)
{
return (type >= TOKEN_VOID && type <= TOKEN_C_ULONGLONG) || type == TOKEN_CT_TYPE_IDENT || type == TOKEN_TYPE_IDENT;
}

View File

@@ -10,7 +10,7 @@ static Type t_f32, t_f64, t_fxx;
static Type t_usz, t_isz;
static Type t_cus, t_cui, t_cul, t_cull;
static Type t_cs, t_ci, t_cl, t_cll;
static Type t_voidstar, t_typeid, t_error_union;
static Type t_voidstar, t_typeid, t_error;
Type *type_bool = &t_u1;
Type *type_void = &t_u0;
@@ -39,8 +39,7 @@ Type *type_c_ushort = &t_cus;
Type *type_c_uint = &t_cui;
Type *type_c_ulong = &t_cul;
Type *type_c_ulonglong = &t_cull;
Type *type_error_union = &t_error_union;
Type *type_error_base = &t_ci;
Type *type_error = &t_error;
static unsigned size_subarray;
static unsigned alignment_subarray;
@@ -100,29 +99,19 @@ const char *type_to_error_string(Type *type)
case TYPE_F64:
case TYPE_FXX:
case TYPE_UNION:
case TYPE_ERROR:
case TYPE_ERRTYPE:
return type->name;
case TYPE_FUNC:
{
asprintf(&buffer, "func %s(", type_to_error_string(type->func.signature->rtype->type));
asprintf(&buffer, type->func.signature->failable ? "func %s!(" : "func %s(",
type_to_error_string(type->func.signature->rtype->type));
VECEACH(type->func.signature->params, i)
{
if (i != 0) buffer = strcat_arena(buffer, ", ");
strcat_arena(buffer, type_to_error_string(type->func.signature->params[i]->type));
}
buffer = strcat_arena(buffer, ")");
if (type->func.signature->throw_any)
{
return strcat_arena(buffer, " throws");
}
if (!vec_size(type->func.signature->throws)) return buffer;
buffer = strcat_arena(buffer, " throws ");
VECEACH(type->func.signature->throws, i)
{
if (i != 0) buffer = strcat_arena(buffer, ", ");
buffer = strcat_arena(buffer, type_to_error_string(type->func.signature->throws[i]->type));
}
return buffer;
return strcat_arena(buffer, ")");
}
case TYPE_TYPEID:
return "typeid";
@@ -146,7 +135,7 @@ const char *type_to_error_string(Type *type)
case TYPE_SUBARRAY:
asprintf(&buffer, "%s[]", type_to_error_string(type->array.base));
return buffer;
case TYPE_ERROR_UNION:
case TYPE_ERR_UNION:
return "error";
}
UNREACHABLE
@@ -167,7 +156,6 @@ static void type_append_signature_name_user_defined(Decl *decl, char *dst, size_
case DECL_VAR:
case DECL_ENUM_CONSTANT:
case DECL_TYPEDEF:
case DECL_ERROR_CONSTANT:
case DECL_ARRAY_VALUE:
case DECL_IMPORT:
case DECL_MACRO:
@@ -177,11 +165,12 @@ static void type_append_signature_name_user_defined(Decl *decl, char *dst, size_
case DECL_CT_ELIF:
case DECL_ATTRIBUTE:
case DECL_MEMBER:
case DECL_LABEL:
UNREACHABLE
case DECL_STRUCT:
case DECL_UNION:
case DECL_ENUM:
case DECL_ERROR:
case DECL_ERR:
{
unsigned len = source_range_len(decl->name_span);
memcpy(dst + *offset, decl->name, len);
@@ -202,7 +191,7 @@ void type_append_signature_name(Type *type, char *dst, size_t *offset)
case TYPE_POISONED:
case TYPE_TYPEDEF:
UNREACHABLE;
case TYPE_ERROR:
case TYPE_ERRTYPE:
case TYPE_ENUM:
case TYPE_STRUCT:
case TYPE_UNION:
@@ -218,76 +207,76 @@ void type_append_signature_name(Type *type, char *dst, size_t *offset)
size_t type_size(Type *canonical)
size_t type_size(Type *type)
{
assert(canonical && canonical->canonical == canonical);
switch (canonical->type_kind)
switch (type->type_kind)
{
case TYPE_POISONED:
case TYPE_TYPEDEF:
UNREACHABLE;
case TYPE_TYPEDEF:
return type_size(type->canonical);
case TYPE_ENUM:
return canonical->decl->enums.type_info->type->canonical->builtin.bytesize;
case TYPE_ERROR:
return type->decl->enums.type_info->type->canonical->builtin.bytesize;
case TYPE_ERRTYPE:
return alignment_error_code;
case TYPE_STRUCT:
case TYPE_UNION:
assert(canonical->decl->resolve_status == RESOLVE_DONE);
return canonical->decl->strukt.size;
assert(type->decl->resolve_status == RESOLVE_DONE);
return type->decl->strukt.size;
case TYPE_VOID:
return 1;
case TYPE_BOOL:
case TYPE_TYPEID:
case ALL_INTS:
case ALL_FLOATS:
return canonical->builtin.bytesize;
case TYPE_ERR_UNION:
return type->builtin.bytesize;
case TYPE_MEMBER:
return type_size(canonical->decl->var.type_info->type);
return type_size(type->decl->var.type_info->type);
case TYPE_FUNC:
case TYPE_POINTER:
case TYPE_VARARRAY:
case TYPE_STRING:
case TYPE_ERROR_UNION:
return t_usz.canonical->builtin.bytesize;
case TYPE_ARRAY:
return type_size(canonical->array.base) * canonical->array.len;
return type_size(type->array.base) * type->array.len;
case TYPE_SUBARRAY:
return size_subarray;
}
UNREACHABLE
}
unsigned int type_abi_alignment(Type *canonical)
unsigned int type_abi_alignment(Type *type)
{
assert(canonical && canonical->canonical == canonical);
switch (canonical->type_kind)
switch (type->type_kind)
{
case TYPE_POISONED:
case TYPE_TYPEDEF:
case TYPE_VOID:
UNREACHABLE;
case TYPE_TYPEDEF:
return type_abi_alignment(type->canonical);
case TYPE_ENUM:
return canonical->decl->enums.type_info->type->canonical->builtin.abi_alignment;
case TYPE_ERROR:
return type->decl->enums.type_info->type->canonical->builtin.abi_alignment;
case TYPE_ERRTYPE:
return alignment_error_code;
case TYPE_STRUCT:
case TYPE_UNION:
return canonical->decl->strukt.abi_alignment;
return type->decl->strukt.abi_alignment;
case TYPE_TYPEID:
case TYPE_BOOL:
case ALL_INTS:
case ALL_FLOATS:
case TYPE_ERROR_UNION:
return canonical->builtin.abi_alignment;
case TYPE_ERR_UNION:
return type->builtin.abi_alignment;
case TYPE_MEMBER:
return type_abi_alignment(canonical->decl->var.type_info->type);
return type_abi_alignment(type->decl->var.type_info->type);
case TYPE_FUNC:
case TYPE_POINTER:
case TYPE_VARARRAY:
case TYPE_STRING:
return t_usz.canonical->builtin.abi_alignment;
case TYPE_ARRAY:
return type_abi_alignment(canonical->array.base);
return type_abi_alignment(type->array.base);
case TYPE_SUBARRAY:
return alignment_subarray;
}
@@ -329,6 +318,7 @@ static Type *type_generate_ptr(Type *ptr_type, bool canonical)
return ptr;
}
static Type *type_generate_subarray(Type *arr_type, bool canonical)
{
if (canonical) arr_type = arr_type->canonical;
@@ -534,7 +524,7 @@ type_create(#_name, &_shortname, _type, _bits, target->align_ ## _align, target-
alignment_subarray = MAX(type_abi_alignment(&t_voidstar), type_abi_alignment(t_usz.canonical));
size_subarray = alignment_subarray * 2;
type_create("error", &t_error_union, TYPE_ERROR_UNION, target->width_pointer, target->align_pointer, target->align_pref_pointer);
type_create("error", &t_error, TYPE_ERR_UNION, target->width_pointer * 2, target->align_pointer, target->align_pref_pointer);
}
/**
@@ -569,7 +559,7 @@ bool type_may_have_sub_elements(Type *type)
case TYPE_UNION:
case TYPE_STRUCT:
case TYPE_ENUM:
case TYPE_ERROR:
case TYPE_ERRTYPE:
return true;
default:
return false;
@@ -743,13 +733,13 @@ Type *type_find_max_type(Type *type, Type *other)
// IMPROVE: should there be implicit conversion between one enum and the other in
// some way?
return NULL;
case TYPE_ERROR:
if (other->type_kind == TYPE_ERROR) return type_error_union;
case TYPE_ERRTYPE:
if (other->type_kind == TYPE_ERRTYPE) return type_error;
return NULL;
case TYPE_MEMBER:
case TYPE_FUNC:
case TYPE_UNION:
case TYPE_ERROR_UNION:
case TYPE_ERR_UNION:
case TYPE_TYPEID:
return NULL;
case TYPE_STRUCT:

View File

@@ -43,6 +43,7 @@ int main(int argc, const char *argv[])
case COMMAND_BENCH:
printf("TODO\n");
}
print_arena_status();
free_arena();
return 0;
}

View File

@@ -16,12 +16,13 @@ void file_add_wildcard_files(const char ***files, const char *path, bool recursi
void memory_init(void);
void *malloc_arena(unsigned long mem);
void free_arena(void);
void print_arena_status(void);
void run_arena_allocator_tests(void);
#define MALLOC(mem) malloc_arena(mem)
#define MALLOCS(type) malloc_arena(sizeof(type))
#define CALLOCS(type) ({ void *__x = malloc_arena(sizeof(type)); memset(__x, 0, sizeof(type)); __x; })
#define CALLOCS(type) ({ type *__x = malloc_arena(sizeof(type)); memset(__x, 0, sizeof(type)); __x; })
#define COPY(value) ({ typeof(value) __x = malloc_arena(sizeof(*value)); memcpy(__x, value, sizeof(*value)); __x; })
static inline bool is_power_of_two(uint64_t x)
{
@@ -305,6 +306,7 @@ static inline bool is_all_lower(const char* string)
char *strcat_arena(const char *a, const char *b);
char *strformat(const char *var, ...) __printflike(1, 2);
char *strcopy(const char *start, size_t len);
#define MAX(_a, _b) ({ \
typeof(_a) __a__ = (_a); \

View File

@@ -23,6 +23,14 @@ char *strformat(const char *var, ...)
return buffer;
}
char *strcopy(const char *start, size_t len)
{
char *buffer = malloc_arena(len + 1);
memcpy(buffer, start, len);
buffer[len] = '\0';
return buffer;
}
char *strcat_arena(const char *a, const char *b)
{
unsigned a_len = strlen(a);