mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
New failable based error handling. Labelled break/continue/next.
This commit is contained in:
committed by
Christoffer Lerno
parent
10f715cec0
commit
dc86c21210
@@ -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
11
resources/c3ir.md
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
```
|
||||
block
|
||||
{
|
||||
z = add(x, y)
|
||||
z = sub(x, y)
|
||||
z = mult(x, y)
|
||||
neg(z, x)
|
||||
goto
|
||||
|
||||
```
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
30
resources/testfragments/test_break.c3
Normal file
30
resources/testfragments/test_break.c3
Normal 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");
|
||||
}
|
||||
}
|
||||
@@ -782,6 +782,7 @@ int i = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
func void test2(int* x, int y, int z)
|
||||
{
|
||||
*(&(&x)[0]);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -101,6 +101,7 @@ void compiler_compile(BuildTarget *target)
|
||||
Context *context = contexts[i];
|
||||
llvm_codegen(context);
|
||||
}
|
||||
print_arena_status();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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]);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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, ¶ms, 0)) return poisoned_expr;
|
||||
if (!parse_param_list(context, ¶ms, 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 },
|
||||
|
||||
@@ -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, ¶ms->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, ¶ms->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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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, ...);
|
||||
|
||||
@@ -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 '&'.");
|
||||
|
||||
@@ -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
@@ -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; })
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -43,6 +43,7 @@ int main(int argc, const char *argv[])
|
||||
case COMMAND_BENCH:
|
||||
printf("TODO\n");
|
||||
}
|
||||
print_arena_status();
|
||||
free_arena();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -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); \
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user