diff --git a/CMakeLists.txt b/CMakeLists.txt index 1dab620d2..cd5ab2415 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O1") -set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3") +set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -gdwarf-3 -O3") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3") set(LLVM_LINK_COMPONENTS @@ -136,6 +136,7 @@ add_executable(c3c src/compiler/dwarf.h src/compiler/enums.h src/compiler/float.c + src/compiler/hashtable.c src/compiler/headers.c src/compiler/lexer.c src/compiler/linker.c diff --git a/lib/std/io.c3 b/lib/std/io.c3 index 8b336d277..e8c7b470b 100644 --- a/lib/std/io.c3 +++ b/lib/std/io.c3 @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Christoffer Lerno. All rights reserved. +// Copyright (c) 2021-2022 Christoffer Lerno. All rights reserved. // Use of this source code is governed by the MIT license // a copy of which can be found in the LICENSE_STDLIB file. module std::io; @@ -22,7 +22,11 @@ fn int putchar(char c) @inline return _putchar(c); } -fn int print(char *message) +/** + * @param [&in] message + * @return `number of bytes printed.` + */ +fn int print(char* message) { char* pointer = message; while (*pointer != '\0') @@ -33,6 +37,10 @@ fn int print(char *message) return 1; } +/** + * @param [&in] message + * @return `number of bytes printed.` + */ fn int println(char *message = "") @inline { return _puts(message); @@ -142,7 +150,12 @@ fn usize File.read(File* file, void* buffer, usize items, usize element_size = 1 } /** - * @require file && file.file + * @param [&in] file + * @param [&out] buffer + * @param items + * @param element_size + * @require file.file `File must be initialized` + * @require element_size > 1 */ fn usize File.write(File* file, void* buffer, usize items, usize element_size = 1) { @@ -150,7 +163,8 @@ fn usize File.write(File* file, void* buffer, usize items, usize element_size = } /** - * @require file && file.file + * @param [&in] file + * @require file.file `File must be initialized` */ fn usize! File.println(File* file, char[] string) { diff --git a/src/build/build_internal.h b/src/build/build_internal.h index 9aa05f3ac..68a42fe3a 100644 --- a/src/build/build_internal.h +++ b/src/build/build_internal.h @@ -7,7 +7,7 @@ #include "utils/lib.h" #include "utils/json.h" #include "build_options.h" -#define DEFAULT_SYMTAB_SIZE (2 * 1024 * 1024) +#define DEFAULT_SYMTAB_SIZE (256 * 1024) #define DEFAULT_SWITCHRANGE_MAX_SIZE (256) typedef struct diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 0a7f9d1fc..816217d08 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -5,22 +5,20 @@ #include "compiler_internal.h" -Decl *decl_new_ct(DeclKind kind, TokenId span) +Decl *decl_new_ct(DeclKind kind, SourceSpan span) { Decl *decl = decl_calloc(); decl->decl_kind = kind; - decl->span = source_span_from_token_id(span); + decl->span = span; return decl; } -Decl *decl_new(DeclKind decl_kind, TokenId name, Visibility visibility) +Decl *decl_new(DeclKind decl_kind, const char *name, SourceSpan span, Visibility visibility) { Decl *decl = decl_calloc(); decl->decl_kind = decl_kind; - decl->name_token = name; - decl->span = source_span_from_token_id(name); - assert(name.index); - decl->name = TOKSTR(name); + decl->span = span; + decl->name = name; decl->visibility = visibility; return decl; } @@ -68,9 +66,9 @@ const char *decl_to_name(Decl *decl) return "enum"; case DECL_ENUM_CONSTANT: return "enum value"; - case DECL_ERRVALUE: + case DECL_OPTVALUE: return "err value"; - case DECL_ERRTYPE: + case DECL_OPTENUM: return "errtype"; case DECL_FUNC: return "function"; @@ -130,23 +128,15 @@ void decl_set_external_name(Decl *decl) scratch_buffer_append(decl->module->name->module); scratch_buffer_append("."); scratch_buffer_append(decl->name ? decl->name : "anon"); - decl->external_name = scratch_buffer_interned(); + decl->external_name = scratch_buffer_copy(); } -Decl *decl_new_with_type(TokenId name, DeclKind decl_type, Visibility visibility) +Decl *decl_new_with_type(const char *name, SourceSpan loc, DeclKind decl_type, Visibility visibility) { Decl *decl = decl_calloc(); decl->decl_kind = decl_type; - if (name.index) - { - decl->name_token = name; - decl->name = TOKSTR(name); - decl->span = source_span_from_token_id(name); - } - else - { - decl->name = NULL; - } + decl->name = name; + decl->span = loc; decl->visibility = visibility; TypeKind kind = TYPE_POISONED; switch (decl_type) @@ -160,7 +150,7 @@ Decl *decl_new_with_type(TokenId name, DeclKind decl_type, Visibility visibility case DECL_STRUCT: kind = TYPE_STRUCT; break; - case DECL_ERRTYPE: + case DECL_OPTENUM: kind = TYPE_ERRTYPE; break; case DECL_ENUM: @@ -178,7 +168,7 @@ Decl *decl_new_with_type(TokenId name, DeclKind decl_type, Visibility visibility case DECL_POISONED: case DECL_VAR: case DECL_ENUM_CONSTANT: - case DECL_ERRVALUE: + case DECL_OPTVALUE: case DECL_IMPORT: case DECL_MACRO: case DECL_GENERIC: @@ -194,7 +184,7 @@ Decl *decl_new_with_type(TokenId name, DeclKind decl_type, Visibility visibility case DECL_DECLARRAY: UNREACHABLE } - Type *type = type_new(kind, !name.index ? "anon" : TOKSTR(name)); + Type *type = type_new(kind, name ? name : "anon"); type->canonical = type; type->decl = decl; decl->type = type; @@ -204,9 +194,9 @@ Decl *decl_new_with_type(TokenId name, DeclKind decl_type, Visibility visibility static Decl poison_decl = { .decl_kind = DECL_POISONED, .resolve_status = RESOLVE_DONE }; Decl *poisoned_decl = &poison_decl; -Decl *decl_new_var(TokenId name, TypeInfo *type, VarDeclKind kind, Visibility visibility) +Decl *decl_new_var(const char *name, SourceSpan loc, TypeInfo *type, VarDeclKind kind, Visibility visibility) { - Decl *decl = decl_new(DECL_VAR, name, visibility); + Decl *decl = decl_new(DECL_VAR, name, loc, visibility); decl->var.kind = kind; decl->var.type_info = type; return decl; @@ -216,9 +206,8 @@ Decl *decl_new_generated_var(const char *name, Type *type, VarDeclKind kind, Sou { Decl *decl = decl_calloc(); decl->decl_kind = DECL_VAR; - decl->name_token = NO_TOKEN_ID; decl->span = span; - decl->name = name; + decl->name = NULL; decl->visibility = VISIBLE_LOCAL; decl->var.kind = kind; decl->type = type; diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 15978acce..a0c1e7035 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -30,11 +30,11 @@ void compiler_init(const char *std_lib_dir) // Skip library detection. //compiler.lib_dir = find_lib_dir(); //DEBUG_LOG("Found std library: %s", compiler.lib_dir); - stable_init(&global_context.modules, 64); + htable_init(&global_context.modules, 16 * 1024); decltable_init(&global_context.symbols, INITIAL_SYMBOL_MAP); decltable_init(&global_context.generic_symbols, INITIAL_GENERIC_SYMBOL_MAP); - stable_init(&global_context.compiler_defines, 512); + htable_init(&global_context.compiler_defines, 16 * 1024); global_context.module_list = NULL; global_context.generic_module_list = NULL; vmem_init(&ast_arena, 4 * 1024); @@ -45,9 +45,6 @@ void compiler_init(const char *std_lib_dir) vmem_init(&tokdata_arena, 4 * 1024); vmem_init(&type_info_arena, 1024); // Create zero index value. - (void) sourceloc_calloc(); - (void) toktype_calloc(); - (void) tokdata_calloc(); if (std_lib_dir) { global_context.lib_dir = std_lib_dir; @@ -66,13 +63,11 @@ static void compiler_lex(void) File *file = source_file_load(global_context.sources[i], &loaded); if (loaded) continue; Lexer lexer = { .file = file }; - lexer_lex_file(&lexer); + lexer_init(&lexer); printf("# %s\n", file->full_path); - uint32_t index = lexer.token_start_id; - while (1) + while (lexer_next_token(&lexer)) { - TokenType token_type = (TokenType)(*toktypeptr(index)); - index++; + TokenType token_type = lexer.token_type; printf("%s ", token_type_to_string(token_type)); if (token_type == TOKEN_EOF) break; } @@ -148,8 +143,6 @@ static void free_arenas(void) decl_arena_free(); expr_arena_free(); type_info_arena_free(); - sourceloc_arena_free(); - tokdata_arena_free(); if (debug_stats) print_arena_status(); } @@ -353,7 +346,7 @@ static void setup_int_define(const char *id, uint64_t i, Type *type) { TokenType token_type = TOKEN_CONST_IDENT; id = symtab_add(id, (uint32_t) strlen(id), fnv1a(id, (uint32_t) strlen(id)), &token_type); - Expr *expr = expr_new(EXPR_CONST, INVALID_RANGE); + Expr *expr = expr_new(EXPR_CONST, INVALID_SPAN); assert(type_is_integer(type)); expr_const_set_int(&expr->const_expr, i, type->type_kind); if (expr_const_will_overflow(&expr->const_expr, type->type_kind)) @@ -362,9 +355,9 @@ static void setup_int_define(const char *id, uint64_t i, Type *type) } expr->type = type; expr->const_expr.narrowable = true; - expr->span = INVALID_RANGE; + expr->span = INVALID_SPAN; expr->resolve_status = RESOLVE_NOT_DONE; - void *previous = stable_set(&global_context.compiler_defines, id, expr); + void *previous = htable_set(&global_context.compiler_defines, id, expr); if (previous) { error_exit("Redefined ident %s", id); @@ -375,12 +368,12 @@ static void setup_bool_define(const char *id, bool value) { TokenType token_type = TOKEN_CONST_IDENT; id = symtab_add(id, (uint32_t) strlen(id), fnv1a(id, (uint32_t) strlen(id)), &token_type); - Expr *expr = expr_new(EXPR_CONST, INVALID_RANGE); + Expr *expr = expr_new(EXPR_CONST, INVALID_SPAN); expr_const_set_bool(&expr->const_expr, value); expr->type = type_bool; - expr->span = INVALID_RANGE; + expr->span = INVALID_SPAN; expr->resolve_status = RESOLVE_NOT_DONE; - void *previous = stable_set(&global_context.compiler_defines, id, expr); + void *previous = htable_set(&global_context.compiler_defines, id, expr); if (previous) { error_exit("Redefined ident %s", id); @@ -462,7 +455,7 @@ void compile() active_target.csources = target_expand_source_names(active_target.csource_dirs, ".c", ".c", false); } global_context.sources = active_target.sources; - symtab_init(active_target.symtab_size ? active_target.symtab_size : 64 * 1024); + symtab_init(active_target.symtab_size); target_setup(&active_target); setup_int_define("C_SHORT_SIZE", platform_target.width_c_short, type_long); @@ -528,10 +521,10 @@ const char *get_object_extension(void) Module *global_context_find_module(const char *name) { - return stable_get(&global_context.modules, name); + return htable_get(&global_context.modules, name); } -Module *compiler_find_or_create_module(Path *module_name, TokenId *parameters, bool is_private) +Module *compiler_find_or_create_module(Path *module_name, const char **parameters, bool is_private) { Module *module = global_context_find_module(module_name->module); if (module) return module; @@ -544,8 +537,8 @@ Module *compiler_find_or_create_module(Path *module_name, TokenId *parameters, b module->parameters = parameters; module->is_generic = vec_size(parameters) > 0; module->is_private = is_private; - stable_init(&module->symbols, 0x10000); - stable_set(&global_context.modules, module_name->module, module); + htable_init(&module->symbols, 0x10000); + htable_set(&global_context.modules, module_name->module, module); if (parameters) { vec_add(global_context.generic_module_list, module); @@ -619,3 +612,8 @@ const char *scratch_buffer_interned(void) return symtab_add(global_context.scratch_buffer, global_context.scratch_buffer_len, fnv1a(global_context.scratch_buffer, global_context.scratch_buffer_len), &type); } + +char *scratch_buffer_copy(void) +{ + return copy_string(global_context.scratch_buffer, global_context.scratch_buffer_len); +} diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index fe583a817..d26ddce85 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -26,18 +26,8 @@ typedef uint32_t ArraySize; typedef uint64_t BitSize; -typedef uint32_t SourceLoc; -typedef struct -{ - unsigned index; -} TokenId; - #define MAX_HASH_SIZE (512 * 1024 * 1024) -#define NO_TOKEN_ID ((TokenId) { 0 }) -#define NO_TOKEN ((Token) { .type = TOKEN_INVALID_TOKEN }) -#define INVALID_TOKEN_ID ((TokenId) { UINT32_MAX }) -#define INVALID_RANGE ((SourceSpan){ INVALID_TOKEN_ID, INVALID_TOKEN_ID }) -#define TOKEN_IS_INVALID(_token_id) ((_token_id).index == INVALID_TOKEN_ID.index) +#define INVALID_SPAN ((SourceSpan){ .row = 0 }) #define MAX_LOCALS 0xFFF #define MAX_SCOPE_DEPTH 0x100 #define MAX_STRING_BUFFER 0x10000 @@ -152,11 +142,6 @@ typedef struct }; } ExprConst; -typedef struct -{ - TokenId loc; - TokenId end_loc; -} SourceSpan; typedef struct { @@ -164,7 +149,7 @@ typedef struct AstId end; } DeferList; -typedef unsigned FileId; +typedef uint16_t FileId; typedef struct { FileId file_id; @@ -174,21 +159,20 @@ typedef struct const char *full_path; } File; -typedef struct +typedef union { - FileId file_id; - uint16_t col; - uint32_t row; - uint32_t start; - uint32_t length; -} SourceLocation; + struct + { + FileId file_id; + unsigned char length; + unsigned char col; + uint32_t row; + }; + uint64_t a; +} SourceSpan; +static_assert(sizeof(SourceSpan) == 8, "Expected 8 bytes"); -typedef struct -{ - TokenId id; - TokenType type : 16; -} Token; typedef struct @@ -205,8 +189,18 @@ typedef struct SEntry *entries; } STable; +typedef struct SEntry2_ +{ + const char *key; + void *value; + struct SEntry2_ *next; +} HTEntry; - +typedef struct +{ + uint32_t mask; + HTEntry **entries; +} HTable; typedef struct Path_ @@ -227,7 +221,8 @@ typedef struct typedef struct { - TokenId name_loc; + const char *name; + SourceSpan span; Path *path; } TypeUnresolved; @@ -292,10 +287,12 @@ struct TypeInfo_ }; }; + typedef struct { Path *path; - TokenId name; + const char *name; + SourceSpan name_span; union { Expr *expr; @@ -307,7 +304,6 @@ typedef struct typedef struct { Path *path; - TokenId symbol; bool private; } ImportDecl; @@ -340,6 +336,9 @@ typedef struct VarDecl_ bool vararg_implicit : 1; bool is_static : 1; bool is_read : 1; + bool not_null : 1; + bool may_not_read : 1; + bool may_not_write : 1; bool is_written : 1; bool is_addr : 1; bool is_threadlocal : 1; @@ -351,7 +350,11 @@ typedef struct VarDecl_ }; union { - struct SemaContext_ *context; // Hash var + struct + { + struct SemaContext_ *context; + SourceSpan span; + } hash_var; unsigned scope_depth; // CT var struct { @@ -493,7 +496,7 @@ typedef struct TypeInfo *rtype; // May be null! struct Ast_ *body; Decl **body_parameters; - TokenId block_parameter; + const char *block_parameter; struct CompilationUnit_ *unit; } MacroDecl; @@ -532,7 +535,8 @@ typedef struct struct { Path *path; - TokenId identifier; + const char *ident; + SourceSpan span; }; }; TypeInfo **generic_params; @@ -555,7 +559,6 @@ typedef struct typedef struct Decl_ { const char *name; - TokenId name_token; SourceSpan span; const char *external_name; Ast *docs; @@ -749,20 +752,15 @@ typedef struct typedef struct { Path *path; - TokenId identifier; + const char *ident; bool is_const; Decl *decl; } ExprIdentifier; -typedef struct -{ - Path *path; - TokenId identifier; -} ExprPlaceholder; typedef struct { - TokenId identifier; + const char *identifier; bool is_ref : 1; bool is_rvalue : 1; Decl *decl; @@ -796,8 +794,8 @@ typedef struct typedef struct { - CastKind kind; - bool implicit; + CastKind kind : 8; + bool implicit : 1; Expr *expr; TypeInfo *type_info; union @@ -843,7 +841,7 @@ typedef struct typedef struct { const char *name; - TokenId span; + SourceSpan span; } Label; typedef struct @@ -902,7 +900,7 @@ typedef struct typedef struct { - Token identifier; + const char *ident; BuiltinFunction builtin; } ExprBuiltin; @@ -914,7 +912,8 @@ typedef struct { struct { - TokenId new_ident; + const char *new_ident; + SourceSpan span; Expr *variant_expr; }; Decl *variable; @@ -957,7 +956,6 @@ struct Expr_ ExprAccess access_expr; ExprDesignator designator_expr; ExprIdentifier identifier_expr; - ExprPlaceholder placeholder_expr; ExprIdentifierRaw ct_ident_expr; ExprCtCall ct_call_expr; ExprIdentifierRaw ct_macro_ident_expr; @@ -978,6 +976,7 @@ struct Expr_ }; + typedef struct { AstId first_stmt; @@ -1131,8 +1130,10 @@ typedef struct typedef struct { - TokenId index; - TokenId value; + const char *index_name; + const char *value_name; + SourceSpan index_span; + SourceSpan value_span; Expr *expr; AstId body; } AstCtForeachStmt; @@ -1157,8 +1158,7 @@ typedef struct struct { Label label; - bool is_type; - void *expr_or_type_info; + Expr *expr; }; struct { @@ -1172,16 +1172,16 @@ typedef struct typedef struct { Expr *expr; - TokenId alias; - TokenId constraints; + const char *alias; + const char *constraints; } AsmOperand; typedef struct { AsmOperand *inputs; AsmOperand *outputs; - TokenId **clobbers; - TokenId **labels; +/* TokenId **clobbers; + TokenId **labels;*/ } AsmParams; typedef struct @@ -1211,22 +1211,25 @@ typedef struct { struct { - TokenId param; - TokenId rest_of_line; + const char *name; + SourceSpan span; + InOutModifier modifier : 4; + bool by_ref : 1; } param; struct { Expr *decl_exprs; - Expr *comment; + const char *comment; + const char *expr_string; } contract; struct { - TokenId rest_of_line; + const char *rest_of_line; } pure; struct { const char *directive_name; - TokenId rest_of_line; + const char *rest_of_line; } generic; }; @@ -1275,7 +1278,7 @@ typedef struct Ast_ typedef struct Module_ { Path *name; - TokenId *parameters; + const char **parameters; bool is_external : 1; bool is_c_library : 1; @@ -1288,7 +1291,7 @@ typedef struct Module_ Decl** method_extensions; Decl** generic_cache; - STable symbols; + HTable symbols; struct CompilationUnit_ **units; } Module; @@ -1309,30 +1312,38 @@ typedef struct DynamicScope_ } DynamicScope; -typedef union +typedef struct { - struct + const char *lex_start; + size_t lex_len; + union { - const char *string; - size_t strlen; - }; - Float value; - struct - { - bool is_base64 : 1; - uint64_t len : 63; - }; - struct - { - Int128 char_value; - char width; + struct + { + const char *string; + size_t strlen; + }; + struct + { + Float value; + }; + struct + { + bool is_base64 : 1; + uint64_t bytes_len : 63; + }; + struct + { + Int128 char_value; + char width; + }; }; } TokenData; typedef struct { + struct ParseContext_ *context; const char *file_begin; - uint32_t token_start_id; const char *lexing_start; const char *current; uint32_t current_row; @@ -1340,9 +1351,10 @@ typedef struct const char *line_start; const char *start_row_start; File *file; - TokenData *latest_token_data; - SourceLocation *latest_token_loc; - unsigned char *latest_token_type; + TokenData data; + SourceSpan tok_span; + TokenType token_type; + LexMode mode; } Lexer; @@ -1370,11 +1382,8 @@ typedef struct CompilationUnit_ Decl **macro_methods; Decl **global_decls; Decl *main_function; - STable local_symbols; - struct { - STable external_symbols; - Decl **external_symbol_list; - }; + HTable local_symbols; + Decl **external_symbol_list; struct { void *debug_file; @@ -1382,17 +1391,14 @@ typedef struct CompilationUnit_ } llvm; } CompilationUnit; -typedef struct +typedef struct ParseContext_ { - uint32_t lexer_index; - Token tok; - TokenId prev_tok; - Token next_tok; + TokenData data; + TokenType tok; + SourceSpan span; + SourceSpan prev_span; CompilationUnit *unit; - Token *comments; - Token *lead_comment; - Token *trailing_comment; - Token *next_lead_comment; + Lexer lexer; } ParseContext; typedef struct SemaContext_ @@ -1402,6 +1408,10 @@ typedef struct SemaContext_ // Compiled in this unit. CompilationUnit *compilation_unit; Decl *current_function; + struct + { + bool current_function_pure : 1; + }; Decl *current_macro; ScopeId scope_id; AstId break_target; @@ -1438,7 +1448,7 @@ typedef struct typedef struct { - STable modules; + HTable modules; Module **module_list; Module **generic_module_list; Type **type; @@ -1452,7 +1462,7 @@ typedef struct char scratch_buffer[MAX_STRING_BUFFER]; Decl ***locals_list; uint32_t scratch_buffer_len; - STable compiler_defines; + HTable compiler_defines; Module std_module; DeclTable symbols; DeclTable generic_symbols; @@ -1580,12 +1590,22 @@ extern Type *type_cuint; extern const char *attribute_list[NUMBER_OF_ATTRIBUTES]; extern const char *builtin_list[NUMBER_OF_BUILTINS]; +extern const char *kw_at_return; +extern const char *kw_at_checked; +extern const char *kw_at_ensure; +extern const char *kw_at_optreturn; +extern const char *kw_at_param; +extern const char *kw_at_require; + extern const char *kw_std; extern const char *kw_max; extern const char *kw_min; extern const char *kw_elements; extern const char *kw_align; +extern const char *kw_in; +extern const char *kw_out; +extern const char *kw_inout; extern const char *kw_deprecated; extern const char *kw_distinct; extern const char *kw_ensure; @@ -1605,6 +1625,7 @@ extern const char *kw_param; extern const char *kw_ptr; extern const char *kw_values; extern const char *kw_errors; +extern const char *kw_type; extern const char *kw_FILE; extern const char *kw_FUNC; extern const char *kw_LINE; @@ -1632,15 +1653,10 @@ extern const char *kw_mainstub; -#define AST_NEW_TOKEN(_kind, _token) new_ast(_kind, source_span_from_token_id((_token).id)) -typedef unsigned char TokenTypeChar; ARENA_DEF(chars, char) ARENA_DEF(ast, Ast) ARENA_DEF(expr, Expr) -ARENA_DEF(sourceloc, SourceLocation) -ARENA_DEF(toktype, TokenTypeChar) -ARENA_DEF(tokdata, TokenData) ARENA_DEF(decl, Decl) ARENA_DEF(type_info, TypeInfo) @@ -1655,12 +1671,13 @@ static inline Ast *new_ast(AstKind kind, SourceSpan range) return ast; } +const char *span_to_string(SourceSpan span); - -static inline Ast *extend_ast_with_prev_token(ParseContext *context, Ast *ast) +static inline SourceSpan extend_span_with_token(SourceSpan loc, SourceSpan after) { - ast->span.end_loc = context->prev_tok; - return ast; + if (loc.row != after.row) return loc; + loc.length = after.col + after.length - loc.col; + return loc; } @@ -1806,7 +1823,7 @@ void global_context_add_type(Type *type); void global_context_add_decl(Decl *type_decl); void global_context_add_generic_decl(Decl *decl); -Module *compiler_find_or_create_module(Path *module_name, TokenId *parameters, bool is_private); +Module *compiler_find_or_create_module(Path *module_name, const char **parameters, bool is_private); Module *global_context_find_module(const char *name); const char *get_object_extension(void); @@ -1814,16 +1831,16 @@ CompilationUnit * unit_create(File *file); void unit_register_global_decl(CompilationUnit *unit, Decl *decl); void unit_register_external_symbol(CompilationUnit *unit, Decl *decl); -bool unit_add_import(CompilationUnit *unit, Path *path, Token token, bool private_import); +bool unit_add_import(CompilationUnit *unit, Path *path, bool private_import); bool context_set_module_from_filename(ParseContext *context); -bool context_set_module(ParseContext *context, Path *path, TokenId *generic_parameters, bool is_private); +bool context_set_module(ParseContext *context, Path *path, const char **generic_parameters, bool is_private); // --- Decl functions -Decl *decl_new(DeclKind decl_kind, TokenId name, Visibility visibility); -Decl *decl_new_ct(DeclKind kind, TokenId span); -Decl *decl_new_with_type(TokenId name, DeclKind decl_type, Visibility visibility); -Decl *decl_new_var(TokenId name, TypeInfo *type, VarDeclKind kind, Visibility visibility); +Decl *decl_new(DeclKind decl_kind, const char *name, SourceSpan span, Visibility visibility); +Decl *decl_new_ct(DeclKind kind, SourceSpan span); +Decl *decl_new_with_type(const char *name, SourceSpan span, DeclKind decl_type, Visibility visibility); +Decl *decl_new_var(const char *name, SourceSpan span, TypeInfo *type, VarDeclKind kind, Visibility visibility); Decl *decl_new_generated_var(const char *name, Type *type, VarDeclKind kind, SourceSpan span); void decl_set_external_name(Decl *decl); const char *decl_to_name(Decl *decl); @@ -1850,10 +1867,11 @@ static inline Decl *decl_flatten(Decl *decl) // --- Diag functions -void diag_verror_range(SourceLocation *location, const char *message, va_list args); +void diag_verror_range(SourceSpan location, const char *message, va_list args); #define EXPR_NEW_EXPR(kind_, expr_) expr_new(kind_, (expr_)->span) -#define EXPR_NEW_TOKEN(kind_, tok_) expr_new(kind_, source_span_from_token_id((tok_).id)) +#define EXPR_NEW_TOKEN(kind_) expr_new(kind_, c->span) + Expr *expr_new(ExprKind kind, SourceSpan start); bool expr_is_simple(Expr *expr); bool expr_is_pure(Expr *expr); @@ -1897,36 +1915,20 @@ bool float_const_fits_type(const ExprConst *expr_const, TypeKind kind); // --- Lexer functions -void lexer_lex_file(Lexer *lexer); +void lexer_init(Lexer *lexer); +bool lexer_next_token(Lexer *lexer); -static inline SourceLocation *tokenid_loc(TokenId token) { return sourcelocptr(token.index); } -static inline SourceLocation *token_loc(Token token) { return sourcelocptr(token.id.index); } -static inline TokenData *tokendata_from_id(TokenId token) { return tokdataptr(token.index); } -static inline TokenData *tokendata_from_token(Token token) { return tokdataptr(token.id.index); } - -#define TOKDATA(T) _Generic((T), TokenId: tokendata_from_id, Token: tokendata_from_token)(T) -#define TOKLOC(T) _Generic((T), TokenId: tokenid_loc, Token: token_loc)(T) - -#define TOKSTR(T) TOKDATA(T)->string - -static inline TokenType tokenid_type(TokenId token) { return (TokenType)toktypeptr(token.index)[0]; } -static inline TokenType token_type(Token token) { return (TokenType)toktypeptr(token.id.index)[0]; } -#define TOKTYPE(T) _Generic((T), TokenId: tokenid_type, Token: token_type)(T) - -#define TOKLEN(T) TOKLOC(T)->length - Decl *module_find_symbol(Module *module, const char *symbol); const char *module_create_object_file_name(Module *module); bool parse_file(File *file); Path *path_create_from_string(const char *string, uint32_t len, SourceSpan span); -#define SEMA_TOKEN_ERROR(_tok, ...) sema_error_range(source_span_from_token_id(_tok.id), __VA_ARGS__) -#define SEMA_TOKID_ERROR(_tok_id, ...) sema_error_range(source_span_from_token_id(_tok_id), __VA_ARGS__) -#define SEMA_ERROR(_node, ...) sema_error_range((_node)->span, __VA_ARGS__) -#define SEMA_PREV(_node, ...) sema_prev_at_range3((_node)->span, __VA_ARGS__) -#define SEMA_TOKID_PREV(_tok_id, ...) sema_prev_at_range3(source_span_from_token_id(_tok_id), __VA_ARGS__) +#define SEMA_ERROR_HERE(...) sema_error_at(c->span, __VA_ARGS__) +#define SEMA_ERROR_LAST(...) sema_error_at(c->prev_span, __VA_ARGS__) +#define SEMA_ERROR(_node, ...) sema_error_at((_node)->span, __VA_ARGS__) +#define SEMA_PREV(_node, ...) sema_error_prev_at((_node)->span, __VA_ARGS__) #define TABLE_MAX_LOAD 0.5 @@ -1954,10 +1956,10 @@ bool sema_expr_analyse_assign_right_side(SemaContext *context, Expr *expr, Type bool sema_expr_analyse_general_call(SemaContext *context, Expr *expr, Decl *decl, Expr *struct_var, bool is_macro, bool failable); Decl *sema_resolve_symbol_in_current_dynamic_scope(SemaContext *context, const char *symbol); -Decl *unit_resolve_parameterized_symbol(CompilationUnit *unit, TokenId symbol, Path *path); +Decl *unit_resolve_parameterized_symbol(CompilationUnit *unit, const char *symbol, SourceSpan loc, Path *path); Decl *sema_resolve_method(CompilationUnit *unit, Decl *type, const char *method_name, Decl **ambiguous_ref, Decl **private_ref); Decl *sema_find_extension_method_in_module(Module *module, Type *type, const char *method_name); -Decl *sema_resolve_normal_symbol(SemaContext *context, TokenId symbol, Path *path, bool handle_error); +Decl *sema_resolve_normal_symbol(SemaContext *context, const char *symbol, SourceSpan span, Path *path, bool handle_error); Decl *sema_resolve_string_symbol(SemaContext *context, const char *symbol, SourceSpan span, Path *path, bool report_error); bool sema_resolve_type(SemaContext *context, Type *type); @@ -1967,30 +1969,28 @@ bool sema_resolve_type_info_maybe_inferred(SemaContext *context, TypeInfo *type_ bool sema_resolve_type_shallow(SemaContext *context, TypeInfo *type_info, bool allow_inferred_type, bool in_shallow); Type *sema_type_lower_by_size(Type *type, ArraySize element_size); -void sema_error_at_prev_end(Token token, const char *message, ...); - -void sema_error_range(SourceSpan span, const char *message, ...); - -void sema_verror_range(SourceLocation *location, const char *message, va_list args); +void sema_error_at(SourceSpan loc, const char *message, ...); +void sema_error_at_after(SourceSpan loc, const char *message, ...); +void sema_error_prev_at(SourceSpan loc, const char *message, ...); +void sema_verror_range(SourceSpan location, const char *message, va_list args); void sema_error(ParseContext *context, const char *message, ...); -void sema_prev_at_range3(SourceSpan span, const char *message, ...); + void sema_shadow_error(Decl *decl, Decl *old); File *source_file_by_id(FileId file); File *source_file_load(const char *filename, bool *already_loaded); -static inline SourceSpan source_span_from_token_id(TokenId id) -{ - return (SourceSpan) { id, id }; -} - -#define RANGE_EXTEND_PREV(x) ((x)->span.end_loc = context->prev_tok) +#define RANGE_EXTEND_PREV(x) do { (x)->span = extend_span_with_token((x)->span, c->prev_span); } while (0) void stable_init(STable *table, uint32_t initial_size); void *stable_set(STable *table, const char *key, void *value); void *stable_get(STable *table, const char *key); +void htable_init(HTable *table, uint32_t initial_size); +void *htable_set(HTable *table, const char *key, void *value); +void *htable_get(HTable *table, const char *key); + UNUSED void stable_clear(STable *table); void decltable_init(DeclTable *table, uint32_t initial_size); @@ -2005,6 +2005,7 @@ void scratch_buffer_append_signed_int(int64_t i); UNUSED void scratch_buffer_append_unsigned_int(uint64_t i); char *scratch_buffer_to_string(void); const char *scratch_buffer_interned(void); +char *scratch_buffer_copy(void); const char *symtab_add(const char *symbol, uint32_t len, uint32_t fnv1hash, TokenType *type); const char *symtab_find(const char *symbol, uint32_t len, uint32_t fnv1hash, TokenType *type); @@ -2017,16 +2018,7 @@ void c_abi_func_create(FunctionPrototype *proto); bool token_is_type(TokenType type); bool token_is_any_type(TokenType type); const char *token_type_to_string(TokenType type); -static inline TokenType advance_token(TokenId *token) -{ - TokenType tok; - while (1) - { - token->index += 1; - tok = TOKTYPE(*token); - if (tok != TOKEN_COMMENT) return tok; - } -} + AlignSize type_abi_alignment(Type *type); @@ -2260,12 +2252,12 @@ static inline const char* struct_union_name_from_token(TokenType type) } -void advance(ParseContext *context); +void advance(ParseContext *c); // Useful sanity check function. static inline void advance_and_verify(ParseContext *context, TokenType token_type) { - assert(context->tok.type == token_type); + assert(context->tok == token_type); advance(context); } @@ -2432,7 +2424,7 @@ static inline bool decl_is_struct_type(Decl *decl) static inline bool decl_is_enum_kind(Decl *decl) { DeclKind kind = decl->decl_kind; - return (kind == DECL_ENUM) | (kind == DECL_ERRTYPE); + return (kind == DECL_ENUM) | (kind == DECL_OPTENUM); } static inline bool decl_is_user_defined_type(Decl *decl) @@ -2497,10 +2489,10 @@ void platform_compiler(const char **files, unsigned file_count, const char* flag #define CAT(a,b) CAT2(a,b) // force expand #define CAT2(a,b) a##b // actually concatenate #define TEMP(X) CAT(X, __LINE__) -#define ASSIGN_AST_ELSE(_assign, _ast_stmt, _res) Ast* TEMP(_ast) = (_ast_stmt); if (!ast_ok(TEMP(_ast))) return _res; _assign = TEMP(_ast) -#define ASSIGN_EXPR_ELSE(_assign, _expr_stmt, _res) Expr* TEMP(_expr) = (_expr_stmt); if (!expr_ok(TEMP(_expr))) return _res; _assign = TEMP(_expr) -#define ASSIGN_TYPE_ELSE(_assign, _type_stmt, _res) TypeInfo* TEMP(_type) = (_type_stmt); if (!type_info_ok(TEMP(_type))) return _res; _assign = TEMP(_type) -#define ASSIGN_DECL_ELSE(_assign, _decl_stmt, _res) Decl* TEMP(_decl) = (_decl_stmt); if (!decl_ok(TEMP(_decl))) return _res; _assign = TEMP(_decl) +#define ASSIGN_AST_OR_RET(_assign, _ast_stmt, _res) Ast* TEMP(_ast) = (_ast_stmt); if (!ast_ok(TEMP(_ast))) return _res; _assign = TEMP(_ast) +#define ASSIGN_EXPR_OR_RET(_assign, _expr_stmt, _res) Expr* TEMP(_expr) = (_expr_stmt); if (!expr_ok(TEMP(_expr))) return _res; _assign = TEMP(_expr) +#define ASSIGN_TYPE_OR_RET(_assign, _type_stmt, _res) TypeInfo* TEMP(_type) = (_type_stmt); if (!type_info_ok(TEMP(_type))) return _res; _assign = TEMP(_type) +#define ASSIGN_DECL_OR_RET(_assign, _decl_stmt, _res) Decl* TEMP(_decl) = (_decl_stmt); if (!decl_ok(TEMP(_decl))) return _res; _assign = TEMP(_decl) static inline void global_context_clear_errors(void) @@ -2534,3 +2526,8 @@ static inline Ast *ast_next(AstId *current_ptr) return ast; } extern ArchOsTarget default_target; + +static inline const char *decl_get_extname(Decl *decl) +{ + return decl->extname ? decl->extname : decl->external_name; +} diff --git a/src/compiler/context.c b/src/compiler/context.c index 8f412e127..97609a827 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -9,13 +9,12 @@ CompilationUnit * unit_create(File *file) { CompilationUnit *unit = CALLOCS(CompilationUnit); unit->file = file; - stable_init(&unit->local_symbols, 1024); - stable_init(&unit->external_symbols, 1024); + htable_init(&unit->local_symbols, 64 * 1024); return unit; } -static inline bool create_module_or_check_name(CompilationUnit *unit, Path *module_name, TokenId *parameters, bool is_private) +static inline bool create_module_or_check_name(CompilationUnit *unit, Path *module_name, const char **parameters, bool is_private) { Module *module = unit->module; if (!module) @@ -38,11 +37,11 @@ static inline bool create_module_or_check_name(CompilationUnit *unit, Path *modu { if (is_private) { - SEMA_ERROR(module_name, "The module is declared as private here, but was declared as public elsewhere."); + SEMA_ERROR(module_name, "The module is declared as private here, but was declared as public elsewhere."); } else { - SEMA_ERROR(module_name, "The module is declared as public here, but was declared as private elsewhere."); + SEMA_ERROR(module_name, "The module is declared as public here, but was declared as private elsewhere."); } return false; @@ -102,18 +101,18 @@ bool context_set_module_from_filename(ParseContext *context) return false; } Path *path = CALLOCS(Path); - path->span = INVALID_RANGE; + path->span = INVALID_SPAN; path->module = module_name; path->len = global_context.scratch_buffer_len; return create_module_or_check_name(context->unit, path, NULL, true); } -bool context_set_module(ParseContext *context, Path *path, TokenId *generic_parameters, bool is_private) +bool context_set_module(ParseContext *context, Path *path, const char **generic_parameters, bool is_private) { // Note that we allow the illegal name for now, to be able to parse further. if (!is_all_lower(path->module)) { - SEMA_ERROR(path, "A module name may not have any upper case characters."); + SEMA_ERROR(path, "A module name may not have any upper case characters."); return false; } @@ -124,12 +123,11 @@ bool context_set_module(ParseContext *context, Path *path, TokenId *generic_para void unit_register_external_symbol(CompilationUnit *unit, Decl *decl) { if (!decl->module || decl->module == unit->module || !decl->external_name) return; - Decl *prev = stable_get(&unit->external_symbols, decl->external_name); - if (prev) return; - if (decl != stable_set(&unit->external_symbols, decl->external_name, decl)) + VECEACH(unit->external_symbol_list, i) { - vec_add(unit->external_symbol_list, decl); + if (decl == unit->external_symbol_list[i]) return; } + vec_add(unit->external_symbol_list, decl); } @@ -146,7 +144,7 @@ void decl_register(Decl *decl) case DECL_CT_SWITCH: case DECL_CT_ASSERT: case DECL_ENUM_CONSTANT: - case DECL_ERRVALUE: + case DECL_OPTVALUE: case DECL_IMPORT: case DECL_LABEL: case DECL_DECLARRAY: @@ -154,7 +152,7 @@ void decl_register(Decl *decl) case DECL_ATTRIBUTE: case DECL_BITSTRUCT: case DECL_DISTINCT: - case DECL_ERRTYPE: + case DECL_OPTENUM: case DECL_ENUM: case DECL_STRUCT: case DECL_TYPEDEF: @@ -227,7 +225,7 @@ void unit_register_global_decl(CompilationUnit *unit, Decl *decl) case DECL_STRUCT: case DECL_UNION: case DECL_TYPEDEF: - case DECL_ERRTYPE: + case DECL_OPTENUM: case DECL_BITSTRUCT: assert(decl->name); vec_add(unit->types, decl); @@ -246,7 +244,7 @@ void unit_register_global_decl(CompilationUnit *unit, Decl *decl) decl_set_external_name(decl); decl_register(decl); break; - case DECL_ERRVALUE: + case DECL_OPTVALUE: case DECL_ENUM_CONSTANT: case DECL_IMPORT: case DECL_CT_ELSE: @@ -267,8 +265,8 @@ void unit_register_global_decl(CompilationUnit *unit, Decl *decl) DEBUG_LOG("Registering symbol '%s' in %s.", decl->name, unit->module->name->module); Decl *old; - if ((old = stable_set(&unit->local_symbols, decl->name, decl))) goto ERR; - if ((old = stable_set(&unit->module->symbols, decl->name, decl))) goto ERR; + if ((old = htable_set(&unit->local_symbols, decl->name, decl))) goto ERR; + if ((old = htable_set(&unit->module->symbols, decl->name, decl))) goto ERR; return; ERR: assert(decl != old); @@ -277,7 +275,7 @@ ERR: decl_poison(old); } -bool unit_add_import(CompilationUnit *unit, Path *path, Token token, bool private_import) +bool unit_add_import(CompilationUnit *unit, Path *path, bool private_import) { DEBUG_LOG("SEMA: Add import of '%s'.", path->module); @@ -294,7 +292,6 @@ bool unit_add_import(CompilationUnit *unit, Path *path, Token token, bool privat import->visibility = VISIBLE_LOCAL; import->import.path = path; import->import.private = private_import; - import->import.symbol = token.id; vec_add(unit->imports, import); DEBUG_LOG("Added import %s", path->module); diff --git a/src/compiler/copying.c b/src/compiler/copying.c index dd43f4112..88d1841b2 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -241,8 +241,8 @@ Ast *ast_copy_deep(Ast *source) { case DOC_DIRECTIVE_REQUIRE: case DOC_DIRECTIVE_ENSURE: + case DOC_DIRECTIVE_CHECKED: MACRO_COPY_EXPR(ast->doc_directive.contract.decl_exprs); - MACRO_COPY_EXPR(ast->doc_directive.contract.comment); break; case DOC_DIRECTIVE_PARAM: case DOC_DIRECTIVE_ERRORS: @@ -338,14 +338,7 @@ Ast *ast_copy_deep(Ast *source) MACRO_COPY_AST(ast->if_stmt.then_body); return ast; case AST_NEXT_STMT: - if (ast->nextcase_stmt.is_type) - { - MACRO_COPY_TYPE(ast->nextcase_stmt.expr_or_type_info); - } - else - { - MACRO_COPY_EXPR(ast->nextcase_stmt.expr_or_type_info); - } + MACRO_COPY_EXPR(ast->nextcase_stmt.expr); return ast; case AST_NOP_STMT: return ast; @@ -412,6 +405,7 @@ TypeInfo *copy_type_info(TypeInfo *source) { case TYPE_INFO_POISON: return copy; + case TYPE_INFO_CT_IDENTIFIER: case TYPE_INFO_IDENTIFIER: return copy; case TYPE_INFO_EXPRESSION: @@ -486,7 +480,7 @@ Decl *copy_decl(Decl *decl) case DECL_BITSTRUCT: UNREACHABLE case DECL_ENUM: - case DECL_ERRTYPE: + case DECL_OPTENUM: copy_decl_type(copy); MACRO_COPY_DECL_LIST(copy->methods); MACRO_COPY_DECL_LIST(copy->enums.parameters); @@ -517,7 +511,7 @@ Decl *copy_decl(Decl *decl) MACRO_COPY_EXPR(copy->enum_constant.expr); MACRO_COPY_EXPR_LIST(copy->enum_constant.args); break; - case DECL_ERRVALUE: + case DECL_OPTVALUE: MACRO_COPY_EXPR(copy->enum_constant.expr); MACRO_COPY_EXPR_LIST(copy->enum_constant.args); break; diff --git a/src/compiler/diagnostics.c b/src/compiler/diagnostics.c index 79fddd6ae..bcdb38fff 100644 --- a/src/compiler/diagnostics.c +++ b/src/compiler/diagnostics.c @@ -15,89 +15,79 @@ typedef enum } PrintType; #define LINES_SHOWN 4 +#define MAX_WIDTH 120 -static void print_error(SourceLocation *location, const char *message, PrintType print_type) +static void print_error(SourceSpan location, const char *message, PrintType print_type) { - File *file = source_file_by_id(location->file_id); + File *file = source_file_by_id(location.file_id); if (active_target.test_output) { switch (print_type) { case PRINT_TYPE_ERROR: - eprintf("Error|%s|%d|%s\n", file->name, location->row, message); + eprintf("Error|%s|%d|%s\n", file->name, location.row, message); return; case PRINT_TYPE_PREV: return; case PRINT_TYPE_WARN: - eprintf("Warning|%s|%d|%s\n", file->name, location->row, message); + eprintf("Warning|%s|%d|%s\n", file->name, location.row, message); return; default: UNREACHABLE } } - unsigned max_line_length = (unsigned)round(log10(location->row)) + 1; - + unsigned max_line_length = (unsigned)round(log10(location.row)) + 1; + unsigned max_lines_for_display = MAX_WIDTH - max_line_length - 2; char number_buffer[20]; + char number_buffer_elided[20]; snprintf(number_buffer, 20, "%%%dd: %%.*s\n", max_line_length); + snprintf(number_buffer_elided, 20, "%%%dd: %%.*s|\n", max_line_length); + snprintf(number_buffer, 20, "%%%dd: %%.*s\n", max_line_length); + snprintf(number_buffer_elided, 20, "%%%dd: %%.*s|\n", max_line_length); // Insert end in case it's not yet there. const char *file_contents = file->contents; - int lines_found = 0; - size_t line_starts[LINES_SHOWN + 1] = { 0, 0, 0, 0 }; - uint32_t start = location->start; - if (start < 2) + int64_t display_row = location.row; + int64_t row_start = display_row - LINES_SHOWN + 1; + if (row_start < 1) row_start = 1; + int64_t row = 1; + const char *current = file_contents; + // Progress to the first row. + while (row < row_start) { - line_starts[++lines_found] = 0; - } - else - { - for (size_t i = start; i > 0; i--) + if (current++[0] == '\n') { - if (file_contents[i - 1] == '\n') - { - line_starts[++lines_found] = i; - if (lines_found >= LINES_SHOWN) break; - } - if (i == 1) - { - line_starts[++lines_found] = 0; - break; - } + row++; } } - for (size_t i = start; ; i++) + int row_len = -1; + while (row <= display_row) { - switch (file_contents[i]) + current += row_len + 1; + row_len = 0; + while (current[row_len] != '\n') row_len++; + if (row_len > max_lines_for_display) { - case '\0': - case '\n': - line_starts[0] = i + 1; - goto FOUND; - default: - continue; + eprintf(number_buffer_elided, row, max_lines_for_display - 1, current); } - } - FOUND:; - const char *start_char = NULL; - for (unsigned i = lines_found; i > 0; i--) - { - SourceLoc line_start = line_starts[i]; - SourceLoc line_end = line_starts[i - 1] - 1; - uint32_t line_number = location->row + 1 - i; - uint32_t line_len = line_end - line_start; - start_char = file->contents + line_start; - eprintf(number_buffer, line_number, line_len, start_char); + else + { + eprintf(number_buffer, row, row_len, current); + } + row++; } eprintf(" "); for (unsigned i = 0; i < max_line_length; i++) { eprintf(" "); } - - for (unsigned i = 1; i < location->col; i++) + unsigned col_location = location.col; + if (!col_location || col_location > max_lines_for_display) col_location = 0; + unsigned space_to = col_location ? col_location : max_lines_for_display - 1; + for (unsigned i = 1; i < space_to; i++) { - switch (start_char[i]) + switch (current[i]) { case '\t': eprintf("\t"); @@ -105,38 +95,70 @@ static void print_error(SourceLocation *location, const char *message, PrintType eprintf(" "); } } - for (uint32_t i = 0; i < location->length; i++) + unsigned len = location.length; + if (!len) len = 1; + if (col_location) { - eprintf("^"); + for (uint32_t i = 0; i < len; i++) + { + eprintf("^"); + } } eprintf("\n"); - switch (print_type) + if (col_location) { - case PRINT_TYPE_ERROR: - eprintf("(%s:%d:%d) Error: %s\n\n", file->full_path, location->row, location->col, message); - break; - case PRINT_TYPE_PREV: - eprintf("(%s:%d:%d) %s\n\n", file->name, location->row, location->col, message); - break; - case PRINT_TYPE_WARN: - eprintf("(%s:%d:%d) Warning: %s\n\n", file->name, location->row, location->col, message); - break; - default: - UNREACHABLE + switch (print_type) + { + case PRINT_TYPE_ERROR: + eprintf("(%s:%d:%d) Error: %s\n\n", file->full_path, location.row, col_location, message); + break; + case PRINT_TYPE_PREV: + eprintf("(%s:%d:%d) %s\n\n", file->name, location.row, col_location, message); + break; + case PRINT_TYPE_WARN: + eprintf("(%s:%d:%d) Warning: %s\n\n", file->name, location.row, col_location, message); + break; + default: + UNREACHABLE + } + } + else + { + switch (print_type) + { + case PRINT_TYPE_ERROR: + eprintf("(%s:%d) Error: %s\n\n", file->full_path, location.row, message); + break; + case PRINT_TYPE_PREV: + eprintf("(%s:%d) %s\n\n", file->name, location.row, message); + break; + case PRINT_TYPE_WARN: + eprintf("(%s:%d) Warning: %s\n\n", file->name, location.row, message); + break; + default: + UNREACHABLE + } + } } -static void vprint_error(SourceLocation *location, const char *message, va_list args) +static void vprint_error(SourceSpan location, const char *message, va_list args) { - char buffer[256]; - vsnprintf(buffer, 256, message, args); +#define MAX_ERROR_LEN 4096 + char buffer[MAX_ERROR_LEN]; + size_t written = vsnprintf(buffer, MAX_ERROR_LEN - 1, message, args); + if (written > MAX_ERROR_LEN - 2) + { + print_error(location, "", PRINT_TYPE_ERROR); + return; + } print_error(location, buffer, PRINT_TYPE_ERROR); } -void diag_verror_range(SourceLocation *location, const char *message, va_list args) +void diag_verror_range(SourceSpan location, const char *message, va_list args) { if (global_context.in_panic_mode) return; global_context.in_panic_mode = true; @@ -145,65 +167,48 @@ void diag_verror_range(SourceLocation *location, const char *message, va_list ar } -void sema_verror_range(SourceLocation *location, const char *message, va_list args) +void sema_verror_range(SourceSpan location, const char *message, va_list args) { vprint_error(location, message, args); global_context.errors_found++; } -void sema_prev_at_range3(SourceSpan span, const char *message, ...) +void sema_error_at(SourceSpan loc, const char *message, ...) { - SourceLocation *start = TOKLOC(span.loc); - SourceLocation *end = TOKLOC(span.end_loc); - va_list args; - va_start(args, message); - char buffer[256]; - vsnprintf(buffer, 256, message, args); - SourceLocation loc = *start; - loc.length = end->start - start->start + end->length; - print_error(&loc, buffer, PRINT_TYPE_PREV); - va_end(args); -} - -void sema_error_range(SourceSpan span, const char *message, ...) -{ - SourceLocation *start = TOKLOC(span.loc); - SourceLocation *end = TOKLOC(span.end_loc); - - SourceLocation loc = *start; - loc.length = end->start - start->start + end->length; va_list list; va_start(list, message); - sema_verror_range(&loc, message, list); + sema_verror_range(loc, message, list); va_end(list); } -void sema_error_at_prev_end(Token token, const char *message, ...) +void sema_error_at_after(SourceSpan loc, const char *message, ...) { - SourceLocation *curr = TOKLOC(token); - SourceLocation *prev = TOKLOC((TokenId) { token.id.index - 1 }); - SourceLocation location; - if (curr->file_id != prev->file_id) + loc.col += loc.length; + loc.length = 1; + va_list list; + va_start(list, message); + sema_verror_range(loc, message, list); + va_end(list); +} + +void sema_error_prev_at(SourceSpan loc, const char *message, ...) +{ + va_list args; + va_start(args, message); +#define MAX_ERROR_LEN 4096 + char buffer[MAX_ERROR_LEN]; + size_t written = vsnprintf(buffer, MAX_ERROR_LEN - 1, message, args); + if (written > MAX_ERROR_LEN - 2) { - // Ok, this is the first location, so then we create a "start" location: - location = *curr; - location.start = 0; - location.row = 1; - location.col = 1; + print_error(loc, "", PRINT_TYPE_PREV); } else { - // TODO handle multiline - location = *prev; - location.col += location.length; - location.start += location.length; + print_error(loc, buffer, PRINT_TYPE_PREV); } - location.length = 1; - va_list list; - va_start(list, message); - sema_verror_range(&location, message, list); - va_end(list); + va_end(args); + return; } @@ -217,8 +222,34 @@ void sema_error(ParseContext *context, const char *message, ...) evprintf(message, list); eprintf("\n"); va_end(list); +} - +// This function is fairly slow, which is a reflection on how +// often it is supposed to be used. +const char *span_to_string(SourceSpan span) +{ + File *file = source_file_by_id(span.file_id); + const char *current = file->contents; + uint32_t row = 1; + uint32_t row_to_find = span.row; + uint32_t length = span.length; + uint32_t col = span.col; + if (!row_to_find || !length || !col) return NULL; + while (row < row_to_find) + { + switch (current++[0]) + { + case '\0': + return NULL; + case '\n': + row++; + default: + break; + } + } + assert(row == row_to_find); + const char *start = current + col - 1; + return copy_string(start, length); } diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 67fa65119..b82180337 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -138,8 +138,8 @@ typedef enum DECL_DISTINCT, DECL_ENUM, DECL_ENUM_CONSTANT, - DECL_ERRTYPE, - DECL_ERRVALUE, + DECL_OPTENUM, + DECL_OPTVALUE, DECL_FUNC, DECL_GENERIC, DECL_IMPORT, @@ -162,6 +162,7 @@ typedef enum DOC_DIRECTIVE_UNKNOWN, DOC_DIRECTIVE_PURE, DOC_DIRECTIVE_REQUIRE, + DOC_DIRECTIVE_CHECKED, DOC_DIRECTIVE_PARAM, DOC_DIRECTIVE_ERRORS, DOC_DIRECTIVE_ENSURE, @@ -286,6 +287,7 @@ typedef enum { TYPE_INFO_POISON, TYPE_INFO_IDENTIFIER, + TYPE_INFO_CT_IDENTIFIER, TYPE_INFO_EXPRESSION, TYPE_INFO_ARRAY, TYPE_INFO_VECTOR, @@ -417,8 +419,8 @@ typedef enum TOKEN_REAL, // 0x23.2p-2a 43.23e23 TOKEN_BYTES, // Base64 or Hex - TOKEN_COMMENT, // Comment - TOKEN_DOC_COMMENT, // Doc Comment + TOKEN_DOC_DIRECTIVE, // Doc Directive + TOKEN_DOC_COMMENT, // Doc Comment start // Keywords TOKEN_ALIAS, // Reserved @@ -495,9 +497,14 @@ typedef enum TOKEN_DOCS_START, // /** TOKEN_DOCS_END, // */ (may start with an arbitrary number of `*` - TOKEN_DOCS_EOL, // "\n" only seen in directives. - TOKEN_DOCS_DIRECTIVE, // @ in the directive - TOKEN_DOCS_LINE, // Any line within /** **/ + TOKEN_DOCS_ENSURE, // @ensure + TOKEN_DOCS_REQUIRE, // @require + TOKEN_DOCS_CHECKED, // @checked + TOKEN_DOCS_PARAM, // @param + TOKEN_DOCS_RETURN, // @return + TOKEN_DOCS_OPTRETURN, // @optreturn + TOKEN_DOCS_PURE, // @pure + TOKEN_EOF, // \n - SHOULD ALWAYS BE THE LAST TOKEN. @@ -732,3 +739,27 @@ typedef enum ATOMIC_ACQUIRE_RELEASE, ATOMIC_SEQ_CONSISTENT, } Atomicity; + + +typedef enum +{ + LEX_NORMAL, + LEX_DOCS, +} LexMode; + +typedef enum +{ + LEX_DOC_NONE, + LEX_DOC_PARAM, + LEX_DOC_REST_OF_LINE, + LEX_DOC_EXPR, + LEX_DOC_OPTLIST, +} LexDocMode; + +typedef enum +{ + PARAM_ANY, + PARAM_IN, + PARAM_OUT, + PARAM_INOUT, +} InOutModifier; \ No newline at end of file diff --git a/src/compiler/hashtable.c b/src/compiler/hashtable.c new file mode 100644 index 000000000..3c334ffc9 --- /dev/null +++ b/src/compiler/hashtable.c @@ -0,0 +1,3 @@ +#include "../utils/lib.h" +#include "enums.h" + diff --git a/src/compiler/headers.c b/src/compiler/headers.c index fefd2d257..a9f6ec1c0 100644 --- a/src/compiler/headers.c +++ b/src/compiler/headers.c @@ -191,7 +191,7 @@ static void header_gen_decl(FILE *file, int indent, Decl *decl) { case NON_TYPE_DECLS: case DECL_ENUM_CONSTANT: - case DECL_ERRVALUE: + case DECL_OPTVALUE: case DECL_POISONED: case DECL_VAR: UNREACHABLE @@ -210,7 +210,7 @@ static void header_gen_decl(FILE *file, int indent, Decl *decl) case DECL_ENUM: header_gen_enum(file, indent, decl); return; - case DECL_ERRTYPE: + case DECL_OPTENUM: header_gen_err(file, indent, decl); return; } diff --git a/src/compiler/lexer.c b/src/compiler/lexer.c index 606cd80ec..645023658 100644 --- a/src/compiler/lexer.c +++ b/src/compiler/lexer.c @@ -4,35 +4,33 @@ #include "compiler_internal.h" -typedef enum -{ - LEX_NORMAL, - LEX_DOCS, -} LexMode; +#define LOWER_CASE \ + 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': \ + case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': \ + case 'u': case 'v': case 'w': case 'x': case 'y': case 'z' +#define UPPER_CASE \ + 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': \ + case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': \ + case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z' +#define NUM_CASE \ + '0': case '1': case '2': case '3': case '4': \ + case '5': case '6': case '7': case '8': case '9' -typedef enum -{ - DOC_END_EOF, - DOC_END_LAST, - DOC_END_EOL, - DOC_END_ERROR, -} DocEnd; static inline uint16_t check_col(intptr_t col, uint32_t row) { - if (col > 65535) error_exit("Column on line %d exceeded %d.", row, 65535); + if (col > 255) return 0; return (uint16_t)col; } -static inline uint32_t check_row(intptr_t line, uint32_t row) +static inline unsigned check_row(intptr_t line, uint32_t row) { - if (line > 1024 * 1024) error_exit("Token on line %d exceeded %d.", row, 1024 * 1024); - return (uint32_t)line; + return line > MAX_SOURCE_LOCATION_LEN ? 0 : (unsigned)line; } // --- Lexing general methods. -static bool lexer_scan_token_inner(Lexer *lexer, LexMode mode); +static bool lexer_scan_token_inner(Lexer *lexer); static inline void begin_new_token(Lexer *lexer) { @@ -60,10 +58,15 @@ static inline void backtrace_to_lexing_start(Lexer *lexer) // Is the current character '\0' if so we assume we reached the end. #define reached_end(lexer_) (lexer_->current[0] == '\0') -// Return the current character and step one character forward. -#define next(lexer_) \ -(((lexer_)->current[0] == '\n' ? ((lexer_)->line_start = (lexer_)->current + 1, (lexer_)->current_row++) : false), \ -(lexer_)->current++) +// Step one character forward and return that character +INLINE char next(Lexer *lexer) +{ + if (*lexer->current == '\n') + { + lexer->line_start = lexer->current + 1, lexer->current_row++; + } + return (++lexer->current)[0]; +} // Backtrack the buffer read one step. static inline void backtrack(Lexer *lexer) @@ -88,8 +91,6 @@ static inline void skip(Lexer *lexer, int steps) } } - - // Match a single character – if successful, more one step forward. static inline bool match(Lexer *lexer, char expected) @@ -106,56 +107,40 @@ static inline bool match(Lexer *lexer, char expected) * This call is doing the basic allocation, with other functions * filling out additional information. **/ -static inline void add_generic_token(Lexer *lexer, TokenType type) +static inline void set_generic_token(Lexer *lexer, TokenType type) { - // Allocate source location, type, data for the token - // each of these use their own arena, - // causing them to be allocated directly into - // what amounts to a huge array. - // Consequently these allocs are actually simultaneously - // allocating data and putting that data in an array. - SourceLocation *location = sourceloc_calloc(); - unsigned char *token_type = (unsigned char *)toktype_calloc(); - TokenData *data = tokdata_calloc(); - token_type[0] = (unsigned char)type; + lexer->token_type = type; // Set the location. - location->file_id = lexer->file->file_id; - location->start = (uint32_t)(lexer->lexing_start - lexer->file_begin); - + lexer->data.lex_len = lexer->current - lexer->lexing_start; + lexer->data.lex_start = lexer->lexing_start; uint32_t line = lexer->start_row; - location->row = line; + uint32_t col; + uint32_t length; if (line == lexer->current_row) { // Col is simple difference. - location->col = check_col(lexer->lexing_start - lexer->line_start + 1, line); - // Start is offset to file begin. - location->start = (SourceLoc) (lexer->lexing_start - lexer->file_begin); + col = check_col(lexer->lexing_start - lexer->line_start + 1, line); // Length is diff between current and start. - location->length = check_row(lexer->current - lexer->lexing_start, line); + length = check_row(lexer->current - lexer->lexing_start, line); } else { - location->col = check_col(lexer->lexing_start - lexer->start_row_start + 1, line); - // Start is offset to file begin. - location->start = (SourceLoc) (lexer->lexing_start - lexer->file_begin); - location->length = 1; + col = check_col(lexer->lexing_start - lexer->start_row_start + 1, line); + length = 1; } - - // Return pointers to the data and the location, - // these maybe be used to fill in data. - lexer->latest_token_data = data; - lexer->latest_token_loc = location; - lexer->latest_token_type = token_type; + lexer->tok_span.length = length; + lexer->tok_span.col = col; + lexer->tok_span.row = line; } // Error? We simply generate an invalid token and print out the error. static bool add_error_token(Lexer *lexer, const char *message, ...) { - add_generic_token(lexer, TOKEN_INVALID_TOKEN); + set_generic_token(lexer, TOKEN_INVALID_TOKEN); va_list list; va_start(list, message); - sema_verror_range(lexer->latest_token_loc, message, list); + sema_verror_range(lexer->tok_span, message, list); va_end(list); return false; } @@ -164,15 +149,15 @@ static bool add_error_token_at_start(Lexer *lexer, const char *message, ...) { va_list list; va_start(list, message); - SourceLocation location = { .file_id = lexer->file->file_id, - .start = (uint32_t) (lexer->lexing_start - lexer->file_begin), - .row = lexer->start_row, - .length = 1, - .col = check_col((lexer->lexing_start - lexer->start_row_start) + 1, lexer->start_row), - }; - sema_verror_range(&location, message, list); + SourceSpan location = { + .file_id = lexer->file->file_id, + .row = lexer->start_row, + .length = 1, + .col = check_col((lexer->lexing_start - lexer->start_row_start) + 1, lexer->start_row), + }; + sema_verror_range(location, message, list); va_end(list); - add_generic_token(lexer, TOKEN_INVALID_TOKEN); + set_generic_token(lexer, TOKEN_INVALID_TOKEN); return false; } @@ -181,15 +166,16 @@ static bool add_error_token_at(Lexer *lexer, const char *loc, uint32_t len, cons va_list list; va_start(list, message); uint32_t current_line = lexer->current_row; - SourceLocation location = { .file_id = lexer->file->file_id, - .start = (uint32_t) (loc - lexer->file_begin), - .row = current_line, - .length = len, - .col = check_col((loc - lexer->line_start) + 1, current_line), - }; - sema_verror_range(&location, message, list); + if (len > MAX_SOURCE_LOCATION_LEN) len = 0; + SourceSpan location = { + .file_id = lexer->file->file_id, + .row = current_line, + .length = len, + .col = check_col((loc - lexer->line_start) + 1, current_line), + }; + sema_verror_range(location, message, list); va_end(list); - add_generic_token(lexer, TOKEN_INVALID_TOKEN); + set_generic_token(lexer, TOKEN_INVALID_TOKEN); return false; } @@ -198,23 +184,23 @@ static bool add_error_token_at_current(Lexer *lexer, const char *message, ...) va_list list; va_start(list, message); uint32_t current_line = lexer->current_row; - SourceLocation location = { .file_id = lexer->file->file_id, - .start = (uint32_t) (lexer->current - lexer->file_begin), - .row = current_line, - .length = 1, - .col = check_col((lexer->current - lexer->line_start) + 1, current_line), - }; - sema_verror_range(&location, message, list); + SourceSpan location = { + .file_id = lexer->file->file_id, + .row = current_line, + .length = 1, + .col = check_col((lexer->current - lexer->line_start) + 1, current_line), + }; + sema_verror_range(location, message, list); va_end(list); - add_generic_token(lexer, TOKEN_INVALID_TOKEN); + set_generic_token(lexer, TOKEN_INVALID_TOKEN); return false; } // Add a new regular token. -static inline bool add_token(Lexer *lexer, TokenType type, const char *string) +static inline bool return_token(Lexer *lexer, TokenType type, const char *string) { - add_generic_token(lexer, type); - lexer->latest_token_data->string = string; + set_generic_token(lexer, type); + lexer->data.string = string; return true; } @@ -229,34 +215,24 @@ static inline bool add_token(Lexer *lexer, TokenType type, const char *string) */ static inline bool parse_line_comment(Lexer *lexer) { - // // style comment - // Skip forward to the end. - - /// is a doc line comment. - TokenType comment_type = match(lexer, '/') ? TOKEN_DOC_COMMENT : TOKEN_COMMENT; - while (!reached_end(lexer) && peek(lexer) != '\n') { next(lexer); } - - bool success = add_token(lexer, comment_type, lexer->lexing_start); - // If we found EOL, then walk past '\n' if (!reached_end(lexer)) { next(lexer); } - return success; + return true; } /** * Parse the common / * * / style multiline comments **/ -static inline bool parse_multiline_comment(Lexer *lexer) +static inline void parse_multiline_comment(Lexer *lexer) { - TokenType type = peek(lexer) == '*' && peek_next(lexer) != '/' ? TOKEN_DOC_COMMENT : TOKEN_COMMENT; int nesting = 1; while (1) { @@ -267,7 +243,7 @@ static inline bool parse_multiline_comment(Lexer *lexer) { skip(lexer, 2); nesting--; - if (nesting == 0) return add_token(lexer, type, lexer->lexing_start); + if (nesting == 0) return; continue; } break; @@ -282,8 +258,7 @@ static inline bool parse_multiline_comment(Lexer *lexer) case '\n': break; case '\0': - if (type != TOKEN_DOC_COMMENT) return add_token(lexer, type, lexer->lexing_start); - return add_error_token(lexer, "Missing '*/' to end the multiline comment."); + return; default: break; } @@ -295,14 +270,29 @@ static inline bool parse_multiline_comment(Lexer *lexer) /** * Skip regular whitespace. */ -static void skip_whitespace(Lexer *lexer, LexMode lex_type) +static void skip_whitespace(Lexer *lexer) { while (1) { switch (peek(lexer)) { + case '/': + if (lexer->mode == LEX_DOCS) return; + if (peek_next(lexer) == '/') + { + skip(lexer, 2); + parse_line_comment(lexer); + continue; + } + if (peek_next(lexer) == '*' && lexer->current[2] != '*') + { + skip(lexer, 2); + parse_multiline_comment(lexer); + continue; + } + return; case '\n': - if (lex_type != LEX_NORMAL) return; + if (lexer->mode == LEX_DOCS) return; FALLTHROUGH; case ' ': case '\t': @@ -343,12 +333,7 @@ static inline bool scan_ident(Lexer *lexer, TokenType normal, TokenType const_to c = peek(lexer); switch (c) { - case 'a': case 'b': case 'c': case 'd': case 'e': - case 'f': case 'g': case 'h': case 'i': case 'j': - case 'k': case 'l': case 'm': case 'n': case 'o': - case 'p': case 'q': case 'r': case 's': case 't': - case 'u': case 'v': case 'w': case 'x': case 'y': - case 'z': + case LOWER_CASE: if (!type) { type = normal; @@ -358,16 +343,10 @@ static inline bool scan_ident(Lexer *lexer, TokenType normal, TokenType const_to type = type_token; } break; - case 'A': case 'B': case 'C': case 'D': case 'E': - case 'F': case 'G': case 'H': case 'I': case 'J': - case 'K': case 'L': case 'M': case 'N': case 'O': - case 'P': case 'Q': case 'R': case 'S': case 'T': - case 'U': case 'V': case 'W': case 'X': case 'Y': - case 'Z': + case UPPER_CASE: if (!type) type = const_token; break; - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': + case NUM_CASE: if (!type) return add_error_token(lexer, "A letter must precede any digit"); case '_': break; @@ -387,11 +366,12 @@ static inline bool scan_ident(Lexer *lexer, TokenType normal, TokenType const_to uint32_t len = (uint32_t)(lexer->current - lexer->lexing_start); if (!type) { - if (!prefix && len == 1) return add_token(lexer, TOKEN_UNDERSCORE, "_"); + if (!prefix && len == 1) return return_token(lexer, TOKEN_UNDERSCORE, "_"); add_error_token(lexer, "An identifier may not consist of only '_' characters."); } const char* interned_string = symtab_add(lexer->lexing_start, len, hash, &type); - return add_token(lexer, type, interned_string); + if (type == TOKEN_RETURN && lexer->mode == LEX_DOCS) type = TOKEN_IDENT; + return return_token(lexer, type, interned_string); } // --- Number scanning @@ -457,7 +437,7 @@ static bool scan_oct(Lexer *lexer) { return add_error_token(lexer, "Octal literals cannot have a floating point suffix."); } - return add_token(lexer, TOKEN_INTEGER, lexer->lexing_start); + return return_token(lexer, TOKEN_INTEGER, lexer->lexing_start); } /** @@ -481,7 +461,7 @@ static bool scan_binary(Lexer *lexer) { return add_error_token(lexer, "Binary literals cannot have a floating point suffix."); } - return add_token(lexer, TOKEN_INTEGER, lexer->lexing_start); + return return_token(lexer, TOKEN_INTEGER, lexer->lexing_start); } /** @@ -552,7 +532,7 @@ static inline bool scan_hex(Lexer *lexer) return add_error_token_at_current(lexer, "The number ended with '_', which isn't allowed, please remove it."); } if (!scan_number_suffix(lexer, &is_float)) return false; - return add_token(lexer, is_float ? TOKEN_REAL : TOKEN_INTEGER, lexer->lexing_start); + return return_token(lexer, is_float ? TOKEN_REAL : TOKEN_INTEGER, lexer->lexing_start); } /** @@ -599,7 +579,7 @@ static inline bool scan_dec(Lexer *lexer) return add_error_token_at_current(lexer, "The number ended with '_', which isn't allowed, please remove it."); } if (!scan_number_suffix(lexer, &is_float)) return false; - return add_token(lexer, is_float ? TOKEN_REAL : TOKEN_INTEGER, lexer->lexing_start); + return return_token(lexer, is_float ? TOKEN_REAL : TOKEN_INTEGER, lexer->lexing_start); } /** @@ -840,9 +820,9 @@ static inline bool scan_char(Lexer *lexer) return add_error_token(lexer, "Character literal exceeded 8 characters."); } DONE: - add_generic_token(lexer, TOKEN_CHAR_LITERAL); - lexer->latest_token_data->char_value = b; - lexer->latest_token_data->width = (char)width; + set_generic_token(lexer, TOKEN_CHAR_LITERAL); + lexer->data.char_value = b; + lexer->data.width = (char)width; return true; UNICODE_IN_MULTI: @@ -1137,8 +1117,8 @@ static inline bool scan_multiline_string(Lexer *lexer) } if (!scan_consume_end_of_multiline(lexer, true)) return false; destination[len] = 0; - add_token(lexer, TOKEN_STRING, destination); - lexer->latest_token_data->strlen = len; + return_token(lexer, TOKEN_STRING, destination); + lexer->data.strlen = len; return true; } @@ -1215,8 +1195,8 @@ static inline bool scan_string(Lexer *lexer) // Skip the `"` next(lexer); destination[len] = 0; - add_token(lexer, TOKEN_STRING, destination); - lexer->latest_token_data->strlen = len; + return_token(lexer, TOKEN_STRING, destination); + lexer->data.strlen = len; return true; } @@ -1251,8 +1231,8 @@ static inline bool scan_raw_string(Lexer *lexer) destination[len++] = c; } destination[len] = 0; - add_token(lexer, TOKEN_STRING, destination); - lexer->latest_token_data->strlen = len; + return_token(lexer, TOKEN_STRING, destination); + lexer->data.strlen = len; return true; } @@ -1295,9 +1275,9 @@ static inline bool scan_hex_array(Lexer *lexer) { return add_error_token(lexer, "The hexadecimal string is not an even length, did you miss a digit somewhere?"); } - if (!add_token(lexer, TOKEN_BYTES, lexer->lexing_start)) return false; - lexer->latest_token_data->is_base64 = false; - lexer->latest_token_data->len = (uint64_t)len / 2; + if (!return_token(lexer, TOKEN_BYTES, lexer->lexing_start)) return false; + lexer->data.is_base64 = false; + lexer->data.bytes_len = (uint64_t)len / 2; return true; } @@ -1374,9 +1354,9 @@ static inline bool scan_base64(Lexer *lexer) "- only need 1 or 2 bytes of extra padding."); } uint64_t decoded_len = (3 * len - end_len) / 4; - if (!add_token(lexer, TOKEN_BYTES, lexer->lexing_start)) return false; - lexer->latest_token_data->is_base64 = true; - lexer->latest_token_data->len = decoded_len; + if (!return_token(lexer, TOKEN_BYTES, lexer->lexing_start)) return false; + lexer->data.is_base64 = true; + lexer->data.bytes_len = decoded_len; return true; } @@ -1384,14 +1364,6 @@ static inline bool scan_base64(Lexer *lexer) // --- Lexer doc lexing -/** - * Skip any stars until we either have no more * or we find '* /' - * @param lexer - */ -static void skip_doc_stars(Lexer *lexer) -{ - while (peek(lexer) == '*' && peek_next(lexer) != '/') next(lexer); -} static bool end_of_docs_found(Lexer *lexer) { @@ -1417,285 +1389,192 @@ static bool parse_add_end_of_docs_if_present(Lexer *lexer) if (lexer->current[lookahead] != '/') return false; // Otherwise, gladly skip ahead and store the end. skip(lexer, lookahead + 1); - add_token(lexer, TOKEN_DOCS_END, lexer->lexing_start); + return_token(lexer, TOKEN_DOCS_END, lexer->lexing_start); begin_new_token(lexer); return true; } -static void parse_add_end_of_doc_line(Lexer *lexer) +INLINE void skip_to_doc_line_end(Lexer *lexer) { - assert(peek(lexer) == '\n'); - // Add the EOL token. + // Let's skip to either EOF, EOL or */ + char c = peek(lexer); + while (1) + { + if (reached_end(lexer)) return; + if (c == '\n') return; + if (c == '*' && peek_next(lexer) == '/') return; + c = next(lexer); + } +} + + +static bool parse_doc_directive(Lexer *lexer) +{ + begin_new_token(lexer); next(lexer); - add_token(lexer, TOKEN_DOCS_EOL, lexer->lexing_start); - begin_new_token(lexer); - // Skip whitespace - skip_whitespace(lexer, LEX_DOCS); - // And any leading stars: - skip_doc_stars(lexer); -} - -/** - * Parse the end of a directive or a simple line, e.g. - * For "* @param lexer The lexer used." then the remainder is "The lexer used." - * For "*** Hello world" the remainder is "Hello world" - */ -static DocEnd parse_doc_remainder(Lexer *lexer) -{ - // Skip all initial whitespace. - skip_whitespace(lexer, LEX_DOCS); - begin_new_token(lexer); - - int characters_read = 0; - while (1) - { - switch (peek(lexer)) - { - case '*': - // Did we find the end of the directives? - // If so return control. - if (!end_of_docs_found(lexer)) break; - - if (characters_read > 0) - { - add_token(lexer, TOKEN_DOCS_LINE, 0); - begin_new_token(lexer); - } - if (parse_add_end_of_docs_if_present(lexer)) return DOC_END_LAST; - // Otherwise use default parsing. - break; - case '\n': - // End of line - if (characters_read > 0) - { - add_token(lexer, TOKEN_DOCS_LINE, 0); - begin_new_token(lexer); - } - return DOC_END_EOL; - case '\0': - if (characters_read > 0) - { - add_token(lexer, TOKEN_DOCS_LINE, 0); - begin_new_token(lexer); - } - return DOC_END_EOF; - default: - break; - } - // Otherwise move forward - characters_read++; - next(lexer); - } -} - -static DocEnd parse_doc_error_directive(Lexer *lexer) -{ - while (1) - { - // Skip any whitespace. - skip_whitespace(lexer, LEX_DOCS); - - // First scan the name - if (!lexer_scan_token_inner(lexer, LEX_DOCS)) return DOC_END_ERROR; - - if (*lexer->latest_token_type != TOKEN_TYPE_IDENT) break; - - // Skip any whitespace. - skip_whitespace(lexer, LEX_DOCS); - - // If we don't reach "|" we exit, since errors are composed using ErrorA | ErrorB - if (peek(lexer) != '|') break; - - if (!lexer_scan_token_inner(lexer, LEX_DOCS)) return DOC_END_ERROR; - - // We might get "|=" or something, in that case exit. - if (*lexer->latest_token_type != TOKEN_BIT_OR) break; - } - return parse_doc_remainder(lexer); -} - -/** - * Contract directives use the style: "@require a > 2, b && c == true : "Must work foo" - * - * @param lexer - * @return - */ -static DocEnd parse_doc_contract_directive(Lexer *lexer) -{ - while (1) - { - // Skip all initial whitespace. - skip_whitespace(lexer, LEX_DOCS); - - switch (peek(lexer)) - { - case '*': - // Did we find the end of the directives? - // If so return control. - if (parse_add_end_of_docs_if_present(lexer)) return DOC_END_LAST; - // Otherwise use default parsing. - break; - case '\n': - return DOC_END_EOL; - case '\0': - return DOC_END_EOF; - default: - break; - } - // Otherwise move forward - if (!lexer_scan_token_inner(lexer, LEX_DOCS)) return DOC_END_ERROR; - - // "return" is an identifier inside. - if (*lexer->latest_token_type == TOKEN_RETURN) - { - *lexer->latest_token_type = TOKEN_IDENT; - } - } -} - -static DocEnd parse_doc_param_directive(Lexer *lexer) -{ - // Skip any whitespace. - skip_whitespace(lexer, LEX_DOCS); - - // First scan the name - if (!lexer_scan_token_inner(lexer, LEX_DOCS)) return DOC_END_ERROR; - - // Then the remainder - return parse_doc_remainder(lexer); -} - - -static DocEnd parse_doc_directive(Lexer *lexer) -{ - // We expect a directive here. - begin_new_token(lexer); - // First parse the '@' - next(lexer); - add_token(lexer, TOKEN_DOCS_DIRECTIVE, "@"); - begin_new_token(lexer); - - if (!is_letter(peek(lexer))) - { - next(lexer); - return add_error_token(lexer, "Expected doc directive here."); - } - // Then our keyword - if (!scan_ident(lexer, TOKEN_IDENT, TOKEN_CONST, TOKEN_TYPE_IDENT, 0)) return DOC_END_ERROR; + if (!scan_ident(lexer, TOKEN_IDENT, TOKEN_CONST, TOKEN_TYPE_IDENT, '@')) + { + return false; + } - assert(*lexer->latest_token_type == TOKEN_IDENT || *lexer->latest_token_type == TOKEN_RETURN); - - const char *last_token_string = lexer->latest_token_data->string; - - if (*lexer->latest_token_type == TOKEN_RETURN) + switch (lexer->token_type) { - // Backpatch the type. - *lexer->latest_token_type = TOKEN_IDENT; - return parse_doc_remainder(lexer); + case TOKEN_DOCS_ENSURE: + case TOKEN_DOCS_CHECKED: + case TOKEN_DOCS_REQUIRE: + case TOKEN_DOCS_OPTRETURN: + case TOKEN_DOCS_PARAM: + case TOKEN_DOCS_RETURN: + return true; + case TOKEN_IDENT: + lexer->token_type = TOKEN_DOC_DIRECTIVE; + return true; + default: + add_error_token_at_current(lexer, "A doc directive was expected."); + return false; } - if (kw_errors == last_token_string) - { - return parse_doc_error_directive(lexer); - } - if (last_token_string == kw_require || last_token_string == kw_ensure || last_token_string == kw_reqparse) - { - return parse_doc_contract_directive(lexer); - } - if (last_token_string == kw_param) - { - // The variable - return parse_doc_param_directive(lexer); - } - return parse_doc_remainder(lexer); } +static bool scan_doc_line(Lexer *lexer) +{ + assert(lexer->mode == LEX_DOCS); +RETRY:; + char c = peek(lexer); + // Go through any initial line whitespace + while (c == ' ' || c == '\t') + { + if (reached_end(lexer)) goto EOF_REACHED; + c = next(lexer); + } + // Skip over all stars + while (c == '*') + { + if (reached_end(lexer)) goto EOF_REACHED; + c = next(lexer); + } + // We might have gotten to the end. + if (c == '/' && prev(lexer) == '*') + { + lexer->mode = LEX_NORMAL; + next(lexer); + return return_token(lexer, TOKEN_DOCS_END, "*/"); + } + + // We need to skip any space afterwards + while (c == ' ' || c == '\t') + { + if (reached_end(lexer)) goto EOF_REACHED; + c = next(lexer); + } + + // If we have '@' [_A-Za-z] then parse the directive + if (c == '@') + { + return parse_doc_directive(lexer); + } + + // Otherwise scan to the end of the line + while (1) + { + if (reached_end(lexer)) goto EOF_REACHED; + // We might find the end of the docs + if (c == '*' && peek_next(lexer) == '/') + { + next(lexer); + lexer->mode = LEX_NORMAL; + return return_token(lexer, TOKEN_DOCS_END, "*/"); + } + // If we find the end of the line we start from the beginning. + if (c == '\n') goto RETRY; + c = next(lexer); + } +EOF_REACHED: + return add_error_token_at_start(lexer, "Missing '*/' to end the doc comment."); +} + + + /** + * * Parse the / ** * / directives comments **/ -static bool parse_doc_comment(Lexer *lexer) +static bool parse_doc_start(Lexer *lexer) { // Add the doc start token. - add_token(lexer, TOKEN_DOCS_START, lexer->lexing_start); - - // Skip any additional stars - skip_doc_stars(lexer); - - // Main "doc parse" loop. - while (1) - { - // 1. Skip any whitespace - skip_whitespace(lexer, LEX_DOCS); - - // 2. Did we find the end? - if (reached_end(lexer)) - { - return add_error_token_at_start(lexer, "Missing '*/' to end the doc comment."); - } - - // 3. See if we reach the end of the docs. - if (parse_add_end_of_docs_if_present(lexer)) return true; - - DocEnd end; - // Parse a segment - switch (peek(lexer)) - { - case '@': - end = parse_doc_directive(lexer); - break; - case '\n': - end = DOC_END_EOL; - break; - default: - end = parse_doc_remainder(lexer); - break; - } - - // We're done parsing a line: - switch (end) - { - case DOC_END_ERROR: - return false; - case DOC_END_EOF: - // Just continue, this will be picked up in the beginning of the loop. - break; - case DOC_END_LAST: - // We're done, so return. - return true; - case DOC_END_EOL: - // Walk past the end of line. - parse_add_end_of_doc_line(lexer); - break; - default: - UNREACHABLE - } - } + return_token(lexer, TOKEN_DOCS_START, lexer->lexing_start); + skip_to_doc_line_end(lexer); + lexer->mode = LEX_DOCS; + return true; } // --- Lexer public functions +// This works because everything would be an expression -static bool lexer_scan_token_inner(Lexer *lexer, LexMode mode) +/** + * @require foo > 102, "Foo needs to be at least 103" + * @require abc != NULL, "abc must be a valid pointer" + * @param [inout] foo, "The foo parameter" + */ + + +/** + * @require foo > 102 *Foo needs to be at least 103* + * @require abc != NULL --> abc must be a valid pointer + */ + +// This works because one could require everything's inside +// of the () + +/** + * @require (foo > 102) Foo needs to be at least 103 + * @require (abc != NULL) abc must be a valid pointer + * @param [inout] foo The foo parameter + */ + +/** + * @require foo > 102 // Foo needs to be at least 103 + * @require abc != NULL // abc must be a valid pointer + * @param [inout] foo // The foo parameter + */ + +/** + * @require foo > 102 `Foo needs to be at least 103` + * @require abc != NULL `abc must be a valid pointer` + * @param [inout] foo `The foo parameter` + */ + +// This works because // could behave differently in docs. +/** + * @require foo > 102 `Foo needs to be at least 103` + * @require abc != NULL // abc must be a valid pointer + */ + + + +static bool lexer_scan_token_inner(Lexer *lexer) { // Now skip the whitespace. - skip_whitespace(lexer, mode); + skip_whitespace(lexer); // Point start to the first non-whitespace character. begin_new_token(lexer); if (reached_end(lexer)) { - assert(mode == LEX_NORMAL); - return add_token(lexer, TOKEN_EOF, "\n") && false; + return return_token(lexer, TOKEN_EOF, "\n") && false; } char c = peek(lexer); next(lexer); switch (c) { + case '\n': + return scan_doc_line(lexer); case '@': - return add_token(lexer, TOKEN_AT, "@"); + return return_token(lexer, TOKEN_AT, "@"); case '\'': return scan_char(lexer); case '`': @@ -1703,104 +1582,107 @@ static bool lexer_scan_token_inner(Lexer *lexer, LexMode mode) case '"': return scan_string(lexer); case '#': - return scan_ident(lexer, TOKEN_HASH_IDENT, TOKEN_HASH_CONST_IDENT, TOKEN_HASH_TYPE_IDENT, '$'); + return scan_ident(lexer, TOKEN_HASH_IDENT, TOKEN_HASH_CONST_IDENT, TOKEN_HASH_TYPE_IDENT, '#'); case '$': if (match(lexer, '$')) { if (is_letter(peek(lexer))) { - add_token(lexer, TOKEN_BUILTIN, "$$"); - begin_new_token(lexer); - return scan_ident(lexer, TOKEN_IDENT, TOKEN_CONST_IDENT, TOKEN_TYPE_IDENT, 0); + return return_token(lexer, TOKEN_BUILTIN, "$$"); } return add_error_token_at_current(lexer, "Expected a letter after $$."); } return scan_ident(lexer, TOKEN_CT_IDENT, TOKEN_CT_CONST_IDENT, TOKEN_CT_TYPE_IDENT, '$'); case ',': - return add_token(lexer, TOKEN_COMMA, ","); + return return_token(lexer, TOKEN_COMMA, ","); case ';': - return add_token(lexer, TOKEN_EOS, ";"); + return return_token(lexer, TOKEN_EOS, ";"); case '{': - return match(lexer, '|') ? add_token(lexer, TOKEN_LBRAPIPE, "{|") : add_token(lexer, TOKEN_LBRACE, "{"); + return match(lexer, '|') ? return_token(lexer, TOKEN_LBRAPIPE, "{|") : return_token(lexer, TOKEN_LBRACE, "{"); case '}': - return add_token(lexer, TOKEN_RBRACE, "}"); + return return_token(lexer, TOKEN_RBRACE, "}"); case '(': - return add_token(lexer, TOKEN_LPAREN, "("); + return return_token(lexer, TOKEN_LPAREN, "("); case ')': - return add_token(lexer, TOKEN_RPAREN, ")"); + return return_token(lexer, TOKEN_RPAREN, ")"); case '[': - if (match(lexer, '<')) return add_token(lexer, TOKEN_LVEC, "[<"); - return add_token(lexer, TOKEN_LBRACKET, "["); + if (match(lexer, '<')) return return_token(lexer, TOKEN_LVEC, "[<"); + return return_token(lexer, TOKEN_LBRACKET, "["); case ']': - return add_token(lexer, TOKEN_RBRACKET, "]"); + return return_token(lexer, TOKEN_RBRACKET, "]"); case '.': if (match(lexer, '.')) { - if (match(lexer, '.')) return add_token(lexer, TOKEN_ELLIPSIS, "..."); - return add_token(lexer, TOKEN_DOTDOT, ".."); + if (match(lexer, '.')) return return_token(lexer, TOKEN_ELLIPSIS, "..."); + return return_token(lexer, TOKEN_DOTDOT, ".."); } - return add_token(lexer, TOKEN_DOT, "."); + return return_token(lexer, TOKEN_DOT, "."); case '~': - return add_token(lexer, TOKEN_BIT_NOT, "~"); + return return_token(lexer, TOKEN_BIT_NOT, "~"); case ':': - return match(lexer, ':') ? add_token(lexer, TOKEN_SCOPE, "::") : add_token(lexer, TOKEN_COLON, ":"); + return match(lexer, ':') ? return_token(lexer, TOKEN_SCOPE, "::") : return_token(lexer, TOKEN_COLON, ":"); case '!': - if (match(lexer, '!')) return add_token(lexer, TOKEN_BANGBANG, "!!"); - return match(lexer, '=') ? add_token(lexer, TOKEN_NOT_EQUAL, "!=") : add_token(lexer, TOKEN_BANG, "!"); + if (match(lexer, '!')) return return_token(lexer, TOKEN_BANGBANG, "!!"); + return match(lexer, '=') ? return_token(lexer, TOKEN_NOT_EQUAL, "!=") : return_token(lexer, TOKEN_BANG, "!"); case '/': // We can't get any directives comments here. - if (mode != LEX_DOCS) + if (lexer->mode != LEX_DOCS && match(lexer, '*')) { - if (match(lexer, '/')) return parse_line_comment(lexer); - if (match(lexer, '*')) return match(lexer, '*') ? parse_doc_comment(lexer) : parse_multiline_comment(lexer); + assert(peek(lexer) == '*'); + next(lexer); + return parse_doc_start(lexer); } - return match(lexer, '=') ? add_token(lexer, TOKEN_DIV_ASSIGN, "/=") : add_token(lexer, TOKEN_DIV, "/"); + return match(lexer, '=') ? return_token(lexer, TOKEN_DIV_ASSIGN, "/=") : return_token(lexer, TOKEN_DIV, "/"); case '*': - return match(lexer, '=') ? add_token(lexer, TOKEN_MULT_ASSIGN, "*=") : add_token(lexer, TOKEN_STAR, "*"); + return match(lexer, '=') ? return_token(lexer, TOKEN_MULT_ASSIGN, "*=") : return_token(lexer, TOKEN_STAR, "*"); case '=': - return match(lexer, '=') ? add_token(lexer, TOKEN_EQEQ, "==") : add_token(lexer, TOKEN_EQ, "="); + return match(lexer, '=') ? return_token(lexer, TOKEN_EQEQ, "==") : return_token(lexer, TOKEN_EQ, "="); case '^': - return match(lexer, '=') ? add_token(lexer, TOKEN_BIT_XOR_ASSIGN, "^=") : add_token(lexer, - TOKEN_BIT_XOR, - "^"); + return match(lexer, '=') ? return_token(lexer, TOKEN_BIT_XOR_ASSIGN, "^=") : return_token(lexer, + TOKEN_BIT_XOR, + "^"); case '?': - if (match(lexer, '?')) return add_token(lexer, TOKEN_QUESTQUEST, "??"); - return match(lexer, ':') ? add_token(lexer, TOKEN_ELVIS, "?:") : add_token(lexer, TOKEN_QUESTION, "?"); + if (match(lexer, '?')) return return_token(lexer, TOKEN_QUESTQUEST, "??"); + return match(lexer, ':') ? return_token(lexer, TOKEN_ELVIS, "?:") : return_token(lexer, TOKEN_QUESTION, "?"); case '<': if (match(lexer, '<')) { - if (match(lexer, '=')) return add_token(lexer, TOKEN_SHL_ASSIGN, "<<="); - return add_token(lexer, TOKEN_SHL, "<<"); + if (match(lexer, '=')) return return_token(lexer, TOKEN_SHL_ASSIGN, "<<="); + return return_token(lexer, TOKEN_SHL, "<<"); } - return match(lexer, '=') ? add_token(lexer, TOKEN_LESS_EQ, "<=") : add_token(lexer, TOKEN_LESS, "<"); + return match(lexer, '=') ? return_token(lexer, TOKEN_LESS_EQ, "<=") : return_token(lexer, TOKEN_LESS, "<"); case '>': if (match(lexer, '>')) { - if (match(lexer, '=')) return add_token(lexer, TOKEN_SHR_ASSIGN, ">>="); - return add_token(lexer, TOKEN_SHR, ">>"); + if (match(lexer, '=')) return return_token(lexer, TOKEN_SHR_ASSIGN, ">>="); + return return_token(lexer, TOKEN_SHR, ">>"); } - if (match(lexer, ']')) return add_token(lexer, TOKEN_RVEC, ">]"); - return match(lexer, '=') ? add_token(lexer, TOKEN_GREATER_EQ, ">=") : add_token(lexer, TOKEN_GREATER, ">"); + if (match(lexer, ']')) return return_token(lexer, TOKEN_RVEC, ">]"); + return match(lexer, '=') ? return_token(lexer, TOKEN_GREATER_EQ, ">=") : return_token(lexer, + TOKEN_GREATER, + ">"); case '%': - return match(lexer, '=') ? add_token(lexer, TOKEN_MOD_ASSIGN, "%=") : add_token(lexer, TOKEN_MOD, "%"); + return match(lexer, '=') ? return_token(lexer, TOKEN_MOD_ASSIGN, "%=") : return_token(lexer, TOKEN_MOD, "%"); case '&': - if (match(lexer, '&')) return add_token(lexer, TOKEN_AND, "&&"); - return match(lexer, '=') ? add_token(lexer, TOKEN_BIT_AND_ASSIGN, "&=") : add_token(lexer, TOKEN_AMP, "&"); + if (match(lexer, '&')) return return_token(lexer, TOKEN_AND, "&&"); + return match(lexer, '=') ? return_token(lexer, TOKEN_BIT_AND_ASSIGN, "&=") : return_token(lexer, + TOKEN_AMP, + "&"); case '|': - if (match(lexer, '}')) return add_token(lexer, TOKEN_RBRAPIPE, "|}"); - if (match(lexer, '|')) return add_token(lexer, TOKEN_OR, "||"); - return match(lexer, '=') ? add_token(lexer, TOKEN_BIT_OR_ASSIGN, "|=") : add_token(lexer, - TOKEN_BIT_OR, - "|"); + if (match(lexer, '}')) return return_token(lexer, TOKEN_RBRAPIPE, "|}"); + if (match(lexer, '|')) return return_token(lexer, TOKEN_OR, "||"); + return match(lexer, '=') ? return_token(lexer, TOKEN_BIT_OR_ASSIGN, "|=") : return_token(lexer, + TOKEN_BIT_OR, + "|"); case '+': - if (match(lexer, '+')) return add_token(lexer, TOKEN_PLUSPLUS, "++"); - if (match(lexer, '=')) return add_token(lexer, TOKEN_PLUS_ASSIGN, "+="); - return add_token(lexer, TOKEN_PLUS, "+"); + if (match(lexer, '+')) return return_token(lexer, TOKEN_PLUSPLUS, "++"); + if (match(lexer, '=')) return return_token(lexer, TOKEN_PLUS_ASSIGN, "+="); + return return_token(lexer, TOKEN_PLUS, "+"); case '-': - if (match(lexer, '>')) return add_token(lexer, TOKEN_ARROW, "->"); - if (match(lexer, '-')) return add_token(lexer, TOKEN_MINUSMINUS, "--"); - if (match(lexer, '=')) return add_token(lexer, TOKEN_MINUS_ASSIGN, "-="); - return add_token(lexer, TOKEN_MINUS, "-"); + if (match(lexer, '>')) return return_token(lexer, TOKEN_ARROW, "->"); + if (match(lexer, '-')) return return_token(lexer, TOKEN_MINUSMINUS, "--"); + if (match(lexer, '=')) return return_token(lexer, TOKEN_MINUS_ASSIGN, "-="); + return return_token(lexer, TOKEN_MINUS, "-"); case 'x': if ((peek(lexer) == '"' || peek(lexer) == '\'')) { @@ -1834,15 +1716,15 @@ static bool lexer_scan_token_inner(Lexer *lexer, LexMode mode) } } -#define tokenid(_ptr) ((unsigned)((TokenOld *)(_ptr) - ((TokenOld *)lexer->memory.ptr))) -void lexer_lex_file(Lexer *lexer) +void lexer_init(Lexer *lexer) { - lexer->token_start_id = (uint32_t) toktype_arena.allocated; lexer->file_begin = lexer->file->contents; lexer->current = lexer->file_begin; lexer->line_start = lexer->current; lexer->current_row = 1; + lexer->tok_span.file_id = lexer->file->file_id; + lexer->mode = LEX_NORMAL; begin_new_token(lexer); const unsigned char *check = (const unsigned char *)lexer->current; unsigned c; @@ -1866,37 +1748,43 @@ void lexer_lex_file(Lexer *lexer) balance++; } break; - case 0x81: - if (type >= 0xA6 && type <= 0xA8) - { - balance++; - } - else if (type == 0xA9) - { - balance--; - if (balance < 0) goto DONE; - } - break; - default: - break; + case 0x81: + if (type >= 0xA6 && type <= 0xA8) + { + balance++; + } + else if (type == 0xA9) + { + balance--; + if (balance < 0) goto DONE; + } + break; + default: + break; } } -DONE: + DONE: if (balance != 0) { add_error_token_at_start(lexer, "Invalid encoding - Unbalanced bidirectional markers."); return; } - while(1) - { - if (!lexer_scan_token_inner(lexer, LEX_NORMAL)) - { - if (reached_end(lexer)) break; - while (!reached_end(lexer) && peek(lexer) != '\n') next(lexer); - begin_new_token(lexer); - continue; - } - } - +} + +bool lexer_next_token(Lexer *lexer) +{ + if (lexer_scan_token_inner(lexer)) return true; + if (reached_end(lexer)) return true; + bool token_is_ok = false; + do + { + if (!token_is_ok) + { + while (!reached_end(lexer) && peek(lexer) != '\n') next(lexer); + } + token_is_ok = lexer_scan_token_inner(lexer); + } + while (!reached_end(lexer)); + return false; } diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 7652a2074..84606b462 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -765,13 +765,13 @@ static void llvm_emit_type_decls(GenContext *context, Decl *decl) // TODO break; case DECL_ENUM_CONSTANT: - case DECL_ERRVALUE: + case DECL_OPTVALUE: // TODO break;; case DECL_STRUCT: case DECL_UNION: case DECL_ENUM: - case DECL_ERRTYPE: + case DECL_OPTENUM: case DECL_BITSTRUCT: llvm_emit_introspection_type_from_decl(context, decl); break; diff --git a/src/compiler/llvm_codegen_debug_info.c b/src/compiler/llvm_codegen_debug_info.c index a7738e17f..f302d7bc7 100644 --- a/src/compiler/llvm_codegen_debug_info.c +++ b/src/compiler/llvm_codegen_debug_info.c @@ -4,24 +4,30 @@ #include "llvm_codegen_internal.h" -#define LINE_ZERO 0 - static unsigned id_counter = 0; static inline LLVMMetadataRef llvm_get_debug_type_internal(GenContext *c, Type *type, LLVMMetadataRef scope); -static inline LLVMMetadataRef llvm_get_debug_member(GenContext *c, Type *type, const char *name, unsigned offset, SourceLocation *loc, LLVMMetadataRef scope, LLVMDIFlags flags); -static inline LLVMMetadataRef llvm_get_debug_struct(GenContext *c, Type *type, const char *external_name, LLVMMetadataRef *elements, unsigned element_count, SourceLocation *loc, LLVMMetadataRef scope, LLVMDIFlags flags); -static LLVMMetadataRef llvm_debug_forward_comp(GenContext *c, Type *type, const char *external_name, SourceLocation *loc, LLVMMetadataRef scope, LLVMDIFlags flags); +static inline LLVMMetadataRef llvm_get_debug_member(GenContext *c, Type *type, const char *name, unsigned offset, SourceSpan *loc, LLVMMetadataRef scope, LLVMDIFlags flags); +static inline LLVMMetadataRef llvm_get_debug_struct(GenContext *c, Type *type, const char *external_name, LLVMMetadataRef *elements, unsigned element_count, SourceSpan *loc, LLVMMetadataRef scope, LLVMDIFlags flags); +static LLVMMetadataRef llvm_debug_forward_comp(GenContext *c, Type *type, const char *external_name, SourceSpan *loc, LLVMMetadataRef scope, LLVMDIFlags flags); -static inline LLVMMetadataRef llvm_get_debug_struct(GenContext *c, Type *type, const char *external_name, LLVMMetadataRef *elements, unsigned element_count, SourceLocation *loc, LLVMMetadataRef scope, LLVMDIFlags flags) +static inline LLVMMetadataRef llvm_get_debug_struct(GenContext *c, Type *type, const char *external_name, LLVMMetadataRef *elements, unsigned element_count, SourceSpan *loc, LLVMMetadataRef scope, LLVMDIFlags flags) { + LLVMMetadataRef file = NULL; + unsigned row = 0; size_t external_name_len = strlen(external_name); + if (loc) + { + file = c->debug.file; + row = loc->row; + if (!row) row = 1; + } LLVMMetadataRef real = LLVMDIBuilderCreateStructType(c->debug.builder, scope, external_name_len ? type->name : "", external_name_len ? strlen(type->name) : 0, - loc ? c->debug.file : NULL, - loc ? loc->row : 0, + file, + row, type_size(type) * 8, (uint32_t)(type_abi_alignment(type) * 8), flags, NULL, @@ -36,7 +42,7 @@ static inline LLVMMetadataRef llvm_get_debug_struct(GenContext *c, Type *type, c return real; } -static inline LLVMMetadataRef llvm_get_debug_member(GenContext *c, Type *type, const char *name, unsigned offset, SourceLocation *loc, LLVMMetadataRef scope, LLVMDIFlags flags) +static inline LLVMMetadataRef llvm_get_debug_member(GenContext *c, Type *type, const char *name, unsigned offset, SourceSpan *loc, LLVMMetadataRef scope, LLVMDIFlags flags) { return LLVMDIBuilderCreateMemberType( c->debug.builder, @@ -71,16 +77,16 @@ LLVMMetadataRef llvm_debug_current_scope(GenContext *context) void llvm_emit_debug_global_var(GenContext *c, Decl *global) { - SourceLocation *loc = TOKLOC(global->span.loc); + SourceSpan loc = global->span; global->var.backend_debug_ref = LLVMDIBuilderCreateGlobalVariableExpression( c->debug.builder, c->debug.file, global->name, - TOKLEN(global->name_token), + strlen(global->name), global->external_name, strlen(global->external_name), c->debug.file, - loc->row, + loc.row ? loc.row : 1, llvm_get_debug_type(c, global->type), global->visibility == VISIBLE_LOCAL, LLVMDIBuilderCreateExpression(c->debug.builder, NULL, 0), @@ -110,17 +116,18 @@ void llvm_emit_debug_function(GenContext *c, Decl *decl) flags |= LLVMDIFlagPrototyped; if (decl->func_decl.attr_noreturn) flags |= LLVMDIFlagNoReturn; - SourceLocation *loc = TOKLOC(decl->span.loc); + uint32_t row = decl->span.row; + if (!row) row = 1; c->debug.function = LLVMDIBuilderCreateFunction(c->debug.builder, c->debug.file, - decl->name, TOKLEN(decl->name_token), + decl->name, strlen(decl->name), decl->external_name, strlen(decl->external_name), c->debug.file, - loc->row, + row, llvm_get_debug_type(c, decl->type), decl->visibility == VISIBLE_LOCAL, true, - loc->row, + row, flags, active_target.optimization_level != OPTIMIZATION_NONE); LLVMSetSubprogram(decl->backend_ref, c->debug.function); @@ -129,14 +136,19 @@ void llvm_emit_debug_function(GenContext *c, Decl *decl) void llvm_emit_debug_local_var(GenContext *c, Decl *decl) { EMIT_LOC(c, decl); - SourceLocation *location = TOKLOC(decl->span.loc); + uint32_t row = decl->span.row; + uint32_t col = decl->span.col; + if (!row) row = 1; + if (!col) col = 1; + const char *name = decl->name; + if (!name) name = "anon"; LLVMMetadataRef var = LLVMDIBuilderCreateAutoVariable( c->debug.builder, c->debug.function, - decl->name, - TOKLEN(decl->name_token), + name, + strlen(name), c->debug.file, - location->row, + row, llvm_get_debug_type(c, decl->type), active_target.optimization_level != OPTIMIZATION_NONE, LLVMDIFlagZero, @@ -147,7 +159,7 @@ void llvm_emit_debug_local_var(GenContext *c, Decl *decl) LLVMDIBuilderInsertDeclareAtEnd(c->debug.builder, decl->backend_ref, var, LLVMDIBuilderCreateExpression(c->debug.builder, NULL, 0), - LLVMDIBuilderCreateDebugLocation(c->context, location->row, location->col, + LLVMDIBuilderCreateDebugLocation(c->context, row, col, c->debug.function, inline_at), LLVMGetInsertBlock(c->builder)); } @@ -160,10 +172,14 @@ void llvm_emit_debug_local_var(GenContext *c, Decl *decl) */ void llvm_emit_debug_parameter(GenContext *c, Decl *parameter, unsigned index) { - SourceLocation *loc = TOKLOC(parameter->span.loc); const char *name = parameter->name ? parameter->name : "anon"; bool always_preserve = false; + unsigned row = parameter->span.row; + if (row == 0) row = 1; + unsigned col = parameter->span.col; + if (col == 0) col = 1; + parameter->var.backend_debug_ref = LLVMDIBuilderCreateParameterVariable( c->debug.builder, c->debug.function, @@ -171,7 +187,7 @@ void llvm_emit_debug_parameter(GenContext *c, Decl *parameter, unsigned index) strlen(name), index + 1, c->debug.file, - loc->row, + row, llvm_get_debug_type(c, parameter->type), always_preserve, LLVMDIFlagZero); @@ -181,7 +197,7 @@ void llvm_emit_debug_parameter(GenContext *c, Decl *parameter, unsigned index) parameter->backend_ref, parameter->var.backend_debug_ref, LLVMDIBuilderCreateExpression(c->debug.builder, NULL, 0), - LLVMDIBuilderCreateDebugLocation(c->context, loc->row, loc->col, c->debug.function, + LLVMDIBuilderCreateDebugLocation(c->context, row, col, c->debug.function, inline_at), LLVMGetInsertBlock(c->builder)); @@ -190,29 +206,35 @@ void llvm_emit_debug_parameter(GenContext *c, Decl *parameter, unsigned index) void llvm_emit_debug_location(GenContext *context, SourceSpan location) { - static TokenId last = { 0 }; + static SourceSpan oldloc = {}; // Avoid re-emitting the same location. - if (last.index == location.loc.index) return; + if (oldloc.a == location.a) return; if (!context->builder) return; - - SourceLocation *source_loc = TOKLOC(location.loc); + oldloc = location; LLVMMetadataRef scope = llvm_debug_current_scope(context); - + unsigned row = location.row; + unsigned col = location.col; LLVMMetadataRef loc = LLVMDIBuilderCreateDebugLocation(context->context, - source_loc->row, - source_loc->col, + row ? row : 1, + col ? col : 1, scope, /* inlined at */ 0); LLVMSetCurrentDebugLocation2(context->builder, loc); } -static LLVMMetadataRef llvm_debug_forward_comp(GenContext *c, Type *type, const char *external_name, SourceLocation *loc, LLVMMetadataRef scope, LLVMDIFlags flags) +static LLVMMetadataRef llvm_debug_forward_comp(GenContext *c, Type *type, const char *external_name, SourceSpan *loc, LLVMMetadataRef scope, LLVMDIFlags flags) { + unsigned row = 0; + if (loc) + { + row = loc->row; + if (!row) row = 1; + } return LLVMDIBuilderCreateReplaceableCompositeType(c->debug.builder, id_counter++, type->name, strlen(type->name), scope, - c->debug.file, loc ? loc->row : 0, + c->debug.file, row, 1 /* version TODO */, type_size(type) * 8, type_abi_alignment(type) * 8, @@ -223,8 +245,6 @@ static LLVMMetadataRef llvm_debug_forward_comp(GenContext *c, Type *type, const } void llvm_debug_push_lexical_scope(GenContext *context, SourceSpan location) { - SourceLocation *source_loc = TOKLOC(location.loc); - LLVMMetadataRef scope; if (vec_size(context->debug.lexical_block_stack) > 0) { @@ -235,10 +255,12 @@ void llvm_debug_push_lexical_scope(GenContext *context, SourceSpan location) scope = context->debug.compile_unit; } + unsigned row = location.row; + unsigned col = location.col; LLVMMetadataRef block = LLVMDIBuilderCreateLexicalBlock(context->debug.builder, scope, context->debug.file, - source_loc->row, - source_loc->col); + row ? row : 1, + col ? col : 1); llvm_debug_scope_push(context, block); } @@ -275,9 +297,8 @@ static LLVMMetadataRef llvm_debug_pointer_type(GenContext *c, Type *type) static LLVMMetadataRef llvm_debug_enum_type(GenContext *c, Type *type, LLVMMetadataRef scope) { Decl *decl = type->decl; - SourceLocation *location = TOKLOC(decl->span.loc); - LLVMMetadataRef forward = llvm_debug_forward_comp(c, type, decl->external_name, location, scope, LLVMDIFlagZero); + LLVMMetadataRef forward = llvm_debug_forward_comp(c, type, decl->external_name, &decl->span, scope, LLVMDIFlagZero); type->backend_debug_type = forward; Type *enum_real_type = decl->enums.type_info->type->canonical; @@ -292,16 +313,17 @@ static LLVMMetadataRef llvm_debug_enum_type(GenContext *c, Type *type, LLVMMetad uint64_t val = int_to_u64(enum_constant->enum_constant.expr->const_expr.ixx); LLVMMetadataRef debug_info = LLVMDIBuilderCreateEnumerator( c->debug.builder, - enum_constant->name, TOKLEN(enum_constant->name_token), + enum_constant->name, strlen(enum_constant->name), (int64_t)val, is_unsigned); vec_add(elements, debug_info); } + unsigned row = decl->span.row; LLVMMetadataRef real = LLVMDIBuilderCreateEnumerationType(c->debug.builder, scope, - type->decl->name, TOKLEN(type->decl->name_token), - c->debug.file, location->row, type_size(type) * 8, + type->decl->name, strlen(type->decl->name), + c->debug.file, row ? row : 1, type_size(type) * 8, type_abi_alignment(type) * 8, elements, vec_size(elements), llvm_get_debug_type(c, enum_real_type)); @@ -315,10 +337,9 @@ static LLVMMetadataRef llvm_debug_structlike_type(GenContext *c, Type *type, LLV Decl *decl = type->decl; LLVMDIFlags flags = 0; - SourceLocation *location = TOKLOC(decl->span.loc); // Create a forward reference in case of recursive data. - LLVMMetadataRef forward = llvm_debug_forward_comp(c, type, decl->external_name, location, scope, flags); + LLVMMetadataRef forward = llvm_debug_forward_comp(c, type, decl->external_name, &decl->span, scope, flags); type->backend_debug_type = forward; LLVMMetadataRef *elements = NULL; @@ -326,12 +347,11 @@ static LLVMMetadataRef llvm_debug_structlike_type(GenContext *c, Type *type, LLV VECEACH(members, i) { Decl *member = members[i]; - SourceLocation *member_loc = TOKLOC(member->span.loc); LLVMMetadataRef debug_info = llvm_get_debug_member(c, member->type, member->name ? member->name : "", member->offset, - member_loc, + &member->span, forward, LLVMDIFlagZero); vec_add(elements, debug_info); @@ -340,11 +360,12 @@ static LLVMMetadataRef llvm_debug_structlike_type(GenContext *c, Type *type, LLV LLVMMetadataRef real; if (type->type_kind == TYPE_UNION) { + unsigned row = decl->span.row; real = LLVMDIBuilderCreateUnionType(c->debug.builder, scope, type->decl->name ? type->decl->name : "", - type->decl->name ? TOKLEN(type->decl->name_token) : 0, - c->debug.file, location->row, type_size(type) * 8, + type->decl->name ? strlen(type->decl->name) : 0, + c->debug.file, row ? row : 1, type_size(type) * 8, type_abi_alignment(type) * 8, LLVMDIFlagZero, elements, vec_size(members), @@ -354,7 +375,7 @@ static LLVMMetadataRef llvm_debug_structlike_type(GenContext *c, Type *type, LLV LLVMMetadataReplaceAllUsesWith(forward, real); return real; } - return llvm_get_debug_struct(c, type, decl->name ? decl->external_name : "", elements, vec_size(elements), location, scope, LLVMDIFlagZero); + return llvm_get_debug_struct(c, type, decl->name ? decl->external_name : "", elements, vec_size(elements), &decl->span, scope, LLVMDIFlagZero); } static LLVMMetadataRef llvm_debug_subarray_type(GenContext *c, Type *type) @@ -431,16 +452,16 @@ static LLVMMetadataRef llvm_debug_typedef_type(GenContext *c, Type *type) Type *original_type = type->type_kind == TYPE_TYPEDEF ? type->canonical : decl->distinct_decl.base_type; - SourceLocation *location = TOKLOC(decl->span.loc); // Use forward references in case we haven't resolved the original type, since we could have this: if (!type->canonical->backend_debug_type) { - type->backend_debug_type = llvm_debug_forward_comp(c, type, type->name, location, NULL, LLVMDIFlagZero); + type->backend_debug_type = llvm_debug_forward_comp(c, type, type->name, &decl->span, NULL, LLVMDIFlagZero); } + unsigned row = decl->span.row; LLVMMetadataRef real = LLVMDIBuilderCreateTypedef(c->debug.builder, llvm_get_debug_type(c, original_type), - decl->name, TOKLEN(decl->name_token), - c->debug.file, location->row, + decl->name, strlen(decl->name), + c->debug.file, row ? row : 1, c->debug.file, type_abi_alignment(type)); if (type->backend_debug_type) { diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 677d6a3a8..b31b9ffb8 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -13,7 +13,7 @@ static inline void llvm_emit_post_inc_dec(GenContext *c, BEValue *value, Expr *e static inline void llvm_emit_pre_inc_dec(GenContext *c, BEValue *value, Expr *expr, int diff, bool use_mod); static inline void llvm_emit_inc_dec_change(GenContext *c, bool use_mod, BEValue *addr, BEValue *after, BEValue *before, Expr *expr, int diff); static void llvm_emit_post_unary_expr(GenContext *context, BEValue *be_value, Expr *expr); -static inline void llvm_emit_subscript_addr_with_base(GenContext *c, BEValue *result, BEValue *parent, BEValue *index, SourceLocation *loc); +static inline void llvm_emit_subscript_addr_with_base(GenContext *c, BEValue *result, BEValue *parent, BEValue *index, SourceSpan loc); static void llvm_emit_initialize_designated(GenContext *c, BEValue *ref, AlignSize offset, DesignatorElement** current, DesignatorElement **last, Expr *expr, BEValue *emitted_value); static inline void llvm_emit_const_initialize_reference(GenContext *c, BEValue *ref, Expr *expr); static inline void llvm_emit_initialize_reference(GenContext *c, BEValue *ref, Expr *expr); @@ -244,7 +244,7 @@ LLVMValueRef llvm_emit_const_padding(GenContext *c, AlignSize size) return LLVMGetUndef(llvm_const_padding_type(c, size)); } -static inline LLVMValueRef llvm_emit_add_int(GenContext *c, Type *type, LLVMValueRef left, LLVMValueRef right, SourceLocation *loc) +static inline LLVMValueRef llvm_emit_add_int(GenContext *c, Type *type, LLVMValueRef left, LLVMValueRef right, SourceSpan loc) { if (active_target.feature.trap_on_wrap) { @@ -470,7 +470,7 @@ void llvm_emit_convert_value_from_coerced(GenContext *c, BEValue *result, LLVMTy llvm_value_set_address_abi_aligned(result, addr, original_type); } -static inline LLVMValueRef llvm_emit_sub_int(GenContext *c, Type *type, LLVMValueRef left, LLVMValueRef right, SourceLocation *loc) +static inline LLVMValueRef llvm_emit_sub_int(GenContext *c, Type *type, LLVMValueRef left, LLVMValueRef right, SourceSpan loc) { if (active_target.feature.trap_on_wrap) { @@ -496,7 +496,7 @@ static inline LLVMValueRef llvm_emit_sub_int(GenContext *c, Type *type, LLVMValu } -static void llvm_emit_array_bounds_check(GenContext *c, BEValue *index, LLVMValueRef array_max_index, SourceLocation *loc) +static void llvm_emit_array_bounds_check(GenContext *c, BEValue *index, LLVMValueRef array_max_index, SourceSpan loc) { BEValue result; llvm_value_rvalue(c, index); @@ -515,7 +515,7 @@ static void llvm_emit_array_bounds_check(GenContext *c, BEValue *index, LLVMValu llvm_emit_panic_if_true(c, &result, "Array index out of bounds", loc); } -static inline void llvm_emit_subscript_addr_with_base(GenContext *c, BEValue *result, BEValue *parent, BEValue *index, SourceLocation *loc) +static inline void llvm_emit_subscript_addr_with_base(GenContext *c, BEValue *result, BEValue *parent, BEValue *index, SourceSpan loc) { assert(llvm_value_is_addr(parent)); Type *type = type_lowering(parent->type); @@ -636,9 +636,9 @@ static inline void gencontext_emit_subscript(GenContext *c, BEValue *value, Expr } if (needs_len && active_target.feature.safe_mode) { - llvm_emit_array_bounds_check(c, &index, len.value, TOKLOC(expr->subscript_expr.index->span.loc)); + llvm_emit_array_bounds_check(c, &index, len.value, expr->subscript_expr.index->span); } - llvm_emit_subscript_addr_with_base(c, value, value, &index, TOKLOC(expr->subscript_expr.index->span.loc)); + llvm_emit_subscript_addr_with_base(c, value, value, &index, expr->subscript_expr.index->span); if (!is_value) { assert(llvm_value_is_addr(value)); @@ -1989,8 +1989,8 @@ static inline void llvm_emit_inc_dec_change(GenContext *c, bool use_mod, BEValue LLVMTypeRef llvm_type = llvm_get_type(c, type); LLVMValueRef diff_value = LLVMConstInt(llvm_type, 1, false); after_value = diff > 0 - ? llvm_emit_add_int(c, type, value.value, diff_value, TOKLOC(expr->span.loc)) - : llvm_emit_sub_int(c, type, value.value, diff_value, TOKLOC(expr->span.loc)); + ? llvm_emit_add_int(c, type, value.value, diff_value, expr->span) + : llvm_emit_sub_int(c, type, value.value, diff_value, expr->span); break; } case TYPE_VECTOR: @@ -2015,8 +2015,8 @@ static inline void llvm_emit_inc_dec_change(GenContext *c, bool use_mod, BEValue if (is_integer) { after_value = diff > 0 - ? llvm_emit_add_int(c, type, value.value, val, TOKLOC(expr->span.loc)) - : llvm_emit_sub_int(c, type, value.value, val, TOKLOC(expr->span.loc)); + ? llvm_emit_add_int(c, type, value.value, val, expr->span) + : llvm_emit_sub_int(c, type, value.value, val, expr->span); } else { @@ -2155,7 +2155,7 @@ static void gencontext_emit_unary_expr(GenContext *c, BEValue *value, Expr *expr &type_to_use, 1, args, 2); value->value = LLVMBuildExtractValue(c->builder, call_res, 0, ""); LLVMValueRef ok = LLVMBuildExtractValue(c->builder, call_res, 1, ""); - llvm_emit_panic_on_true(c, ok, "Signed negation overflow", TOKLOC(expr->span.loc)); + llvm_emit_panic_on_true(c, ok, "Signed negation overflow", expr->span); return; } value->value = LLVMBuildNeg(c->builder, value->value, "neg"); @@ -2230,10 +2230,10 @@ static void llvm_emit_trap_negative(GenContext *c, Expr *expr, LLVMValueRef valu LLVMValueRef zero = llvm_const_int(c, expr->type, 0); LLVMValueRef ok = LLVMBuildICmp(c->builder, LLVMIntSLT, value, zero, "underflow"); - llvm_emit_panic_on_true(c, ok, error, TOKLOC(expr->span.loc)); + llvm_emit_panic_on_true(c, ok, error, expr->span); } -static void llvm_emit_trap_zero(GenContext *c, Type *type, LLVMValueRef value, const char *error, SourceLocation *loc) +static void llvm_emit_trap_zero(GenContext *c, Type *type, LLVMValueRef value, const char *error, SourceSpan loc) { if (!active_target.feature.safe_mode) return; @@ -2243,7 +2243,7 @@ static void llvm_emit_trap_zero(GenContext *c, Type *type, LLVMValueRef value, c } -static void llvm_emit_trap_invalid_shift(GenContext *c, LLVMValueRef value, Type *type, const char *error, SourceLocation *loc) +static void llvm_emit_trap_invalid_shift(GenContext *c, LLVMValueRef value, Type *type, const char *error, SourceSpan loc) { if (!active_target.feature.safe_mode) return; unsigned type_bit_size = type_size(type) * 8; @@ -2321,7 +2321,7 @@ static void llvm_emit_slice_values(GenContext *c, Expr *slice, BEValue *parent_r // Walk from end if it is slice from the back. if (slice->slice_expr.start_from_back) { - start_index.value = llvm_emit_sub_int(c, start_type, len.value, start_index.value, TOKLOC(slice->span.loc)); + start_index.value = llvm_emit_sub_int(c, start_type, len.value, start_index.value, slice->span); } // Check that index does not extend beyond the length. @@ -2330,7 +2330,7 @@ static void llvm_emit_slice_values(GenContext *c, Expr *slice, BEValue *parent_r assert(len.value); BEValue exceeds_size; llvm_emit_int_comparison(c, &exceeds_size, &start_index, &len, BINARYOP_GE); - llvm_emit_panic_if_true(c, &exceeds_size, "Index exceeds array length.", TOKLOC(slice->span.loc)); + llvm_emit_panic_if_true(c, &exceeds_size, "Index exceeds array length.", slice->span); } // Insert trap for negative start offset for non pointers. @@ -2352,7 +2352,7 @@ static void llvm_emit_slice_values(GenContext *c, Expr *slice, BEValue *parent_r // Reverse if it is "from back" if (slice->slice_expr.end_from_back) { - end_index.value = llvm_emit_sub_int(c, end_type, len.value, end_index.value, TOKLOC(slice->span.loc)); + end_index.value = llvm_emit_sub_int(c, end_type, len.value, end_index.value, slice->span); llvm_value_rvalue(c, &end_index); } @@ -2361,13 +2361,13 @@ static void llvm_emit_slice_values(GenContext *c, Expr *slice, BEValue *parent_r { BEValue excess; llvm_emit_int_comparison(c, &excess, &start_index, &end_index, BINARYOP_GT); - llvm_emit_panic_if_true(c, &excess, "Negative size", TOKLOC(slice->span.loc)); + llvm_emit_panic_if_true(c, &excess, "Negative size", slice->span); if (len.value) { llvm_emit_int_comparison(c, &excess, &len, &end_index, BINARYOP_LT); - llvm_emit_panic_if_true(c, &excess, "Size exceeds index", TOKLOC(slice->span.loc)); + llvm_emit_panic_if_true(c, &excess, "Size exceeds index", slice->span); } } } @@ -2465,7 +2465,7 @@ static void llvm_emit_slice_assign(GenContext *c, BEValue *be_value, Expr *expr) for (uint64_t i = start_val; i <= end_val; i++) { llvm_value_set_int(c, &offset_val, type_usize, i); - llvm_emit_subscript_addr_with_base(c, &addr, &parent, &offset_val, TOKLOC(expr->span.loc)); + llvm_emit_subscript_addr_with_base(c, &addr, &parent, &offset_val, expr->span); // And store the value. llvm_store_value(c, &addr, be_value); @@ -2503,13 +2503,13 @@ static void llvm_emit_slice_assign(GenContext *c, BEValue *be_value, Expr *expr) llvm_emit_block(c, assign_block); // Reuse this calculation BEValue addr; - llvm_emit_subscript_addr_with_base(c, &addr, &parent, &offset_val, TOKLOC(expr->span.loc)); + llvm_emit_subscript_addr_with_base(c, &addr, &parent, &offset_val, expr->span); // And store the value. llvm_store_value(c, &addr, be_value); // Create the new offset - LLVMValueRef next_offset = llvm_emit_add_int(c, start.type, offset, llvm_const_int(c, start.type, 1), TOKLOC(expr->span.loc)); + LLVMValueRef next_offset = llvm_emit_add_int(c, start.type, offset, llvm_const_int(c, start.type, 1), expr->span); // And jump back llvm_emit_br(c, cond_block); @@ -2817,7 +2817,7 @@ static void llvm_emit_ptr_comparison(GenContext *c, BEValue *result, BEValue *lh llvm_value_set_bool(result, val); } -static inline LLVMValueRef llvm_fixup_shift_rhs(GenContext *c, LLVMValueRef left, LLVMValueRef right, SourceLocation *loc) +static inline LLVMValueRef llvm_fixup_shift_rhs(GenContext *c, LLVMValueRef left, LLVMValueRef right) { LLVMTypeRef left_type = LLVMTypeOf(left); LLVMTypeRef right_type = LLVMTypeOf(right); @@ -2829,7 +2829,7 @@ static inline LLVMValueRef llvm_fixup_shift_rhs(GenContext *c, LLVMValueRef left return LLVMBuildZExt(c->builder, right, left_type, ""); } -static inline LLVMValueRef llvm_emit_mult_int(GenContext *c, Type *type, LLVMValueRef left, LLVMValueRef right, SourceLocation *loc) +static inline LLVMValueRef llvm_emit_mult_int(GenContext *c, Type *type, LLVMValueRef left, LLVMValueRef right, SourceSpan loc) { if (active_target.feature.trap_on_wrap) { @@ -3059,7 +3059,7 @@ void gencontext_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValu val = LLVMBuildFMul(c->builder, lhs_value, rhs_value, "fmul"); break; } - val = llvm_emit_mult_int(c, lhs_type, lhs_value, rhs_value, TOKLOC(expr->span.loc)); + val = llvm_emit_mult_int(c, lhs_type, lhs_value, rhs_value, expr->span); break; case BINARYOP_SUB: if (lhs_type->type_kind == TYPE_POINTER) @@ -3082,7 +3082,7 @@ void gencontext_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValu val = LLVMBuildFSub(c->builder, lhs_value, rhs_value, "fsub"); break; } - val = llvm_emit_sub_int(c, lhs_type, lhs_value, rhs_value, TOKLOC(expr->span.loc)); + val = llvm_emit_sub_int(c, lhs_type, lhs_value, rhs_value, expr->span); break; case BINARYOP_ADD: if (lhs_type->type_kind == TYPE_POINTER) @@ -3096,10 +3096,10 @@ void gencontext_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValu val = LLVMBuildFAdd(c->builder, lhs_value, rhs_value, "fadd"); break; } - val = llvm_emit_add_int(c, lhs_type, lhs_value, rhs_value, TOKLOC(expr->span.loc)); + val = llvm_emit_add_int(c, lhs_type, lhs_value, rhs_value, expr->span); break; case BINARYOP_DIV: - llvm_emit_trap_zero(c, rhs_type, rhs_value, "% by zero", TOKLOC(expr->span.loc)); + llvm_emit_trap_zero(c, rhs_type, rhs_value, "% by zero", expr->span); if (is_float) { val = LLVMBuildFDiv(c->builder, lhs_value, rhs_value, "fdiv"); @@ -3110,22 +3110,22 @@ void gencontext_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValu : LLVMBuildSDiv(c->builder, lhs_value, rhs_value, "sdiv"); break; case BINARYOP_MOD: - llvm_emit_trap_zero(c, rhs_type, rhs_value, "% by zero", TOKLOC(expr->span.loc)); + llvm_emit_trap_zero(c, rhs_type, rhs_value, "% by zero", expr->span); val = type_is_unsigned(lhs_type) ? LLVMBuildURem(c->builder, lhs_value, rhs_value, "umod") : LLVMBuildSRem(c->builder, lhs_value, rhs_value, "smod"); break; case BINARYOP_SHR: - rhs_value = llvm_fixup_shift_rhs(c, lhs_value, rhs_value, TOKLOC(expr->span.loc)); - llvm_emit_trap_invalid_shift(c, rhs_value, lhs_type, "Shift amount out of range.", TOKLOC(expr->span.loc)); + rhs_value = llvm_fixup_shift_rhs(c, lhs_value, rhs_value); + llvm_emit_trap_invalid_shift(c, rhs_value, lhs_type, "Shift amount out of range.", expr->span); val = type_is_unsigned(lhs_type) ? LLVMBuildLShr(c->builder, lhs_value, rhs_value, "lshr") : LLVMBuildAShr(c->builder, lhs_value, rhs_value, "ashr"); val = LLVMBuildFreeze(c->builder, val, ""); break; case BINARYOP_SHL: - rhs_value = llvm_fixup_shift_rhs(c, lhs_value, rhs_value, TOKLOC(expr->span.loc)); - llvm_emit_trap_invalid_shift(c, rhs_value, lhs_type, "Shift amount out of range.", TOKLOC(expr->span.loc)); + rhs_value = llvm_fixup_shift_rhs(c, lhs_value, rhs_value); + llvm_emit_trap_invalid_shift(c, rhs_value, lhs_type, "Shift amount out of range.", expr->span); val = LLVMBuildShl(c->builder, lhs_value, rhs_value, "shl"); val = LLVMBuildFreeze(c->builder, val, ""); break; @@ -3588,9 +3588,9 @@ static inline void llvm_emit_force_unwrap_expr(GenContext *c, BEValue *be_value, if (llvm_emit_check_block_branch(c)) { // TODO, we should add info about the error. - SourceLocation *loc = TOKLOC(expr->span.loc); - File *file = source_file_by_id(loc->file_id); - llvm_emit_debug_output(c, "Runtime error force unwrap!", file->name, c->cur_func_decl->external_name, loc->row); + SourceSpan loc = expr->span; + File *file = source_file_by_id(loc.file_id); + llvm_emit_debug_output(c, "Runtime error force unwrap!", file->name, c->cur_func_decl->external_name, loc.row ? loc.row : 1); llvm_emit_call_intrinsic(c, intrinsic_id.trap, NULL, 0, NULL, 0); LLVMBuildUnreachable(c->builder); c->current_block = NULL; @@ -3983,7 +3983,7 @@ static void llvm_emit_const_expr(GenContext *c, BEValue *be_value, Expr *expr) { Decl *decl = expr->const_expr.enum_val; return llvm_emit_const_expr(c, be_value, expr->const_expr.err_val->enum_constant.expr); - assert(decl->decl_kind == DECL_ERRVALUE); + assert(decl->decl_kind == DECL_OPTVALUE); llvm_value_set(be_value, LLVMBuildPtrToInt(c->builder, decl->backend_ref, llvm_get_type(c, type_anyerr), ""), type_anyerr); diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index f70b4c0d3..e16732a92 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -652,17 +652,20 @@ void llvm_emit_methods(GenContext *c, Decl **methods) void llvm_emit_extern_decl(GenContext *context, Decl *decl) { + const char *name; switch (decl->decl_kind) { case DECL_POISONED: UNREACHABLE; case DECL_FUNC: - decl->backend_ref = LLVMAddFunction(context->module, decl->extname ? decl->extname : decl->external_name, + name = decl_get_extname(decl); + decl->backend_ref = LLVMAddFunction(context->module, name, llvm_get_type(context, decl->type)); LLVMSetVisibility(decl->backend_ref, LLVMDefaultVisibility); break; case DECL_VAR: - decl->backend_ref = LLVMAddGlobal(context->module, llvm_get_type(context, decl->type), decl->extname ? decl->extname : decl->external_name); + name = decl_get_extname(decl); + decl->backend_ref = LLVMAddGlobal(context->module, llvm_get_type(context, decl->type), name); LLVMSetVisibility(decl->backend_ref, LLVMDefaultVisibility); break; case DECL_BITSTRUCT: @@ -673,7 +676,7 @@ void llvm_emit_extern_decl(GenContext *context, Decl *decl) break; case DECL_ENUM: break; - case DECL_ERRTYPE: + case DECL_OPTENUM: llvm_emit_introspection_type_from_decl(context, decl); // TODO // Fix typeid return; @@ -681,7 +684,7 @@ void llvm_emit_extern_decl(GenContext *context, Decl *decl) case DECL_DISTINCT: case NON_TYPE_DECLS: case DECL_ENUM_CONSTANT: - case DECL_ERRVALUE: + case DECL_OPTVALUE: return; } } diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index acee87014..3ee1258d6 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -299,8 +299,8 @@ void llvm_emit_memcpy(GenContext *c, LLVMValueRef dest, unsigned dest_align, LLV void llvm_emit_memcpy_to_decl(GenContext *c, Decl *decl, LLVMValueRef source, unsigned source_alignment); void llvm_emit_stmt(GenContext *c, Ast *ast); static inline LLVMValueRef llvm_emit_store(GenContext *context, Decl *decl, LLVMValueRef value); -void llvm_emit_panic_on_true(GenContext *c, LLVMValueRef value, const char *panic_name, SourceLocation *loc); -void llvm_emit_panic_if_true(GenContext *c, BEValue *value, const char *panic_name, SourceLocation *loc); +void llvm_emit_panic_on_true(GenContext *c, LLVMValueRef value, const char *panic_name, SourceSpan loc); +void llvm_emit_panic_if_true(GenContext *c, BEValue *value, const char *panic_name, SourceSpan loc); void llvm_emit_ptr_from_array(GenContext *c, BEValue *value); void llvm_emit_debug_output(GenContext *c, const char *message, const char *file, const char *func, unsigned line); void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failable); diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 17fb9a438..ab1f8f9d1 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -416,9 +416,9 @@ void llvm_emit_for_stmt(GenContext *c, Ast *ast) { if (loop == LOOP_INFINITE) { - SourceLocation *loc = TOKLOC(ast->span.loc); - File *file = source_file_by_id(loc->file_id); - llvm_emit_debug_output(c, "Infinite loop found", file->name, c->cur_func_decl->external_name, loc->row); + SourceSpan loc = ast->span; + File *file = source_file_by_id(loc.file_id); + llvm_emit_debug_output(c, "Infinite loop found", file->name, c->cur_func_decl->external_name, loc.row ? loc.row : 1); LLVMBuildUnreachable(c->builder); LLVMBasicBlockRef block = llvm_basic_block_new(c, "unreachable_block"); c->current_block = NULL; @@ -971,7 +971,7 @@ static inline void llvm_emit_assert_stmt(GenContext *c, Ast *ast) assert(value.kind == BE_BOOLEAN); llvm_emit_cond_br(c, &value, on_ok, on_fail); llvm_emit_block(c, on_fail); - SourceLocation *loc = TOKLOC(ast->assert_stmt.expr->span.loc); + SourceSpan loc = ast->assert_stmt.expr->span; const char *error; if (ast->assert_stmt.message) { @@ -981,8 +981,8 @@ static inline void llvm_emit_assert_stmt(GenContext *c, Ast *ast) { error = "Assert violation"; } - File *file = source_file_by_id(loc->file_id); - llvm_emit_debug_output(c, error, file->name, c->cur_func_decl->name, loc->row); + File *file = source_file_by_id(loc.file_id); + llvm_emit_debug_output(c, error, file->name, c->cur_func_decl->name, loc.row ? loc.row : 1); llvm_emit_call_intrinsic(c, intrinsic_id.trap, NULL, 0, NULL, 0); llvm_emit_br(c, on_ok); llvm_emit_block(c, on_ok); @@ -1032,9 +1032,9 @@ static inline void llvm_emit_asm_stmt(GenContext *c, Ast *ast) static inline void gencontext_emit_unreachable_stmt(GenContext *context, Ast *ast) { - SourceLocation *loc = TOKLOC(ast->span.loc); - File *file = source_file_by_id(loc->file_id); - llvm_emit_debug_output(context, "Unreachable statement reached.", file->name, context->cur_func_decl->external_name, loc->row); + File *file = source_file_by_id(ast->span.file_id); + unsigned row = ast->span.row; + llvm_emit_debug_output(context, "Unreachable statement reached.", file->name, context->cur_func_decl->external_name, row ? row : 1); llvm_emit_call_intrinsic(context, intrinsic_id.trap, NULL, 0, NULL, 0); LLVMBuildUnreachable(context->builder); LLVMBasicBlockRef block = llvm_basic_block_new(context, "unreachable_block"); @@ -1203,30 +1203,30 @@ void llvm_emit_debug_output(GenContext *c, const char *message, const char *file } -void llvm_emit_panic_if_true(GenContext *c, BEValue *value, const char *panic_name, SourceLocation *loc) +void llvm_emit_panic_if_true(GenContext *c, BEValue *value, const char *panic_name, SourceSpan loc) { LLVMBasicBlockRef panic_block = llvm_basic_block_new(c, "panic"); LLVMBasicBlockRef ok_block = llvm_basic_block_new(c, "checkok"); assert(llvm_value_is_bool(value)); llvm_emit_cond_br(c, value, panic_block, ok_block); llvm_emit_block(c, panic_block); - File *file = source_file_by_id(loc->file_id); - llvm_emit_debug_output(c, panic_name, file->name, c->cur_func_decl->name, loc->row); + File *file = source_file_by_id(loc.file_id); + llvm_emit_debug_output(c, panic_name, file->name, c->cur_func_decl->name, loc.row); llvm_emit_call_intrinsic(c, intrinsic_id.trap, NULL, 0, NULL, 0); llvm_emit_br(c, ok_block); llvm_emit_block(c, ok_block); } -void llvm_emit_panic_on_true(GenContext *c, LLVMValueRef value, const char *panic_name, SourceLocation *loc) +void llvm_emit_panic_on_true(GenContext *c, LLVMValueRef value, const char *panic_name, SourceSpan loc) { - File *file = source_file_by_id(loc->file_id); + File *file = source_file_by_id(loc.file_id); LLVMBasicBlockRef panic_block = llvm_basic_block_new(c, "panic"); LLVMBasicBlockRef ok_block = llvm_basic_block_new(c, "checkok"); BEValue be_value; llvm_value_set_bool(&be_value, value); llvm_emit_cond_br(c, &be_value, panic_block, ok_block); llvm_emit_block(c, panic_block); - llvm_emit_debug_output(c, panic_name, file->name, c->cur_func_decl->name, loc->row); + llvm_emit_debug_output(c, panic_name, file->name, c->cur_func_decl->name, loc.row ? loc.row : 1); llvm_emit_call_intrinsic(c, intrinsic_id.trap, NULL, 0, NULL, 0); llvm_emit_br(c, ok_block); llvm_emit_block(c, ok_block); diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index 54d498986..75630fe60 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -11,7 +11,7 @@ static inline LLVMTypeRef llvm_type_from_decl(GenContext *c, Decl *decl) { case DECL_VAR: case DECL_ENUM_CONSTANT: - case DECL_ERRVALUE: + case DECL_OPTVALUE: case DECL_POISONED: case NON_TYPE_DECLS: UNREACHABLE @@ -75,7 +75,7 @@ static inline LLVMTypeRef llvm_type_from_decl(GenContext *c, Decl *decl) } case DECL_ENUM: return llvm_get_type(c, decl->type); - case DECL_ERRTYPE: + case DECL_OPTENUM: return llvm_get_type(c, type_iptr); } UNREACHABLE diff --git a/src/compiler/module.c b/src/compiler/module.c index 0b2399ebd..9bbef4ec8 100644 --- a/src/compiler/module.c +++ b/src/compiler/module.c @@ -6,7 +6,7 @@ Decl *module_find_symbol(Module *module, const char *symbol) { - return stable_get(&module->symbols, symbol); + return htable_get(&module->symbols, symbol); } const char *module_create_object_file_name(Module *module) diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 505da0e32..241193bc7 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -6,8 +6,10 @@ #include "parser_internal.h" +#define IF_TRY_CATCH_PREC (PREC_AND + 1) typedef Expr *(*ParseFn)(ParseContext *context, Expr *); -static Expr *parse_rethrow_expr(ParseContext *context, Expr *left); +static Expr *parse_rethrow_expr(ParseContext *c, Expr *left); +static Expr *parse_expr_or_type_prec(ParseContext *c, TypeInfo **type_ref, Precedence prec); typedef struct { @@ -19,15 +21,16 @@ typedef struct extern ParseRule rules[TOKEN_EOF + 1]; -inline Expr *parse_precedence_with_left_side(ParseContext *context, Expr *left_side, Precedence precedence) +inline Expr *parse_precedence_with_left_side(ParseContext *c, Expr *left_side, Precedence precedence) { while (1) { - Precedence token_precedence = rules[context->tok.type].precedence; + TokenType tok = c->tok; + Precedence token_precedence = rules[tok].precedence; bool special_question = false; - if (context->tok.type == TOKEN_QUESTION) + if (tok == TOKEN_QUESTION) { - ParseRule rule = rules[context->next_tok.type]; + ParseRule rule = rules[peek(c)]; if (!rule.prefix) { token_precedence = PREC_CALL; @@ -36,74 +39,77 @@ inline Expr *parse_precedence_with_left_side(ParseContext *context, Expr *left_s } if (precedence > token_precedence) break; if (!expr_ok(left_side)) return left_side; - ParseFn infix_rule = special_question ? &parse_rethrow_expr : rules[context->tok.type].infix; + ParseFn infix_rule = special_question ? &parse_rethrow_expr : rules[tok].infix; if (!infix_rule) { - SEMA_TOKEN_ERROR(context->tok, "An expression was expected."); + SEMA_ERROR_HERE("An expression was expected."); return poisoned_expr; } - left_side = infix_rule(context, left_side); + left_side = infix_rule(c, left_side); } return left_side; } -static Expr *parse_precedence(ParseContext *context, Precedence precedence) +static Expr *parse_precedence(ParseContext *c, Precedence precedence) { // Get the rule for the previous token. - ParseFn prefix_rule = rules[context->tok.type].prefix; + ParseFn prefix_rule = rules[c->tok].prefix; if (prefix_rule == NULL) { - SEMA_TOKEN_ERROR(context->tok, "An expression was expected."); + SEMA_ERROR_HERE("An expression was expected."); return poisoned_expr; } - Expr *left_side = prefix_rule(context, NULL); + Expr *left_side = prefix_rule(c, NULL); if (!expr_ok(left_side)) return left_side; - return parse_precedence_with_left_side(context, left_side, precedence); + return parse_precedence_with_left_side(c, left_side, precedence); } -Expr *parse_expr_or_initializer_list(ParseContext *context) +Expr *parse_expr_or_initializer_list(ParseContext *c) { - return parse_expr(context); + return parse_expr(c); } -static inline bool next_is_try_unwrap(ParseContext *context) +static inline bool next_is_try_unwrap(ParseContext *c) { - return tok_is(context, TOKEN_TRY) && context->next_tok.type != TOKEN_LPAREN; + return tok_is(c, TOKEN_TRY) && peek(c) != TOKEN_LPAREN; } -static inline bool next_is_catch_unwrap(ParseContext *context) +static inline bool next_is_catch_unwrap(ParseContext *c) { - return tok_is(context, TOKEN_CATCH) && context->next_tok.type != TOKEN_LPAREN; + return tok_is(c, TOKEN_CATCH) && peek(c) != TOKEN_LPAREN; } -static inline Expr *parse_for_try_expr(ParseContext *context) +static inline Expr *parse_for_try_expr(ParseContext *c) { - return parse_precedence(context, PREC_AND + 1); + return parse_precedence(c, PREC_AND + 1); } /** * catch_unwrap ::= CATCH IDENT | (type? IDENT '=' (expr | '(' expr (',' expr) ')')) */ -static inline Expr *parse_catch_unwrap(ParseContext *context) +static inline Expr *parse_catch_unwrap(ParseContext *c) { - advance_and_verify(context, TOKEN_CATCH); - Expr *expr = expr_new(EXPR_CATCH_UNWRAP, source_span_from_token_id(context->prev_tok)); - if (parse_next_is_decl(context)) + Expr *expr = expr_new(EXPR_CATCH_UNWRAP, c->span); + advance_and_verify(c, TOKEN_CATCH); + TypeInfo *type = NULL; + ASSIGN_EXPR_OR_RET(Expr * lhs, parse_expr_or_type_prec(c, &type, IF_TRY_CATCH_PREC), poisoned_expr); + if (!lhs) { - ASSIGN_TYPE_ELSE(expr->catch_unwrap_expr.type, parse_type(context), poisoned_expr); + ASSIGN_TYPE_OR_RET(expr->catch_unwrap_expr.type, type, poisoned_expr); + ASSIGN_EXPR_OR_RET(expr->catch_unwrap_expr.variable, parse_precedence(c, IF_TRY_CATCH_PREC), poisoned_expr); } else { expr->catch_unwrap_expr.type = NULL; + expr->catch_unwrap_expr.variable = lhs; } - ASSIGN_EXPR_ELSE(expr->catch_unwrap_expr.variable, parse_for_try_expr(context), poisoned_expr); - if (!try_consume(context, TOKEN_EQ)) + if (!try_consume(c, TOKEN_EQ)) { if (expr->catch_unwrap_expr.type) { - SEMA_TOKEN_ERROR(context->tok, "Expected a '=' here."); + SEMA_ERROR_HERE("Expected a '=' here."); return poisoned_expr; } vec_add(expr->catch_unwrap_expr.exprs, expr->catch_unwrap_expr.variable); @@ -111,18 +117,18 @@ static inline Expr *parse_catch_unwrap(ParseContext *context) RANGE_EXTEND_PREV(expr); return expr; } - if (try_consume(context, TOKEN_LPAREN)) + if (try_consume(c, TOKEN_LPAREN)) { do { - ASSIGN_EXPR_ELSE(Expr *init_expr, parse_expr(context), poisoned_expr); + ASSIGN_EXPR_OR_RET(Expr * init_expr, parse_expr(c), poisoned_expr); vec_add(expr->catch_unwrap_expr.exprs, init_expr); - } while (try_consume(context, TOKEN_COMMA)); - CONSUME_OR(TOKEN_RPAREN, poisoned_expr); + } while (try_consume(c, TOKEN_COMMA)); + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); } else { - ASSIGN_EXPR_ELSE(Expr *init_expr, parse_expr(context), poisoned_expr); + ASSIGN_EXPR_OR_RET(Expr * init_expr, parse_expr(c), poisoned_expr); vec_add(expr->catch_unwrap_expr.exprs, init_expr); } RANGE_EXTEND_PREV(expr); @@ -132,23 +138,29 @@ static inline Expr *parse_catch_unwrap(ParseContext *context) /** * try_unwrap ::= TRY (IDENT | type? IDENT '=' non_and_expr) */ -static inline Expr *parse_try_unwrap(ParseContext *context) +static inline Expr *parse_try_unwrap(ParseContext *c) { - Expr *expr = EXPR_NEW_TOKEN(EXPR_TRY_UNWRAP, context->tok); - advance_and_verify(context, TOKEN_TRY); - if (parse_next_is_decl(context)) + Expr *expr = EXPR_NEW_TOKEN(EXPR_TRY_UNWRAP); + advance_and_verify(c, TOKEN_TRY); + TypeInfo *type = NULL; + ASSIGN_EXPR_OR_RET(Expr * lhs, parse_expr_or_type_prec(c, &type, IF_TRY_CATCH_PREC), poisoned_expr); + if (!lhs) { - ASSIGN_TYPE_ELSE(expr->try_unwrap_expr.type, parse_type(context), poisoned_expr); + ASSIGN_TYPE_OR_RET(expr->try_unwrap_expr.type, type, poisoned_expr); + ASSIGN_EXPR_OR_RET(expr->try_unwrap_expr.variable, parse_precedence(c, IF_TRY_CATCH_PREC), poisoned_expr); } - ASSIGN_EXPR_ELSE(expr->try_unwrap_expr.variable, parse_for_try_expr(context), poisoned_expr); - if (expr->try_unwrap_expr.type && expr->try_unwrap_expr.variable->expr_kind != EXPR_IDENTIFIER) + else { - SEMA_ERROR(expr->try_unwrap_expr.variable, "Expected a variable name after the type."); + expr->try_unwrap_expr.variable = lhs; + } + if (type && expr->try_unwrap_expr.variable->expr_kind != EXPR_IDENTIFIER) + { + SEMA_ERROR(expr->try_unwrap_expr.variable, "A new variable was expected."); return poisoned_expr; } - if (try_consume(context, TOKEN_EQ)) + if (try_consume(c, TOKEN_EQ)) { - ASSIGN_EXPR_ELSE(expr->try_unwrap_expr.init, parse_for_try_expr(context), poisoned_expr); + ASSIGN_EXPR_OR_RET(expr->try_unwrap_expr.init, parse_precedence(c, PREC_AND + 1), poisoned_expr); } RANGE_EXTEND_PREV(expr); return expr; @@ -158,20 +170,20 @@ static inline Expr *parse_try_unwrap(ParseContext *context) * try_unwrap_chain ::= try_unwrap ('&&' (try_unwrap | non_and_expr))* * try_unwrap ::= TRY (IDENT | type? IDENT '=' non_and_expr) */ -static inline Expr *parse_try_unwrap_chain(ParseContext *context) +static inline Expr *parse_try_unwrap_chain(ParseContext *c) { Expr **unwraps = NULL; - ASSIGN_EXPR_ELSE(Expr *first_unwrap , parse_try_unwrap(context), poisoned_expr); + ASSIGN_EXPR_OR_RET(Expr * first_unwrap , parse_try_unwrap(c), poisoned_expr); vec_add(unwraps, first_unwrap); - while (try_consume(context, TOKEN_AND)) + while (try_consume(c, TOKEN_AND)) { - if (next_is_try_unwrap(context)) + if (next_is_try_unwrap(c)) { - ASSIGN_EXPR_ELSE(Expr *expr, parse_try_unwrap(context), poisoned_expr); + ASSIGN_EXPR_OR_RET(Expr * expr, parse_try_unwrap(c), poisoned_expr); vec_add(unwraps, expr); continue; } - ASSIGN_EXPR_ELSE(Expr *next_unwrap, parse_for_try_expr(context), poisoned_expr); + ASSIGN_EXPR_OR_RET(Expr * next_unwrap, parse_for_try_expr(c), poisoned_expr); vec_add(unwraps, next_unwrap); } Expr *try_unwrap_chain = EXPR_NEW_EXPR(EXPR_TRY_UNWRAP_CHAIN, first_unwrap); @@ -180,13 +192,13 @@ static inline Expr *parse_try_unwrap_chain(ParseContext *context) return try_unwrap_chain; } -Expr *parse_assert_expr(ParseContext *context) +Expr *parse_assert_expr(ParseContext *c) { - if (next_is_try_unwrap(context)) + if (next_is_try_unwrap(c)) { - return parse_try_unwrap_chain(context); + return parse_try_unwrap_chain(c); } - return parse_expr(context); + return parse_expr(c); } /** @@ -194,47 +206,44 @@ Expr *parse_assert_expr(ParseContext *context) * * @return bool */ -Expr *parse_cond(ParseContext *context) +Expr *parse_cond(ParseContext *c) { - Expr *decl_expr = EXPR_NEW_TOKEN(EXPR_COND, context->tok); + Expr *decl_expr = EXPR_NEW_TOKEN(EXPR_COND); decl_expr->cond_expr = NULL; while (1) { - if (next_is_try_unwrap(context)) + if (next_is_try_unwrap(c)) { - ASSIGN_EXPR_ELSE(Expr *try_unwrap, parse_try_unwrap_chain(context), poisoned_expr); + ASSIGN_EXPR_OR_RET(Expr * try_unwrap, parse_try_unwrap_chain(c), poisoned_expr); vec_add(decl_expr->cond_expr, try_unwrap); - if (tok_is(context, TOKEN_COMMA)) + if (tok_is(c, TOKEN_COMMA)) { SEMA_ERROR(try_unwrap, "The 'try' must be placed last, can you change it?"); return poisoned_expr; } break; } - if (next_is_catch_unwrap(context)) + if (next_is_catch_unwrap(c)) { - ASSIGN_EXPR_ELSE(Expr *catch_unwrap, parse_catch_unwrap(context), poisoned_expr); + ASSIGN_EXPR_OR_RET(Expr * catch_unwrap, parse_catch_unwrap(c), poisoned_expr); vec_add(decl_expr->cond_expr, catch_unwrap); - if (tok_is(context, TOKEN_COMMA)) + if (tok_is(c, TOKEN_COMMA)) { SEMA_ERROR(catch_unwrap, "The 'catch' must be placed last, can you change it?"); return poisoned_expr; } break; } - if (parse_next_is_decl(context)) + + Decl *decl; + ASSIGN_EXPR_OR_RET(Expr * expr, parse_decl_or_expr(c, &decl), poisoned_expr); + if (!expr) { - ASSIGN_DECL_ELSE(Decl *decl, parse_decl(context), poisoned_expr); - Expr *expr = expr_new(EXPR_DECL, decl->span); + expr = expr_new(EXPR_DECL, decl->span); expr->decl_expr = decl; - vec_add(decl_expr->cond_expr, expr); } - else - { - ASSIGN_EXPR_ELSE(Expr *expr, parse_expr(context), poisoned_expr); - vec_add(decl_expr->cond_expr, expr); - } - if (!try_consume(context, TOKEN_COMMA)) break; + vec_add(decl_expr->cond_expr, expr); + if (!try_consume(c, TOKEN_COMMA)) break; } RANGE_EXTEND_PREV(decl_expr); return decl_expr; @@ -242,55 +251,55 @@ Expr *parse_cond(ParseContext *context) // These used to be explicitly inlined, but that seems to lead to confusing MSVC linker errors. // They are probably still inlined by the compiler, though I haven't checked. -Expr* parse_expr(ParseContext *context) +Expr* parse_expr(ParseContext *c) { - return parse_precedence(context, PREC_ASSIGNMENT); + return parse_precedence(c, PREC_ASSIGNMENT); } -Expr* parse_constant_expr(ParseContext *context) +Expr* parse_constant_expr(ParseContext *c) { - return parse_precedence(context, PREC_TERNARY); + return parse_precedence(c, PREC_TERNARY); } /** * param_path : ('[' expression ']' | '.' IDENT)* * - * @param context + * @param c * @param path reference to the path to return * @return true if parsing succeeds, false otherwise. */ -static bool parse_param_path(ParseContext *context, DesignatorElement ***path) +static bool parse_param_path(ParseContext *c, DesignatorElement ***path) { *path = NULL; while (true) { - if (TOKEN_IS(TOKEN_LBRACKET)) + if (tok_is(c, TOKEN_LBRACKET)) { // Parse the inside of [ ] DesignatorElement *element = CALLOCS(DesignatorElement); element->kind = DESIGNATOR_ARRAY; - advance_and_verify(context, TOKEN_LBRACKET); - ASSIGN_EXPR_ELSE(element->index_expr, parse_expr(context), false); + advance_and_verify(c, TOKEN_LBRACKET); + ASSIGN_EXPR_OR_RET(element->index_expr, parse_expr(c), false); // Possible range - if (try_consume(context, TOKEN_DOTDOT)) + if (try_consume(c, TOKEN_DOTDOT)) { - ASSIGN_EXPR_ELSE(element->index_end_expr, parse_expr(context), false); + ASSIGN_EXPR_OR_RET(element->index_end_expr, parse_expr(c), false); element->kind = DESIGNATOR_RANGE; } - CONSUME_OR(TOKEN_RBRACKET, false); + CONSUME_OR_RET(TOKEN_RBRACKET, false); // Include ] in the expr vec_add(*path, element); continue; } - if (TOKEN_IS(TOKEN_DOT)) + if (tok_is(c, TOKEN_DOT)) { - advance(context); + advance(c); DesignatorElement *element = CALLOCS(DesignatorElement); element->kind = DESIGNATOR_FIELD; - element->field = TOKSTR(context->tok.id); - EXPECT_OR(TOKEN_IDENT, false); - advance(context); + element->field = symstr(c); + EXPECT_OR_RET(TOKEN_IDENT, false); + advance(c); vec_add(*path, element); continue; } @@ -302,7 +311,7 @@ static bool parse_param_path(ParseContext *context, DesignatorElement ***path) * * parameter ::= (param_path '=')? expr */ -bool parse_arg_list(ParseContext *context, Expr ***result, TokenType param_end, bool *unsplat) +bool parse_arg_list(ParseContext *c, Expr ***result, TokenType param_end, bool *unsplat) { *result = NULL; if (unsplat) *unsplat = false; @@ -310,38 +319,37 @@ bool parse_arg_list(ParseContext *context, Expr ***result, TokenType param_end, { Expr *expr = NULL; DesignatorElement **path; - Token current = context->tok; - if (!parse_param_path(context, &path)) return false; + if (!parse_param_path(c, &path)) return false; if (path != NULL) { // Create the parameter expr - expr = EXPR_NEW_TOKEN(EXPR_DESIGNATOR, current); + expr = EXPR_NEW_TOKEN(EXPR_DESIGNATOR); expr->designator_expr.path = path; RANGE_EXTEND_PREV(expr); // Expect the '=' after. - CONSUME_OR(TOKEN_EQ, false); + CONSUME_OR_RET(TOKEN_EQ, false); // Now parse the rest - ASSIGN_EXPR_ELSE(expr->designator_expr.value, parse_expr_or_initializer_list(context), false); + ASSIGN_EXPR_OR_RET(expr->designator_expr.value, parse_expr_or_initializer_list(c), false); } else { if (unsplat) { - *unsplat = try_consume(context, TOKEN_ELLIPSIS); + *unsplat = try_consume(c, TOKEN_ELLIPSIS); } - ASSIGN_EXPR_ELSE(expr, parse_expr_or_initializer_list(context), false); + ASSIGN_EXPR_OR_RET(expr, parse_expr_or_initializer_list(c), false); } vec_add(*result, expr); - if (!try_consume(context, TOKEN_COMMA)) + if (!try_consume(c, TOKEN_COMMA)) { return true; } - if (TOKEN_IS(param_end)) return true; + if (tok_is(c, param_end)) return true; if (unsplat && *unsplat) { - SEMA_TOKEN_ERROR(context->tok, "'...' is only allowed on the last argument in a call."); + SEMA_ERROR_HERE("'...' is only allowed on the last argument in a call."); return false; } } @@ -350,12 +358,12 @@ bool parse_arg_list(ParseContext *context, Expr ***result, TokenType param_end, /** * macro_expansion ::= '@' non_at_expression */ -static Expr *parse_macro_expansion(ParseContext *context, Expr *left) +static Expr *parse_macro_expansion(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); - Expr *macro_expression = EXPR_NEW_TOKEN(EXPR_MACRO_EXPANSION, context->tok); - advance_and_verify(context, TOKEN_AT); - ASSIGN_EXPR_ELSE(Expr *inner, parse_precedence(context, PREC_MACRO), poisoned_expr); + Expr *macro_expression = EXPR_NEW_TOKEN(EXPR_MACRO_EXPANSION); + advance_and_verify(c, TOKEN_AT); + ASSIGN_EXPR_OR_RET(Expr * inner, parse_precedence(c, PREC_MACRO), poisoned_expr); macro_expression->macro_expansion_expr.inner = inner; assert(inner); RANGE_EXTEND_PREV(macro_expression); @@ -370,45 +378,41 @@ static Expr *parse_macro_expansion(ParseContext *context, Expr *left) * ; * @return Ast * */ -Expr *parse_expression_list(ParseContext *context, bool allow_decl) +Expr *parse_expression_list(ParseContext *c, bool allow_decl) { - Expr *expr_list = EXPR_NEW_TOKEN(EXPR_EXPRESSION_LIST, context->tok); + Expr *expr_list = EXPR_NEW_TOKEN(EXPR_EXPRESSION_LIST); while (1) { - Expr *expr; - if (parse_next_is_decl(context)) + Decl *decl; + ASSIGN_EXPR_OR_RET(Expr * expr, parse_decl_or_expr(c, &decl), poisoned_expr); + if (!expr) { - ASSIGN_DECL_ELSE(Decl *decl, parse_decl(context), poisoned_expr); if (!allow_decl) { - SEMA_TOKEN_ERROR(context->tok, "This looks like a declaration, which isn't allowed here."); + SEMA_ERROR_HERE("This looks like a declaration, which isn't allowed here."); return poisoned_expr; } expr = expr_new(EXPR_DECL, decl->span); expr->decl_expr = decl; } - else - { - ASSIGN_EXPR_ELSE(expr, parse_expr(context), poisoned_expr); - } vec_add(expr_list->expression_list, expr); - if (!try_consume(context, TOKEN_COMMA)) break; + if (!try_consume(c, TOKEN_COMMA)) break; } return expr_list; } -Expr *parse_ct_expression_list(ParseContext *context, bool allow_decl) +Expr *parse_ct_expression_list(ParseContext *c, bool allow_decl) { - Expr *expr_list = EXPR_NEW_TOKEN(EXPR_EXPRESSION_LIST, context->tok); + Expr *expr_list = EXPR_NEW_TOKEN(EXPR_EXPRESSION_LIST); while (1) { Expr *expr; - if (tok_is(context, TOKEN_VAR)) + if (tok_is(c, TOKEN_VAR)) { - ASSIGN_DECL_ELSE(Decl *decl, parse_var_decl(context), poisoned_expr); + ASSIGN_DECL_OR_RET(Decl *decl, parse_var_decl(c), poisoned_expr); if (!allow_decl) { - SEMA_TOKEN_ERROR(context->tok, "This looks like a declaration, which isn't allowed here."); + SEMA_ERROR_HERE("This looks like a declaration, which isn't allowed here."); return poisoned_expr; } expr = expr_new(EXPR_DECL, decl->span); @@ -416,10 +420,10 @@ Expr *parse_ct_expression_list(ParseContext *context, bool allow_decl) } else { - ASSIGN_EXPR_ELSE(expr, parse_expr(context), poisoned_expr); + ASSIGN_EXPR_OR_RET(expr, parse_expr(c), poisoned_expr); } vec_add(expr_list->expression_list, expr); - if (!try_consume(context, TOKEN_COMMA)) break; + if (!try_consume(c, TOKEN_COMMA)) break; } return expr_list; } @@ -434,61 +438,74 @@ static Expr *parse_type_identifier(ParseContext *context, Expr *left) return parse_type_expression_with_path(context, NULL); } -static Expr *parse_typeof_expr(ParseContext *context, Expr *left) +static Expr *parse_typeof_expr(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); - Expr *expr = EXPR_NEW_TOKEN(EXPR_TYPEINFO, context->tok); - ASSIGN_TYPE_ELSE(TypeInfo *type, parse_type(context), poisoned_expr); + Expr *expr = EXPR_NEW_TOKEN(EXPR_TYPEINFO); + ASSIGN_TYPE_OR_RET(TypeInfo *type, parse_type(c), poisoned_expr); expr->span = type->span; expr->type_expr = type; return expr; } -static Expr *parse_ct_stringify(ParseContext *context, Expr *left) +static Expr *parse_ct_stringify(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); - Expr *expr = EXPR_NEW_TOKEN(EXPR_STRINGIFY, context->tok); - advance(context); - CONSUME_OR(TOKEN_LPAREN, poisoned_expr); - ASSIGN_EXPR_ELSE(expr->inner_expr, parse_expr(context), poisoned_expr); - CONSUME_OR(TOKEN_RPAREN, poisoned_expr); + SourceSpan start_span = c->span; + advance(c); + CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); + const char *start = c->lexer.current; + ASSIGN_EXPR_OR_RET(Expr *inner, parse_expr(c), poisoned_expr); + const char *end = c->lexer.current; + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); + if (inner->expr_kind == EXPR_HASH_IDENT) + { + Expr *expr = expr_new(EXPR_STRINGIFY, start_span); + expr->inner_expr = inner; + RANGE_EXTEND_PREV(expr); + return expr; + } + size_t len = end - start; + const char *content = copy_string(start, len); + Expr *expr = expr_new(EXPR_CONST, start_span); + expr->const_expr.const_kind = CONST_STRING; + expr->const_expr.string.chars = content; + expr->const_expr.string.len = len; return expr; } -static Expr *parse_unary_expr(ParseContext *context, Expr *left) +static Expr *parse_unary_expr(ParseContext *c, Expr *left) { assert(!left && "Did not expect a left hand side!"); - TokenType operator_type = context->tok.type; + Expr *unary = EXPR_NEW_TOKEN(EXPR_UNARY); + unary->unary_expr.operator = unaryop_from_token(c->tok); + advance(c); + Expr *right_side = parse_precedence(c, PREC_UNARY); - Expr *unary = EXPR_NEW_TOKEN(EXPR_UNARY, context->tok); - unary->unary_expr.operator = unaryop_from_token(operator_type); - advance(context); - Expr *right_side = parse_precedence(context, PREC_UNARY); - - CHECK_EXPR(right_side); + CHECK_EXPR_OR_RET(right_side); unary->unary_expr.expr = right_side; - unary->span.end_loc = right_side->span.end_loc; + RANGE_EXTEND_PREV(unary); return unary; } -static Expr *parse_post_unary(ParseContext *context, Expr *left) +static Expr *parse_post_unary(ParseContext *c, Expr *left) { assert(expr_ok(left)); - Expr *unary = EXPR_NEW_TOKEN(EXPR_POST_UNARY, context->tok); + Expr *unary = EXPR_NEW_EXPR(EXPR_POST_UNARY, left); unary->unary_expr.expr = left; - unary->unary_expr.operator = unaryop_from_token(context->tok.type); - unary->span.loc = left->span.loc; - advance(context); + unary->unary_expr.operator = unaryop_from_token(c->tok); + advance(c); + RANGE_EXTEND_PREV(unary); return unary; } -static Expr *parse_ternary_expr(ParseContext *context, Expr *left_side) +static Expr *parse_ternary_expr(ParseContext *c, Expr *left_side) { assert(expr_ok(left_side)); @@ -497,19 +514,19 @@ static Expr *parse_ternary_expr(ParseContext *context, Expr *left_side) // Check for elvis - if (try_consume(context, TOKEN_ELVIS)) + if (try_consume(c, TOKEN_ELVIS)) { expr_ternary->ternary_expr.then_expr = NULL; } else { - advance_and_verify(context, TOKEN_QUESTION); - ASSIGN_EXPR_ELSE(Expr *true_expr, parse_precedence(context, PREC_TERNARY + 1), poisoned_expr); + advance_and_verify(c, TOKEN_QUESTION); + ASSIGN_EXPR_OR_RET(Expr * true_expr, parse_precedence(c, PREC_TERNARY + 1), poisoned_expr); expr_ternary->ternary_expr.then_expr = true_expr; - CONSUME_OR(TOKEN_COLON, poisoned_expr); + CONSUME_OR_RET(TOKEN_COLON, poisoned_expr); } - ASSIGN_EXPR_ELSE(Expr *false_expr, parse_precedence(context, PREC_TERNARY + 1), poisoned_expr); + ASSIGN_EXPR_OR_RET(Expr * false_expr, parse_precedence(c, PREC_TERNARY + 1), poisoned_expr); expr_ternary->ternary_expr.else_expr = false_expr; RANGE_EXTEND_PREV(expr_ternary); return expr_ternary; @@ -520,25 +537,25 @@ static Expr *parse_ternary_expr(ParseContext *context, Expr *left_side) * : '(' expression ')' ('(' expression ')')? * ; */ -static Expr *parse_grouping_expr(ParseContext *context, Expr *left) +static Expr *parse_grouping_expr(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); - Expr *expr = EXPR_NEW_TOKEN(EXPR_GROUP, context->tok); - advance_and_verify(context, TOKEN_LPAREN); - ASSIGN_EXPR_ELSE(expr->inner_expr, parse_expr(context), poisoned_expr); - CONSUME_OR(TOKEN_RPAREN, poisoned_expr); + Expr *expr = EXPR_NEW_TOKEN(EXPR_GROUP); + advance_and_verify(c, TOKEN_LPAREN); + ASSIGN_EXPR_OR_RET(expr->inner_expr, parse_expr(c), poisoned_expr); + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); // Look at what follows. if (expr->inner_expr->expr_kind == EXPR_TYPEINFO) { TypeInfo *info = expr->inner_expr->type_expr; - if (TOKEN_IS(TOKEN_LBRACE) && info->resolve_status != RESOLVE_DONE) + if (tok_is(c, TOKEN_LBRACE) && info->resolve_status != RESOLVE_DONE) { - SEMA_TOKEN_ERROR(context->tok, "Unexpected start of a block '{' here. If you intended a compound literal, remove the () around the type."); + SEMA_ERROR_HERE("Unexpected start of a block '{' here. If you intended a compound literal, remove the () around the type."); return poisoned_expr; } - if (rules[context->tok.type].prefix) + if (rules[c->tok].prefix) { - ASSIGN_EXPR_ELSE(Expr *cast_expr, parse_precedence(context, PREC_CALL), poisoned_expr); + ASSIGN_EXPR_OR_RET(Expr * cast_expr, parse_precedence(c, PREC_CALL), poisoned_expr); expr->expr_kind = EXPR_CAST; expr->cast_expr.type_info = info; expr->cast_expr.expr = cast_expr; @@ -555,20 +572,20 @@ static Expr *parse_grouping_expr(ParseContext *context, Expr *left) * | void * ; * - * @param context + * @param c * @return the parsed expression */ -Expr *parse_initializer(ParseContext *context) +Expr *parse_initializer(ParseContext *c) { - if (TOKEN_IS(TOKEN_VOID)) + if (tok_is(c, TOKEN_VOID)) { - Expr *expr = EXPR_NEW_TOKEN(EXPR_UNDEF, context->tok); + Expr *expr = EXPR_NEW_TOKEN(EXPR_UNDEF); expr->type = type_void; expr->resolve_status = RESOLVE_DONE; - advance(context); + advance(c); return expr; } - return parse_expr(context); + return parse_expr(c); } /** @@ -585,15 +602,15 @@ Expr *parse_initializer(ParseContext *context) * @param elements * @return */ -Expr *parse_initializer_list(ParseContext *context, Expr *left) +Expr *parse_initializer_list(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); - Expr *initializer_list = EXPR_NEW_TOKEN(EXPR_INITIALIZER_LIST, context->tok); - advance_and_verify(context, TOKEN_LBRACE); - if (!try_consume(context, TOKEN_RBRACE)) + Expr *initializer_list = EXPR_NEW_TOKEN(EXPR_INITIALIZER_LIST); + advance_and_verify(c, TOKEN_LBRACE); + if (!try_consume(c, TOKEN_RBRACE)) { Expr **exprs = NULL; - if (!parse_arg_list(context, &exprs, TOKEN_RBRACE, NULL)) return poisoned_expr; + if (!parse_arg_list(c, &exprs, TOKEN_RBRACE, NULL)) return poisoned_expr; int designated = -1; VECEACH(exprs, i) { @@ -615,7 +632,7 @@ Expr *parse_initializer_list(ParseContext *context, Expr *left) } designated = 0; } - CONSUME_OR(TOKEN_RBRACE, poisoned_expr); + CONSUME_OR_RET(TOKEN_RBRACE, poisoned_expr); RANGE_EXTEND_PREV(initializer_list); if (designated == 1) { @@ -631,10 +648,10 @@ Expr *parse_initializer_list(ParseContext *context, Expr *left) return initializer_list; } -static Expr *parse_failable(ParseContext *context, Expr *left_side) +static Expr *parse_failable(ParseContext *c, Expr *left_side) { Expr *failable = expr_new(EXPR_FAILABLE, left_side->span); - advance_and_verify(context, TOKEN_BANG); + advance_and_verify(c, TOKEN_BANG); failable->inner_expr = left_side; RANGE_EXTEND_PREV(failable); return failable; @@ -642,59 +659,64 @@ static Expr *parse_failable(ParseContext *context, Expr *left_side) -static Expr *parse_binary(ParseContext *context, Expr *left_side) +static Expr *parse_binary(ParseContext *c, Expr *left_side) { assert(left_side && expr_ok(left_side)); // Remember the operator. - TokenType operator_type = context->tok.type; + TokenType operator_type = c->tok; - advance(context); + advance(c); Expr *right_side; // Assignment operators have precedence right -> left. if (rules[operator_type].precedence == PREC_ASSIGNMENT) { - ASSIGN_EXPR_ELSE(right_side, parse_precedence(context, PREC_ASSIGNMENT), poisoned_expr); + ASSIGN_EXPR_OR_RET(right_side, parse_precedence(c, PREC_ASSIGNMENT), poisoned_expr); } else { - ASSIGN_EXPR_ELSE(right_side, parse_precedence(context, rules[operator_type].precedence + 1), poisoned_expr); + ASSIGN_EXPR_OR_RET(right_side, parse_precedence(c, rules[operator_type].precedence + 1), poisoned_expr); } Expr *expr = EXPR_NEW_EXPR(EXPR_BINARY, left_side); expr->binary_expr.operator = binaryop_from_token(operator_type); expr->binary_expr.left = left_side; expr->binary_expr.right = right_side; - - expr->span.end_loc = right_side->span.end_loc; + + RANGE_EXTEND_PREV(expr); return expr; } -static Expr *parse_call_expr(ParseContext *context, Expr *left) +static Expr *parse_call_expr(ParseContext *c, Expr *left) { assert(left && expr_ok(left)); Expr **params = NULL; - advance_and_verify(context, TOKEN_LPAREN); + advance_and_verify(c, TOKEN_LPAREN); bool unsplat = false; Decl **body_args = NULL; - if (!TOKEN_IS(TOKEN_RPAREN)) + if (!tok_is(c, TOKEN_RPAREN)) { // Pick a modest guess. params = VECNEW(Expr*, 4); - if (!parse_arg_list(context, ¶ms, TOKEN_RPAREN, &unsplat)) return poisoned_expr; + if (!parse_arg_list(c, ¶ms, TOKEN_RPAREN, &unsplat)) return poisoned_expr; } - if (try_consume(context, TOKEN_EOS) && parse_next_is_type(context)) + if (try_consume(c, TOKEN_EOS) && !tok_is(c, TOKEN_RPAREN)) { - if (!parse_parameters(context, VISIBLE_LOCAL, &body_args)) return poisoned_expr; + if (!parse_next_may_be_type_or_ident(c)) + { + SEMA_ERROR_LAST("Expected an ending ')'. Did you forget a ')' before this ';'?"); + return poisoned_expr; + } + if (!parse_parameters(c, VISIBLE_LOCAL, &body_args)) return poisoned_expr; } - if (!TOKEN_IS(TOKEN_RPAREN)) + if (!tok_is(c, TOKEN_RPAREN)) { - SEMA_TOKID_ERROR(context->prev_tok, "Expected the ending ')' here."); + SEMA_ERROR_LAST("Expected the ending ')' here."); return poisoned_expr; } - advance(context); + advance(c); Expr *call = EXPR_NEW_EXPR(EXPR_CALL, left); call->call_expr.function = left; @@ -702,25 +724,25 @@ static Expr *parse_call_expr(ParseContext *context, Expr *left) call->call_expr.unsplat_last = unsplat; call->call_expr.body_arguments = body_args; RANGE_EXTEND_PREV(call); - if (body_args && !TOKEN_IS(TOKEN_LBRACE)) + if (body_args && !tok_is(c, TOKEN_LBRACE)) { - SEMA_TOKEN_ERROR(context->tok, "Expected a macro body here."); + SEMA_ERROR_HERE("Expected a macro body here."); return poisoned_expr; } - if (TOKEN_IS(TOKEN_LBRACE)) + if (tok_is(c, TOKEN_LBRACE)) { - ASSIGN_AST_ELSE(call->call_expr.body, parse_compound_stmt(context), poisoned_expr); + ASSIGN_AST_OR_RET(call->call_expr.body, parse_compound_stmt(c), poisoned_expr); } - if (!parse_attributes(context, &call->call_expr.attributes)) return false; + if (!parse_attributes(c, &call->call_expr.attributes)) return false; return call; } -static Expr *parse_subscript_expr(ParseContext *context, Expr *left) +static Expr *parse_subscript_expr(ParseContext *c, Expr *left) { assert(left && expr_ok(left)); - advance_and_verify(context, TOKEN_LBRACKET); + advance_and_verify(c, TOKEN_LBRACKET); Expr *subs_expr = EXPR_NEW_EXPR(EXPR_SUBSCRIPT, left); Expr *index = NULL; @@ -730,29 +752,29 @@ static Expr *parse_subscript_expr(ParseContext *context, Expr *left) Expr *end = NULL; // Not range with missing entry - if (!TOKEN_IS(TOKEN_DOTDOT)) + if (!tok_is(c, TOKEN_DOTDOT)) { // Might be ^ prefix - from_back = try_consume(context, TOKEN_BIT_XOR); - ASSIGN_EXPR_ELSE(index, parse_expr(context), poisoned_expr); + from_back = try_consume(c, TOKEN_BIT_XOR); + ASSIGN_EXPR_OR_RET(index, parse_expr(c), poisoned_expr); } else { - index = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); + index = EXPR_NEW_TOKEN(EXPR_CONST); index->type = type_uint; index->resolve_status = RESOLVE_DONE; expr_const_set_int(&index->const_expr, 0, type_uint->type_kind); } - if (try_consume(context, TOKEN_DOTDOT)) + if (try_consume(c, TOKEN_DOTDOT)) { is_range = true; - if (!TOKEN_IS(TOKEN_RBRACKET)) + if (!tok_is(c, TOKEN_RBRACKET)) { - end_from_back = try_consume(context, TOKEN_BIT_XOR); - ASSIGN_EXPR_ELSE(end, parse_expr(context), poisoned_expr); + end_from_back = try_consume(c, TOKEN_BIT_XOR); + ASSIGN_EXPR_OR_RET(end, parse_expr(c), poisoned_expr); } } - CONSUME_OR(TOKEN_RBRACKET, poisoned_expr); + CONSUME_OR_RET(TOKEN_RBRACKET, poisoned_expr); RANGE_EXTEND_PREV(subs_expr); if (is_range) @@ -774,62 +796,64 @@ static Expr *parse_subscript_expr(ParseContext *context, Expr *left) } -static Expr *parse_access_expr(ParseContext *context, Expr *left) +static Expr *parse_access_expr(ParseContext *c, Expr *left) { assert(left && expr_ok(left)); - advance_and_verify(context, TOKEN_DOT); + advance_and_verify(c, TOKEN_DOT); Expr *access_expr = EXPR_NEW_EXPR(EXPR_ACCESS, left); access_expr->access_expr.parent = left; - ASSIGN_EXPR_ELSE(access_expr->access_expr.child, parse_precedence(context, PREC_CALL + 1), poisoned_expr); + ASSIGN_EXPR_OR_RET(access_expr->access_expr.child, parse_precedence(c, PREC_CALL + 1), poisoned_expr); RANGE_EXTEND_PREV(access_expr); return access_expr; } -static Expr *parse_ct_ident(ParseContext *context, Expr *left) +static Expr *parse_ct_ident(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); - if (try_consume(context, TOKEN_CT_CONST_IDENT)) + if (try_consume(c, TOKEN_CT_CONST_IDENT)) { - SEMA_TOKID_ERROR(context->prev_tok, "Compile time identifiers may not be constants."); + SEMA_ERROR_LAST("Compile time identifiers may not be constants."); return poisoned_expr; } - Expr *expr = EXPR_NEW_TOKEN(EXPR_CT_IDENT, context->tok); - expr->ct_ident_expr.identifier = context->tok.id; - advance(context); + Expr *expr = EXPR_NEW_TOKEN(EXPR_CT_IDENT); + expr->ct_ident_expr.identifier = symstr(c); + advance(c); return expr; } -static Expr *parse_hash_ident(ParseContext *context, Expr *left) +static Expr *parse_hash_ident(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); - Expr *expr = EXPR_NEW_TOKEN(EXPR_HASH_IDENT, context->tok); - expr->ct_ident_expr.identifier = context->tok.id; - advance(context); + Expr *expr = EXPR_NEW_TOKEN(EXPR_HASH_IDENT); + expr->ct_ident_expr.identifier = symstr(c); + + advance(c); return expr; } -static Expr *parse_ct_call(ParseContext *context, Expr *left) +static Expr *parse_ct_call(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); - Expr *expr = EXPR_NEW_TOKEN(EXPR_CT_CALL, context->tok); - expr->ct_call_expr.token_type = context->tok.type; - advance(context); - CONSUME_OR(TOKEN_LPAREN, poisoned_expr); - ASSIGN_EXPR_ELSE(Expr *internal, parse_precedence(context, PREC_FIRST + 1), poisoned_expr); + Expr *expr = EXPR_NEW_TOKEN(EXPR_CT_CALL); + expr->ct_call_expr.token_type = c->tok; + advance(c); + CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); + ASSIGN_EXPR_OR_RET(Expr * internal, parse_precedence(c, PREC_FIRST + 1), poisoned_expr); ExprFlatElement *flat_path = NULL; - if (context->tok.type == TOKEN_DOT || context->tok.type == TOKEN_LBRACKET) + TokenType tok = c->tok; + if (tok == TOKEN_DOT || tok == TOKEN_LBRACKET) { while (1) { ExprFlatElement flat_element; - if (try_consume(context, TOKEN_LBRACKET)) + if (try_consume(c, TOKEN_LBRACKET)) { - ASSIGN_EXPR_ELSE(Expr *int_expr, parse_expr(context), poisoned_expr); + ASSIGN_EXPR_OR_RET(Expr * int_expr, parse_expr(c), poisoned_expr); if (int_expr->expr_kind != EXPR_CONST || int_expr->const_expr.const_kind != CONST_INTEGER) { - SEMA_TOKEN_ERROR(context->tok, "Expected an integer index."); + SEMA_ERROR_HERE("Expected an integer index."); return poisoned_expr; } Int value = int_expr->const_expr.ixx; @@ -843,76 +867,161 @@ static Expr *parse_ct_call(ParseContext *context, Expr *left) SEMA_ERROR(int_expr, "Array index must be zero or greater."); return poisoned_expr; } - TRY_CONSUME_OR(TOKEN_RBRACKET, "Expected a ']' after the number.", poisoned_expr); + TRY_CONSUME_OR_RET(TOKEN_RBRACKET, "Expected a ']' after the number.", poisoned_expr); flat_element.array = true; flat_element.index = (MemberIndex) value.i.low; } - else if (try_consume(context, TOKEN_DOT)) + else if (try_consume(c, TOKEN_DOT)) { - TRY_CONSUME_OR(TOKEN_IDENT, "Expected an identifier here.", poisoned_expr); + flat_element.ident = symstr(c); + TRY_CONSUME_OR_RET(TOKEN_IDENT, "Expected an identifier here.", poisoned_expr); flat_element.array = false; - flat_element.ident = TOKSTR(context->prev_tok); } else { - SEMA_TOKEN_ERROR(context->tok, "Expected '.' or '[' here."); + SEMA_ERROR_HERE("Expected '.' or '[' here."); return poisoned_expr; } vec_add(flat_path, flat_element); - if (TOKEN_IS(TOKEN_RPAREN)) break; + if (tok_is(c, TOKEN_RPAREN)) break; } RANGE_EXTEND_PREV(internal); } expr->ct_call_expr.main_var = internal; expr->ct_call_expr.flat_path = flat_path; - CONSUME_OR(TOKEN_RPAREN, poisoned_expr); + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); RANGE_EXTEND_PREV(expr); return expr; } -static Expr *parse_identifier(ParseContext *context, Expr *left) +static Expr *parse_identifier(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); - Expr *expr = EXPR_NEW_TOKEN(EXPR_IDENTIFIER , context->tok); - expr->identifier_expr.identifier = context->tok.id; - expr->identifier_expr.is_const = context->tok.type == TOKEN_CONST_IDENT; - advance(context); + Expr *expr = EXPR_NEW_TOKEN(EXPR_IDENTIFIER); + expr->identifier_expr.ident = symstr(c); + expr->identifier_expr.is_const = tok_is(c, TOKEN_CONST_IDENT); + advance(c); return expr; } +static Expr *parse_type_or_expression_with_path(ParseContext *c, Path *path, TypeInfo **type_ref) +{ + TypeInfo *type; + if (path) + { + type = type_info_new(TYPE_INFO_IDENTIFIER, path->span); + type->unresolved.path = path; + type->unresolved.span = c->span; + type->unresolved.name = symstr(c); + advance_and_verify(c, TOKEN_TYPE_IDENT); + RANGE_EXTEND_PREV(type); + ASSIGN_TYPE_OR_RET(type, parse_type_with_base(c, type), poisoned_expr); + type->failable = try_consume(c, TOKEN_BANG); + } + else + { + ASSIGN_TYPE_OR_RET(type, parse_failable_type(c), poisoned_expr); + } + if (tok_is(c, TOKEN_LBRACE)) + { + return parse_type_compound_literal_expr_after_type(c, type); + } + if (tok_is(c, TOKEN_DOT)) + { + Expr *expr = expr_new(EXPR_TYPEINFO, type->span); + expr->type_expr = type; + return parse_access_expr(c, expr); + } + *type_ref = type; + return NULL; +} -static Expr *parse_identifier_starting_expression(ParseContext *context, Expr *left) + + +static Expr *parse_expr_or_type_prec(ParseContext *c, TypeInfo **type_ref, Precedence prec) +{ + switch (c->tok) + { + case TYPELIKE_TOKENS: + return parse_type_or_expression_with_path(c, NULL, type_ref); + case TOKEN_IDENT: + { + bool had_error; + Path *path = parse_path_prefix(c, &had_error); + if (had_error) return poisoned_expr; + if (!path) return parse_precedence(c, prec); + bool is_const = false; + switch (c->tok) + { + case TOKEN_TYPE_IDENT: + return parse_type_or_expression_with_path(c, path, type_ref); + case TOKEN_CONST_IDENT: + is_const = true; + FALLTHROUGH; + case TOKEN_IDENT: + { + Expr *expr = EXPR_NEW_TOKEN(EXPR_IDENTIFIER); + expr->identifier_expr.ident = symstr(c); + expr->identifier_expr.is_const = is_const; + expr->identifier_expr.path = path; + advance(c); + return parse_precedence_with_left_side(c, expr, prec); + } + case TOKEN_HASH_IDENT: + case TOKEN_HASH_CONST_IDENT: + case TOKEN_HASH_TYPE_IDENT: + case TOKEN_CT_CONST_IDENT: + case TOKEN_CT_IDENT: + case TOKEN_CT_TYPE_IDENT: + SEMA_ERROR_HERE("'%s' cannot be used with paths.", symstr(c)); + return poisoned_expr; + default: + SEMA_ERROR_HERE("An identifier was expected after the path."); + return poisoned_expr; + } + } + default: + return parse_precedence(c, prec); + } +} + +Expr *parse_expr_or_type(ParseContext *c, TypeInfo **type_ref) +{ + return parse_expr_or_type_prec(c, type_ref, PREC_ASSIGNMENT); +} + +static Expr *parse_identifier_starting_expression(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); bool had_error; - Path *path = parse_path_prefix(context, &had_error); + Path *path = parse_path_prefix(c, &had_error); if (had_error) return poisoned_expr; - switch (context->tok.type) + switch (c->tok) { case TOKEN_IDENT: case TOKEN_CONST_IDENT: { - Expr *expr = parse_identifier(context, NULL); + Expr *expr = parse_identifier(c, NULL); expr->identifier_expr.path = path; return expr; } case TOKEN_TYPE_IDENT: - return parse_type_expression_with_path(context, path); + return parse_type_expression_with_path(c, path); default: - SEMA_TOKEN_ERROR(context->tok, "Expected a type, function or constant."); + SEMA_ERROR_HERE("Expected a type, function or constant."); return poisoned_expr; } } -static Expr *parse_try_expr(ParseContext *context, Expr *left) +static Expr *parse_try_expr(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); - bool is_try = TOKEN_IS(TOKEN_TRY); - advance(context); - Expr *try_expr = expr_new(is_try ? EXPR_TRY : EXPR_CATCH, source_span_from_token_id(context->prev_tok)); - if (!try_consume(context, TOKEN_LPAREN)) + bool is_try = tok_is(c, TOKEN_TRY); + Expr *try_expr = EXPR_NEW_TOKEN(is_try ? EXPR_TRY : EXPR_CATCH); + advance(c); + if (!try_consume(c, TOKEN_LPAREN)) { if (is_try) { @@ -925,46 +1034,46 @@ static Expr *parse_try_expr(ParseContext *context, Expr *left) return poisoned_expr; } } - ASSIGN_EXPR_ELSE(try_expr->inner_expr, parse_expr(context), poisoned_expr); - CONSUME_OR(TOKEN_RPAREN, poisoned_expr); + ASSIGN_EXPR_OR_RET(try_expr->inner_expr, parse_expr(c), poisoned_expr); + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); RANGE_EXTEND_PREV(try_expr); return try_expr; } -static Expr *parse_rethrow_expr(ParseContext *context, Expr *left) +static Expr *parse_rethrow_expr(ParseContext *c, Expr *left) { Expr *rethrow_expr = EXPR_NEW_EXPR(EXPR_RETHROW, left); - advance(context); + advance(c); rethrow_expr->rethrow_expr.inner = left; RANGE_EXTEND_PREV(rethrow_expr); return rethrow_expr; } -static Expr *parse_force_unwrap_expr(ParseContext *context, Expr *left) +static Expr *parse_force_unwrap_expr(ParseContext *c, Expr *left) { Expr *force_unwrap_expr = EXPR_NEW_EXPR(EXPR_FORCE_UNWRAP, left); - advance(context); + advance(c); force_unwrap_expr->inner_expr = left; RANGE_EXTEND_PREV(force_unwrap_expr); return force_unwrap_expr; } -static Expr *parse_or_error_expr(ParseContext *context, Expr *left) +static Expr *parse_or_error_expr(ParseContext *c, Expr *left) { - Expr *else_expr = EXPR_NEW_TOKEN(EXPR_OR_ERROR, context->tok); - advance_and_verify(context, TOKEN_QUESTQUEST); + Expr *else_expr = EXPR_NEW_TOKEN(EXPR_OR_ERROR); + advance_and_verify(c, TOKEN_QUESTQUEST); else_expr->or_error_expr.expr = left; - switch (context->tok.type) + switch (c->tok) { case TOKEN_RETURN: case TOKEN_BREAK: case TOKEN_CONTINUE: case TOKEN_NEXTCASE: { - ASSIGN_AST_ELSE(Ast *ast, parse_jump_stmt_no_eos(context), poisoned_expr); + ASSIGN_AST_OR_RET(Ast *ast, parse_jump_stmt_no_eos(c), poisoned_expr); else_expr->or_error_expr.is_jump = true; else_expr->or_error_expr.or_error_stmt = ast; - if (!TOKEN_IS(TOKEN_EOS)) + if (!tok_is(c, TOKEN_EOS)) { SEMA_ERROR(ast, "An else jump statement must end with a ';'"); return poisoned_expr; @@ -973,31 +1082,31 @@ static Expr *parse_or_error_expr(ParseContext *context, Expr *left) } default: { - ASSIGN_EXPR_ELSE(else_expr->or_error_expr.or_error_expr, parse_precedence(context, PREC_ASSIGNMENT), poisoned_expr); + ASSIGN_EXPR_OR_RET(else_expr->or_error_expr.or_error_expr, parse_precedence(c, PREC_ASSIGNMENT), poisoned_expr); break; } } return else_expr; } -static Expr *parse_builtin(ParseContext *context, Expr *left) +static Expr *parse_builtin(ParseContext *c, Expr *left) { assert(!left && "Had left hand side"); - Expr *expr = EXPR_NEW_TOKEN(EXPR_BUILTIN, context->tok); - advance_and_verify(context, TOKEN_BUILTIN); - if (!token_is_some_ident(context->tok.type)) + Expr *expr = EXPR_NEW_TOKEN(EXPR_BUILTIN); + advance_and_verify(c, TOKEN_BUILTIN); + if (!token_is_some_ident(c->tok)) { - SEMA_TOKEN_ERROR(context->tok, "Expected a name here."); + SEMA_ERROR_HERE("Expected a name here."); return poisoned_expr; } - expr->builtin_expr.identifier = context->tok; - if (try_consume(context, TOKEN_CONST_IDENT)) + expr->builtin_expr.ident = symstr(c); + if (try_consume(c, TOKEN_CONST_IDENT)) { expr->expr_kind = EXPR_COMPILER_CONST; } else { - CONSUME_OR(TOKEN_IDENT, poisoned_expr); + CONSUME_OR_RET(TOKEN_IDENT, poisoned_expr); } RANGE_EXTEND_PREV(expr); return expr; @@ -1017,12 +1126,12 @@ static int read_num_type(const char *string, size_t loc, size_t len) return i; } -static Expr *parse_integer(ParseContext *context, Expr *left) +static Expr *parse_integer(ParseContext *c, Expr *left) { assert(!left && "Had left hand side"); - Expr *expr_int = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); - size_t len = TOKLEN(context->tok); - const char *string = TOKSTR(context->tok); + Expr *expr_int = EXPR_NEW_TOKEN(EXPR_CONST); + size_t len = c->data.lex_len; + const char *string = symstr(c); Int128 i = { 0, 0 }; bool is_unsigned = false; int type_bits = 0; @@ -1038,22 +1147,22 @@ static Expr *parse_integer(ParseContext *context, Expr *left) max = UINT64_MAX >> 4; for (size_t loc = 2; loc < len; loc++) { - char c = string[loc]; - if (c == 'u' || c == 'U') + char ch = string[loc]; + if (ch == 'u' || ch == 'U') { type_bits = read_num_type(string, loc, len); break; } - if (c == 'i' || c == 'I') + if (ch == 'i' || ch == 'I') { is_unsigned = false; type_bits = read_num_type(string, loc, len); break; } - if (c == '_') continue; + if (ch == '_') continue; if (i.high > max) wrapped = true; i = i128_shl64(i, 4); - i = i128_add64(i, (uint64_t) hex_nibble(c)); + i = i128_add64(i, (uint64_t) hex_nibble(ch)); hex_characters++; } break; @@ -1062,22 +1171,22 @@ static Expr *parse_integer(ParseContext *context, Expr *left) max = UINT64_MAX >> 3; for (size_t loc = 2; loc < len; loc++) { - char c = string[loc]; - if (c == 'i' || c == 'I') + char ch = string[loc]; + if (ch == 'i' || ch == 'I') { is_unsigned = false; type_bits = read_num_type(string, loc, len); break; } - if (c == 'u' || c == 'U') + if (ch == 'u' || ch == 'U') { type_bits = read_num_type(string, loc, len); break; } - if (c == '_') continue; + if (ch == '_') continue; if (i.high > max) wrapped = true; i = i128_shl64(i, 3); - i = i128_add64(i, (uint64_t)(c - '0')); + i = i128_add64(i, (uint64_t)(ch - '0')); oct_characters++; } break; @@ -1085,19 +1194,19 @@ static Expr *parse_integer(ParseContext *context, Expr *left) max = UINT64_MAX >> 1; for (size_t loc = 2; loc < len; loc++) { - char c = string[loc]; - if (c == '_') continue; + char ch = string[loc]; + if (ch == '_') continue; binary_characters++; if (i.high > max) wrapped = true; i = i128_shl64(i, 1); - i = i128_add64(i, (uint64_t)(c - '0')); + i = i128_add64(i, (uint64_t)(ch - '0')); } break; default: for (size_t loc = 0; loc < len; loc++) { - char c = string[loc]; - switch (c) + char ch = string[loc]; + switch (ch) { case 'i': case 'I': @@ -1115,7 +1224,7 @@ static Expr *parse_integer(ParseContext *context, Expr *left) { uint64_t old_top = i.high; i = i128_mult64(i, 10); - i = i128_add64(i, (uint64_t)(c - '0')); + i = i128_add64(i, (uint64_t)(ch - '0')); if (!wrapped && old_top > i.high) wrapped = true; break; } @@ -1128,7 +1237,7 @@ static Expr *parse_integer(ParseContext *context, Expr *left) } if (wrapped) { - SEMA_TOKEN_ERROR(context->tok, "Integer size exceeded 128 bits, max 128 bits are supported."); + SEMA_ERROR_HERE("Integer size exceeded 128 bits, max 128 bits are supported."); return poisoned_expr; } expr_int->const_expr.const_kind = CONST_INTEGER; @@ -1139,7 +1248,7 @@ static Expr *parse_integer(ParseContext *context, Expr *left) { if (type_bits < 0 || !is_power_of_two((uint64_t)type_bits) || type_bits > 128) { - SEMA_TOKEN_ERROR(context->tok, "Integer type suffix should be i8, i16, i32, i64 or i128."); + SEMA_ERROR_HERE("Integer type suffix should be i8, i16, i32, i64 or i128."); return poisoned_expr; } } @@ -1150,7 +1259,7 @@ static Expr *parse_integer(ParseContext *context, Expr *left) type_bits = 4 * hex_characters; if (type_bits > 128) { - SEMA_TOKEN_ERROR(context->tok, "%d hex digits indicates a bit width over 128, which is not supported.", hex_characters); + SEMA_ERROR_HERE("%d hex digits indicates a bit width over 128, which is not supported.", hex_characters); return poisoned_expr; } } @@ -1159,7 +1268,7 @@ static Expr *parse_integer(ParseContext *context, Expr *left) type_bits = 3 * oct_characters; if (type_bits > 128) { - SEMA_TOKEN_ERROR(context->tok, "%d octal digits indicates a bit width over 128, which is not supported.", oct_characters); + SEMA_ERROR_HERE("%d octal digits indicates a bit width over 128, which is not supported.", oct_characters); return poisoned_expr; } } @@ -1168,7 +1277,7 @@ static Expr *parse_integer(ParseContext *context, Expr *left) type_bits = binary_characters; if (type_bits > 128) { - SEMA_TOKEN_ERROR(context->tok, "%d binary digits indicates a bit width over 128, which is not supported.", binary_characters); + SEMA_ERROR_HERE("%d binary digits indicates a bit width over 128, which is not supported.", binary_characters); return poisoned_expr; } } @@ -1193,18 +1302,18 @@ static Expr *parse_integer(ParseContext *context, Expr *left) if (binary_characters) radix = 2; if (type_bits) { - SEMA_TOKEN_ERROR(context->tok, "'%s' does not fit in a '%c%d' literal.", - i128_to_string(i, radix, true), is_unsigned ? 'u' : 'i', type_bits); + SEMA_ERROR_HERE("'%s' does not fit in a '%c%d' literal.", + i128_to_string(i, radix, true), is_unsigned ? 'u' : 'i', type_bits); } else { - SEMA_TOKEN_ERROR(context->tok, "'%s' does not fit in an %s literal.", - i128_to_string(i, radix, true), is_unsigned ? "unsigned int" : "int"); + SEMA_ERROR_HERE("'%s' does not fit in an %s literal.", + i128_to_string(i, radix, true), is_unsigned ? "unsigned int" : "int"); } return poisoned_expr; } expr_int->type = type; - advance(context); + advance(c); return expr_int; } @@ -1214,9 +1323,9 @@ static Expr *parse_integer(ParseContext *context, Expr *left) * @param data start pointer * @param end end pointer */ -static void parse_hex(char **result_pointer, const char *data, const char *end) +static void parse_hex(char *result_pointer, const char *data, const char *end) { - char *data_current = *result_pointer; + char *data_current = result_pointer; assert(data_current); while (data < end) { @@ -1227,8 +1336,7 @@ static void parse_hex(char **result_pointer, const char *data, const char *end) *(data_current++) = (char)((val << 4) | val2); } - DONE: - *result_pointer = data_current; + DONE:; } /** @@ -1250,9 +1358,9 @@ static char base64_to_sextet(char c) * @param data start pointer * @param end end pointer */ -static void parse_base64(char **result_pointer, char *result_pointer_end, const char *data, const char *end) +static void parse_base64(char *result_pointer, char *result_pointer_end, const char *data, const char *end) { - char *data_current = *result_pointer; + char *data_current = result_pointer; assert(data_current); while (data < end) { @@ -1269,62 +1377,62 @@ static void parse_base64(char **result_pointer, char *result_pointer_end, const if (data_current < result_pointer_end) *(data_current++) = (char)((triplet >> 8) & 0xFF); if (data_current < result_pointer_end) *(data_current++) = (char)(triplet & 0xFF); } - DONE: - *result_pointer = data_current; + DONE:; } -static Expr *parse_bytes_expr(ParseContext *context, Expr *left) +static Expr *parse_bytes_expr(ParseContext *c, Expr *left) { assert(!left && "Had left hand side"); - TokenId tok = context->tok.id; ArraySize len = 0; - while (TOKTYPE(tok) == TOKEN_BYTES) + char *data = NULL; + while (c->tok == TOKEN_BYTES) { - len += TOKDATA(tok)->len; - tok.index++; - } - char *data = len > 0 ? MALLOC(len) : NULL; - char *data_current = data; - - Expr *expr_bytes = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); - while (context->tok.type == TOKEN_BYTES) - { - TokenData *token_data = tokendata_from_token(context->tok); - SourceLocation *loc = TOKLOC(context->tok); - File *file = source_file_by_id(loc->file_id); - if (token_data->is_base64) + ArraySize next_len = c->data.bytes_len; + if (!next_len) { - const char *base64data = &file->contents[loc->start] + 4; - const char *end = base64data + loc->length - 1; - parse_base64(&data_current, data_current + token_data->len, base64data, end); + advance(c); + continue; + } + ArraySize new_len = len + next_len; + char *new_data = MALLOC(new_len); + if (data) + { + memmove(new_data, data, len); + } + data = new_data; + if (c->data.is_base64) + { + const char *base64data = c->data.lex_start + 4; + const char *end = base64data + c->data.lex_len - 1 - 4; + parse_base64(new_data + len, new_data + next_len, base64data, end); } else { - const char *hexdata = &file->contents[loc->start] + 2; - const char *end = hexdata + loc->length - 1; - parse_hex(&data_current, hexdata, end); + const char *hexdata = c->data.lex_start + 2; + const char *end = hexdata + c->data.lex_len - 1 - 2; + parse_hex(new_data + len, hexdata, end); } - advance(context); + len = new_len; + advance(c); } + Expr *expr_bytes = EXPR_NEW_TOKEN(EXPR_CONST); expr_bytes->const_expr.bytes.ptr = data; expr_bytes->const_expr.bytes.len = len; expr_bytes->const_expr.const_kind = CONST_BYTES; Type *type = type_get_array(type_char, len); expr_bytes->type = type; - assert(data + len == data_current); return expr_bytes; } -static Expr *parse_char_lit(ParseContext *context, Expr *left) +static Expr *parse_char_lit(ParseContext *c, Expr *left) { assert(!left && "Had left hand side"); - Expr *expr_int = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); + Expr *expr_int = EXPR_NEW_TOKEN(EXPR_CONST); expr_int->const_expr.is_character = true; - TokenData *data = tokendata_from_id(context->tok.id); - expr_int->const_expr.ixx.i = data->char_value; + expr_int->const_expr.ixx.i = c->data.char_value; expr_int->const_expr.narrowable = true; expr_int->const_expr.const_kind = CONST_INTEGER; - switch (data->width) + switch (c->data.width) { case 1: expr_int->type = type_char; @@ -1351,22 +1459,22 @@ static Expr *parse_char_lit(ParseContext *context, Expr *left) expr_int->const_expr.ixx.type = TYPE_U128; break; } - advance(context); + advance(c); return expr_int; } -static Expr *parse_double(ParseContext *context, Expr *left) +static Expr *parse_double(ParseContext *c, Expr *left) { assert(!left && "Had left hand side"); char *err; - Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); - const char *original = TOKSTR(context->tok); + Expr *number = EXPR_NEW_TOKEN(EXPR_CONST); + const char *original = symstr(c); bool is_hex = original[0] == '0' && original[1] == 'x'; Float f = is_hex ? float_from_hex(original, &err) : float_from_string(original, &err); if (f.type == TYPE_POISONED) { - SEMA_TOKEN_ERROR(context->tok, err); + SEMA_ERROR_HERE(err); return poisoned_expr; } number->const_expr.fxx = f; @@ -1389,7 +1497,7 @@ static Expr *parse_double(ParseContext *context, Expr *left) } number->const_expr.const_kind = CONST_FLOAT; number->const_expr.narrowable = true; - advance(context); + advance(c); return number; } @@ -1466,33 +1574,33 @@ static int append_esc_string_token(char *restrict dest, const char *restrict src return scanned; } -static Expr *parse_string_literal(ParseContext *context, Expr *left) +static Expr *parse_string_literal(ParseContext *c, Expr *left) { assert(!left && "Had left hand side"); - Expr *expr_string = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); + Expr *expr_string = EXPR_NEW_TOKEN(EXPR_CONST); - TokenData *data = TOKDATA(context->tok); - const char *str = data->string; - size_t len = data->strlen; - advance_and_verify(context, TOKEN_STRING); + const char *str = symstr(c); + size_t len = c->data.strlen; + advance_and_verify(c, TOKEN_STRING); // This is wasteful for adding many tokens together // and can be optimized. - while (TOKEN_IS(TOKEN_STRING)) + while (tok_is(c, TOKEN_STRING)) { - data = TOKDATA(context->tok); - char *buffer = malloc_string(len + data->strlen + 1); + size_t next_len = c->data.strlen; + if (!next_len) continue; + char *buffer = malloc_string(len + next_len + 1); memcpy(buffer, str, len); - memcpy(buffer + len, data->string, data->strlen); - len += data->strlen; + memcpy(buffer + len, symstr(c), next_len); + len += next_len; buffer[len] = '\0'; str = buffer; - advance_and_verify(context, TOKEN_STRING); + advance_and_verify(c, TOKEN_STRING); } if (len > UINT32_MAX) { - SEMA_TOKEN_ERROR(context->tok, "String exceeded max size."); + SEMA_ERROR_HERE("String exceeded max size."); return poisoned_expr; } assert(str); @@ -1503,32 +1611,32 @@ static Expr *parse_string_literal(ParseContext *context, Expr *left) return expr_string; } -static Expr *parse_bool(ParseContext *context, Expr *left) +static Expr *parse_bool(ParseContext *c, Expr *left) { assert(!left && "Had left hand side"); - Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); - number->const_expr = (ExprConst) { .b = TOKEN_IS(TOKEN_TRUE), .const_kind = CONST_BOOL }; + Expr *number = EXPR_NEW_TOKEN(EXPR_CONST); + number->const_expr = (ExprConst) { .b = tok_is(c, TOKEN_TRUE), .const_kind = CONST_BOOL }; number->type = type_bool; - advance(context); + advance(c); return number; } -static Expr *parse_null(ParseContext *context, Expr *left) +static Expr *parse_null(ParseContext *c, Expr *left) { assert(!left && "Had left hand side"); - Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); + Expr *number = EXPR_NEW_TOKEN(EXPR_CONST); number->const_expr.const_kind = CONST_POINTER; number->type = type_voidptr; - advance(context); + advance(c); return number; } -Expr *parse_type_compound_literal_expr_after_type(ParseContext *context, TypeInfo *type_info) +Expr *parse_type_compound_literal_expr_after_type(ParseContext *c, TypeInfo *type_info) { Expr *expr = expr_new(EXPR_COMPOUND_LITERAL, type_info->span); expr->expr_compound_literal.type_info = type_info; - EXPECT_OR(TOKEN_LBRACE, poisoned_expr); - ASSIGN_EXPR_ELSE(expr->expr_compound_literal.initializer, parse_initializer_list(context, NULL), poisoned_expr); + EXPECT_OR_RET(TOKEN_LBRACE, poisoned_expr); + ASSIGN_EXPR_OR_RET(expr->expr_compound_literal.initializer, parse_initializer_list(c, NULL), poisoned_expr); RANGE_EXTEND_PREV(expr); return expr; } @@ -1536,31 +1644,32 @@ Expr *parse_type_compound_literal_expr_after_type(ParseContext *context, TypeInf /** - * type_identifier ::= VIRTUAL? TYPE_IDENT initializer_list? + * type_identifier ::= TYPE_IDENT initializer_list? * * @param left must be null. * @return Expr* */ -Expr *parse_type_expression_with_path(ParseContext *context, Path *path) +Expr *parse_type_expression_with_path(ParseContext *c, Path *path) { TypeInfo *type; if (path) { type = type_info_new(TYPE_INFO_IDENTIFIER, path->span); type->unresolved.path = path; - type->unresolved.name_loc = context->tok.id; - advance_and_verify(context, TOKEN_TYPE_IDENT); + type->unresolved.span = c->span; + type->unresolved.name = symstr(c); + advance_and_verify(c, TOKEN_TYPE_IDENT); RANGE_EXTEND_PREV(type); - ASSIGN_TYPE_ELSE(type, parse_type_with_base(context, type), poisoned_expr); - type->failable = try_consume(context, TOKEN_BANG); + ASSIGN_TYPE_OR_RET(type, parse_type_with_base(c, type), poisoned_expr); + type->failable = try_consume(c, TOKEN_BANG); } else { - ASSIGN_TYPE_ELSE(type, parse_failable_type(context), poisoned_expr); + ASSIGN_TYPE_OR_RET(type, parse_failable_type(c), poisoned_expr); } - if (TOKEN_IS(TOKEN_LBRACE)) + if (tok_is(c, TOKEN_LBRACE)) { - return parse_type_compound_literal_expr_after_type(context, type); + return parse_type_compound_literal_expr_after_type(c, type); } Expr *expr = expr_new(EXPR_TYPEINFO, type->span); expr->type_expr = type; @@ -1574,15 +1683,15 @@ Expr *parse_type_expression_with_path(ParseContext *context, Path *path) * function_block * : '{|' stmt_list '|}' */ -static Expr* parse_expr_block(ParseContext *context, Expr *left) +static Expr* parse_expr_block(ParseContext *c, Expr *left) { assert(!left && "Had left hand side"); - Expr *expr = EXPR_NEW_TOKEN(EXPR_EXPR_BLOCK, context->tok); - advance_and_verify(context, TOKEN_LBRAPIPE); + Expr *expr = EXPR_NEW_TOKEN(EXPR_EXPR_BLOCK); + advance_and_verify(c, TOKEN_LBRAPIPE); AstId *next = &expr->expr_block.first_stmt; - while (!try_consume(context, TOKEN_RBRAPIPE)) + while (!try_consume(c, TOKEN_RBRAPIPE)) { - Ast *stmt = parse_stmt(context); + Ast *stmt = parse_stmt(c); if (!ast_ok(stmt)) return poisoned_expr; ast_append(&next, stmt); } diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 1e6fc90c0..873a6c853 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -1,59 +1,19 @@ #include "compiler_internal.h" #include "parser_internal.h" -static Decl *parse_const_declaration(ParseContext *context, Visibility visibility); -static inline Decl *parse_func_definition(ParseContext *context, Visibility visibility, bool is_interface); -static inline bool parse_bitstruct_body(ParseContext *context, Decl *decl); -static bool context_next_is_path_prefix_start(ParseContext *context) +static Decl *parse_const_declaration(ParseContext *c, Visibility visibility); +static inline Decl *parse_func_definition(ParseContext *c, Visibility visibility, bool is_interface); +static inline bool parse_bitstruct_body(ParseContext *c, Decl *decl); + +#define DECL_VAR_NEW(type__, var__, visible__) decl_new_var(symstr(c), c->span, type__, var__, visible__); + + +static bool context_next_is_path_prefix_start(ParseContext *c) { - return context->tok.type == TOKEN_IDENT && context->next_tok.type == TOKEN_SCOPE; + return tok_is(c, TOKEN_IDENT) && peek(c) == TOKEN_SCOPE; } -/** - * Walk forward through the token stream to identify a type on the format: foo::bar::Type - * - * @return true if there is a type at the end. - */ -static bool context_next_is_type_with_path_prefix(ParseContext *context) -{ - // We assume it's called after "foo::" parsing. - if (!context_next_is_path_prefix_start(context)) return false; - - TokenId current = context->next_tok.id; - while (1) - { - TokenType tok; - - // 1. Step past the '::' and any following comment (doc comments are not allowed here!) - tok = advance_token(¤t); - - // 2. Check that we have an ident, otherwise if - // we see a type token, we're done and return true - // on any other - if (tok != TOKEN_IDENT) return tok == TOKEN_TYPE_IDENT; - - // 3. Now we've confirmed that there is an ident, step past it - // and any following comments. - tok = advance_token(¤t); - - // 4. If we don't see '::' after an ident we're done. - // And we know it's not a type. - if (tok != TOKEN_SCOPE) return false; - - // 5. Do another pass - } -} - -static bool context_next_is_type_and_not_ident(ParseContext *context) -{ - if (context->tok.type == TOKEN_IDENT) - { - if (context->next_tok.type != TOKEN_SCOPE) return false; - return context_next_is_type_with_path_prefix(context); - } - return token_is_any_type(context->tok.type); -} /** @@ -65,12 +25,12 @@ static bool context_next_is_type_and_not_ident(ParseContext *context) * doc comment start, asm, typeof, TYPE_IDENT, const, IDENT * - are sync points only if they appear in the first column. */ -void recover_top_level(ParseContext *context) +void recover_top_level(ParseContext *c) { - advance(context); - while (!TOKEN_IS(TOKEN_EOF)) + advance(c); + while (!tok_is(c, TOKEN_EOF)) { - switch (context->tok.type) + switch (c->tok) { case TOKEN_PRIVATE: case TOKEN_IMPORT: @@ -100,11 +60,11 @@ void recover_top_level(ParseContext *context) case TOKEN_BITSTRUCT: case TYPELIKE_TOKENS: // Only recover if this is in the first col. - if (TOKLOC(context->tok)->col == 1) return; - advance(context); + if (c->span.col == 1) return; + advance(c); break; default: - advance(context); + advance(c); break; } } @@ -112,12 +72,12 @@ void recover_top_level(ParseContext *context) // --- Parse CT conditional code -static inline bool parse_top_level_block(ParseContext *context, Decl ***decls, TokenType end1, TokenType end2, TokenType end3) +static inline bool parse_top_level_block(ParseContext *c, Decl ***decls, TokenType end1, TokenType end2, TokenType end3) { - CONSUME_OR(TOKEN_COLON, false); - while (!TOKEN_IS(end1) && !TOKEN_IS(end2) && !TOKEN_IS(end3) && !TOKEN_IS(TOKEN_EOF)) + CONSUME_OR_RET(TOKEN_COLON, false); + while (!tok_is(c, end1) && !tok_is(c, end2) && !tok_is(c, end3) && !tok_is(c, TOKEN_EOF)) { - Decl *decl = parse_top_level_statement(context); + Decl *decl = parse_top_level_statement(c); assert(decl); if (decl_ok(decl)) { @@ -136,70 +96,70 @@ static inline bool parse_top_level_block(ParseContext *context, Decl ***decls, T (CT_ELIF const_paren_expr ':' top_level_block)* (CT_ELSE top_level_block)? CT_ENDIF - * @param context + * @param c * @return the declaration if successfully parsed, poisoned_decl otherwise. */ -static inline Decl *parse_ct_if_top_level(ParseContext *context) +static inline Decl *parse_ct_if_top_level(ParseContext *c) { - advance_and_verify(context, TOKEN_CT_IF); - Decl *ct = decl_new_ct(DECL_CT_IF, context->prev_tok); - ASSIGN_EXPR_ELSE(ct->ct_if_decl.expr, parse_const_paren_expr(context), poisoned_decl); + Decl *ct = decl_new_ct(DECL_CT_IF, c->span); + advance_and_verify(c, TOKEN_CT_IF); + ASSIGN_EXPR_OR_RET(ct->ct_if_decl.expr, parse_const_paren_expr(c), poisoned_decl); - if (!parse_top_level_block(context, &ct->ct_if_decl.then, TOKEN_CT_ENDIF, TOKEN_CT_ELIF, TOKEN_CT_ELSE)) return poisoned_decl; + if (!parse_top_level_block(c, &ct->ct_if_decl.then, TOKEN_CT_ENDIF, TOKEN_CT_ELIF, TOKEN_CT_ELSE)) return poisoned_decl; CtIfDecl *ct_if_decl = &ct->ct_if_decl; - while (TOKEN_IS(TOKEN_CT_ELIF)) + while (tok_is(c, TOKEN_CT_ELIF)) { - advance_and_verify(context, TOKEN_CT_ELIF); - Decl *ct_elif = decl_new_ct(DECL_CT_ELIF, context->prev_tok); - ASSIGN_EXPR_ELSE(ct_elif->ct_elif_decl.expr, parse_const_paren_expr(context), poisoned_decl); + Decl *ct_elif = decl_new_ct(DECL_CT_ELIF, c->span); + advance_and_verify(c, TOKEN_CT_ELIF); + ASSIGN_EXPR_OR_RET(ct_elif->ct_elif_decl.expr, parse_const_paren_expr(c), poisoned_decl); - if (!parse_top_level_block(context, &ct_elif->ct_elif_decl.then, TOKEN_CT_ENDIF, TOKEN_CT_ELIF, TOKEN_CT_ELSE)) return poisoned_decl; + if (!parse_top_level_block(c, &ct_elif->ct_elif_decl.then, TOKEN_CT_ENDIF, TOKEN_CT_ELIF, TOKEN_CT_ELSE)) return poisoned_decl; ct_if_decl->elif = ct_elif; ct_if_decl = &ct_elif->ct_elif_decl; } - if (TOKEN_IS(TOKEN_CT_ELSE)) + if (tok_is(c, TOKEN_CT_ELSE)) { - advance_and_verify(context, TOKEN_CT_ELSE); - Decl *ct_else = decl_new_ct(DECL_CT_ELSE, context->prev_tok); + Decl *ct_else = decl_new_ct(DECL_CT_ELSE, c->span); + advance_and_verify(c, TOKEN_CT_ELSE); ct_if_decl->elif = ct_else; - if (!parse_top_level_block(context, &ct_else->ct_else_decl, TOKEN_CT_ENDIF, TOKEN_CT_ENDIF, TOKEN_CT_ENDIF)) return poisoned_decl; + if (!parse_top_level_block(c, &ct_else->ct_else_decl, TOKEN_CT_ENDIF, TOKEN_CT_ENDIF, TOKEN_CT_ENDIF)) return poisoned_decl; } - CONSUME_OR(TOKEN_CT_ENDIF, poisoned_decl); - CONSUME_OR(TOKEN_EOS, poisoned_decl); + CONSUME_OR_RET(TOKEN_CT_ENDIF, poisoned_decl); + CONSUME_OR_RET(TOKEN_EOS, poisoned_decl); return ct; } /** * ct_case ::= (CT_DEFAULT | CT_CASE type) ':' top_level_statement* * - * @param context + * @param c * @return poisoned decl if parsing fails. */ -static inline Decl *parse_ct_case(ParseContext *context) +static inline Decl *parse_ct_case(ParseContext *c) { Decl *decl; - switch (context->tok.type) + switch (c->tok) { case TOKEN_CT_DEFAULT: - advance(context); - decl = decl_new_ct(DECL_CT_CASE, context->tok.id); + decl = decl_new_ct(DECL_CT_CASE, c->span); + advance(c); break; case TOKEN_CT_CASE: - decl = decl_new_ct(DECL_CT_CASE, context->tok.id); - advance(context); - ASSIGN_EXPR_ELSE(decl->ct_case_decl.expr, parse_constant_expr(context), poisoned_decl); + decl = decl_new_ct(DECL_CT_CASE, c->span); + advance(c); + ASSIGN_EXPR_OR_RET(decl->ct_case_decl.expr, parse_constant_expr(c), poisoned_decl); break; default: - SEMA_TOKEN_ERROR(context->tok, "Expected a $case or $default statement here."); + SEMA_ERROR_HERE("Expected a $case or $default statement here."); return poisoned_decl; } - TRY_CONSUME_OR(TOKEN_COLON, "Expected ':' here.", poisoned_decl); + TRY_CONSUME_OR_RET(TOKEN_COLON, "Expected ':' here.", poisoned_decl); while (1) { - TokenType type = context->tok.type; + TokenType type = c->tok; if (type == TOKEN_CT_DEFAULT || type == TOKEN_CT_CASE || type == TOKEN_CT_ENDSWITCH) break; - ASSIGN_DECL_ELSE(Decl *stmt, parse_top_level_statement(context), poisoned_decl); + ASSIGN_DECL_OR_RET(Decl * stmt, parse_top_level_statement(c), poisoned_decl); vec_add(decl->ct_case_decl.body, stmt); } return decl; @@ -207,22 +167,22 @@ static inline Decl *parse_ct_case(ParseContext *context) /** * ct_switch_top_level ::= CT_SWITCH const_paren_expr '{' ct_case* '}' - * @param context + * @param c * @return the declaration if successfully parsed, NULL otherwise. */ -static inline Decl *parse_ct_switch_top_level(ParseContext *context) +static inline Decl *parse_ct_switch_top_level(ParseContext *c) { - advance_and_verify(context, TOKEN_CT_SWITCH); - Decl *ct = decl_new_ct(DECL_CT_SWITCH, context->prev_tok); - ASSIGN_EXPR_ELSE(ct->ct_switch_decl.expr, parse_const_paren_expr(context), poisoned_decl); + Decl *ct = decl_new_ct(DECL_CT_SWITCH, c->span); + advance_and_verify(c, TOKEN_CT_SWITCH); + ASSIGN_EXPR_OR_RET(ct->ct_switch_decl.expr, parse_const_paren_expr(c), poisoned_decl); - CONSUME_OR(TOKEN_COLON, poisoned_decl); - while (!try_consume(context, TOKEN_CT_ENDSWITCH)) + CONSUME_OR_RET(TOKEN_COLON, poisoned_decl); + while (!try_consume(c, TOKEN_CT_ENDSWITCH)) { - ASSIGN_DECL_ELSE(Decl *result, parse_ct_case(context), poisoned_decl); + ASSIGN_DECL_OR_RET(Decl * result, parse_ct_case(c), poisoned_decl); vec_add(ct->ct_switch_decl.cases, result); } - CONSUME_OR(TOKEN_EOS, poisoned_decl); + CONSUME_OR_RET(TOKEN_EOS, poisoned_decl); return ct; } @@ -232,42 +192,41 @@ static inline Decl *parse_ct_switch_top_level(ParseContext *context) /** * module_path ::= IDENT (SCOPE IDENT)* * - * @param context + * @param c * @return path or null if parsing failed. */ -static inline Path *parse_module_path(ParseContext *context) +static inline Path *parse_module_path(ParseContext *c) { - assert(TOKEN_IS(TOKEN_IDENT)); + assert(tok_is(c, TOKEN_IDENT)); scratch_buffer_clear(); - SourceSpan span = source_span_from_token_id(context->tok.id); + SourceSpan span = c->span; while (1) { - TokenId last_token = context->tok.id; - const char *string = TOKSTR(context->tok); - if (!try_consume(context, TOKEN_IDENT)) + const char *string = symstr(c); + if (!try_consume(c, TOKEN_IDENT)) { - if (token_is_keyword(context->tok.type)) + if (token_is_keyword(c->tok)) { - SEMA_TOKEN_ERROR(context->tok, "The module path cannot contain a reserved keyword, try another name."); + SEMA_ERROR_HERE("The module path cannot contain a reserved keyword, try another name."); return false; } - if (token_is_some_ident(context->tok.type)) + if (token_is_some_ident(c->tok)) { - SEMA_TOKEN_ERROR(context->tok, "The elements of a module path must consist of only lower case letters, 0-9 and '_'."); + SEMA_ERROR_HERE("The elements of a module path must consist of only lower case letters, 0-9 and '_'."); return false; } - SEMA_TOKEN_ERROR(context->tok, "Each '::' must be followed by a regular lower case sub module name."); + SEMA_ERROR_HERE("Each '::' must be followed by a regular lower case sub module name."); return NULL; } if (string == kw_main) { - SEMA_TOKID_ERROR(context->prev_tok, "'main' is not a valid name in a module path, please pick something else."); + SEMA_ERROR_LAST("'main' is not a valid name in a module path, please pick something else."); return NULL; } - scratch_buffer_append_len(string, TOKLEN(context->prev_tok)); - if (!try_consume(context, TOKEN_SCOPE)) + scratch_buffer_append(string); + if (!try_consume(c, TOKEN_SCOPE)) { - span.end_loc = last_token; + span = extend_span_with_token(span, c->prev_span); break; } scratch_buffer_append("::"); @@ -290,45 +249,45 @@ static inline Path *parse_module_path(ParseContext *context) * | module_params ',' module_param * ; */ -static inline bool parse_optional_module_params(ParseContext *context, TokenId **tokens) +static inline bool parse_optional_module_params(ParseContext *c, const char ***tokens_ref) { - *tokens = NULL; + *tokens_ref = NULL; - if (!try_consume(context, TOKEN_LESS)) return true; + if (!try_consume(c, TOKEN_LESS)) return true; - if (try_consume(context, TOKEN_GREATER)) + if (try_consume(c, TOKEN_GREATER)) { - SEMA_TOKEN_ERROR(context->tok, "Generic parameter list cannot be empty."); + SEMA_ERROR_HERE("Generic parameter list cannot be empty."); return false; } // No params while (1) { - switch (context->tok.type) + switch (c->tok) { case TOKEN_TYPE_IDENT: break; case TOKEN_COMMA: - SEMA_TOKEN_ERROR(context->tok, "Unexpected ','"); + SEMA_ERROR_HERE("Unexpected ','"); return false; case TOKEN_IDENT: - SEMA_TOKEN_ERROR(context->tok, "The module parameter must be a type."); + SEMA_ERROR_HERE("The module parameter must be a type."); return false; case TOKEN_CT_IDENT: case TOKEN_CT_TYPE_IDENT: - SEMA_TOKEN_ERROR(context->tok, "The module parameter cannot be a $-prefixed name."); + SEMA_ERROR_HERE("The module parameter cannot be a $-prefixed name."); return false; default: - SEMA_TOKEN_ERROR(context->tok, "Only generic parameters are allowed here as parameters to the module."); + SEMA_ERROR_HERE("Only generic parameters are allowed here as parameters to the module."); return false; } - vec_add(*tokens, context->tok.id); - advance(context); - if (!try_consume(context, TOKEN_COMMA)) + vec_add(*tokens_ref, symstr(c)); + advance(c); + if (!try_consume(c, TOKEN_COMMA)) { - return consume(context, TOKEN_GREATER, "Expected '>'."); + return consume(c, TOKEN_GREATER, "Expected '>'."); } } @@ -336,39 +295,39 @@ static inline bool parse_optional_module_params(ParseContext *context, TokenId * /** * module ::= MODULE module_path ('<' module_params '>')? EOS */ -bool parse_module(ParseContext *context) +bool parse_module(ParseContext *c) { - if (!try_consume(context, TOKEN_MODULE)) + if (!try_consume(c, TOKEN_MODULE)) { - return context_set_module_from_filename(context); + return context_set_module_from_filename(c); } - bool is_private = try_consume(context, TOKEN_PRIVATE); + bool is_private = try_consume(c, TOKEN_PRIVATE); - if (TOKEN_IS(TOKEN_STRING)) + if (tok_is(c, TOKEN_STRING)) { - SEMA_TOKEN_ERROR(context->tok, "'module' should be followed by a plain identifier, not a string. Did you accidentally put the module name between \"\"?"); + SEMA_ERROR_HERE("'module' should be followed by a plain identifier, not a string. Did you accidentally put the module name between \"\"?"); return false; } - if (!TOKEN_IS(TOKEN_IDENT)) + if (!tok_is(c, TOKEN_IDENT)) { - if (token_is_keyword(context->tok.type)) + if (token_is_keyword(c->tok)) { - SEMA_TOKEN_ERROR(context->tok, "The module name cannot contain a reserved keyword, try another name."); + SEMA_ERROR_HERE("The module name cannot contain a reserved keyword, try another name."); return false; } - if (token_is_some_ident(context->tok.type)) + if (token_is_some_ident(c->tok)) { - SEMA_TOKEN_ERROR(context->tok, "The module name must consist of only lower case letters, 0-9 and '_'."); + SEMA_ERROR_HERE("The module name must consist of only lower case letters, 0-9 and '_'."); return false; } - SEMA_TOKEN_ERROR(context->tok, "'module' should be followed by a module name."); + SEMA_ERROR_HERE("'module' should be followed by a module name."); return false; } - Path *path = parse_module_path(context); + Path *path = parse_module_path(c); // Expect the module name if (!path) @@ -376,100 +335,100 @@ bool parse_module(ParseContext *context) path = CALLOCS(Path); path->len = strlen("#invalid"); path->module = "#invalid"; - path->span = INVALID_RANGE; - context_set_module(context, path, NULL, false); - recover_top_level(context); + path->span = INVALID_SPAN; + context_set_module(c, path, NULL, false); + recover_top_level(c); return false; } // Is this a generic module? - TokenId *generic_parameters = NULL; - if (!parse_optional_module_params(context, &generic_parameters)) + const char **generic_parameters = NULL; + if (!parse_optional_module_params(c, &generic_parameters)) { - context_set_module(context, path, NULL, is_private); - recover_top_level(context); + context_set_module(c, path, NULL, is_private); + recover_top_level(c); return true; } - context_set_module(context, path, generic_parameters, is_private); - TRY_CONSUME_EOS_OR(false); + context_set_module(c, path, generic_parameters, is_private); + CONSUME_EOS_OR_RET(false); return true; } -bool consume_ident(ParseContext *context, const char* name) +bool consume_ident(ParseContext *c, const char* name) { - if (try_consume(context, TOKEN_IDENT)) return true; - if (TOKEN_IS(TOKEN_TYPE_IDENT) || TOKEN_IS(TOKEN_CONST_IDENT)) + if (try_consume(c, TOKEN_IDENT)) return true; + if (tok_is(c, TOKEN_TYPE_IDENT) || tok_is(c, TOKEN_CONST_IDENT)) { - SEMA_TOKEN_ERROR(context->tok, "A %s must start with a lower case letter.", name); + SEMA_ERROR_HERE("A %s must start with a lower case letter.", name); return false; } - if (token_is_keyword(context->tok.type)) + if (token_is_keyword(c->tok)) { - SEMA_TOKEN_ERROR(context->tok, "This is a reserved keyword, did you accidentally use it?"); + SEMA_ERROR_HERE("This is a reserved keyword, did you accidentally use it?"); return false; } - SEMA_TOKEN_ERROR(context->tok, "A %s was expected.", name); + SEMA_ERROR_HERE("A %s was expected.", name); return false; } -static bool consume_type_name(ParseContext *context, const char* type) +static bool consume_type_name(ParseContext *c, const char* type) { - if (TOKEN_IS(TOKEN_IDENT) || token_is_keyword(context->tok.type)) + if (tok_is(c, TOKEN_IDENT) || token_is_keyword(c->tok)) { - SEMA_TOKEN_ERROR(context->tok, "Names of %ss must start with an upper case letter.", type); + SEMA_ERROR_HERE("Names of %ss must start with an upper case letter.", type); return false; } - if (TOKEN_IS(TOKEN_CONST_IDENT)) + if (tok_is(c, TOKEN_CONST_IDENT)) { - SEMA_TOKEN_ERROR(context->tok, "Names of %ss cannot be all upper case.", type); + SEMA_ERROR_HERE("Names of %ss cannot be all upper case.", type); return false; } - if (!consume(context, TOKEN_TYPE_IDENT, "'%s' should be followed by the name of the %s.", type, type)) return false; + if (!consume(c, TOKEN_TYPE_IDENT, "'%s' should be followed by the name of the %s.", type, type)) return false; return true; } -bool consume_const_name(ParseContext *context, const char* type) +bool consume_const_name(ParseContext *c, const char* type) { - if (TOKEN_IS(TOKEN_IDENT) || TOKEN_IS(TOKEN_TYPE_IDENT)) + if (tok_is(c, TOKEN_IDENT) || tok_is(c, TOKEN_TYPE_IDENT)) { - SEMA_TOKEN_ERROR(context->tok, "Names of %ss must be all upper case.", type); + SEMA_ERROR_HERE("Names of %ss must be all upper case.", type); return false; } - if (!consume(context, TOKEN_CONST_IDENT, "The constant name was expected here, did you forget it?")) return false; + if (!consume(c, TOKEN_CONST_IDENT, "A constant name was expected here, did you forget it?")) return false; return true; } -Path *parse_path_prefix(ParseContext *context, bool *had_error) +Path *parse_path_prefix(ParseContext *c, bool *had_error) { *had_error = false; - if (!TOKEN_IS(TOKEN_IDENT) || context->next_tok.type != TOKEN_SCOPE) return NULL; + if (!tok_is(c, TOKEN_IDENT) || peek(c) != TOKEN_SCOPE) return NULL; char *scratch_ptr = global_context.scratch_buffer; uint32_t offset = 0; Path *path = CALLOCS(Path); - path->span = source_span_from_token_id(context->tok.id); - unsigned len = TOKLEN(context->tok); - memcpy(scratch_ptr, TOKSTR(context->tok.id), len); + path->span = c->span; + unsigned len = strlen(symstr(c)); + memcpy(scratch_ptr, symstr(c), len); offset += len; - TokenId last_token = context->tok.id; - advance(context); - advance(context); - while (TOKEN_IS(TOKEN_IDENT) && context->next_tok.type == TOKEN_SCOPE) + SourceSpan last_loc = c->span; + advance(c); + advance(c); + while (tok_is(c, TOKEN_IDENT) && peek(c) == TOKEN_SCOPE) { - last_token = context->tok.id; + last_loc = c->span; scratch_ptr[offset++] = ':'; scratch_ptr[offset++] = ':'; - len = TOKLEN(context->tok); - memcpy(scratch_ptr + offset, TOKSTR(context->tok.id), len); + len = c->data.lex_len; + memcpy(scratch_ptr + offset, symstr(c), len); offset += len; - advance(context); advance(context); + advance(c); advance(c); } TokenType type = TOKEN_IDENT; - path->span.end_loc = last_token; + path->span = extend_span_with_token(path->span, last_loc); path->module = symtab_add(scratch_ptr, offset, fnv1a(scratch_ptr, offset), &type); if (type != TOKEN_IDENT) { @@ -509,56 +468,62 @@ Path *parse_path_prefix(ParseContext *context, bool *had_error) * Assume prev_token is the type. * @return TypeInfo (poisoned if fails) */ -static inline TypeInfo *parse_base_type(ParseContext *context) +static inline TypeInfo *parse_base_type(ParseContext *c) { - if (try_consume(context, TOKEN_CT_TYPEOF)) + if (try_consume(c, TOKEN_CT_TYPEOF)) { - TypeInfo *type_info = type_info_new(TYPE_INFO_EXPRESSION, source_span_from_token_id(context->prev_tok)); - CONSUME_OR(TOKEN_LPAREN, poisoned_type_info); - ASSIGN_EXPR_ELSE(type_info->unresolved_type_expr, parse_expr(context), poisoned_type_info); - CONSUME_OR(TOKEN_RPAREN, poisoned_type_info); + TypeInfo *type_info = type_info_new(TYPE_INFO_EXPRESSION, c->prev_span); + CONSUME_OR_RET(TOKEN_LPAREN, poisoned_type_info); + ASSIGN_EXPR_OR_RET(type_info->unresolved_type_expr, parse_expr(c), poisoned_type_info); + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_type_info); RANGE_EXTEND_PREV(type_info); return type_info; } - SourceSpan range = source_span_from_token_id(context->tok.id); + SourceSpan range = c->span; bool had_error; - Path *path = parse_path_prefix(context, &had_error); + Path *path = parse_path_prefix(c, &had_error); if (had_error) return poisoned_type_info; if (path) { TypeInfo *type_info = type_info_new(TYPE_INFO_IDENTIFIER, range); type_info->unresolved.path = path; - type_info->unresolved.name_loc = context->tok.id; - if (!consume_type_name(context, "type")) return poisoned_type_info; + type_info->unresolved.name = symstr(c); + type_info->unresolved.span = c->span; + if (!consume_type_name(c, "type")) return poisoned_type_info; RANGE_EXTEND_PREV(type_info); return type_info; } TypeInfo *type_info = NULL; Type *type_found = NULL; - switch (context->tok.type) + switch (c->tok) { case TOKEN_TYPE_IDENT: + type_info = type_info_new_curr(c, TYPE_INFO_IDENTIFIER); + type_info->unresolved.name = symstr(c); + type_info->unresolved.span = c->span; + break; case TOKEN_CT_TYPE_IDENT: - type_info = type_info_new(TYPE_INFO_IDENTIFIER, source_span_from_token_id(context->tok.id)); - type_info->unresolved.name_loc = context->tok.id; + type_info = type_info_new_curr(c, TYPE_INFO_CT_IDENTIFIER); + type_info->unresolved.name = symstr(c); + type_info->unresolved.span = c->span; break; case TYPE_TOKENS: - type_found = type_from_token(context->tok.type); + type_found = type_from_token(c->tok); break; default: - SEMA_TOKEN_ERROR(context->tok, "A type name was expected here."); + SEMA_ERROR_HERE("A type name was expected here."); return poisoned_type_info; } if (type_found) { assert(!type_info); - type_info = type_info_new(TYPE_INFO_IDENTIFIER, source_span_from_token_id(context->tok.id)); + type_info = type_info_new_curr(c, TYPE_INFO_IDENTIFIER); type_info->resolve_status = RESOLVE_DONE; type_info->type = type_found; } assert(type_info); - advance(context); + advance(c); RANGE_EXTEND_PREV(type_info); return type_info; } @@ -573,20 +538,20 @@ static inline TypeInfo *parse_base_type(ParseContext *context) * @param type the type to wrap, may not be poisoned. * @return type (poisoned if fails) */ -static inline TypeInfo *parse_array_type_index(ParseContext *context, TypeInfo *type) +static inline TypeInfo *parse_array_type_index(ParseContext *c, TypeInfo *type) { assert(type_info_ok(type)); - advance_and_verify(context, TOKEN_LBRACKET); - if (try_consume(context, TOKEN_STAR)) + advance_and_verify(c, TOKEN_LBRACKET); + if (try_consume(c, TOKEN_STAR)) { - CONSUME_OR(TOKEN_RBRACKET, poisoned_type_info); + CONSUME_OR_RET(TOKEN_RBRACKET, poisoned_type_info); TypeInfo *inferred_array = type_info_new(TYPE_INFO_INFERRED_ARRAY, type->span); inferred_array->array.base = type; RANGE_EXTEND_PREV(inferred_array); return inferred_array; } - if (try_consume(context, TOKEN_RBRACKET)) + if (try_consume(c, TOKEN_RBRACKET)) { TypeInfo *subarray = type_info_new(TYPE_INFO_SUBARRAY, type->span); subarray->array.base = type; @@ -596,8 +561,8 @@ static inline TypeInfo *parse_array_type_index(ParseContext *context, TypeInfo * } TypeInfo *array = type_info_new(TYPE_INFO_ARRAY, type->span); array->array.base = type; - ASSIGN_EXPR_ELSE(array->array.len, parse_expr(context), poisoned_type_info); - CONSUME_OR(TOKEN_RBRACKET, poisoned_type_info); + ASSIGN_EXPR_OR_RET(array->array.len, parse_expr(c), poisoned_type_info); + CONSUME_OR_RET(TOKEN_RBRACKET, poisoned_type_info); RANGE_EXTEND_PREV(array); return array; } @@ -610,15 +575,15 @@ static inline TypeInfo *parse_array_type_index(ParseContext *context, TypeInfo * * @param type the type to wrap, may not be poisoned. * @return type (poisoned if fails) */ -static inline TypeInfo *parse_vector_type_index(ParseContext *context, TypeInfo *type) +static inline TypeInfo *parse_vector_type_index(ParseContext *c, TypeInfo *type) { assert(type_info_ok(type)); - advance_and_verify(context, TOKEN_LVEC); + advance_and_verify(c, TOKEN_LVEC); TypeInfo *vector = type_info_new(TYPE_INFO_VECTOR, type->span); vector->array.base = type; - ASSIGN_EXPR_ELSE(vector->array.len, parse_expr(context), poisoned_type_info); - CONSUME_OR(TOKEN_RVEC, poisoned_type_info); + ASSIGN_EXPR_OR_RET(vector->array.len, parse_expr(c), poisoned_type_info); + CONSUME_OR_RET(TOKEN_RVEC, poisoned_type_info); RANGE_EXTEND_PREV(vector); return vector; } @@ -632,20 +597,20 @@ static inline TypeInfo *parse_vector_type_index(ParseContext *context, TypeInfo * Assume already stepped into. * @return Type, poisoned if parsing is invalid. */ -TypeInfo *parse_type_with_base(ParseContext *context, TypeInfo *type_info) +TypeInfo *parse_type_with_base(ParseContext *c, TypeInfo *type_info) { while (type_info_ok(type_info)) { - switch (context->tok.type) + switch (c->tok) { case TOKEN_LVEC: - type_info = parse_vector_type_index(context, type_info); + type_info = parse_vector_type_index(c, type_info); break; case TOKEN_LBRACKET: - type_info = parse_array_type_index(context, type_info); + type_info = parse_array_type_index(c, type_info); break; case TOKEN_STAR: - advance(context); + advance(c); { TypeInfo *ptr_type = type_info_new(TYPE_INFO_POINTER, type_info->span); assert(type_info); @@ -670,17 +635,17 @@ TypeInfo *parse_type_with_base(ParseContext *context, TypeInfo *type_info) * Assume already stepped into. * @return Type, poisoned if parsing is invalid. */ -TypeInfo *parse_type(ParseContext *context) +TypeInfo *parse_type(ParseContext *c) { - ASSIGN_TYPE_ELSE(TypeInfo *base, parse_base_type(context), poisoned_type_info); - return parse_type_with_base(context, base); + ASSIGN_TYPE_OR_RET(TypeInfo *base, parse_base_type(c), poisoned_type_info); + return parse_type_with_base(c, base); } -TypeInfo *parse_failable_type(ParseContext *context) +TypeInfo *parse_failable_type(ParseContext *c) { - ASSIGN_TYPE_ELSE(TypeInfo *info, parse_base_type(context), poisoned_type_info); - ASSIGN_TYPE_ELSE(info, parse_type_with_base(context, info), poisoned_type_info); - if (try_consume(context, TOKEN_BANG)) + ASSIGN_TYPE_OR_RET(TypeInfo *info, parse_base_type(c), poisoned_type_info); + ASSIGN_TYPE_OR_RET(info, parse_type_with_base(c, info), poisoned_type_info); + if (try_consume(c, TOKEN_BANG)) { assert(!info->failable); info->failable = true; @@ -702,53 +667,51 @@ TypeInfo *parse_failable_type(ParseContext *context) * @param type * @return */ -Decl *parse_decl_after_type(ParseContext *context, TypeInfo *type) +Decl *parse_decl_after_type(ParseContext *c, TypeInfo *type) { - if (TOKEN_IS(TOKEN_LPAREN)) + if (tok_is(c, TOKEN_LPAREN)) { - SEMA_TOKEN_ERROR(context->tok, "Expected '{'."); + SEMA_ERROR_HERE("Expected '{'."); return poisoned_decl; } EXPECT_IDENT_FOR_OR("variable name", poisoned_decl); - TokenId name = context->tok.id; - advance(context); + Decl *decl = DECL_VAR_NEW(type, VARDECL_LOCAL, VISIBLE_LOCAL); + advance(c); - Decl *decl = decl_new_var(name, type, VARDECL_LOCAL, VISIBLE_LOCAL); - if (!parse_attributes(context, &decl->attributes)) return poisoned_decl; - if (TOKEN_IS(TOKEN_EQ)) + if (!parse_attributes(c, &decl->attributes)) return poisoned_decl; + if (tok_is(c, TOKEN_EQ)) { if (!decl) { - SEMA_TOKEN_ERROR(context->tok, "Expected an identifier before '='."); + SEMA_ERROR_HERE("Expected an identifier before '='."); return poisoned_decl; } - advance_and_verify(context, TOKEN_EQ); - ASSIGN_EXPR_ELSE(decl->var.init_expr, parse_initializer(context), poisoned_decl); + advance_and_verify(c, TOKEN_EQ); + ASSIGN_EXPR_OR_RET(decl->var.init_expr, parse_initializer(c), poisoned_decl); } return decl; } - /** * declaration ::= ('static' | 'const')? type variable ('=' expr)? * * @return Decl* (poisoned on error) */ -Decl *parse_decl(ParseContext *context) +Decl *parse_decl(ParseContext *c) { - if (TOKEN_IS(TOKEN_CONST)) + if (tok_is(c, TOKEN_CONST)) { - return parse_const_declaration(context, VISIBLE_LOCAL); + return parse_const_declaration(c, VISIBLE_LOCAL); } - bool is_threadlocal = try_consume(context, TOKEN_TLOCAL); - bool is_static = !is_threadlocal && try_consume(context, TOKEN_STATIC); + bool is_threadlocal = try_consume(c, TOKEN_TLOCAL); + bool is_static = !is_threadlocal && try_consume(c, TOKEN_STATIC); - ASSIGN_TYPE_ELSE(TypeInfo *type, parse_failable_type(context), poisoned_decl); + ASSIGN_TYPE_OR_RET(TypeInfo *type, parse_failable_type(c), poisoned_decl); - ASSIGN_DECL_ELSE(Decl *decl, parse_decl_after_type(context, type), poisoned_decl); + ASSIGN_DECL_OR_RET(Decl * decl, parse_decl_after_type(c, type), poisoned_decl); if (type->failable && decl->var.unwrap) { SEMA_ERROR(decl, "You cannot use unwrap with a failable variable."); @@ -759,6 +722,14 @@ Decl *parse_decl(ParseContext *context) return decl; } +Expr *parse_decl_or_expr(ParseContext *c, Decl **decl_ref) +{ + TypeInfo *type_info; + Expr *expr = parse_expr_or_type(c, &type_info); + if (expr) return expr; + ASSIGN_DECL_OR_RET(*decl_ref, parse_decl_after_type(c, type_info), poisoned_expr); + return NULL; +} /** @@ -766,108 +737,58 @@ Decl *parse_decl(ParseContext *context) * : 'const' type? IDENT '=' const_expr * ; */ -static Decl *parse_const_declaration(ParseContext *context, Visibility visibility) +static Decl *parse_const_declaration(ParseContext *c, Visibility visibility) { - advance_and_verify(context, TOKEN_CONST); + advance_and_verify(c, TOKEN_CONST); TypeInfo *type_info = NULL; - if (parse_next_is_decl(context)) + if (c->tok != TOKEN_CONST_IDENT) { - ASSIGN_TYPE_ELSE(type_info, parse_type(context), poisoned_decl); + ASSIGN_TYPE_OR_RET(type_info, parse_type(c), poisoned_decl); } + Decl *decl = decl_new_var(symstr(c), c->span, type_info, VARDECL_CONST, visibility); - if (!consume_const_name(context, "const")) return poisoned_decl; + if (!consume_const_name(c, "const")) return poisoned_decl; - Decl *decl = decl_new_var(context->prev_tok, type_info, VARDECL_CONST, visibility); + CONSUME_OR_RET(TOKEN_EQ, poisoned_decl); - CONSUME_OR(TOKEN_EQ, poisoned_decl); - - ASSIGN_EXPR_ELSE(decl->var.init_expr, parse_initializer(context), poisoned_decl); + ASSIGN_EXPR_OR_RET(decl->var.init_expr, parse_initializer(c), poisoned_decl); RANGE_EXTEND_PREV(decl); return decl; } -Decl *parse_var_decl(ParseContext *context) +Decl *parse_var_decl(ParseContext *c) { - TokenId start = context->tok.id; - advance_and_verify(context, TOKEN_VAR); + advance_and_verify(c, TOKEN_VAR); Decl *decl; - switch (context->tok.type) + switch (c->tok) { case TOKEN_CT_IDENT: - decl = decl_new_var(context->tok.id, NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL); - advance(context); - if (try_consume(context, TOKEN_EQ)) + decl = DECL_VAR_NEW(NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL); + advance(c); + if (try_consume(c, TOKEN_EQ)) { - ASSIGN_EXPR_ELSE(decl->var.init_expr, parse_expr(context), poisoned_decl); + ASSIGN_EXPR_OR_RET(decl->var.init_expr, parse_expr(c), poisoned_decl); } break; case TOKEN_CT_TYPE_IDENT: - decl = decl_new_var(context->tok.id, NULL, VARDECL_LOCAL_CT_TYPE, VISIBLE_LOCAL); - advance(context); - if (try_consume(context, TOKEN_EQ)) + decl = DECL_VAR_NEW(NULL, VARDECL_LOCAL_CT_TYPE, VISIBLE_LOCAL); + advance(c); + if (try_consume(c, TOKEN_EQ)) { - ASSIGN_EXPR_ELSE(decl->var.init_expr, parse_expr(context), poisoned_decl); + ASSIGN_EXPR_OR_RET(decl->var.init_expr, parse_expr(c), poisoned_decl); } break; default: - SEMA_TOKEN_ERROR(context->tok, "Expected a compile time variable name ('$Foo' or '$foo')."); + SEMA_ERROR_HERE("Expected a compile time variable name ('$Foo' or '$foo')."); return poisoned_decl; } - decl->span.loc = start; - RANGE_EXTEND_PREV(decl); return decl; } -bool parse_next_is_decl(ParseContext *context) -{ - TokenType next_tok = context->next_tok.type; - switch (context->tok.type) - { - case TYPELIKE_TOKENS: - return next_tok != TOKEN_DOT && next_tok != TOKEN_LPAREN && next_tok != TOKEN_LBRACE; - case TOKEN_IDENT: - if (next_tok != TOKEN_SCOPE) return false; - return context_next_is_type_with_path_prefix(context); - default: - return false; - } -} - -bool parse_next_is_type(ParseContext *context) -{ - TokenType next_tok = context->next_tok.type; - switch (context->tok.type) - { - case TYPELIKE_TOKENS: - return true; - case TOKEN_IDENT: - if (next_tok != TOKEN_SCOPE) return false; - return context_next_is_type_with_path_prefix(context); - default: - return false; - } -} - - -bool parse_next_is_case_type(ParseContext *context) -{ - TokenType next_tok = context->next_tok.type; - switch (context->tok.type) - { - case TYPELIKE_TOKENS: - return (next_tok == TOKEN_STAR) | (next_tok == TOKEN_LBRACKET) | (next_tok == TOKEN_COMMA) | (next_tok == TOKEN_COLON) | (next_tok == TOKEN_EOS); - case TOKEN_IDENT: - if (next_tok != TOKEN_SCOPE) return false; - return context_next_is_type_with_path_prefix(context); - default: - return false; - } -} - // --- Parse parameters & throws & attributes @@ -888,41 +809,42 @@ bool parse_next_is_case_type(ParseContext *context) * * @return true if parsing succeeded, false if recovery is needed */ -bool parse_attributes(ParseContext *context, Attr ***attributes_ref) +bool parse_attributes(ParseContext *c, Attr ***attributes_ref) { *attributes_ref = NULL; - while (try_consume(context, TOKEN_AT)) + while (try_consume(c, TOKEN_AT)) { bool had_error; - Path *path = parse_path_prefix(context, &had_error); + Path *path = parse_path_prefix(c, &had_error); if (had_error) return false; Attr *attr = CALLOCS(Attr); - attr->name = context->tok.id; + attr->name = symstr(c); + attr->name_span = c->span; attr->path = path; - if (tok_is(context, TOKEN_IDENT) || tok_is(context, TOKEN_TYPE_IDENT)) + if (tok_is(c, TOKEN_TYPE_IDENT) || tok_is(c, TOKEN_TYPE_IDENT)) { - advance(context); + advance(c); } else { - TRY_CONSUME_OR(TOKEN_IDENT, "Expected an attribute", false); + TRY_CONSUME_OR_RET(TOKEN_IDENT, "Expected an attribute", false); } - if (TOKEN_IS(TOKEN_LPAREN)) + if (tok_is(c, TOKEN_LPAREN)) { - ASSIGN_EXPR_ELSE(attr->expr, parse_const_paren_expr(context), false); + ASSIGN_EXPR_OR_RET(attr->expr, parse_const_paren_expr(c), false); } - const char *name = TOKSTR(attr->name); + const char *name = attr->name; VECEACH(*attributes_ref, i) { Attr *other_attr = *attributes_ref[i]; - if (TOKSTR(other_attr->name) == name) + if (other_attr->name == name) { - SEMA_TOKID_ERROR(attr->name, "Repeat of attribute '%s' here.", name); + sema_error_at(attr->name_span, "Repeat of attribute '%s' here.", name); return false; } } @@ -942,40 +864,39 @@ bool parse_attributes(ParseContext *context, Attr ***attributes_ref) * @param visibility * @return true if parsing succeeded */ -static inline Decl *parse_global_declaration(ParseContext *context, Visibility visibility) +static inline Decl *parse_global_declaration(ParseContext *c, Visibility visibility) { - bool threadlocal = try_consume(context, TOKEN_TLOCAL); + bool threadlocal = try_consume(c, TOKEN_TLOCAL); - ASSIGN_TYPE_ELSE(TypeInfo *type, parse_failable_type(context), poisoned_decl); + ASSIGN_TYPE_OR_RET(TypeInfo *type, parse_failable_type(c), poisoned_decl); - Decl *decl = decl_new_var(context->tok.id, type, VARDECL_GLOBAL, visibility); + Decl *decl = DECL_VAR_NEW(type, VARDECL_GLOBAL, visibility); decl->var.is_threadlocal = threadlocal; - if (TOKEN_IS(TOKEN_CONST_IDENT)) + if (tok_is(c, TOKEN_CONST_IDENT)) { - SEMA_TOKEN_ERROR(context->tok, "This looks like a constant variable, did you forget 'const'?"); + SEMA_ERROR_HERE("This looks like a constant variable, did you forget 'const'?"); return poisoned_decl; } - - if (!try_consume(context, TOKEN_IDENT)) + if (!try_consume(c, TOKEN_IDENT)) { - if (token_is_some_ident(context->tok.type)) + if (token_is_some_ident(c->tok)) { - SEMA_TOKEN_ERROR(context->tok, "I expected a variable name here, but global variables need to start with lower case."); + SEMA_ERROR_HERE("I expected a variable name here, but global variables need to start with lower case."); return poisoned_decl; } - SEMA_TOKEN_ERROR(context->tok, "The name of a global variable was expected here"); + SEMA_ERROR_HERE("The name of a global variable was expected here"); return poisoned_decl; } - if (!parse_attributes(context, &decl->attributes)) return poisoned_decl; - if (try_consume(context, TOKEN_EQ)) + if (!parse_attributes(c, &decl->attributes)) return poisoned_decl; + if (try_consume(c, TOKEN_EQ)) { - ASSIGN_EXPR_ELSE(decl->var.init_expr, parse_initializer(context), poisoned_decl); + ASSIGN_EXPR_OR_RET(decl->var.init_expr, parse_initializer(c), poisoned_decl); } - TRY_CONSUME_EOS_OR(poisoned_decl); + CONSUME_EOS_OR_RET(poisoned_decl); return decl; } @@ -985,15 +906,13 @@ static inline Decl *parse_global_declaration(ParseContext *context, Visibility v * param_declaration ::= type_expression '...'?) (IDENT ('=' initializer)?)? * ; */ -static inline bool parse_param_decl(ParseContext *context, Visibility parent_visibility, Decl*** parameters, bool require_name) +static inline bool parse_param_decl(ParseContext *c, Visibility parent_visibility, Decl*** parameters, bool require_name) { - TokenId first = context->tok.id; - ASSIGN_TYPE_ELSE(TypeInfo *type, parse_type(context), false); - bool vararg = try_consume(context, TOKEN_ELLIPSIS); - Decl *param = decl_new_var(context->tok.id, type, VARDECL_PARAM, parent_visibility); - param->span = (SourceSpan) { first, context->tok.id }; + ASSIGN_TYPE_OR_RET(TypeInfo *type, parse_type(c), false); + bool vararg = try_consume(c, TOKEN_ELLIPSIS); + Decl *param = DECL_VAR_NEW(type, VARDECL_PARAM, parent_visibility); param->var.vararg = vararg; - if (!try_consume(context, TOKEN_IDENT)) + if (!try_consume(c, TOKEN_IDENT)) { param->name = NULL; } @@ -1001,22 +920,22 @@ static inline bool parse_param_decl(ParseContext *context, Visibility parent_vis if (!name && require_name) { - if (!TOKEN_IS(TOKEN_COMMA) && !TOKEN_IS(TOKEN_RPAREN)) + if (!tok_is(c, TOKEN_COMMA) && !tok_is(c, TOKEN_RPAREN)) { - if (TOKEN_IS(TOKEN_CT_IDENT)) + if (tok_is(c, TOKEN_CT_IDENT)) { - SEMA_TOKEN_ERROR(context->tok, "Compile time identifiers are only allowed as macro parameters."); + SEMA_ERROR_HERE("Compile time identifiers are only allowed as macro parameters."); return false; } - sema_error_at_prev_end(context->tok, "Unexpected end of the parameter list, did you forget an ')'?"); + sema_error_at_after(type->span, "Unexpected end of the parameter list, did you forget an ')'?"); return false; } SEMA_ERROR(type, "The parameter must be named."); return false; } - if (name && try_consume(context, TOKEN_EQ)) + if (name && try_consume(c, TOKEN_EQ)) { - ASSIGN_EXPR_ELSE(param->var.init_expr, parse_initializer(context), false); + ASSIGN_EXPR_OR_RET(param->var.init_expr, parse_initializer(c), false); } vec_add(*parameters, param); @@ -1024,92 +943,110 @@ static inline bool parse_param_decl(ParseContext *context, Visibility parent_vis return true; } +bool parse_next_is_typed_parameter(ParseContext *c) +{ + switch (c->tok) + { + case TOKEN_IDENT: + { + return peek(c) == TOKEN_SCOPE; + } + case TYPE_TOKENS: + case TOKEN_TYPE_IDENT: + return true; + default: + return false; + } +} + /** * parameters ::= (parameter (',' parameter)*)? * non_type_ident = IDENT | HASH_IDENT | CT_IDENT * parameter ::= type ELLIPSIS? (non_type_ident ('=' expr))? * | ELLIPSIS (CT_TYPE_IDENT | non_type_ident ('=' expr)?)? */ -bool parse_parameters(ParseContext *context, Visibility visibility, Decl ***params_ref) +bool parse_parameters(ParseContext *c, Visibility visibility, Decl ***params_ref) { Decl** params = NULL; bool var_arg_found = false; - while (!TOKEN_IS(TOKEN_EOS) && !TOKEN_IS(TOKEN_RPAREN)) + while (!tok_is(c, TOKEN_EOS) && !tok_is(c, TOKEN_RPAREN)) { TypeInfo *type = NULL; - bool ellipsis = try_consume(context, TOKEN_ELLIPSIS); + bool ellipsis = try_consume(c, TOKEN_ELLIPSIS); - // Special case, we might see foo($Type). - // there is an ambiguity here, since ($Type) and ($Type x) is potentially possible - // to evaluate. However, at the top level we never have global compile time values. - // so consequently we need fix this and ignore CT_TYPE_IDENT - if (!ellipsis && context_next_is_type_and_not_ident(context) && context->tok.type != TOKEN_CT_TYPE_IDENT ) + if (!ellipsis && parse_next_is_typed_parameter(c)) { - ASSIGN_TYPE_ELSE(type, parse_type(context), false); - ellipsis = try_consume(context, TOKEN_ELLIPSIS); + ASSIGN_TYPE_OR_RET(type, parse_type(c), false); + ellipsis = try_consume(c, TOKEN_ELLIPSIS); } if (ellipsis && var_arg_found) { - SEMA_TOKID_ERROR(context->prev_tok, "Only a single vararg parameter is allowed."); + SEMA_ERROR_LAST("Only a single vararg parameter is allowed."); return false; } VarDeclKind param_kind; - TokenId token = context->tok.id; + const char *name = NULL; + SourceSpan span = c->span; bool no_name = false; bool vararg_implicit = false; - switch (context->tok.type) + switch (c->tok) { case TOKEN_IDENT: // normal foo + name = symstr(c); param_kind = VARDECL_PARAM; // Check for "foo..." which defines an implicit "any" vararg - if (context->next_tok.type == TOKEN_ELLIPSIS) + if (peek(c) == TOKEN_ELLIPSIS) { if (ellipsis) { - SEMA_TOKEN_ERROR(context->tok, "Unexpected '...' here."); + SEMA_ERROR_HERE("Unexpected '...' here."); return false; } - advance(context); + advance(c); if (type) { - SEMA_TOKEN_ERROR(context->tok, "The '...' should appear after the type."); + SEMA_ERROR_HERE("The '...' should appear after the type."); return false; } - type = type_info_new_base(type_any, source_span_from_token_id(token)); + type = type_info_new_base(type_any, c->span); ellipsis = true; vararg_implicit = true; } break; case TOKEN_CT_IDENT: // ct_var $foo + name = symstr(c); param_kind = VARDECL_PARAM_CT; break; case TOKEN_AMP: // reference &foo - advance(context); - token = context->tok.id; - if (!TOKEN_IS(TOKEN_IDENT)) + advance(c); + name = symstr(c); + span = extend_span_with_token(span, c->span); + if (!tok_is(c, TOKEN_IDENT)) { - SEMA_TOKEN_ERROR(context->tok, "Only normal variables may be passed by reference."); + SEMA_ERROR_HERE("Only normal variables may be passed by reference."); return false; } param_kind = VARDECL_PARAM_REF; break; case TOKEN_HASH_TYPE_IDENT: // #Foo (not allowed) - SEMA_TOKEN_ERROR(context->tok, "An unevaluated expression can never be a type, did you mean to use $Type?"); + SEMA_ERROR_HERE("An unevaluated expression can never be a type, did you mean to use $Type?"); return false; case TOKEN_HASH_IDENT: // expression #foo + name = symstr(c); param_kind = VARDECL_PARAM_EXPR; break; // Compile time type $Type case TOKEN_CT_TYPE_IDENT: + name = symstr(c); param_kind = VARDECL_PARAM_CT_TYPE; break; case TOKEN_COMMA: @@ -1117,37 +1054,41 @@ bool parse_parameters(ParseContext *context, Visibility visibility, Decl ***para case TOKEN_RPAREN: if (!type && !ellipsis) { - SEMA_TOKEN_ERROR(context->tok, "Expected a parameter."); + SEMA_ERROR_HERE("Expected a parameter."); return false; } no_name = true; - token = context->prev_tok; + span = c->prev_span; param_kind = VARDECL_PARAM; break; default: - SEMA_TOKEN_ERROR(context->tok, "Expected a parameter."); + if (!type && parse_next_may_be_type(c)) + { + ASSIGN_TYPE_OR_RET(type, parse_type(c), false); + param_kind = VARDECL_PARAM; + no_name = true; + span = type->span; + break; + } + SEMA_ERROR_HERE("Expected a parameter."); return false; } - Decl *param = decl_new_var(token, type, param_kind, visibility); + Decl *param = decl_new_var(name, span, type, param_kind, visibility); param->var.type_info = type; - if (no_name) + if (!no_name) { - param->name = NULL; - } - else - { - advance(context); - if (try_consume(context, TOKEN_EQ)) + advance(c); + if (try_consume(c, TOKEN_EQ)) { - ASSIGN_EXPR_ELSE(param->var.init_expr, parse_initializer(context), false); + ASSIGN_EXPR_OR_RET(param->var.init_expr, parse_initializer(c), false); } } - if (!parse_attributes(context, ¶m->attributes)) return false; + if (!parse_attributes(c, ¶m->attributes)) return false; var_arg_found |= ellipsis; param->var.vararg = ellipsis; param->var.vararg_implicit = vararg_implicit; vec_add(params, param); - if (!try_consume(context, TOKEN_COMMA)) break; + if (!try_consume(c, TOKEN_COMMA)) break; } *params_ref = params; return true; @@ -1158,11 +1099,11 @@ bool parse_parameters(ParseContext *context, Visibility visibility, Decl ***para * * parameter_type_list ::= '(' parameters ')' */ -static inline bool parse_parameter_list(ParseContext *context, Visibility parent_visibility, FunctionSignature *signature, bool is_interface) +static inline bool parse_parameter_list(ParseContext *c, Visibility parent_visibility, FunctionSignature *signature, bool is_interface) { - CONSUME_OR(TOKEN_LPAREN, false); + CONSUME_OR_RET(TOKEN_LPAREN, false); Decl **decls; - if (!parse_parameters(context, parent_visibility, &decls)) return false; + if (!parse_parameters(c, parent_visibility, &decls)) return false; if (vec_size(decls)) { Decl *last = VECLAST(decls); @@ -1180,7 +1121,7 @@ static inline bool parse_parameter_list(ParseContext *context, Visibility parent } } signature->params = decls; - CONSUME_OR(TOKEN_RPAREN, false); + CONSUME_OR_RET(TOKEN_RPAREN, false); return true; } @@ -1207,42 +1148,40 @@ static inline bool parse_parameter_list(ParseContext *context, Visibility parent * * @param parent the parent of the struct */ -bool parse_struct_body(ParseContext *context, Decl *parent) +bool parse_struct_body(ParseContext *c, Decl *parent) { - CONSUME_OR(TOKEN_LBRACE, false); + CONSUME_OR_RET(TOKEN_LBRACE, false); assert(decl_is_struct_type(parent)); MemberIndex index = 0; - while (!TOKEN_IS(TOKEN_RBRACE)) + while (!tok_is(c, TOKEN_RBRACE)) { - TokenType token_type = context->tok.type; + TokenType token_type = c->tok; if (token_type == TOKEN_STRUCT || token_type == TOKEN_UNION || token_type == TOKEN_BITSTRUCT) { DeclKind decl_kind = decl_from_token(token_type); Decl *member; - if (context->next_tok.type != TOKEN_IDENT) + if (peek(c) != TOKEN_IDENT) { - member = decl_new_with_type(NO_TOKEN_ID, decl_kind, parent->visibility); - member->span = source_span_from_token_id(context->tok.id); - advance(context); + member = decl_new_with_type(NULL, c->span, decl_kind, parent->visibility); + advance(c); } else { - advance(context); - member = decl_new_with_type(context->tok.id, decl_kind, parent->visibility); - member->span.loc = context->prev_tok; - advance_and_verify(context, TOKEN_IDENT); + advance(c); + member = decl_new_with_type(symstr(c), c->span, decl_kind, parent->visibility); + advance_and_verify(c, TOKEN_IDENT); } if (decl_kind == DECL_BITSTRUCT) { - TRY_CONSUME_OR(TOKEN_COLON, "':' followed by bitstruct type (e.g. 'int') was expected here.", poisoned_decl); - ASSIGN_TYPE_ELSE(member->bitstruct.base_type, parse_type(context), poisoned_decl); - if (!parse_bitstruct_body(context, member)) return decl_poison(parent); + TRY_CONSUME_OR_RET(TOKEN_COLON, "':' followed by bitstruct type (e.g. 'int') was expected here.", poisoned_decl); + ASSIGN_TYPE_OR_RET(member->bitstruct.base_type, parse_type(c), poisoned_decl); + if (!parse_bitstruct_body(c, member)) return decl_poison(parent); } else { - if (!parse_attributes(context, &member->attributes)) return false; - if (!parse_struct_body(context, member)) return decl_poison(parent); + if (!parse_attributes(c, &member->attributes)) return false; + if (!parse_struct_body(c, member)) return decl_poison(parent); } vec_add(parent->strukt.members, member); index++; @@ -1254,28 +1193,28 @@ bool parse_struct_body(ParseContext *context, Decl *parent) continue; } bool was_inline = false; - if (token_type == TOKEN_IDENT && TOKSTR(context->tok) == kw_inline) + if (token_type == TOKEN_IDENT && symstr(c) == kw_inline) { if (parent->decl_kind != DECL_STRUCT) { - SEMA_TOKEN_ERROR(context->tok, "Only structs may have 'inline' elements, did you make a mistake?"); + SEMA_ERROR_HERE("Only structs may have 'inline' elements, did you make a mistake?"); return false; } if (index > 0) { - SEMA_TOKID_ERROR(context->prev_tok, "Only the first element may be 'inline', did you order your fields wrong?"); + SEMA_ERROR_LAST("Only the first element may be 'inline', did you order your fields wrong?"); return false; } parent->is_substruct = true; was_inline = true; - advance(context); + advance(c); } - ASSIGN_TYPE_ELSE(TypeInfo *type, parse_type(context), false); + ASSIGN_TYPE_OR_RET(TypeInfo *type, parse_type(c), false); while (1) { - EXPECT_OR(TOKEN_IDENT, false); - Decl *member = decl_new_var(context->tok.id, type, VARDECL_MEMBER, parent->visibility); + EXPECT_OR_RET(TOKEN_IDENT, false); + Decl *member = DECL_VAR_NEW(type, VARDECL_MEMBER, parent->visibility); vec_add(parent->strukt.members, member); index++; if (index > MAX_MEMBERS) @@ -1283,18 +1222,18 @@ bool parse_struct_body(ParseContext *context, Decl *parent) SEMA_ERROR(member, "Can't add another member: the count would exceed maximum of %d elements.", MAX_MEMBERS); return false; } - advance(context); - if (!parse_attributes(context, &member->attributes)) return false; - if (!try_consume(context, TOKEN_COMMA)) break; + advance(c); + if (!parse_attributes(c, &member->attributes)) return false; + if (!try_consume(c, TOKEN_COMMA)) break; if (was_inline) { SEMA_ERROR(member, "'Inline' can only be applied to a single member, so please define it on its own line."); return false; } } - CONSUME_OR(TOKEN_EOS, false); + CONSUME_OR_RET(TOKEN_EOS, false); } - advance_and_verify(context, TOKEN_RBRACE); + advance_and_verify(c, TOKEN_RBRACE); return true; } @@ -1306,67 +1245,66 @@ bool parse_struct_body(ParseContext *context, Decl *parent) * * @param visibility */ -static inline Decl *parse_struct_declaration(ParseContext *context, Visibility visibility) +static inline Decl *parse_struct_declaration(ParseContext *c, Visibility visibility) { - TokenType type = context->tok.type; + TokenType type = c->tok; - advance(context); + advance(c); const char* type_name = struct_union_name_from_token(type); - TokenId name = context->tok.id; + Decl *decl = decl_new_with_type(symstr(c), c->span, decl_from_token(type), visibility); - if (!consume_type_name(context, type_name)) return poisoned_decl; - Decl *decl = decl_new_with_type(name, decl_from_token(type), visibility); + if (!consume_type_name(c, type_name)) return poisoned_decl; - if (!parse_attributes(context, &decl->attributes)) + if (!parse_attributes(c, &decl->attributes)) { return poisoned_decl; } - if (!parse_struct_body(context, decl)) + if (!parse_struct_body(c, decl)) { return poisoned_decl; } - DEBUG_LOG("Parsed %s %s completely.", type_name, TOKSTR(name)); + DEBUG_LOG("Parsed %s %s completely.", type_name, decl->name); return decl; } /** * body ::= '{' (TYPE IDENT ':' expr '..' expr EOS)* '}' - * @param context + * @param c * @param decl * @return */ -static inline bool parse_bitstruct_body(ParseContext *context, Decl *decl) +static inline bool parse_bitstruct_body(ParseContext *c, Decl *decl) { - CONSUME_OR(TOKEN_LBRACE, false); + CONSUME_OR_RET(TOKEN_LBRACE, false); - while (!try_consume(context, TOKEN_RBRACE)) + while (!try_consume(c, TOKEN_RBRACE)) { - ASSIGN_TYPE_ELSE(TypeInfo *type, parse_type(context), false); + ASSIGN_TYPE_OR_RET(TypeInfo *type, parse_type(c), false); + Decl *member_decl = DECL_VAR_NEW(type, VARDECL_BITMEMBER, VISIBLE_LOCAL); - if (!try_consume(context, TOKEN_IDENT)) + if (!try_consume(c, TOKEN_IDENT)) { - if (try_consume(context, TOKEN_CONST_IDENT) || try_consume(context, TOKEN_TYPE_IDENT)) + if (try_consume(c, TOKEN_CONST_IDENT) || try_consume(c, TOKEN_TYPE_IDENT)) { - SEMA_TOKID_ERROR(context->prev_tok, "Expected a field name with an initial lower case."); + SEMA_ERROR_LAST("Expected a field name with an initial lower case."); return false; } - SEMA_TOKEN_ERROR(context->tok, "Expected a field name at this position."); + SEMA_ERROR_HERE("Expected a field name at this position."); return false; } - Decl *member_decl = decl_new_var(context->prev_tok, type, VARDECL_BITMEMBER, VISIBLE_LOCAL); - CONSUME_OR(TOKEN_COLON, false); - ASSIGN_EXPR_ELSE(member_decl->var.start, parse_constant_expr(context), false); - if (try_consume(context, TOKEN_DOTDOT)) + CONSUME_OR_RET(TOKEN_COLON, false); + ASSIGN_EXPR_OR_RET(member_decl->var.start, parse_constant_expr(c), false); + if (try_consume(c, TOKEN_DOTDOT)) { - ASSIGN_EXPR_ELSE(member_decl->var.end, parse_constant_expr(context), false); + ASSIGN_EXPR_OR_RET(member_decl->var.end, parse_constant_expr(c), false); } else { member_decl->var.end = NULL; } - CONSUME_OR(TOKEN_EOS, false); + CONSUME_OR_RET(TOKEN_EOS, false); vec_add(decl->bitstruct.members, member_decl); } @@ -1375,25 +1313,23 @@ static inline bool parse_bitstruct_body(ParseContext *context, Decl *decl) /** * bitstruct_declaration = 'bitstruct' IDENT ':' type bitstruct_body */ -static inline Decl *parse_bitstruct_declaration(ParseContext *context, Visibility visibility) +static inline Decl *parse_bitstruct_declaration(ParseContext *c, Visibility visibility) { - advance_and_verify(context, TOKEN_BITSTRUCT); + advance_and_verify(c, TOKEN_BITSTRUCT); - TokenId name = context->tok.id; + Decl *decl = decl_new_with_type(symstr(c), c->span, DECL_BITSTRUCT, visibility); + if (!consume_type_name(c, "bitstruct")) return poisoned_decl; - if (!consume_type_name(context, "bitstruct")) return poisoned_decl; - Decl *decl = decl_new_with_type(name, DECL_BITSTRUCT, visibility); + TRY_CONSUME_OR_RET(TOKEN_COLON, "':' followed by bitstruct type (e.g. 'int') was expected here.", poisoned_decl); - TRY_CONSUME_OR(TOKEN_COLON, "':' followed by bitstruct type (e.g. 'int') was expected here.", poisoned_decl); + ASSIGN_TYPE_OR_RET(decl->bitstruct.base_type, parse_type(c), poisoned_decl); - ASSIGN_TYPE_ELSE(decl->bitstruct.base_type, parse_type(context), poisoned_decl); - - if (!parse_attributes(context, &decl->attributes)) + if (!parse_attributes(c, &decl->attributes)) { return poisoned_decl; } - if (!parse_bitstruct_body(context, decl)) + if (!parse_bitstruct_body(c, decl)) { return poisoned_decl; } @@ -1402,10 +1338,10 @@ static inline Decl *parse_bitstruct_declaration(ParseContext *context, Visibilit } -static inline Decl *parse_top_level_const_declaration(ParseContext *context, Visibility visibility) +static inline Decl *parse_top_level_const_declaration(ParseContext *c, Visibility visibility) { - ASSIGN_DECL_ELSE(Decl *decl, parse_const_declaration(context, visibility), poisoned_decl); - TRY_CONSUME_EOS_OR(poisoned_decl); + ASSIGN_DECL_OR_RET(Decl * decl, parse_const_declaration(c, visibility), poisoned_decl); + CONSUME_EOS_OR_RET(poisoned_decl); return decl; } @@ -1415,31 +1351,30 @@ static inline Decl *parse_top_level_const_declaration(ParseContext *context, Vis * * trailing_block_parameter ::= '@' IDENT ( '(' parameters ')' )? */ -static bool parse_macro_arguments(ParseContext *context, Visibility visibility, Decl ***params_ref, Decl ***body_params, TokenId *block_parameter) +static bool parse_macro_arguments(ParseContext *c, Visibility visibility, Decl ***params_ref, Decl ***body_params, const char **block_parameter) { - CONSUME_OR(TOKEN_LPAREN, false); + CONSUME_OR_RET(TOKEN_LPAREN, false); *params_ref = NULL; *body_params = NULL; - *block_parameter = (TokenId) { 0 }; + *block_parameter = NULL; // Parse the regular parameters. - if (!parse_parameters(context, visibility, params_ref)) return false; + if (!parse_parameters(c, visibility, params_ref)) return false; // Do we have trailing block parameters? - if (try_consume(context, TOKEN_EOS)) + if (try_consume(c, TOKEN_EOS)) { // Consume '@' IDENT - TRY_CONSUME_OR(TOKEN_AT, "Expected a trailing block with the format '@block(...).", false); - *block_parameter = context->tok.id; - if (!consume_ident(context, "variable name")) return false; - TokenId name = context->prev_tok; - if (try_consume(context, TOKEN_LPAREN)) + TRY_CONSUME_OR_RET(TOKEN_AT, "Expected a trailing block with the format '@block(...).", false); + *block_parameter = symstr(c); + if (!consume_ident(c, "variable name")) return false; + if (try_consume(c, TOKEN_LPAREN)) { - if (!parse_parameters(context, visibility, body_params)) return false; - CONSUME_OR(TOKEN_RPAREN, false); + if (!parse_parameters(c, visibility, body_params)) return false; + CONSUME_OR_RET(TOKEN_RPAREN, false); } // TODO use the body param. } - CONSUME_OR(TOKEN_RPAREN, false); + CONSUME_OR_RET(TOKEN_RPAREN, false); return true; } @@ -1448,16 +1383,17 @@ static bool parse_macro_arguments(ParseContext *context, Visibility visibility, * * @return NULL if parsing failed, otherwise a list of Type* */ -static inline TypeInfo **parse_generic_parameters(ParseContext *context) +static inline TypeInfo **parse_generic_parameters(ParseContext *c) { TypeInfo **types = NULL; - while (!try_consume(context, TOKEN_GREATER)) + while (!try_consume(c, TOKEN_GREATER)) { - ASSIGN_TYPE_ELSE(TypeInfo *type_info, parse_type(context), NULL); + ASSIGN_TYPE_OR_RET(TypeInfo *type_info, parse_type(c), NULL); vec_add(types, type_info); - if (context->tok.type != TOKEN_RPAREN && context->tok.type != TOKEN_GREATER) + TokenType tok = c->tok; + if (tok != TOKEN_RPAREN && tok != TOKEN_GREATER) { - TRY_CONSUME_OR(TOKEN_COMMA, "Expected ',' after argument.", NULL); + TRY_CONSUME_OR_RET(TOKEN_COMMA, "Expected ',' after argument.", NULL); } } return types; @@ -1468,60 +1404,57 @@ static inline TypeInfo **parse_generic_parameters(ParseContext *context) * * func_typedef ::= 'func' failable_type parameter_type_list */ -static inline Decl *parse_define_type(ParseContext *context, Visibility visibility) +static inline Decl *parse_define_type(ParseContext *c, Visibility visibility) { - TokenId start = context->tok.id; - advance_and_verify(context, TOKEN_DEFINE); + advance_and_verify(c, TOKEN_DEFINE); - TokenId alias_name = context->tok.id; - DEBUG_LOG("Parse define %s", TOKSTR(alias_name)); - advance_and_verify(context, TOKEN_TYPE_IDENT); - CONSUME_OR(TOKEN_EQ, poisoned_decl); + const char *alias_name = symstr(c); + SourceSpan name_loc = c->span; + DEBUG_LOG("Parse define %s", alias_name); + advance_and_verify(c, TOKEN_TYPE_IDENT); + CONSUME_OR_RET(TOKEN_EQ, poisoned_decl); bool distinct = false; - if (context->tok.type == TOKEN_IDENT && TOKSTR(context->tok) == kw_distinct) + if (tok_is(c, TOKEN_IDENT) && symstr(c) == kw_distinct) { distinct = true; - advance(context); + advance(c); } // 1. Did we have `func`? In that case it's a function pointer. - if (try_consume(context, TOKEN_FUNC) || try_consume(context, TOKEN_FN)) + if (try_consume(c, TOKEN_FUNC) || try_consume(c, TOKEN_FN)) { - Decl *decl = decl_new_with_type(alias_name, DECL_TYPEDEF, visibility); - decl->span.loc = start; + Decl *decl = decl_new_with_type(alias_name, name_loc, DECL_TYPEDEF, visibility); decl->typedef_decl.is_func = true; decl->typedef_decl.is_distinct = distinct; - ASSIGN_TYPE_ELSE(TypeInfo *type_info, parse_failable_type(context), poisoned_decl); + ASSIGN_TYPE_OR_RET(TypeInfo *type_info, parse_failable_type(c), poisoned_decl); decl->typedef_decl.function_signature.returntype = type_info; - if (!parse_parameter_list(context, decl->visibility, &(decl->typedef_decl.function_signature), true)) + if (!parse_parameter_list(c, decl->visibility, &(decl->typedef_decl.function_signature), true)) { return poisoned_decl; } RANGE_EXTEND_PREV(decl); - TRY_CONSUME_EOS_OR(poisoned_decl); + CONSUME_EOS_OR_RET(poisoned_decl); return decl; } // 2. Now parse the type which we know is here. - ASSIGN_TYPE_ELSE(TypeInfo *type_info, parse_type(context), poisoned_decl); + ASSIGN_TYPE_OR_RET(TypeInfo *type_info, parse_type(c), poisoned_decl); // 3. Do we have '<' if so it's a parameterized type e.g. foo::bar::Type. - if (try_consume(context, TOKEN_LESS)) + if (try_consume(c, TOKEN_LESS)) { - TypeInfo **params = parse_generic_parameters(context); + TypeInfo **params = parse_generic_parameters(c); if (!params) return poisoned_decl; - Decl *decl = decl_new(DECL_DEFINE, alias_name, visibility); - decl->span.loc = start; + Decl *decl = decl_new(DECL_DEFINE, alias_name, name_loc, visibility); decl->define_decl.define_kind = DEFINE_TYPE_GENERIC; decl->define_decl.type_info = type_info; decl->define_decl.generic_params = params; RANGE_EXTEND_PREV(decl); - TRY_CONSUME_EOS_OR(poisoned_decl); + CONSUME_EOS_OR_RET(poisoned_decl); return decl; } - Decl *decl = decl_new_with_type(alias_name, distinct ? DECL_DISTINCT : DECL_TYPEDEF, visibility); - decl->span.loc = start; + Decl *decl = decl_new_with_type(alias_name, name_loc, distinct ? DECL_DISTINCT : DECL_TYPEDEF, visibility); decl->typedef_decl.type_info = type_info; decl->typedef_decl.is_func = false; if (distinct) @@ -1532,7 +1465,7 @@ static inline Decl *parse_define_type(ParseContext *context, Visibility visibili decl->decl_kind = DECL_DISTINCT; } RANGE_EXTEND_PREV(decl); - TRY_CONSUME_EOS_OR(poisoned_decl); + CONSUME_EOS_OR_RET(poisoned_decl); return decl; } @@ -1541,33 +1474,31 @@ static inline Decl *parse_define_type(ParseContext *context, Visibility visibili * * identifier_alias ::= path? (IDENT | CONST_IDENT) */ -static inline Decl *parse_define_ident(ParseContext *context, Visibility visibility) +static inline Decl *parse_define_ident(ParseContext *c, Visibility visibility) { // 1. Store the beginning of the define. - TokenId start = context->tok.id; - advance_and_verify(context, TOKEN_DEFINE); + advance_and_verify(c, TOKEN_DEFINE); // 2. At this point we expect an ident or a const token. // since the Type is handled. - TokenType alias_type = context->tok.type; + TokenType alias_type = c->tok; if (alias_type != TOKEN_IDENT && alias_type != TOKEN_CONST_IDENT) { if (token_is_any_type(alias_type)) { - SEMA_TOKEN_ERROR(context->tok, "'%s' is the name of a built-in type and can't be used as an alias.", - token_type_to_string(alias_type)); + SEMA_ERROR_HERE("'%s' is the name of a built-in type and can't be used as an alias.", + token_type_to_string(alias_type)); } else { - SEMA_TOKEN_ERROR(context->tok, "An identifier was expected here."); + SEMA_ERROR_HERE("An identifier was expected here."); } return poisoned_decl; } // 3. Set up the define. - Decl *decl = decl_new(DECL_DEFINE, context->tok.id, visibility); + Decl *decl = decl_new(DECL_DEFINE, symstr(c), c->span, visibility); decl->define_decl.define_kind = DEFINE_IDENT_ALIAS; - decl->span.loc = start; if (decl->name == kw_main) { @@ -1575,148 +1506,149 @@ static inline Decl *parse_define_ident(ParseContext *context, Visibility visibi return poisoned_decl; } // 4. Advance and consume the '=' - advance(context); - CONSUME_OR(TOKEN_EQ, poisoned_decl); + advance(c); + CONSUME_OR_RET(TOKEN_EQ, poisoned_decl); // 5. Here we may an (optional) path, we just check if it starts // with IDENT '::' Path *path = NULL; - if (context_next_is_path_prefix_start(context)) + if (context_next_is_path_prefix_start(c)) { bool error; - path = parse_path_prefix(context, &error); + path = parse_path_prefix(c, &error); if (error) return poisoned_decl; } decl->define_decl.path = path; // 6. Check that the token after the path is of the same type. - if (context->tok.type != alias_type) + if (c->tok != alias_type) { - if (token_is_any_type(context->tok.type) || context->tok.type == TOKEN_TYPE_IDENT) + if (token_is_any_type(c->tok) || tok_is(c, TOKEN_TYPE_IDENT)) { - SEMA_TOKID_ERROR(decl->name_token, "A type alias must start with an upper case letter and contain at least one lower case letter."); + SEMA_ERROR(decl, "A type alias must start with an upper case letter and contain at least one lower case letter."); return poisoned_decl; } if (alias_type == TOKEN_CONST_IDENT) { - SEMA_TOKEN_ERROR(context->tok, "Expected a constant name here."); + SEMA_ERROR_HERE("Expected a constant name here."); return poisoned_decl; } - SEMA_TOKEN_ERROR(context->tok, "Expected a function or variable name here."); + SEMA_ERROR_HERE("Expected a function or variable name here."); return poisoned_decl; } // 7. Consume the identifier - decl->define_decl.identifier = context->tok.id; - advance(context); + decl->define_decl.ident = symstr(c); + decl->define_decl.span = c->span; + advance(c); - if (try_consume(context, TOKEN_LESS)) + if (try_consume(c, TOKEN_LESS)) { decl->define_decl.define_kind = DEFINE_IDENT_GENERIC; - TypeInfo **params = parse_generic_parameters(context); + TypeInfo **params = parse_generic_parameters(c); if (!params) return poisoned_decl; decl->define_decl.generic_params = params; } RANGE_EXTEND_PREV(decl); - TRY_CONSUME_EOS_OR(poisoned_decl); + CONSUME_EOS_OR_RET(poisoned_decl); return decl; } /** * define_attribute ::= 'define' '@' IDENT '(' parameter_list ')' ('=' attribute_list)? */ -static inline Decl *parse_define_attribute(ParseContext *context, Visibility visibility) +static inline Decl *parse_define_attribute(ParseContext *c, Visibility visibility) { // 1. Store the beginning of the define. - TokenId start = context->tok.id; - advance_and_verify(context, TOKEN_DEFINE); + advance_and_verify(c, TOKEN_DEFINE); - advance_and_verify(context, TOKEN_AT); + advance_and_verify(c, TOKEN_AT); - TokenType alias_type = context->tok.type; + TokenType alias_type = c->tok; if (alias_type != TOKEN_TYPE_IDENT) { if (token_is_some_ident(alias_type) || token_is_keyword(alias_type)) { - SEMA_TOKEN_ERROR(context->tok, "A user defined attribute must start with an uppercase character, followed by at least one lower case."); + SEMA_ERROR_HERE("A user defined attribute must start with an uppercase character, followed by at least one lower case."); return false; } - SEMA_TOKEN_ERROR(context->tok, "The attribute name was expected here."); + SEMA_ERROR_HERE("The attribute name was expected here."); return false; } - Decl *decl = decl_new(DECL_DEFINE, context->tok.id, visibility); - advance_and_verify(context, TOKEN_TYPE_IDENT); + Decl *decl = decl_new(DECL_DEFINE, symstr(c), c->span, visibility); + advance_and_verify(c, TOKEN_TYPE_IDENT); Decl **parameters = NULL; - if (try_consume(context, TOKEN_LPAREN)) + if (try_consume(c, TOKEN_LPAREN)) { - if (!parse_parameters(context, visibility, ¶meters)) return false; - CONSUME_OR(TOKEN_RPAREN, poisoned_decl); + if (!parse_parameters(c, visibility, ¶meters)) return false; + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_decl); } Attr **attributes = NULL; - if (try_consume(context, TOKEN_EQ)) + if (try_consume(c, TOKEN_EQ)) { - if (!parse_attributes(context, &attributes)) return false; + if (!parse_attributes(c, &attributes)) return false; } decl->define_decl.define_kind = DEFINE_ATTRIBUTE; decl->define_decl.attributes.attrs = attributes; decl->define_decl.attributes.params = parameters; - // 3. Set up the define. - decl->span.loc = start; - RANGE_EXTEND_PREV(decl); - TRY_CONSUME_EOS_OR(poisoned_decl); + CONSUME_EOS_OR_RET(poisoned_decl); return decl; } /** * define_decl ::= DEFINE define_type_body | */ -static inline Decl *parse_define(ParseContext *context, Visibility visibility) +static inline Decl *parse_define(ParseContext *c, Visibility visibility) { - if (context->next_tok.type == TOKEN_AT) + switch (peek(c)) { - // define @foo = @inline, @noreturn - return parse_define_attribute(context, visibility); + case TOKEN_AT: + // define @foo = @inline, @noreturn + return parse_define_attribute(c, visibility); + case TOKEN_TYPE_IDENT: + return parse_define_type(c, visibility); + default: + return parse_define_ident(c, visibility); } - if (context->next_tok.type == TOKEN_TYPE_IDENT) - { - return parse_define_type(context, visibility); - } - return parse_define_ident(context, visibility); } +static inline bool parse_is_func_name(ParseContext *c) +{ + return tok_is(c, TOKEN_IDENT) && peek(c) != TOKEN_SCOPE; +} /** * func_header ::= type '!'? (type '.')? IDENT * macro_header ::= (type '!'?)? (type '.')? IDENT */ static inline bool -parse_func_macro_header(ParseContext *context, bool rtype_is_optional, TypeInfo **rtype_ref, TypeInfo **method_type_ref, - TokenId *name_ref) +parse_func_macro_header(ParseContext *c, bool rtype_is_optional, TypeInfo **rtype_ref, TypeInfo **method_type_ref, + const char **name_ref, SourceSpan *name_span) { TypeInfo *rtype = NULL; TypeInfo *method_type = NULL; - // 1. We have a macro with just a name, if so, then we set the name and we're done. - if (rtype_is_optional && !context_next_is_type_and_not_ident(context)) + // 1. If we have a macro and see the name, we're done. + if (rtype_is_optional && parse_is_func_name(c)) { goto RESULT; } // 2. Now we must have a type - either that is the return type or the method type. - ASSIGN_TYPE_ELSE(rtype, parse_failable_type(context), false); + ASSIGN_TYPE_OR_RET(rtype, parse_failable_type(c), false); // 4. We might have a type here, if so then we read it. - if (!TOKEN_IS(TOKEN_DOT) && context_next_is_type_and_not_ident(context)) + if (!tok_is(c, TOKEN_DOT) && !parse_is_func_name(c)) { - ASSIGN_TYPE_ELSE(method_type, parse_type(context), false); + ASSIGN_TYPE_OR_RET(method_type, parse_type(c), false); } // 5. If we have a dot here, then we need to interpret this as method function. - if (try_consume(context, TOKEN_DOT)) + if (try_consume(c, TOKEN_DOT)) { // 5a. What if we don't have a method type? if (!method_type) @@ -1724,8 +1656,7 @@ parse_func_macro_header(ParseContext *context, bool rtype_is_optional, TypeInfo // 5b. If the rtype is not optional or the return type was a failable, then this is an error. if (!rtype_is_optional || rtype->failable) { - SEMA_TOKID_ERROR(context->prev_tok, - "This looks like you are declaring a method without a return type?"); + SEMA_ERROR_LAST("This looks like you are declaring a method without a return type?"); return false; } method_type = rtype; @@ -1739,8 +1670,9 @@ parse_func_macro_header(ParseContext *context, bool rtype_is_optional, TypeInfo return false; } RESULT: - TRY_CONSUME_OR(TOKEN_IDENT, "Expected a name here.", false); - *name_ref = context->prev_tok; + *name_ref = symstr(c); + *name_span = c->span; + TRY_CONSUME_OR_RET(TOKEN_IDENT, "Expected a name here.", false); *rtype_ref = rtype; *method_type_ref = method_type; return true; @@ -1750,27 +1682,26 @@ parse_func_macro_header(ParseContext *context, bool rtype_is_optional, TypeInfo /** * macro ::= macro_header '(' macro_params ')' compound_statement */ -static inline Decl *parse_macro_declaration(ParseContext *context, Visibility visibility) +static inline Decl *parse_macro_declaration(ParseContext *c, Visibility visibility) { - DeclKind kind = try_consume(context, TOKEN_MACRO) ? DECL_MACRO : DECL_GENERIC; - if (kind == DECL_GENERIC) advance_and_verify(context, TOKEN_GENERIC); + DeclKind kind = try_consume(c, TOKEN_MACRO) ? DECL_MACRO : DECL_GENERIC; + if (kind == DECL_GENERIC) advance_and_verify(c, TOKEN_GENERIC); - Decl *decl = decl_new(kind, context->tok.id, visibility); + Decl *decl = decl_calloc(); + decl->decl_kind = kind; + decl->visibility = visibility; TypeInfo **rtype_ref = &decl->macro_decl.rtype; TypeInfo **method_type_ref = &decl->macro_decl.type_parent; - TokenId name; - if (!parse_func_macro_header(context, true, rtype_ref, method_type_ref, &name)) return poisoned_decl; + if (!parse_func_macro_header(c, true, rtype_ref, method_type_ref, &decl->name, &decl->span)) return poisoned_decl; - decl->name = TOKSTR(name); - decl->name_token = name; - TokenId block_parameter = NO_TOKEN_ID; - if (!parse_macro_arguments(context, visibility, &decl->macro_decl.parameters, &decl->macro_decl.body_parameters, &block_parameter)) return poisoned_decl; + const char *block_parameter = NULL; + if (!parse_macro_arguments(c, visibility, &decl->macro_decl.parameters, &decl->macro_decl.body_parameters, &block_parameter)) return poisoned_decl; decl->macro_decl.block_parameter = block_parameter; - if (!parse_attributes(context, &decl->attributes)) return poisoned_decl; + if (!parse_attributes(c, &decl->attributes)) return poisoned_decl; - ASSIGN_AST_ELSE(decl->macro_decl.body, parse_stmt(context), poisoned_decl); + ASSIGN_AST_OR_RET(decl->macro_decl.body, parse_stmt(c), poisoned_decl); return decl; } @@ -1781,55 +1712,44 @@ static inline Decl *parse_macro_declaration(ParseContext *context, Visibility vi * | ERRTYPE TYPE_IDENT '{' error_data '}' * ; */ -static inline Decl *parse_error_declaration(ParseContext *context, Visibility visibility) +static inline Decl *parse_optenum_declaration(ParseContext *c, Visibility visibility) { - advance(context); + advance(c); // advance_and_verify(context, TOKEN_ERRTYPE); - Decl *decl = decl_new_with_type(context->tok.id, DECL_ERRTYPE, visibility); + Decl *decl = decl_new_with_type(symstr(c), c->span, DECL_OPTENUM, visibility); - if (!consume_type_name(context, "error type")) return poisoned_decl; + if (!consume_type_name(c, "optenum")) return poisoned_decl; TypeInfo *type = NULL; - CONSUME_OR(TOKEN_LBRACE, poisoned_decl); + CONSUME_OR_RET(TOKEN_LBRACE, poisoned_decl); decl->enums.type_info = type_info_new_base(type_iptr->canonical, decl->span); - while (!try_consume(context, TOKEN_RBRACE)) + while (!try_consume(c, TOKEN_RBRACE)) { - Decl *enum_const = decl_new(DECL_ERRVALUE, context->tok.id, decl->visibility); - const char *name = enum_const->name; + Decl *opt_const = decl_new(DECL_OPTVALUE, symstr(c), c->span, decl->visibility); + if (!consume_const_name(c, "optional value")) + { + return poisoned_decl; + } + const char *name = opt_const->name; VECEACH(decl->enums.values, i) { Decl *other_constant = decl->enums.values[i]; if (other_constant->name == name) { - SEMA_TOKEN_ERROR(context->tok, "This enum constant is declared twice."); + SEMA_ERROR(opt_const, "This opt constant is declared twice."); SEMA_PREV(other_constant, "The previous declaration was here."); - decl_poison(enum_const); + decl_poison(opt_const); break; } } - if (!consume_const_name(context, "enum constant")) - { - return poisoned_decl; - } - if (try_consume(context, TOKEN_LPAREN)) - { - Expr **result = NULL; - if (!parse_arg_list(context, &result, TOKEN_RPAREN, NULL)) return poisoned_decl; - enum_const->enum_constant.args = result; - CONSUME_OR(TOKEN_RPAREN, poisoned_decl); - } - if (try_consume(context, TOKEN_EQ)) - { - ASSIGN_EXPR_ELSE(enum_const->enum_constant.expr, parse_expr(context), poisoned_decl); - } - vec_add(decl->enums.values, enum_const); + vec_add(decl->enums.values, opt_const); // Allow trailing ',' - if (!try_consume(context, TOKEN_COMMA)) + if (!try_consume(c, TOKEN_COMMA)) { - EXPECT_OR(TOKEN_RBRACE, poisoned_decl); + EXPECT_OR_RET(TOKEN_RBRACE, poisoned_decl); } } return decl; @@ -1841,24 +1761,24 @@ static inline Decl *parse_error_declaration(ParseContext *context, Visibility vi * | type '(' opt_parameter_type_list ')' * ; */ -static inline bool parse_enum_spec(ParseContext *context, TypeInfo **type_ref, Decl*** parameters_ref, Visibility parent_visibility) +static inline bool parse_enum_spec(ParseContext *c, TypeInfo **type_ref, Decl*** parameters_ref, Visibility parent_visibility) { - ASSIGN_TYPE_ELSE(*type_ref, parse_type(context), false); + ASSIGN_TYPE_OR_RET(*type_ref, parse_type(c), false); - if (!try_consume(context, TOKEN_LPAREN)) return true; - while (!try_consume(context, TOKEN_RPAREN)) + if (!try_consume(c, TOKEN_LPAREN)) return true; + while (!try_consume(c, TOKEN_RPAREN)) { - if (!parse_param_decl(context, parent_visibility, parameters_ref, true)) return false; + if (!parse_param_decl(c, parent_visibility, parameters_ref, true)) return false; assert(VECLAST(*parameters_ref)); if (VECLAST(*parameters_ref)->var.vararg) { - SEMA_TOKID_ERROR(context->prev_tok, "Vararg parameters are not allowed as enum parameters."); + SEMA_ERROR_LAST("Vararg parameters are not allowed as enum parameters."); return false; } - if (!try_consume(context, TOKEN_COMMA)) + if (!try_consume(c, TOKEN_COMMA)) { - EXPECT_OR(TOKEN_RPAREN, false); + EXPECT_OR_RET(TOKEN_RPAREN, false); } } return true; @@ -1886,59 +1806,58 @@ static inline bool parse_enum_spec(ParseContext *context, TypeInfo **type_ref, D * ; * */ -static inline Decl *parse_enum_declaration(ParseContext *context, Visibility visibility) +static inline Decl *parse_enum_declaration(ParseContext *c, Visibility visibility) { - advance_and_verify(context, TOKEN_ENUM); + advance_and_verify(c, TOKEN_ENUM); - if (!consume_type_name(context, "enum")) return poisoned_decl; - - Decl *decl = decl_new_with_type(context->prev_tok, DECL_ENUM, visibility); + Decl *decl = decl_new_with_type(symstr(c), c->span, DECL_ENUM, visibility); + if (!consume_type_name(c, "enum")) return poisoned_decl; TypeInfo *type = NULL; - if (try_consume(context, TOKEN_COLON)) + if (try_consume(c, TOKEN_COLON)) { - if (!parse_enum_spec(context, &type, &decl->enums.parameters, visibility)) return poisoned_decl; + if (!parse_enum_spec(c, &type, &decl->enums.parameters, visibility)) return poisoned_decl; } - CONSUME_OR(TOKEN_LBRACE, poisoned_decl); + CONSUME_OR_RET(TOKEN_LBRACE, poisoned_decl); decl->enums.type_info = type ? type : type_info_new_base(type_int, decl->span); - while (!try_consume(context, TOKEN_RBRACE)) + while (!try_consume(c, TOKEN_RBRACE)) { - Decl *enum_const = decl_new(DECL_ENUM_CONSTANT, context->tok.id, decl->visibility); + Decl *enum_const = decl_new(DECL_ENUM_CONSTANT, symstr(c), c->span, decl->visibility); const char *name = enum_const->name; + if (!consume_const_name(c, "enum constant")) + { + return poisoned_decl; + } VECEACH(decl->enums.values, i) { Decl *other_constant = decl->enums.values[i]; if (other_constant->name == name) { - SEMA_TOKEN_ERROR(context->tok, "This enum constant is declared twice."); + SEMA_ERROR(enum_const, "This enum constant is declared twice."); SEMA_PREV(other_constant, "The previous declaration was here."); decl_poison(enum_const); break; } } - if (!consume_const_name(context, "enum constant")) - { - return poisoned_decl; - } - if (try_consume(context, TOKEN_LPAREN)) + if (try_consume(c, TOKEN_LPAREN)) { Expr **result = NULL; - if (!parse_arg_list(context, &result, TOKEN_RPAREN, NULL)) return poisoned_decl; + if (!parse_arg_list(c, &result, TOKEN_RPAREN, NULL)) return poisoned_decl; enum_const->enum_constant.args = result; - CONSUME_OR(TOKEN_RPAREN, poisoned_decl); + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_decl); } - if (try_consume(context, TOKEN_EQ)) + if (try_consume(c, TOKEN_EQ)) { - ASSIGN_EXPR_ELSE(enum_const->enum_constant.expr, parse_expr(context), poisoned_decl); + ASSIGN_EXPR_OR_RET(enum_const->enum_constant.expr, parse_expr(c), poisoned_decl); } vec_add(decl->enums.values, enum_const); // Allow trailing ',' - if (!try_consume(context, TOKEN_COMMA)) + if (!try_consume(c, TOKEN_COMMA)) { - EXPECT_OR(TOKEN_RBRACE, poisoned_decl); + EXPECT_OR_RET(TOKEN_RBRACE, poisoned_decl); } } return decl; @@ -1969,60 +1888,58 @@ static inline Decl *parse_enum_declaration(ParseContext *context, Visibility vis * @param visibility * @return Decl* */ -static inline Decl *parse_func_definition(ParseContext *context, Visibility visibility, bool is_interface) +static inline Decl *parse_func_definition(ParseContext *c, Visibility visibility, bool is_interface) { - Decl *func = decl_new(DECL_FUNC, context->next_tok.id, visibility); - if (TOKEN_IS(TOKEN_FUNC)) + if (tok_is(c, TOKEN_FUNC)) { - advance_and_verify(context, TOKEN_FUNC); + advance_and_verify(c, TOKEN_FUNC); } else { - advance_and_verify(context, TOKEN_FN); + advance_and_verify(c, TOKEN_FN); } + Decl *func = decl_calloc(); + func->decl_kind = DECL_FUNC; + func->visibility = visibility; TypeInfo **rtype_ref = &func->func_decl.function_signature.returntype; TypeInfo **method_type_ref = &func->func_decl.type_parent; - TokenId name; - if (!parse_func_macro_header(context, false, rtype_ref, method_type_ref, &name)) return poisoned_decl; - func->name = TOKSTR(name); - func->name_token = name; - RANGE_EXTEND_PREV(func); - if (!parse_parameter_list(context, visibility, &(func->func_decl.function_signature), is_interface)) return poisoned_decl; - - if (!parse_attributes(context, &func->attributes)) return poisoned_decl; + if (!parse_func_macro_header(c, false, rtype_ref, method_type_ref, &func->name, &func->span)) return poisoned_decl; + if (!parse_parameter_list(c, visibility, &(func->func_decl.function_signature), is_interface)) return poisoned_decl; + if (!parse_attributes(c, &func->attributes)) return poisoned_decl; // TODO remove - is_interface = TOKEN_IS(TOKEN_EOS); + is_interface = tok_is(c, TOKEN_EOS); if (is_interface) { - if (TOKEN_IS(TOKEN_LBRACE)) + if (tok_is(c, TOKEN_LBRACE)) { - SEMA_TOKEN_ERROR(context->next_tok, "A function body is not allowed here."); + advance(c); + SEMA_ERROR_HERE("A function body is not allowed here."); return poisoned_decl; } - TRY_CONSUME_OR(TOKEN_EOS, "Expected ';' after function declaration.", poisoned_decl); + TRY_CONSUME_OR_RET(TOKEN_EOS, "Expected ';' after function declaration.", poisoned_decl); return func; } - TRY_EXPECT_OR(TOKEN_LBRACE, "Expected the beginning of a block with '{'", poisoned_decl); + TRY_EXPECT_OR_RET(TOKEN_LBRACE, "Expected the beginning of a block with '{'", poisoned_decl); - ASSIGN_AST_ELSE(func->func_decl.body, parse_compound_stmt(context), poisoned_decl); + ASSIGN_AST_OR_RET(func->func_decl.body, parse_compound_stmt(c), poisoned_decl); DEBUG_LOG("Finished parsing function %s", func->name); return func; } -static inline bool check_no_visibility_before(ParseContext *context, Visibility visibility) +static inline bool check_no_visibility_before(ParseContext *c, Visibility visibility) { switch (visibility) { case VISIBLE_MODULE: - SEMA_TOKEN_ERROR(context->tok, "Unexpected 'static' before '%.*s'.", TOKLEN(context->tok.id), TOKSTR(context->tok.id)); + SEMA_ERROR_HERE("Unexpected 'static' before '%s'", symstr(c)); return false; case VISIBLE_EXTERN: - SEMA_TOKEN_ERROR(context->tok, "Unexpected 'extern' before '%.*s'.", TOKLEN(context->tok.id), TOKSTR(context->tok.id)); + SEMA_ERROR_HERE("Unexpected 'extern' before '%s'.", symstr(c)); return false; default: return true; @@ -2037,37 +1954,37 @@ static inline bool check_no_visibility_before(ParseContext *context, Visibility * * @return true if import succeeded */ -static inline bool parse_import(ParseContext *context) +static inline bool parse_import(ParseContext *c) { - advance_and_verify(context, TOKEN_IMPORT); + advance_and_verify(c, TOKEN_IMPORT); - bool private = try_consume(context, TOKEN_PRIVATE); + bool private = try_consume(c, TOKEN_PRIVATE); bool is_not_first = false; while (1) { - if (!TOKEN_IS(TOKEN_IDENT)) + if (!tok_is(c, TOKEN_IDENT)) { if (is_not_first) { - SEMA_TOKID_ERROR(context->prev_tok, "Another module name was expected after the comma."); + SEMA_ERROR_LAST("Another module name was expected after the comma."); return false; } - if (TOKEN_IS(TOKEN_STRING)) + if (tok_is(c, TOKEN_STRING)) { - SEMA_TOKEN_ERROR(context->tok, "An import should be followed by a plain identifier, not a string. Did you accidentally put the module name between \"\"?"); + SEMA_ERROR_HERE("An import should be followed by a plain identifier, not a string. Did you accidentally put the module name between \"\"?"); return false; } - SEMA_TOKEN_ERROR(context->tok, "Import statement should be followed by the name of the module to import."); + SEMA_ERROR_HERE("Import statement should be followed by the name of the module to import."); return false; } is_not_first = true; - Path *path = parse_module_path(context); - unit_add_import(context->unit, path, NO_TOKEN, private); - if (!try_consume(context, TOKEN_COMMA)) break; + Path *path = parse_module_path(c); + unit_add_import(c->unit, path, private); + if (!try_consume(c, TOKEN_COMMA)) break; } - TRY_CONSUME_EOS_OR(false); + CONSUME_EOS_OR_RET(false); return true; } @@ -2075,22 +1992,80 @@ static inline bool parse_import(ParseContext *context) /** * imports ::= import* */ -void parse_imports(ParseContext *context) +void parse_imports(ParseContext *c) { - while (TOKEN_IS(TOKEN_IMPORT)) + while (tok_is(c, TOKEN_IMPORT)) { - if (!parse_import(context)) recover_top_level(context); + if (!parse_import(c)) recover_top_level(c); } } -static inline TokenId parse_doc_opt_rest_of_line(ParseContext *context) + + +static inline bool parse_doc_contract(ParseContext *c, Ast ***docs_ref, DocDirectiveKind kind) { - return try_consume(context, TOKEN_DOCS_LINE) ? context->prev_tok : INVALID_TOKEN_ID; + Ast *doc = new_ast(AST_DOC_DIRECTIVE, c->span); + const char *start = c->lexer.data.lex_start; + advance(c); + doc->doc_directive.kind = kind; + ASSIGN_EXPR_OR_RET(doc->doc_directive.contract.decl_exprs, parse_expression_list(c, kind == DOC_DIRECTIVE_CHECKED), false); + const char *end = c->data.lex_start; + while (end[-1] == ' ') end--; + scratch_buffer_clear(); + scratch_buffer_append("@require \""); + scratch_buffer_append_len(start, end - start); + scratch_buffer_append("\" violated"); + if (tok_is(c, TOKEN_STRING)) + { + scratch_buffer_append(": '"); + scratch_buffer_append(symstr(c)); + scratch_buffer_append("'."); + doc->doc_directive.contract.comment = scratch_buffer_copy(); + advance(c); + } + else + { + scratch_buffer_append("."); + doc->doc_directive.contract.expr_string = scratch_buffer_copy(); + } + vec_add(*docs_ref, doc); + return true; } -static inline bool parse_doc_param(ParseContext *context, Ast *docs) +static inline bool parse_doc_param(ParseContext *c, Ast ***docs_ref) { - switch (context->tok.type) + Ast *doc = new_ast(AST_DOC_DIRECTIVE, c->span); + advance(c); + + // [&inout] [in] [out] + bool is_ref = false; + InOutModifier mod = PARAM_ANY; + if (try_consume(c, TOKEN_LBRACKET)) + { + is_ref = try_consume(c, TOKEN_AMP); + const char *modifier = symstr(c); + if (!consume_ident(c, "Expected 'in', 'inout' or 'out'")) return false; + if (modifier == kw_in) + { + mod = PARAM_IN; + } + else if (modifier == kw_inout) + { + mod = PARAM_INOUT; + } + else if (modifier == kw_out) + { + mod = PARAM_OUT; + } + else + { + SEMA_ERROR_LAST("'in', 'out' or 'inout' were expected."); + return false; + } + CONSUME_OR_RET(TOKEN_RBRACKET, false); + } + + switch (c->tok) { case TOKEN_IDENT: case TOKEN_CT_IDENT: @@ -2103,27 +2078,32 @@ static inline bool parse_doc_param(ParseContext *context, Ast *docs) case TOKEN_HASH_IDENT: break; default: - SEMA_TOKEN_ERROR(context->tok, "Expected a parameter name here."); + SEMA_ERROR_HERE("Expected a parameter name here."); return false; } - docs->doc_directive.kind = DOC_DIRECTIVE_PARAM; - docs->doc_directive.param.param = context->tok.id; - advance(context); - docs->doc_directive.param.rest_of_line = parse_doc_opt_rest_of_line(context); + + doc->doc_directive.kind = DOC_DIRECTIVE_PARAM; + doc->doc_directive.param.name = symstr(c); + doc->doc_directive.param.span = c->span; + doc->doc_directive.param.modifier = mod; + doc->doc_directive.param.by_ref = is_ref; + advance(c); + try_consume(c, TOKEN_STRING); + vec_add(*docs_ref, doc); return true; } -static inline bool parse_doc_errors(ParseContext *context, Ast *docs) +static inline bool parse_doc_errors(ParseContext *c, Ast *docs) { TODO while (1) { - if (context->tok.type != TOKEN_TYPE_IDENT) + if (c->tok != TOKEN_TYPE_IDENT) { - SEMA_TOKEN_ERROR(context->tok, "Expected an error type here."); + SEMA_ERROR_HERE("Expected an optenum type here."); } } - switch (context->tok.type) + switch (c->tok) { case TOKEN_TYPE_IDENT: break; @@ -2131,79 +2111,68 @@ static inline bool parse_doc_errors(ParseContext *context, Ast *docs) return false; } docs->doc_directive.kind = DOC_DIRECTIVE_PARAM; - docs->doc_directive.param.param = context->tok.id; - advance(context); - docs->doc_directive.param.rest_of_line = parse_doc_opt_rest_of_line(context); + docs->doc_directive.param.name = symstr(c); + docs->doc_directive.param.span = c->span; + advance(c); return true; } -static inline bool parse_doc_contract(ParseContext *context, Ast *docs) -{ - ASSIGN_EXPR_ELSE(docs->doc_directive.contract.decl_exprs, parse_cond(context), false); - if (try_consume(context, TOKEN_COLON)) - { - ASSIGN_EXPR_ELSE(docs->doc_directive.contract.comment, parse_expr(context), false); - } - return true; -} - -static bool parse_docs(ParseContext *context, Ast **docs) +static bool parse_docs(ParseContext *c, Ast **docs) { *docs = NULL; - if (!try_consume(context, TOKEN_DOCS_START)) return true; + if (!try_consume(c, TOKEN_DOCS_START)) return true; + Ast *ast = ast_new_curr(c, AST_DOCS); - Ast *ast = new_ast(AST_DOCS, (SourceSpan) { .loc = context->prev_tok, .end_loc = context->prev_tok }); - while (!try_consume(context, TOKEN_DOCS_END)) + uint32_t row_last_row = c->span.row; + while (1) { + uint32_t row = c->span.row; // Spin past the lines and line ends - if (try_consume(context, TOKEN_DOCS_EOL)) continue; - if (try_consume(context, TOKEN_DOCS_LINE)) continue; - CONSUME_OR(TOKEN_DOCS_DIRECTIVE, false); - CONSUME_OR(TOKEN_IDENT, false); - const char *directive = TOKSTR(context->prev_tok); - SourceSpan span = { context->prev_tok, context->prev_tok }; - Ast *doc_ast = new_ast(AST_DOC_DIRECTIVE, span); - if (directive == kw_param) + switch (c->tok) { - if (!parse_doc_param(context, doc_ast)) return false; - goto LINE_END; + case TOKEN_DOCS_PARAM: + if (!parse_doc_param(c, &ast->directives)) return false; + break; + case TOKEN_DOCS_RETURN: + advance(c); + if (!consume(c, TOKEN_STRING, "Expected a string description.")) return false; + break; + case TOKEN_DOCS_REQUIRE: + if (!parse_doc_contract(c, &ast->directives, DOC_DIRECTIVE_REQUIRE)) return false; + break; + case TOKEN_DOCS_CHECKED: + if (!parse_doc_contract(c, &ast->directives, DOC_DIRECTIVE_CHECKED)) return false; + break; + case TOKEN_DOCS_ENSURE: + if (!parse_doc_contract(c, &ast->directives, DOC_DIRECTIVE_ENSURE)) return false; + break; + case TOKEN_DOCS_OPTRETURN: + if (!parse_doc_errors(c, ast)) return false; + break; + case TOKEN_DOCS_PURE: + advance(c); + // if (!parse_doc_pure(c, ast)) return false; + break; + case TOKEN_DOC_DIRECTIVE: + advance(c); + // Ignore + break; + case TOKEN_DOCS_END: + *docs = ast; + advance(c); + return true; + default: + if (row_last_row == row) + { + SEMA_ERROR_HERE("Expected end of line."); + return false; + } + SEMA_ERROR_HERE("Expected a directive or a comment."); + return false; } - if (directive == kw_pure) - { - vec_add(ast->directives, doc_ast); - doc_ast->doc_directive.kind = DOC_DIRECTIVE_PURE; - doc_ast->doc_directive.pure.rest_of_line = parse_doc_opt_rest_of_line(context); - goto LINE_END; - } - if (directive == kw_ensure) - { - doc_ast->doc_directive.kind = DOC_DIRECTIVE_ENSURE; - if (!parse_doc_contract(context, doc_ast)) return false; - goto LINE_END; - } - if (directive == kw_require) - { - doc_ast->doc_directive.kind = DOC_DIRECTIVE_REQUIRE; - if (!parse_doc_contract(context, doc_ast)) return false; - goto LINE_END; - } - if (directive == kw_errors) - { - if (!parse_doc_errors(context, doc_ast)) return false; - goto LINE_END; - } - doc_ast->doc_directive.kind = DOC_DIRECTIVE_UNKNOWN; - doc_ast->doc_directive.generic.directive_name = directive; - doc_ast->doc_directive.generic.rest_of_line = parse_doc_opt_rest_of_line(context); - -LINE_END: - vec_add(ast->directives, doc_ast); - if (try_consume(context, TOKEN_DOCS_EOL)) continue; - EXPECT_OR(TOKEN_DOCS_END, false); + row_last_row = row; } - *docs = ast; - return true; } /** @@ -2225,52 +2194,52 @@ LINE_END: * @param visibility * @return Decl* or a poison value if parsing failed */ -Decl *parse_top_level_statement(ParseContext *context) +Decl *parse_top_level_statement(ParseContext *c) { Ast *docs = NULL; - if (!parse_docs(context, &docs)) return poisoned_decl; + if (!parse_docs(c, &docs)) return poisoned_decl; Visibility visibility = VISIBLE_PUBLIC; - switch (context->tok.type) + switch (c->tok) { case TOKEN_PRIVATE: visibility = VISIBLE_MODULE; - advance(context); + advance(c); break; case TOKEN_EXTERN: visibility = VISIBLE_EXTERN; - advance(context); + advance(c); default: break; } Decl *decl; - TokenType type = context->tok.type; - switch (type) + TokenType tok = c->tok; + switch (tok) { case TOKEN_DOCS_START: if (visibility != VISIBLE_PUBLIC) { - SEMA_TOKEN_ERROR(context->tok, "Did not expect doc comments after visibility."); + SEMA_ERROR_HERE("Did not expect doc comments after visibility."); return poisoned_decl; } - SEMA_TOKEN_ERROR(context->tok, "There are more than one doc comment in a row, that is not allowed."); + SEMA_ERROR_HERE("There are more than one doc comment in a row, that is not allowed."); return poisoned_decl; case TOKEN_DEFINE: { - ASSIGN_DECL_ELSE(decl, parse_define(context, visibility), poisoned_decl); + ASSIGN_DECL_OR_RET(decl, parse_define(c, visibility), poisoned_decl); break; } case TOKEN_FUNC: case TOKEN_FN: { - ASSIGN_DECL_ELSE(decl, parse_func_definition(context, visibility, false), poisoned_decl); + ASSIGN_DECL_OR_RET(decl, parse_func_definition(c, visibility, false), poisoned_decl); break; } case TOKEN_CT_ASSERT: - if (!check_no_visibility_before(context, visibility)) return poisoned_decl; + if (!check_no_visibility_before(c, visibility)) return poisoned_decl; { - ASSIGN_AST_ELSE(Ast *ast, parse_ct_assert_stmt(context), poisoned_decl); - decl = decl_new_ct(DECL_CT_ASSERT, ast->span.loc); + ASSIGN_AST_OR_RET(Ast *ast, parse_ct_assert_stmt(c), poisoned_decl); + decl = decl_new_ct(DECL_CT_ASSERT, ast->span); decl->ct_assert_decl = ast; if (docs) { @@ -2281,8 +2250,8 @@ Decl *parse_top_level_statement(ParseContext *context) } case TOKEN_CT_IF: { - if (!check_no_visibility_before(context, visibility)) return poisoned_decl; - ASSIGN_DECL_ELSE(decl, parse_ct_if_top_level(context), poisoned_decl); + if (!check_no_visibility_before(c, visibility)) return poisoned_decl; + ASSIGN_DECL_OR_RET(decl, parse_ct_if_top_level(c), poisoned_decl); if (docs) { SEMA_ERROR(docs, "Unexpected doc comment before $if, did you mean to use a regular comment?"); @@ -2292,8 +2261,8 @@ Decl *parse_top_level_statement(ParseContext *context) } case TOKEN_CT_SWITCH: { - if (!check_no_visibility_before(context, visibility)) return poisoned_decl; - ASSIGN_DECL_ELSE(decl, parse_ct_switch_top_level(context), poisoned_decl); + if (!check_no_visibility_before(c, visibility)) return poisoned_decl; + ASSIGN_DECL_OR_RET(decl, parse_ct_switch_top_level(c), poisoned_decl); if (docs) { SEMA_ERROR(docs, "Unexpected doc comment before $switch, did you mean to use a regular comment?"); @@ -2303,29 +2272,29 @@ Decl *parse_top_level_statement(ParseContext *context) } case TOKEN_BITSTRUCT: { - ASSIGN_DECL_ELSE(decl, parse_bitstruct_declaration(context, visibility), poisoned_decl); + ASSIGN_DECL_OR_RET(decl, parse_bitstruct_declaration(c, visibility), poisoned_decl); break; } case TOKEN_CONST: { - ASSIGN_DECL_ELSE(decl, parse_top_level_const_declaration(context, visibility), poisoned_decl); + ASSIGN_DECL_OR_RET(decl, parse_top_level_const_declaration(c, visibility), poisoned_decl); break; } case TOKEN_STRUCT: case TOKEN_UNION: { - ASSIGN_DECL_ELSE(decl, parse_struct_declaration(context, visibility), poisoned_decl); + ASSIGN_DECL_OR_RET(decl, parse_struct_declaration(c, visibility), poisoned_decl); break; } case TOKEN_GENERIC: case TOKEN_MACRO: { - ASSIGN_DECL_ELSE(decl, parse_macro_declaration(context, visibility), poisoned_decl); + ASSIGN_DECL_OR_RET(decl, parse_macro_declaration(c, visibility), poisoned_decl); break; } case TOKEN_ENUM: { - ASSIGN_DECL_ELSE(decl, parse_enum_declaration(context, visibility), poisoned_decl); + ASSIGN_DECL_OR_RET(decl, parse_enum_declaration(c, visibility), poisoned_decl); break; } case TOKEN_OPTENUM: @@ -2334,36 +2303,37 @@ Decl *parse_top_level_statement(ParseContext *context) case TOKEN_RESNUM: case TOKEN_ERRTYPE: { - ASSIGN_DECL_ELSE(decl, parse_error_declaration(context, visibility), poisoned_decl); + ASSIGN_DECL_OR_RET(decl, parse_optenum_declaration(c, visibility), poisoned_decl); break; } case TOKEN_IDENT: - return parse_global_declaration(context, visibility); + return parse_global_declaration(c, visibility); case TOKEN_EOF: - SEMA_TOKID_ERROR(context->prev_tok, "Expected a top level declaration"); + SEMA_ERROR_LAST("Expected a top level declaration"); return poisoned_decl; case TOKEN_CT_CONST_IDENT: - if (context->next_tok.type == TOKEN_EQ) + { + if (peek(c) == TOKEN_EQ) { - SEMA_TOKEN_ERROR(context->tok, - "Did you forget a 'const' before the name of this compile time constant?"); + SEMA_ERROR_HERE("Did you forget a 'const' before the name of this compile time constant?"); } else { - SEMA_TOKEN_ERROR(context->tok, "Compile time constant unexpectedly found."); + SEMA_ERROR_HERE("Compile time constant unexpectedly found."); } + } return poisoned_decl; case TOKEN_IMPORT: - SEMA_TOKEN_ERROR(context->tok, "Imports are only allowed directly after the module declaration."); + SEMA_ERROR_HERE("Imports are only allowed directly after the module declaration."); return poisoned_decl; case TOKEN_TLOCAL: case TYPELIKE_TOKENS: { - ASSIGN_DECL_ELSE(decl, parse_global_declaration(context, visibility), poisoned_decl); + ASSIGN_DECL_OR_RET(decl, parse_global_declaration(c, visibility), poisoned_decl); break; } default: - SEMA_TOKEN_ERROR(context->tok, "Expected a top level declaration here."); + SEMA_ERROR_HERE("Expected a top level declaration here."); return poisoned_decl; break; } diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index dae1de8f6..dbb38d30e 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -6,9 +6,9 @@ #include "parser_internal.h" -Ast *parse_unreachable_stmt(ParseContext *context); +Ast *parse_unreachable_stmt(ParseContext *c); -Ast *parse_scoping_stmt(ParseContext *context); +Ast *parse_scoping_stmt(ParseContext *c); // --- Internal functions @@ -17,21 +17,21 @@ Ast *parse_scoping_stmt(ParseContext *context); * declaration_stmt * : declaration ';' */ -static inline Ast *parse_declaration_stmt(ParseContext *context) +static inline Ast *parse_declaration_stmt(ParseContext *c) { - Ast *decl_stmt = AST_NEW_TOKEN(AST_DECLARE_STMT, context->tok); - ASSIGN_DECL_ELSE(decl_stmt->declare_stmt, parse_decl(context), poisoned_ast); - CONSUME_OR(TOKEN_EOS, poisoned_ast); + Ast *decl_stmt = new_ast(AST_DECLARE_STMT, c->span); + ASSIGN_DECL_OR_RET(decl_stmt->declare_stmt, parse_decl(c), poisoned_ast); + CONSUME_OR_RET(TOKEN_EOS, poisoned_ast); return decl_stmt; } -static inline Decl *parse_optional_label(ParseContext *context, Ast *parent) +static inline Decl *parse_optional_label(ParseContext *c, Ast *parent) { - if (!TOKEN_IS(TOKEN_CONST_IDENT)) return NULL; - Decl *decl = decl_new(DECL_LABEL, context->tok.id, VISIBLE_LOCAL); + if (!tok_is(c, TOKEN_CONST_IDENT)) return NULL; + Decl *decl = decl_new(DECL_LABEL, symstr(c), c->span, VISIBLE_LOCAL); decl->label.parent = astid(parent); - advance_and_verify(context, TOKEN_CONST_IDENT); - if (!try_consume(context, TOKEN_COLON)) + advance_and_verify(c, TOKEN_CONST_IDENT); + if (!try_consume(c, TOKEN_COLON)) { SEMA_ERROR(decl, "The name must be followed by a ':', did you forget it?"); return poisoned_decl; @@ -39,32 +39,32 @@ static inline Decl *parse_optional_label(ParseContext *context, Ast *parent) return decl; } -static inline void parse_optional_label_target(ParseContext *context, Label *label) +static inline void parse_optional_label_target(ParseContext *c, Label *label) { - if (TOKEN_IS(TOKEN_CONST_IDENT)) + if (tok_is(c, TOKEN_CONST_IDENT)) { - label->span = context->tok.id; - label->name = TOKSTR(context->tok); - advance_and_verify(context, TOKEN_CONST_IDENT); + label->span = c->span; + label->name = symstr(c); + advance_and_verify(c, TOKEN_CONST_IDENT); } } /** * asm ::= 'asm' '(' string ')' - * @param context + * @param c * @return */ -static inline Ast* parse_asm_stmt(ParseContext *context) +static inline Ast* parse_asm_stmt(ParseContext *c) { - Ast *ast = AST_NEW_TOKEN(AST_ASM_STMT, context->tok); - advance_and_verify(context, TOKEN_ASM); + Ast *ast = new_ast(AST_ASM_STMT, c->span); + advance_and_verify(c, TOKEN_ASM); // TODO use attributes, like volatile - CONSUME_OR(TOKEN_LPAREN, poisoned_ast); - ASSIGN_EXPR_ELSE(ast->asm_stmt.body, parse_expr(context), poisoned_ast); + CONSUME_OR_RET(TOKEN_LPAREN, poisoned_ast); + ASSIGN_EXPR_OR_RET(ast->asm_stmt.body, parse_expr(c), poisoned_ast); ast->asm_stmt.is_volatile = true; - CONSUME_OR(TOKEN_RPAREN, poisoned_ast); + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_ast); RANGE_EXTEND_PREV(ast); - CONSUME_OR(TOKEN_EOS, poisoned_ast); + CONSUME_OR_RET(TOKEN_EOS, poisoned_ast); return ast; } @@ -73,21 +73,21 @@ static inline Ast* parse_asm_stmt(ParseContext *context) * do_stmt * : DO statement WHILE '(' expression ')' ';' */ -static inline Ast* parse_do_stmt(ParseContext *context) +static inline Ast* parse_do_stmt(ParseContext *c) { - Ast *do_ast = AST_NEW_TOKEN(AST_DO_STMT, context->tok); + Ast *do_ast = new_ast(AST_DO_STMT, c->span); - advance_and_verify(context, TOKEN_DO); + advance_and_verify(c, TOKEN_DO); - ASSIGN_DECL_ELSE(do_ast->do_stmt.flow.label, parse_optional_label(context, do_ast), poisoned_ast); - ASSIGN_AST_ELSE(do_ast->do_stmt.body, parse_stmt(context), poisoned_ast); + ASSIGN_DECL_OR_RET(do_ast->do_stmt.flow.label, parse_optional_label(c, do_ast), poisoned_ast); + ASSIGN_AST_OR_RET(do_ast->do_stmt.body, parse_stmt(c), poisoned_ast); - if (try_consume(context, TOKEN_WHILE)) + if (try_consume(c, TOKEN_WHILE)) { - CONSUME_OR(TOKEN_LPAREN, poisoned_ast); - ASSIGN_EXPR_ELSE(do_ast->do_stmt.expr, parse_expr(context), poisoned_ast); - CONSUME_OR(TOKEN_RPAREN, poisoned_ast); - CONSUME_OR(TOKEN_EOS, poisoned_ast); + CONSUME_OR_RET(TOKEN_LPAREN, poisoned_ast); + ASSIGN_EXPR_OR_RET(do_ast->do_stmt.expr, parse_expr(c), poisoned_ast); + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_ast); + CONSUME_OR_RET(TOKEN_EOS, poisoned_ast); } return do_ast; @@ -98,14 +98,14 @@ static inline bool token_type_ends_case(TokenType type, TokenType case_type, Tok return type == case_type || type == default_type || type == TOKEN_RBRACE || type == TOKEN_CT_ENDSWITCH; } -static inline Ast *parse_case_stmts(ParseContext *context, TokenType case_type, TokenType default_type) +static inline Ast *parse_case_stmts(ParseContext *c, TokenType case_type, TokenType default_type) { - if (token_type_ends_case(context->tok.type, case_type, default_type)) return NULL; - Ast *compound = AST_NEW_TOKEN(AST_COMPOUND_STMT, context->tok); + if (token_type_ends_case(c->tok, case_type, default_type)) return NULL; + Ast *compound = new_ast(AST_COMPOUND_STMT, c->span); AstId *next = &compound->compound_stmt.first_stmt; - while (!token_type_ends_case(context->tok.type, case_type, default_type)) + while (!token_type_ends_case(c->tok, case_type, default_type)) { - ASSIGN_AST_ELSE(Ast *stmt, parse_stmt(context), poisoned_ast); + ASSIGN_AST_OR_RET(Ast *stmt, parse_stmt(c), poisoned_ast); ast_append(&next, stmt); } return compound; @@ -117,11 +117,11 @@ static inline Ast *parse_case_stmts(ParseContext *context, TokenType case_type, * defer_stmt * : DEFER statement */ -static inline Ast* parse_defer_stmt(ParseContext *context) +static inline Ast* parse_defer_stmt(ParseContext *c) { - advance_and_verify(context, TOKEN_DEFER); - Ast *defer_stmt = AST_NEW_TOKEN(AST_DEFER_STMT, context->tok); - ASSIGN_AST_ELSE(defer_stmt->defer_stmt.body, parse_stmt(context), poisoned_ast); + advance_and_verify(c, TOKEN_DEFER); + Ast *defer_stmt = new_ast(AST_DEFER_STMT, c->span); + ASSIGN_AST_OR_RET(defer_stmt->defer_stmt.body, parse_stmt(c), poisoned_ast); return defer_stmt; } @@ -129,17 +129,17 @@ static inline Ast* parse_defer_stmt(ParseContext *context) * while_stmt * : WHILE '(' control_expression ')' statement */ -static inline Ast* parse_while_stmt(ParseContext *context) +static inline Ast* parse_while_stmt(ParseContext *c) { - Ast *while_ast = AST_NEW_TOKEN(AST_WHILE_STMT, context->tok); - advance_and_verify(context, TOKEN_WHILE); + Ast *while_ast = new_ast(AST_WHILE_STMT, c->span); + advance_and_verify(c, TOKEN_WHILE); - ASSIGN_DECL_ELSE(while_ast->while_stmt.flow.label, parse_optional_label(context, while_ast), poisoned_ast); + ASSIGN_DECL_OR_RET(while_ast->while_stmt.flow.label, parse_optional_label(c, while_ast), poisoned_ast); - CONSUME_OR(TOKEN_LPAREN, poisoned_ast); - ASSIGN_EXPR_ELSE(while_ast->while_stmt.cond, parse_cond(context), poisoned_ast); - CONSUME_OR(TOKEN_RPAREN, poisoned_ast); - ASSIGN_AST_ELSE(while_ast->while_stmt.body, parse_stmt(context), poisoned_ast); + CONSUME_OR_RET(TOKEN_LPAREN, poisoned_ast); + ASSIGN_EXPR_OR_RET(while_ast->while_stmt.cond, parse_cond(c), poisoned_ast); + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_ast); + ASSIGN_AST_OR_RET(while_ast->while_stmt.body, parse_stmt(c), poisoned_ast); return while_ast; } @@ -162,49 +162,43 @@ static inline Ast* parse_while_stmt(ParseContext *context) * | IF '(' control_expression ')' compound_statement ELSE compound_statement * ; */ -static inline Ast* parse_if_stmt(ParseContext *context) +static inline Ast* parse_if_stmt(ParseContext *c) { - Ast *if_ast = AST_NEW_TOKEN(AST_IF_STMT, context->tok); - advance_and_verify(context, TOKEN_IF); - ASSIGN_DECL_ELSE(if_ast->if_stmt.flow.label, parse_optional_label(context, if_ast), poisoned_ast); - CONSUME_OR(TOKEN_LPAREN, poisoned_ast); - ASSIGN_EXPR_ELSE(if_ast->if_stmt.cond, parse_cond(context), poisoned_ast); - CONSUME_OR(TOKEN_RPAREN, poisoned_ast); + Ast *if_ast = new_ast(AST_IF_STMT, c->span); + advance_and_verify(c, TOKEN_IF); + ASSIGN_DECL_OR_RET(if_ast->if_stmt.flow.label, parse_optional_label(c, if_ast), poisoned_ast); + CONSUME_OR_RET(TOKEN_LPAREN, poisoned_ast); + ASSIGN_EXPR_OR_RET(if_ast->if_stmt.cond, parse_cond(c), poisoned_ast); + unsigned row = c->span.row; + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_ast); // Special case, we might have if ( ) { case ... } - if (tok_is(context, TOKEN_LBRACE) && (context->next_tok.type == TOKEN_CASE || context->next_tok.type == TOKEN_DEFAULT)) + if (tok_is(c, TOKEN_LBRACE) && (peek(c) == TOKEN_CASE || peek(c) == TOKEN_DEFAULT)) { - Ast *stmt = AST_NEW_TOKEN(AST_IF_CATCH_SWITCH_STMT, context->tok); + Ast *stmt = new_ast(AST_IF_CATCH_SWITCH_STMT, c->span); Ast **cases = NULL; - if (!parse_switch_body(context, &cases, TOKEN_CASE, TOKEN_DEFAULT, true)) return poisoned_ast; + if (!parse_switch_body(c, &cases, TOKEN_CASE, TOKEN_DEFAULT, true)) return poisoned_ast; stmt->switch_stmt.cases = cases; if_ast->if_stmt.then_body = stmt; } else { - ASSIGN_AST_ELSE(if_ast->if_stmt.then_body, parse_stmt(context), poisoned_ast); + unsigned next_row = c->span.row; + ASSIGN_AST_OR_RET(if_ast->if_stmt.then_body, parse_stmt(c), poisoned_ast); + if (row != next_row && if_ast->if_stmt.then_body->ast_kind != AST_COMPOUND_STMT) + { + // Poison it and pick it up later. + ast_poison(if_ast->if_stmt.then_body); + } } - if (!try_consume(context, TOKEN_ELSE)) + if (!try_consume(c, TOKEN_ELSE)) { return if_ast; } - ASSIGN_AST_ELSE(if_ast->if_stmt.else_body, parse_stmt(context), poisoned_ast); + ASSIGN_AST_OR_RET(if_ast->if_stmt.else_body, parse_stmt(c), poisoned_ast); return if_ast; } - -static bool parse_type_or_expr(ParseContext *context, TypeInfo **type_info, Expr **expr) -{ - if (parse_next_is_case_type(context)) - { - ASSIGN_TYPE_ELSE(*type_info, parse_type(context), false); - return true; - } - ASSIGN_EXPR_ELSE(*expr, parse_constant_expr(context), false); - - return true; -} - /** * * case_stmt @@ -213,23 +207,23 @@ static bool parse_type_or_expr(ParseContext *context, TypeInfo **type_info, Expr * | CAST type ':' cast_stmts * ; */ -static inline Ast *parse_case_stmt(ParseContext *context, TokenType case_type, TokenType default_type) +static inline Ast *parse_case_stmt(ParseContext *c, TokenType case_type, TokenType default_type) { - Ast *ast = AST_NEW_TOKEN(AST_CASE_STMT, context->tok); - advance(context); - ASSIGN_EXPR_ELSE(ast->case_stmt.expr, parse_expr(context), poisoned_ast); + Ast *ast = new_ast(AST_CASE_STMT, c->span); + advance(c); + ASSIGN_EXPR_OR_RET(ast->case_stmt.expr, parse_expr(c), poisoned_ast); // Change type -> type.typeid if (ast->case_stmt.expr->expr_kind == EXPR_TYPEINFO) { ast->case_stmt.expr->expr_kind = EXPR_TYPEID; } - if (try_consume(context, TOKEN_DOTDOT)) + if (try_consume(c, TOKEN_DOTDOT)) { - ASSIGN_EXPR_ELSE(ast->case_stmt.to_expr, parse_expr(context), poisoned_ast); + ASSIGN_EXPR_OR_RET(ast->case_stmt.to_expr, parse_expr(c), poisoned_ast); } TRY_CONSUME(TOKEN_COLON, "Missing ':' after case"); - extend_ast_with_prev_token(context, ast); - ASSIGN_AST_ELSE(ast->case_stmt.body, parse_case_stmts(context, case_type, default_type), poisoned_ast); + RANGE_EXTEND_PREV(ast); + ASSIGN_AST_OR_RET(ast->case_stmt.body, parse_case_stmts(c, case_type, default_type), poisoned_ast); return ast; } @@ -237,13 +231,13 @@ static inline Ast *parse_case_stmt(ParseContext *context, TokenType case_type, T * default_stmt * : DEFAULT ':' case_stmts */ -static inline Ast *parse_default_stmt(ParseContext *context, TokenType case_type, TokenType default_type) +static inline Ast *parse_default_stmt(ParseContext *c, TokenType case_type, TokenType default_type) { - Ast *ast = AST_NEW_TOKEN(AST_DEFAULT_STMT, context->tok); - advance(context); - TRY_CONSUME_OR(TOKEN_COLON, "Expected ':' after 'default'.", poisoned_ast); - extend_ast_with_prev_token(context, ast); - ASSIGN_AST_ELSE(ast->case_stmt.body, parse_case_stmts(context, case_type, default_type), poisoned_ast); + Ast *ast = new_ast(AST_DEFAULT_STMT, c->span); + advance(c); + TRY_CONSUME_OR_RET(TOKEN_COLON, "Expected ':' after 'default'.", poisoned_ast); + RANGE_EXTEND_PREV(ast); + ASSIGN_AST_OR_RET(ast->case_stmt.body, parse_case_stmts(c, case_type, default_type), poisoned_ast); ast->case_stmt.expr = NULL; return ast; } @@ -256,25 +250,25 @@ static inline Ast *parse_default_stmt(ParseContext *context, TokenType case_type * | default_stmt switch body * ; */ -bool parse_switch_body(ParseContext *context, Ast ***cases, TokenType case_type, TokenType default_type, +bool parse_switch_body(ParseContext *c, Ast ***cases, TokenType case_type, TokenType default_type, bool allow_multiple_values) { - CONSUME_OR(TOKEN_LBRACE, false); - while (!try_consume(context, TOKEN_RBRACE)) + CONSUME_OR_RET(TOKEN_LBRACE, false); + while (!try_consume(c, TOKEN_RBRACE)) { Ast *result; - TokenType next = context->tok.type; - if (next == case_type) + TokenType tok = c->tok; + if (tok == case_type) { - ASSIGN_AST_ELSE(result, parse_case_stmt(context, case_type, default_type), false); + ASSIGN_AST_OR_RET(result, parse_case_stmt(c, case_type, default_type), false); } - else if (next == default_type) + else if (tok == default_type) { - ASSIGN_AST_ELSE(result, parse_default_stmt(context, case_type, default_type), false); + ASSIGN_AST_OR_RET(result, parse_default_stmt(c, case_type, default_type), false); } else { - SEMA_TOKEN_ERROR(context->tok, "A 'case' or 'default' would be needed here, '%.*s' is not allowed.", TOKLEN(context->tok.id), TOKSTR(context->tok.id)); + SEMA_ERROR_HERE("A 'case' or 'default' would be needed here."); return false; } vec_add((*cases), result); @@ -287,16 +281,16 @@ bool parse_switch_body(ParseContext *context, Ast ***cases, TokenType case_type, * switch * : SWITCH '(' decl_expr_list ')' '{' switch_body '}' */ -static inline Ast* parse_switch_stmt(ParseContext *context) +static inline Ast* parse_switch_stmt(ParseContext *c) { - Ast *switch_ast = AST_NEW_TOKEN(AST_SWITCH_STMT, context->tok); - advance_and_verify(context, TOKEN_SWITCH); - ASSIGN_DECL_ELSE(switch_ast->switch_stmt.flow.label, parse_optional_label(context, switch_ast), poisoned_ast); - CONSUME_OR(TOKEN_LPAREN, poisoned_ast); - ASSIGN_EXPR_ELSE(switch_ast->switch_stmt.cond, parse_cond(context), poisoned_ast); - CONSUME_OR(TOKEN_RPAREN, poisoned_ast); + Ast *switch_ast = new_ast(AST_SWITCH_STMT, c->span); + advance_and_verify(c, TOKEN_SWITCH); + ASSIGN_DECL_OR_RET(switch_ast->switch_stmt.flow.label, parse_optional_label(c, switch_ast), poisoned_ast); + CONSUME_OR_RET(TOKEN_LPAREN, poisoned_ast); + ASSIGN_EXPR_OR_RET(switch_ast->switch_stmt.cond, parse_cond(c), poisoned_ast); + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_ast); - if (!parse_switch_body(context, &switch_ast->switch_stmt.cases, TOKEN_CASE, TOKEN_DEFAULT, false)) return poisoned_ast; + if (!parse_switch_body(c, &switch_ast->switch_stmt.cases, TOKEN_CASE, TOKEN_DEFAULT, false)) return poisoned_ast; return switch_ast; } @@ -311,71 +305,71 @@ static inline Ast* parse_switch_stmt(ParseContext *context) * | FOR '(' ';' expression ';' expression_list ')' statement * ; */ -static inline Ast* parse_for_stmt(ParseContext *context) +static inline Ast* parse_for_stmt(ParseContext *c) { - Ast *ast = AST_NEW_TOKEN(AST_FOR_STMT, context->tok); - advance_and_verify(context, TOKEN_FOR); - ASSIGN_DECL_ELSE(ast->for_stmt.flow.label, parse_optional_label(context, ast), poisoned_ast); - CONSUME_OR(TOKEN_LPAREN, poisoned_ast); + Ast *ast = new_ast(AST_FOR_STMT, c->span); + advance_and_verify(c, TOKEN_FOR); + ASSIGN_DECL_OR_RET(ast->for_stmt.flow.label, parse_optional_label(c, ast), poisoned_ast); + CONSUME_OR_RET(TOKEN_LPAREN, poisoned_ast); - if (!TOKEN_IS(TOKEN_EOS)) + if (!tok_is(c, TOKEN_EOS)) { - ASSIGN_EXPR_ELSE(ast->for_stmt.init, parse_expression_list(context, true), poisoned_ast); + ASSIGN_EXPR_OR_RET(ast->for_stmt.init, parse_expression_list(c, true), poisoned_ast); } else { ast->for_stmt.init = NULL; } - CONSUME_OR(TOKEN_EOS, poisoned_ast); + CONSUME_OR_RET(TOKEN_EOS, poisoned_ast); - if (!TOKEN_IS(TOKEN_EOS)) + if (!tok_is(c, TOKEN_EOS)) { - ASSIGN_EXPR_ELSE(ast->for_stmt.cond, parse_cond(context), poisoned_ast); + ASSIGN_EXPR_OR_RET(ast->for_stmt.cond, parse_cond(c), poisoned_ast); } - CONSUME_OR(TOKEN_EOS, poisoned_ast); + CONSUME_OR_RET(TOKEN_EOS, poisoned_ast); - if (!TOKEN_IS(TOKEN_RPAREN)) + if (!tok_is(c, TOKEN_RPAREN)) { - ast->for_stmt.incr = parse_expression_list(context, false); + ast->for_stmt.incr = parse_expression_list(c, false); } - CONSUME_OR(TOKEN_RPAREN, poisoned_ast); + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_ast); - extend_ast_with_prev_token(context, ast); - ASSIGN_AST_ELSE(Ast *body, parse_stmt(context), poisoned_ast); + RANGE_EXTEND_PREV(ast); + ASSIGN_AST_OR_RET(Ast *body, parse_stmt(c), poisoned_ast); ast->for_stmt.body = astid(body); return ast; } -static inline bool parse_foreach_var(ParseContext *context, Ast *foreach) +static inline bool parse_foreach_var(ParseContext *c, Ast *foreach) { TypeInfo *type = NULL; // If we don't get foreach (foo ... or foreach (*foo ... then a type is expected. - if (!TOKEN_IS(TOKEN_IDENT) && !TOKEN_IS(TOKEN_AMP)) + if (!tok_is(c, TOKEN_IDENT) && !tok_is(c, TOKEN_AMP)) { - ASSIGN_TYPE_ELSE(type, parse_failable_type(context), false); + ASSIGN_TYPE_OR_RET(type, parse_failable_type(c), false); // Add the failable to the type for nicer error reporting. RANGE_EXTEND_PREV(type); } - if (try_consume(context, TOKEN_AMP)) + if (try_consume(c, TOKEN_AMP)) { foreach->foreach_stmt.value_by_ref = true; } - if (!try_consume(context, TOKEN_IDENT)) + Decl *var = decl_new_var(symstr(c), c->span, type, VARDECL_LOCAL, VISIBLE_LOCAL); + if (!try_consume(c, TOKEN_IDENT)) { if (type) { - SEMA_TOKEN_ERROR(context->tok, "Expected an identifier after the type."); + SEMA_ERROR_HERE("Expected an identifier after the type."); return false; } - SEMA_TOKEN_ERROR(context->tok, "Expected an identifier or type."); + SEMA_ERROR_HERE("Expected an identifier or type."); return false; } - Decl *var = decl_new_var(context->prev_tok, type, VARDECL_LOCAL, VISIBLE_LOCAL); foreach->foreach_stmt.variable = var; return true; } @@ -384,18 +378,18 @@ static inline bool parse_foreach_var(ParseContext *context, Ast *foreach) * : FOREACH (CONST_IDENT ':')? '(' type? '*'? IDENT (',' type? '*'? IDENT) ':' expression ')' statement * ; */ -static inline Ast* parse_foreach_stmt(ParseContext *context) +static inline Ast* parse_foreach_stmt(ParseContext *c) { - Ast *ast = AST_NEW_TOKEN(AST_FOREACH_STMT, context->tok); - advance_and_verify(context, TOKEN_FOREACH); - ASSIGN_DECL_ELSE(ast->foreach_stmt.flow.label, parse_optional_label(context, ast), poisoned_ast); - CONSUME_OR(TOKEN_LPAREN, poisoned_ast); + Ast *ast = new_ast(AST_FOREACH_STMT, c->span); + advance_and_verify(c, TOKEN_FOREACH); + ASSIGN_DECL_OR_RET(ast->foreach_stmt.flow.label, parse_optional_label(c, ast), poisoned_ast); + CONSUME_OR_RET(TOKEN_LPAREN, poisoned_ast); // Parse the first variable. - if (!parse_foreach_var(context, ast)) return poisoned_ast; + if (!parse_foreach_var(c, ast)) return poisoned_ast; // Do we have a second variable? - if (try_consume(context, TOKEN_COMMA)) + if (try_consume(c, TOKEN_COMMA)) { // Copy the first variable to "index" ast->foreach_stmt.index = ast->foreach_stmt.variable; @@ -403,17 +397,17 @@ static inline Ast* parse_foreach_stmt(ParseContext *context) ast->foreach_stmt.value_by_ref = false; // Parse the second variable - if (!parse_foreach_var(context, ast)) return poisoned_ast; + if (!parse_foreach_var(c, ast)) return poisoned_ast; } - CONSUME_OR(TOKEN_COLON, poisoned_ast); + CONSUME_OR_RET(TOKEN_COLON, poisoned_ast); - ASSIGN_EXPR_ELSE(ast->foreach_stmt.enumeration, parse_initializer(context), poisoned_ast); + ASSIGN_EXPR_OR_RET(ast->foreach_stmt.enumeration, parse_initializer(c), poisoned_ast); - CONSUME_OR(TOKEN_RPAREN, poisoned_ast); + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_ast); - extend_ast_with_prev_token(context, ast); - ASSIGN_AST_ELSE(ast->foreach_stmt.body, parse_stmt(context), poisoned_ast); + RANGE_EXTEND_PREV(ast); + ASSIGN_AST_OR_RET(ast->foreach_stmt.body, parse_stmt(c), poisoned_ast); return ast; } @@ -421,11 +415,11 @@ static inline Ast* parse_foreach_stmt(ParseContext *context) * continue_stmt * : CONTINUE */ -static inline Ast* parse_continue(ParseContext *context) +static inline Ast* parse_continue(ParseContext *c) { - 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); + Ast *ast = new_ast(AST_CONTINUE_STMT, c->span); + advance_and_verify(c, TOKEN_CONTINUE); + parse_optional_label_target(c, &ast->contbreak_stmt.label); if (ast->contbreak_stmt.label.name) ast->contbreak_stmt.is_label = true; return ast; } @@ -436,29 +430,21 @@ static inline Ast* parse_continue(ParseContext *context) * : NEXT * | NEXT expr */ -static inline Ast* parse_next(ParseContext *context) +static inline Ast* parse_next(ParseContext *c) { - Ast *ast = AST_NEW_TOKEN(AST_NEXT_STMT, context->tok); - advance_and_verify(context, TOKEN_NEXTCASE); - if (!TOKEN_IS(TOKEN_EOS)) + Ast *ast = new_ast(AST_NEXT_STMT, c->span); + advance_and_verify(c, TOKEN_NEXTCASE); + if (!tok_is(c, TOKEN_EOS)) { - if (TOKEN_IS(TOKEN_CONST_IDENT) && context->next_tok.type == TOKEN_COLON) + if (tok_is(c, TOKEN_CONST_IDENT) && peek(c) == TOKEN_COLON) { - parse_optional_label_target(context, &ast->nextcase_stmt.label); - advance_and_verify(context, TOKEN_COLON); + parse_optional_label_target(c, &ast->nextcase_stmt.label); + advance_and_verify(c, TOKEN_COLON); } TypeInfo *type = NULL; Expr *expr = NULL; - if (!parse_type_or_expr(context, &type, &expr)) return poisoned_ast; - if (type) - { - ast->nextcase_stmt.is_type = true; - ast->nextcase_stmt.expr_or_type_info = type; - } - else - { - ast->nextcase_stmt.expr_or_type_info = expr; - } + + ASSIGN_EXPR_OR_RET(ast->nextcase_stmt.expr, parse_expr(c), poisoned_ast); } return ast; } @@ -467,11 +453,11 @@ static inline Ast* parse_next(ParseContext *context) * break * : BREAK */ -static inline Ast* parse_break(ParseContext *context) +static inline Ast* parse_break(ParseContext *c) { - 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); + Ast *ast = new_ast(AST_BREAK_STMT, c->span); + advance_and_verify(c, TOKEN_BREAK); + parse_optional_label_target(c, &ast->contbreak_stmt.label); if (ast->contbreak_stmt.label.name) ast->contbreak_stmt.is_label = true; return ast; } @@ -481,19 +467,29 @@ static inline Ast* parse_break(ParseContext *context) * : expression EOS * ; */ -static inline Ast *parse_expr_stmt(ParseContext *context) +static inline Ast *parse_expr_stmt(ParseContext *c) { - Ast *stmt = AST_NEW_TOKEN(AST_EXPR_STMT, context->tok); - ASSIGN_EXPR_ELSE(stmt->expr_stmt, parse_expr(context), poisoned_ast); - TRY_CONSUME_EOS(); + Ast *stmt = new_ast(AST_EXPR_STMT, c->span); + ASSIGN_EXPR_OR_RET(stmt->expr_stmt, parse_expr(c), poisoned_ast); + RANGE_EXTEND_PREV(stmt); + do + { + if (!tok_is(c, TOKEN_EOS)) + { + sema_error_at_after(c->prev_span, "Expected ';'"); + return poisoned_ast; + } + advance(c); + } + while (0); return stmt; } -static inline Ast *parse_decl_or_expr_stmt(ParseContext *context) +static inline Ast *parse_decl_or_expr_stmt(ParseContext *c) { - ASSIGN_EXPR_ELSE(Expr *expr, parse_expr(context), poisoned_ast); + ASSIGN_EXPR_OR_RET(Expr * expr, parse_expr(c), poisoned_ast); Ast *ast = ast_calloc(); ast->span = expr->span; // We might be parsing "int!" @@ -506,14 +502,14 @@ static inline Ast *parse_decl_or_expr_stmt(ParseContext *context) if (expr->expr_kind == EXPR_TYPEINFO) { ast->ast_kind = AST_DECLARE_STMT; - ASSIGN_DECL_ELSE(ast->declare_stmt, parse_decl_after_type(context, expr->type_expr), poisoned_ast); + ASSIGN_DECL_OR_RET(ast->declare_stmt, parse_decl_after_type(c, expr->type_expr), poisoned_ast); } else { ast->ast_kind = AST_EXPR_STMT; ast->expr_stmt = expr; } - CONSUME_OR(TOKEN_EOS, poisoned_ast); + CONSUME_OR_RET(TOKEN_EOS, poisoned_ast); return ast; } @@ -523,24 +519,33 @@ static inline Ast *parse_decl_or_expr_stmt(ParseContext *context) * | var CT_TYPE '=' const_expr EOS * ; */ -static inline Ast *parse_var_stmt(ParseContext *context) +static inline Ast *parse_var_stmt(ParseContext *c) { - Ast *ast = AST_NEW_TOKEN(AST_DECLARE_STMT, context->tok); - ASSIGN_DECL_ELSE(ast->var_stmt, parse_var_decl(context), poisoned_ast); + Ast *ast = new_ast(AST_DECLARE_STMT, c->span); + ASSIGN_DECL_OR_RET(ast->var_stmt, parse_var_decl(c), poisoned_ast); RANGE_EXTEND_PREV(ast); - TRY_CONSUME_EOS(); + do + { + if (!tok_is(c, TOKEN_EOS)) + { + sema_error_at_after(c->prev_span, "Expected ';'"); + return poisoned_ast; + } + advance(c); + } + while (0); return ast; } -static inline Ast* parse_ct_compound_stmt(ParseContext *context) +static inline Ast* parse_ct_compound_stmt(ParseContext *c) { - Ast *stmts = AST_NEW_TOKEN(AST_CT_COMPOUND_STMT, context->tok); + Ast *stmts = new_ast(AST_CT_COMPOUND_STMT, c->span); AstId *next = &stmts->compound_stmt.first_stmt; while (1) { - TokenType token = context->tok.type; - if (token == TOKEN_CT_ELSE || token == TOKEN_CT_ELIF || token == TOKEN_CT_ENDIF) break; - ASSIGN_AST_ELSE(Ast *stmt, parse_stmt(context), poisoned_ast); + TokenType tok = c->tok; + if (tok == TOKEN_CT_ELSE || tok == TOKEN_CT_ELIF || tok == TOKEN_CT_ENDIF) break; + ASSIGN_AST_OR_RET(Ast *stmt, parse_stmt(c), poisoned_ast); ast_append(&next, stmt); RANGE_EXTEND_PREV(stmts); } @@ -551,12 +556,12 @@ static inline Ast* parse_ct_compound_stmt(ParseContext *context) * ct_else_stmt * : CT_ELSE ':' ct_compound_stmt */ -static inline Ast* parse_ct_else_stmt(ParseContext *context) +static inline Ast* parse_ct_else_stmt(ParseContext *c) { - Ast *ast = AST_NEW_TOKEN(AST_CT_ELSE_STMT, context->tok); - advance_and_verify(context, TOKEN_CT_ELSE); + Ast *ast = new_ast(AST_CT_ELSE_STMT, c->span); + advance_and_verify(c, TOKEN_CT_ELSE); TRY_CONSUME(TOKEN_COLON, "$else needs a ':', did you forget it?"); - ASSIGN_AST_ELSE(ast->ct_else_stmt, parse_ct_compound_stmt(context), poisoned_ast); + ASSIGN_AST_OR_RET(ast->ct_else_stmt, parse_ct_compound_stmt(c), poisoned_ast); return ast; } @@ -564,21 +569,21 @@ static inline Ast* parse_ct_else_stmt(ParseContext *context) * ct_elif_stmt * : CT_ELIF '(' expression ')' ':' ct_compound_stmt (ct_elif_stmt | ct_else_stmt)? */ -static inline Ast *parse_ct_elif_stmt(ParseContext *context) +static inline Ast *parse_ct_elif_stmt(ParseContext *c) { - Ast *ast = AST_NEW_TOKEN(AST_CT_ELIF_STMT, context->tok); - advance_and_verify(context, TOKEN_CT_ELIF); - ASSIGN_EXPR_ELSE(ast->ct_elif_stmt.expr, parse_const_paren_expr(context), poisoned_ast); + Ast *ast = ast_new_curr(c, AST_CT_ELIF_STMT); + advance_and_verify(c, TOKEN_CT_ELIF); + ASSIGN_EXPR_OR_RET(ast->ct_elif_stmt.expr, parse_const_paren_expr(c), poisoned_ast); TRY_CONSUME(TOKEN_COLON, "$elif needs a ':' after the expression, did you forget it?"); - ASSIGN_AST_ELSE(ast->ct_elif_stmt.then, parse_ct_compound_stmt(context), poisoned_ast); + ASSIGN_AST_OR_RET(ast->ct_elif_stmt.then, parse_ct_compound_stmt(c), poisoned_ast); - if (TOKEN_IS(TOKEN_CT_ELIF)) + if (tok_is(c, TOKEN_CT_ELIF)) { - ASSIGN_AST_ELSE(ast->ct_elif_stmt.elif, parse_ct_elif_stmt(context), poisoned_ast); + ASSIGN_AST_OR_RET(ast->ct_elif_stmt.elif, parse_ct_elif_stmt(c), poisoned_ast); } - else if (TOKEN_IS(TOKEN_CT_ELSE)) + else if (tok_is(c, TOKEN_CT_ELSE)) { - ASSIGN_AST_ELSE(ast->ct_elif_stmt.elif, parse_ct_else_stmt(context), poisoned_ast); + ASSIGN_AST_OR_RET(ast->ct_elif_stmt.elif, parse_ct_else_stmt(c), poisoned_ast); } return ast; } @@ -588,25 +593,34 @@ static inline Ast *parse_ct_elif_stmt(ParseContext *context) * : CT_IF '(' expression ')' ':' ct_compound_stmt (ct_elif_stmt | ct_else_stmt) CT_ENDIF EOS * ; */ -static inline Ast* parse_ct_if_stmt(ParseContext *context) +static inline Ast* parse_ct_if_stmt(ParseContext *c) { - Ast *ast = AST_NEW_TOKEN(AST_CT_IF_STMT, context->tok); - advance_and_verify(context, TOKEN_CT_IF); - ASSIGN_EXPR_ELSE(ast->ct_if_stmt.expr, parse_const_paren_expr(context), poisoned_ast); + Ast *ast = ast_new_curr(c, AST_CT_IF_STMT); + advance_and_verify(c, TOKEN_CT_IF); + ASSIGN_EXPR_OR_RET(ast->ct_if_stmt.expr, parse_const_paren_expr(c), poisoned_ast); TRY_CONSUME(TOKEN_COLON, "$if needs a ':' after the expression, did you forget it?"); - ASSIGN_AST_ELSE(ast->ct_if_stmt.then, parse_ct_compound_stmt(context), poisoned_ast); + ASSIGN_AST_OR_RET(ast->ct_if_stmt.then, parse_ct_compound_stmt(c), poisoned_ast); - if (TOKEN_IS(TOKEN_CT_ELIF)) + if (tok_is(c, TOKEN_CT_ELIF)) { - ASSIGN_AST_ELSE(ast->ct_if_stmt.elif, parse_ct_elif_stmt(context), poisoned_ast); + ASSIGN_AST_OR_RET(ast->ct_if_stmt.elif, parse_ct_elif_stmt(c), poisoned_ast); } - else if (TOKEN_IS(TOKEN_CT_ELSE)) + else if (tok_is(c, TOKEN_CT_ELSE)) { - ASSIGN_AST_ELSE(ast->ct_if_stmt.elif, parse_ct_else_stmt(context), poisoned_ast); + ASSIGN_AST_OR_RET(ast->ct_if_stmt.elif, parse_ct_else_stmt(c), poisoned_ast); } - advance_and_verify(context, TOKEN_CT_ENDIF); + advance_and_verify(c, TOKEN_CT_ENDIF); RANGE_EXTEND_PREV(ast); - TRY_CONSUME_EOS(); + do + { + if (!tok_is(c, TOKEN_EOS)) + { + sema_error_at_after(c->prev_span, "Expected ';'"); + return poisoned_ast; + } + advance(c); + } + while (0); return ast; } @@ -618,14 +632,14 @@ static inline Ast* parse_ct_if_stmt(ParseContext *context) * | RETURN * ; */ -static inline Ast *parse_return(ParseContext *context) +static inline Ast *parse_return(ParseContext *c) { - advance_and_verify(context, TOKEN_RETURN); - Ast *ast = AST_NEW_TOKEN(AST_RETURN_STMT, context->tok); + advance_and_verify(c, TOKEN_RETURN); + Ast *ast = ast_new_curr(c, AST_RETURN_STMT); ast->return_stmt.defer = 0; - if (!TOKEN_IS(TOKEN_EOS)) + if (!tok_is(c, TOKEN_EOS)) { - ASSIGN_EXPR_ELSE(ast->return_stmt.expr, parse_expr(context), poisoned_ast); + ASSIGN_EXPR_OR_RET(ast->return_stmt.expr, parse_expr(c), poisoned_ast); } return ast; } @@ -643,29 +657,31 @@ static inline Ast *parse_return(ParseContext *context) * * @return */ -static inline Ast* parse_ct_foreach_stmt(ParseContext *context) +static inline Ast* parse_ct_foreach_stmt(ParseContext *c) { - Ast *ast = AST_NEW_TOKEN(AST_CT_FOREACH_STMT, context->tok); - advance_and_verify(context, TOKEN_CT_FOREACH); - CONSUME_OR(TOKEN_LPAREN, poisoned_ast); - if (context->next_tok.type == TOKEN_COMMA) + Ast *ast = ast_new_curr(c, AST_CT_FOREACH_STMT); + advance_and_verify(c, TOKEN_CT_FOREACH); + CONSUME_OR_RET(TOKEN_LPAREN, poisoned_ast); + if (peek(c) == TOKEN_COMMA) { - ast->ct_foreach_stmt.index = context->tok.id; - TRY_CONSUME_OR(TOKEN_CT_IDENT, "Expected a compile time index variable", poisoned_ast); - advance_and_verify(context, TOKEN_COMMA); + ast->ct_foreach_stmt.index_name = symstr(c); + ast->ct_foreach_stmt.index_span = c->span; + TRY_CONSUME_OR_RET(TOKEN_CT_IDENT, "Expected a compile time index variable", poisoned_ast); + advance_and_verify(c, TOKEN_COMMA); } - ast->ct_foreach_stmt.value = context->tok.id; - TRY_CONSUME_OR(TOKEN_CT_IDENT, "Expected a compile time variable", poisoned_ast); - TRY_CONSUME_OR(TOKEN_COLON, "Expected ':'.", poisoned_ast); - ASSIGN_EXPR_ELSE(ast->ct_foreach_stmt.expr, parse_expr(context), poisoned_ast); - CONSUME_OR(TOKEN_RPAREN, poisoned_ast); - CONSUME_OR(TOKEN_COLON, poisoned_ast); + ast->ct_foreach_stmt.value_name = symstr(c); + ast->ct_foreach_stmt.value_span = c->span; + TRY_CONSUME_OR_RET(TOKEN_CT_IDENT, "Expected a compile time variable", poisoned_ast); + TRY_CONSUME_OR_RET(TOKEN_COLON, "Expected ':'.", poisoned_ast); + ASSIGN_EXPR_OR_RET(ast->ct_foreach_stmt.expr, parse_expr(c), poisoned_ast); + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_ast); + CONSUME_OR_RET(TOKEN_COLON, poisoned_ast); Ast *body = new_ast(AST_COMPOUND_STMT, ast->span); ast->ct_foreach_stmt.body = astid(body); AstId *current = &body->compound_stmt.first_stmt; - while (!try_consume(context, TOKEN_CT_ENDFOREACH)) + while (!try_consume(c, TOKEN_CT_ENDFOREACH)) { - ASSIGN_AST_ELSE(Ast *stmt, parse_stmt(context), poisoned_ast); + ASSIGN_AST_OR_RET(Ast *stmt, parse_stmt(c), poisoned_ast); *current = astid(stmt); current = &stmt->next; } @@ -677,36 +693,36 @@ static inline Ast* parse_ct_foreach_stmt(ParseContext *context) * | CT_FOR '(' decl_expr_list? ';' expression_list? ';' expression_list? ')' ':' statement* CT_ENDFOR ';' * ; */ -static inline Ast* parse_ct_for_stmt(ParseContext *context) +static inline Ast* parse_ct_for_stmt(ParseContext *c) { - Ast *ast = AST_NEW_TOKEN(AST_CT_FOR_STMT, context->tok); - advance_and_verify(context, TOKEN_CT_FOR); - CONSUME_OR(TOKEN_LPAREN, poisoned_ast); + Ast *ast = ast_new_curr(c, AST_CT_FOR_STMT); + advance_and_verify(c, TOKEN_CT_FOR); + CONSUME_OR_RET(TOKEN_LPAREN, poisoned_ast); - if (!TOKEN_IS(TOKEN_EOS)) + if (!tok_is(c, TOKEN_EOS)) { - ASSIGN_EXPR_ELSE(ast->for_stmt.init, parse_ct_expression_list(context, true), poisoned_ast); + ASSIGN_EXPR_OR_RET(ast->for_stmt.init, parse_ct_expression_list(c, true), poisoned_ast); } - CONSUME_OR(TOKEN_EOS, poisoned_ast); + CONSUME_OR_RET(TOKEN_EOS, poisoned_ast); // Cond is required. - ASSIGN_EXPR_ELSE(ast->for_stmt.cond, parse_expr(context), poisoned_ast); - CONSUME_OR(TOKEN_EOS, poisoned_ast); + ASSIGN_EXPR_OR_RET(ast->for_stmt.cond, parse_expr(c), poisoned_ast); + CONSUME_OR_RET(TOKEN_EOS, poisoned_ast); - if (!TOKEN_IS(TOKEN_RPAREN)) + if (!tok_is(c, TOKEN_RPAREN)) { - ast->for_stmt.incr = parse_ct_expression_list(context, false); + ast->for_stmt.incr = parse_ct_expression_list(c, false); } - CONSUME_OR(TOKEN_RPAREN, poisoned_ast); + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_ast); - CONSUME_OR(TOKEN_COLON, poisoned_ast); + CONSUME_OR_RET(TOKEN_COLON, poisoned_ast); Ast *body = new_ast(AST_COMPOUND_STMT, ast->span); ast->for_stmt.body = astid(body); AstId *current = &body->compound_stmt.first_stmt; - while (!try_consume(context, TOKEN_CT_ENDFOR)) + while (!try_consume(c, TOKEN_CT_ENDFOR)) { - ASSIGN_AST_ELSE(Ast *stmt, parse_stmt(context), poisoned_ast); + ASSIGN_AST_OR_RET(Ast *stmt, parse_stmt(c), poisoned_ast); *current = astid(stmt); current = &stmt->next; } @@ -728,50 +744,68 @@ static inline Ast* parse_ct_for_stmt(ParseContext *context) * * @return */ -static inline Ast* parse_ct_switch_stmt(ParseContext *context) +static inline Ast* parse_ct_switch_stmt(ParseContext *c) { - Ast *ast = AST_NEW_TOKEN(AST_CT_SWITCH_STMT, context->tok); - advance_and_verify(context, TOKEN_CT_SWITCH); - ASSIGN_EXPR_ELSE(ast->ct_switch_stmt.cond, parse_const_paren_expr(context), poisoned_ast); + Ast *ast = ast_new_curr(c, AST_CT_SWITCH_STMT); + advance_and_verify(c, TOKEN_CT_SWITCH); + ASSIGN_EXPR_OR_RET(ast->ct_switch_stmt.cond, parse_const_paren_expr(c), poisoned_ast); TRY_CONSUME(TOKEN_COLON, "Expected ':' after $switch expression, did you forget it?"); Ast **cases = NULL; - while (!try_consume(context, TOKEN_CT_ENDSWITCH)) + while (!try_consume(c, TOKEN_CT_ENDSWITCH)) { Ast *result; - TokenType next = context->tok.type; - if (next == TOKEN_CT_CASE) + TokenType tok = c->tok; + if (tok == TOKEN_CT_CASE) { - ASSIGN_AST_ELSE(result, parse_case_stmt(context, TOKEN_CT_CASE, TOKEN_CT_DEFAULT), poisoned_ast); + ASSIGN_AST_OR_RET(result, parse_case_stmt(c, TOKEN_CT_CASE, TOKEN_CT_DEFAULT), poisoned_ast); } - else if (next == TOKEN_CT_DEFAULT) + else if (tok == TOKEN_CT_DEFAULT) { - ASSIGN_AST_ELSE(result, parse_default_stmt(context, TOKEN_CT_CASE, TOKEN_CT_DEFAULT), poisoned_ast); + ASSIGN_AST_OR_RET(result, parse_default_stmt(c, TOKEN_CT_CASE, TOKEN_CT_DEFAULT), poisoned_ast); } else { - SEMA_TOKEN_ERROR(context->tok, "A '$case' or '$default' would be needed here, '%.*s' is not allowed.", TOKLEN(context->tok.id), TOKSTR(context->tok.id)); + SEMA_ERROR_HERE("A '$case' or '$default' would be needed here."); return poisoned_ast; } vec_add(cases, result); } - TRY_CONSUME_EOS(); + do + { + if (!tok_is(c, TOKEN_EOS)) + { + sema_error_at_after(c->prev_span, "Expected ';'"); + return poisoned_ast; + } + advance(c); + } + while (0); ast->ct_switch_stmt.body = cases; return ast; } -static inline Ast *parse_assert_stmt(ParseContext *context) +static inline Ast *parse_assert_stmt(ParseContext *c) { - Ast *ast = AST_NEW_TOKEN(AST_ASSERT_STMT, context->tok); - advance_and_verify(context, TOKEN_ASSERT); - TRY_CONSUME_OR(TOKEN_LPAREN, "'assert' needs a '(' here, did you forget it?", poisoned_ast); - ASSIGN_EXPR_ELSE(ast->assert_stmt.expr, parse_assert_expr(context), poisoned_ast); + Ast *ast = ast_new_curr(c, AST_ASSERT_STMT); + advance_and_verify(c, TOKEN_ASSERT); + TRY_CONSUME_OR_RET(TOKEN_LPAREN, "'assert' needs a '(' here, did you forget it?", poisoned_ast); + ASSIGN_EXPR_OR_RET(ast->assert_stmt.expr, parse_assert_expr(c), poisoned_ast); - if (try_consume(context, TOKEN_COMMA)) + if (try_consume(c, TOKEN_COMMA)) { - ASSIGN_EXPR_ELSE(ast->assert_stmt.message, parse_expr(context), poisoned_ast); + ASSIGN_EXPR_OR_RET(ast->assert_stmt.message, parse_expr(c), poisoned_ast); } - TRY_CONSUME_OR(TOKEN_RPAREN, "The ending ')' was expected here.", poisoned_ast); - TRY_CONSUME_EOS(); + TRY_CONSUME_OR_RET(TOKEN_RPAREN, "The ending ')' was expected here.", poisoned_ast); + do + { + if (!tok_is(c, TOKEN_EOS)) + { + sema_error_at_after(c->prev_span, "Expected ';'"); + return poisoned_ast; + } + advance(c); + } + while (0); return ast; } @@ -779,108 +813,116 @@ static inline Ast *parse_assert_stmt(ParseContext *context) /** * ct_assert_stmt ::= CT_ASSERT '(' constant_expression (',' constant_expression) ')' ';' - * @param context + * @param c * @return */ -Ast *parse_ct_assert_stmt(ParseContext *context) +Ast *parse_ct_assert_stmt(ParseContext *c) { - Ast *ast = AST_NEW_TOKEN(AST_CT_ASSERT, context->tok); - advance_and_verify(context, TOKEN_CT_ASSERT); - TRY_CONSUME_OR(TOKEN_LPAREN, "'$assert' needs a '(' here, did you forget it?", poisoned_ast); - ASSIGN_EXPR_ELSE(ast->ct_assert_stmt.expr, parse_constant_expr(context), poisoned_ast); + Ast *ast = ast_new_curr(c, AST_CT_ASSERT); + advance_and_verify(c, TOKEN_CT_ASSERT); + TRY_CONSUME_OR_RET(TOKEN_LPAREN, "'$assert' needs a '(' here, did you forget it?", poisoned_ast); + ASSIGN_EXPR_OR_RET(ast->ct_assert_stmt.expr, parse_constant_expr(c), poisoned_ast); - if (try_consume(context, TOKEN_COMMA)) + if (try_consume(c, TOKEN_COMMA)) { - ASSIGN_EXPR_ELSE(ast->ct_assert_stmt.message, parse_constant_expr(context), poisoned_ast); + ASSIGN_EXPR_OR_RET(ast->ct_assert_stmt.message, parse_constant_expr(c), poisoned_ast); } - TRY_CONSUME_OR(TOKEN_RPAREN, "The ending ')' was expected here.", poisoned_ast); - TRY_CONSUME_EOS(); + TRY_CONSUME_OR_RET(TOKEN_RPAREN, "The ending ')' was expected here.", poisoned_ast); + do + { + if (!tok_is(c, TOKEN_EOS)) + { + sema_error_at_after(c->prev_span, "Expected ';'"); + return poisoned_ast; + } + advance(c); + } + while (0); return ast; } -Ast *parse_stmt(ParseContext *context) +Ast *parse_stmt(ParseContext *c) { - switch (context->tok.type) + switch (c->tok) { case TOKEN_ASM_STRING: case TOKEN_ASM_CONSTRAINT: - case TOKEN_DOCS_DIRECTIVE: UNREACHABLE case TOKEN_LBRACE: - return parse_compound_stmt(context); + return parse_compound_stmt(c); case TYPELIKE_TOKENS: case TOKEN_HASH_TYPE_IDENT: case TOKEN_HASH_CONST_IDENT: case TOKEN_HASH_IDENT: case TOKEN_IDENT: case TOKEN_CONST_IDENT: - return parse_decl_or_expr_stmt(context); + return parse_decl_or_expr_stmt(c); case TOKEN_SCOPING: - return parse_scoping_stmt(context); + return parse_scoping_stmt(c); case TOKEN_VAR: - return parse_var_stmt(context); + return parse_var_stmt(c); case TOKEN_TLOCAL: // Global means declaration! case TOKEN_STATIC: // Static means declaration! case TOKEN_CONST: // Const means declaration! - return parse_declaration_stmt(context); + return parse_declaration_stmt(c); case TOKEN_AT: - return parse_expr_stmt(context); + return parse_expr_stmt(c); case TOKEN_RETURN: { - ASSIGN_AST_ELSE(Ast *ast, parse_return(context), poisoned_ast); + ASSIGN_AST_OR_RET(Ast *ast, parse_return(c), poisoned_ast); RETURN_AFTER_EOS(ast); } case TOKEN_IF: - return parse_if_stmt(context); + return parse_if_stmt(c); case TOKEN_WHILE: - return parse_while_stmt(context); + return parse_while_stmt(c); case TOKEN_DEFER: - return parse_defer_stmt(context); + return parse_defer_stmt(c); case TOKEN_SWITCH: - return parse_switch_stmt(context); + return parse_switch_stmt(c); case TOKEN_DO: - return parse_do_stmt(context); + return parse_do_stmt(c); case TOKEN_FOR: - return parse_for_stmt(context); + return parse_for_stmt(c); case TOKEN_FOREACH: - return parse_foreach_stmt(context); + return parse_foreach_stmt(c); case TOKEN_CONTINUE: { - ASSIGN_AST_ELSE(Ast *ast, parse_continue(context), poisoned_ast); + ASSIGN_AST_OR_RET(Ast *ast, parse_continue(c), poisoned_ast); RETURN_AFTER_EOS(ast); } case TOKEN_CASE: - SEMA_TOKEN_ERROR(context->tok, "'case' was found outside of 'switch', did you mismatch a '{ }' pair?"); - advance(context); + SEMA_ERROR_HERE("'case' was found outside of 'switch', did you mismatch a '{ }' pair?"); + advance(c); return poisoned_ast; case TOKEN_BREAK: { - ASSIGN_AST_ELSE(Ast *ast, parse_break(context), poisoned_ast); + ASSIGN_AST_OR_RET(Ast *ast, parse_break(c), poisoned_ast); RETURN_AFTER_EOS(ast); } case TOKEN_NEXTCASE: { - ASSIGN_AST_ELSE(Ast *ast, parse_next(context), poisoned_ast); + ASSIGN_AST_OR_RET(Ast *ast, parse_next(c), poisoned_ast); RETURN_AFTER_EOS(ast); } case TOKEN_ASM: - return parse_asm_stmt(context); + return parse_asm_stmt(c); case TOKEN_DEFAULT: - SEMA_TOKEN_ERROR(context->tok, "'default' was found outside of 'switch', did you mismatch a '{ }' pair?"); - advance(context); + SEMA_ERROR_HERE("'default' was found outside of 'switch', did you mismatch a '{ }' pair?"); + advance(c); return poisoned_ast; case TOKEN_CT_ASSERT: - return parse_ct_assert_stmt(context); + return parse_ct_assert_stmt(c); case TOKEN_CT_IF: - return parse_ct_if_stmt(context); + return parse_ct_if_stmt(c); case TOKEN_CT_SWITCH: - return parse_ct_switch_stmt(context); + return parse_ct_switch_stmt(c); case TOKEN_CT_FOREACH: - return parse_ct_foreach_stmt(context); + return parse_ct_foreach_stmt(c); case TOKEN_CT_FOR: - return parse_ct_for_stmt(context); + return parse_ct_for_stmt(c); case TOKEN_CT_UNREACHABLE: - return parse_unreachable_stmt(context); + return parse_unreachable_stmt(c); case TOKEN_STAR: case TOKEN_AMP: case TOKEN_INTEGER: @@ -915,11 +957,11 @@ Ast *parse_stmt(ParseContext *context) case TOKEN_CATCH: case TOKEN_BYTES: case TOKEN_BUILTIN: - return parse_expr_stmt(context); + return parse_expr_stmt(c); case TOKEN_ASSERT: - return parse_assert_stmt(context); + return parse_assert_stmt(c); case TOKEN_INVALID_TOKEN: - advance(context); + advance(c); return poisoned_ast; case TOKEN_COLON: case TOKEN_COMMA: @@ -977,10 +1019,7 @@ Ast *parse_stmt(ParseContext *context) case TOKEN_DEFINE: case TOKEN_DOCS_START: case TOKEN_DOCS_END: - case TOKEN_DOCS_EOL: case TOKEN_DOC_COMMENT: - case TOKEN_COMMENT: - case TOKEN_DOCS_LINE: case TOKEN_CT_CASE: case TOKEN_CT_ELIF: case TOKEN_CT_ELSE: @@ -996,56 +1035,67 @@ Ast *parse_stmt(ParseContext *context) case TOKEN_RVEC: case TOKEN_CT_ENDFOR: case TOKEN_CT_ENDFOREACH: - SEMA_TOKEN_ERROR(context->tok, "Unexpected '%s' found when expecting a statement.", token_type_to_string(context->tok.type)); - advance(context); + SEMA_ERROR_HERE("Unexpected '%s' found when expecting a statement.", + token_type_to_string(c->tok)); + advance(c); return poisoned_ast; case TOKEN_RPAREN: case TOKEN_RBRACE: case TOKEN_RBRACKET: - SEMA_TOKEN_ERROR(context->tok, "Mismatched '%s' found.", token_type_to_string(context->tok.type)); - advance(context); + SEMA_ERROR_HERE("Mismatched '%s' found.", token_type_to_string(c->tok)); + advance(c); return poisoned_ast; case TOKEN_EOS: - advance(context); - return AST_NEW_TOKEN(AST_NOP_STMT, context->tok); + advance(c); + return ast_new_curr(c, AST_NOP_STMT); case TOKEN_EOF: - SEMA_TOKID_ERROR(context->tok.id, "Reached the end of the file when expecting a statement."); + SEMA_ERROR_HERE("Reached the end of the file when expecting a statement."); + return poisoned_ast; + case TOKEN_DOC_DIRECTIVE: + case TOKEN_DOCS_ENSURE: + case TOKEN_DOCS_REQUIRE: + case TOKEN_DOCS_CHECKED: + case TOKEN_DOCS_PARAM: + case TOKEN_DOCS_RETURN: + case TOKEN_DOCS_OPTRETURN: + case TOKEN_DOCS_PURE: + SEMA_ERROR_HERE("Unexpectedly encountered doc directives."); return poisoned_ast; } UNREACHABLE } -Ast *parse_scoping_stmt(ParseContext *context) +Ast *parse_scoping_stmt(ParseContext *c) { - Ast *ast = AST_NEW_TOKEN(AST_SCOPING_STMT, context->tok); - advance_and_verify(context, TOKEN_SCOPING); - CONSUME_OR(TOKEN_LPAREN, poisoned_ast); - ASSIGN_EXPR_ELSE(ast->scoping_stmt.scoped, parse_expression_list(context, false), poisoned_ast); - CONSUME_OR(TOKEN_RPAREN, poisoned_ast); - ASSIGN_AST_ELSE(ast->scoping_stmt.stmt, parse_compound_stmt(context), poisoned_ast); + Ast *ast = ast_new_curr(c, AST_SCOPING_STMT); + advance_and_verify(c, TOKEN_SCOPING); + CONSUME_OR_RET(TOKEN_LPAREN, poisoned_ast); + ASSIGN_EXPR_OR_RET(ast->scoping_stmt.scoped, parse_expression_list(c, false), poisoned_ast); + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_ast); + ASSIGN_AST_OR_RET(ast->scoping_stmt.stmt, parse_compound_stmt(c), poisoned_ast); return ast; } -Ast *parse_unreachable_stmt(ParseContext *context) +Ast *parse_unreachable_stmt(ParseContext *c) { - Ast *ast = AST_NEW_TOKEN(AST_UNREACHABLE_STMT, context->tok); - advance_and_verify(context, TOKEN_CT_UNREACHABLE); - TRY_CONSUME_EOS_OR(poisoned_ast); + Ast *ast = ast_new_curr(c, AST_UNREACHABLE_STMT); + advance_and_verify(c, TOKEN_CT_UNREACHABLE); + CONSUME_EOS_OR_RET(poisoned_ast); return ast; } -Ast *parse_jump_stmt_no_eos(ParseContext *context) +Ast *parse_jump_stmt_no_eos(ParseContext *c) { - switch (context->tok.type) + switch (c->tok) { case TOKEN_NEXTCASE: - return parse_next(context); + return parse_next(c); case TOKEN_RETURN: - return parse_return(context); + return parse_return(c); case TOKEN_BREAK: - return parse_break(context); + return parse_break(c); case TOKEN_CONTINUE: - return parse_continue(context); + return parse_continue(c); default: UNREACHABLE } @@ -1062,17 +1112,17 @@ Ast *parse_jump_stmt_no_eos(ParseContext *context) * | stmt stmt_list * ; * - * @param context + * @param c * @return a compound statement */ -Ast* parse_compound_stmt(ParseContext *context) +Ast* parse_compound_stmt(ParseContext *c) { - CONSUME_OR(TOKEN_LBRACE, poisoned_ast); - Ast *ast = AST_NEW_TOKEN(AST_COMPOUND_STMT, context->tok); + CONSUME_OR_RET(TOKEN_LBRACE, poisoned_ast); + Ast *ast = ast_new_curr(c, AST_COMPOUND_STMT); AstId *next = &ast->compound_stmt.first_stmt; - while (!try_consume(context, TOKEN_RBRACE)) + while (!try_consume(c, TOKEN_RBRACE)) { - ASSIGN_AST_ELSE(Ast *stmt, parse_stmt(context), poisoned_ast); + ASSIGN_AST_OR_RET(Ast *stmt, parse_stmt(c), poisoned_ast); ast_append(&next, stmt); } return ast; diff --git a/src/compiler/parser.c b/src/compiler/parser.c index 7e8a4f094..08436e344 100644 --- a/src/compiler/parser.c +++ b/src/compiler/parser.c @@ -10,95 +10,44 @@ /** * Advance to the next non-comment token. * - * @param context the current context. + * @param c the current context. */ -inline void advance(ParseContext *context) +inline void advance(ParseContext *c) { - context->lead_comment = context->next_lead_comment; - context->trailing_comment = NULL; - context->next_lead_comment = NULL; - context->prev_tok = context->tok.id; - context->tok = context->next_tok; - - while (1) + if (tok_is(c, TOKEN_EOF)) { - if (context->tok.type == TOKEN_EOF) - { - context->next_tok = context->tok; - return; - } - - uint32_t index = context->lexer_index++; - TokenType next_type = (TokenType)(*toktypeptr(index)); - context->next_tok.id.index = index; - context->next_tok.type = next_type; - - // At this point we should not have any invalid tokens. - assert(next_type != TOKEN_INVALID_TOKEN); - - // Walk through any regular comments - if (next_type == TOKEN_COMMENT) - { - vec_add(context->comments, context->next_tok); - continue; - } - - // Handle doc comments - if (next_type != TOKEN_DOC_COMMENT) return; - - { - SourceLocation *curr = TOKLOC(context->tok); - SourceLocation *next = TOKLOC(context->next_tok); - vec_add(context->comments, context->next_tok); - if (curr->row == next->row) - { - if (context->trailing_comment) - { - SEMA_TOKEN_ERROR(context->next_tok, - "You have multiple trailing doc-style comments, should the second one go on the next line?"); - } - else - { - context->trailing_comment = context->comments + vec_size(context->comments) - 1; - } - } - else - { - if (context->lead_comment) - { - SEMA_TOKEN_ERROR(context->next_tok, - "You have multiple doc-style comments in a row, are all of them really meant to document the code that follows?"); - } - else - { - context->lead_comment = context->comments + vec_size(context->comments) - 1; - } - } - } + return; + } + c->tok = c->lexer.token_type; + c->data = c->lexer.data; + c->prev_span = c->span; + c->span = c->lexer.tok_span; + if (!lexer_next_token(&c->lexer)) + { + exit_compiler(1); } - } -bool try_consume(ParseContext *context, TokenType type) +bool try_consume(ParseContext *c, TokenType type) { - if (context->tok.type == type) + if (tok_is(c, type)) { - advance(context); + advance(c); return true; } return false; } -bool consume(ParseContext *context, TokenType type, const char *message, ...) +bool consume(ParseContext *c, TokenType type, const char *message, ...) { - if (try_consume(context, type)) + if (try_consume(c, type)) { return true; } va_list args; va_start(args, message); - sema_verror_range(TOKLOC(context->tok), message, args); + sema_verror_range(c->span, message, args); va_end(args); return false; } @@ -108,35 +57,35 @@ bool consume(ParseContext *context, TokenType type, const char *message, ...) /** * module? imports top_level_statement* - * @param context + * @param c */ -static inline void parse_translation_unit(ParseContext *context) +static inline void parse_translation_unit(ParseContext *c) { // Prime everything - advance(context); - advance(context); + advance(c); + advance(c); NEXT_CONTEXT: - if (!parse_module(context)) return; - parse_imports(context); - while (!TOKEN_IS(TOKEN_EOF)) + if (!parse_module(c)) return; + parse_imports(c); + while (!tok_is(c, TOKEN_EOF)) { - if (TOKEN_IS(TOKEN_MODULE)) + if (tok_is(c, TOKEN_MODULE)) { ParseContext *new_context = CALLOCS(ParseContext); - *new_context = *context; - new_context->unit = unit_create(context->unit->file); - context = new_context; + *new_context = *c; + new_context->unit = unit_create(c->unit->file); + c = new_context; goto NEXT_CONTEXT; } - Decl *decl = parse_top_level_statement(context); + Decl *decl = parse_top_level_statement(c); if (!decl) continue; if (decl_ok(decl)) { - vec_add(context->unit->global_decls, decl); + vec_add(c->unit->global_decls, decl); } else { - recover_top_level(context); + recover_top_level(c); } } } @@ -150,13 +99,12 @@ static inline void parse_translation_unit(ParseContext *context) */ bool parse_file(File *file) { - Lexer lexer = { .file = file }; - lexer_lex_file(&lexer); - if (global_context.errors_found) return false; CompilationUnit *unit = unit_create(file); - ParseContext lex_context = { .lexer_index = lexer.token_start_id, - .unit = unit }; - parse_translation_unit(&lex_context); + ParseContext parse_context = { .unit = unit }; + parse_context.lexer = (Lexer) { .file = file, .context = &parse_context }; + lexer_init(&parse_context.lexer); + if (global_context.errors_found) return false; + parse_translation_unit(&parse_context); return !global_context.errors_found; } diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h index 6317313d9..ac4569433 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -4,81 +4,90 @@ // Use of this source code is governed by a LGPLv3.0 // a copy of which can be found in the LICENSE file. -#define TOKEN_IS(_type) (context->tok.type == _type) -#define EXPECT_IDENT_FOR_OR(_name, _res) do { if (!expect_ident(context, _name)) return _res; } while(0) -#define EXPECT_OR(_tok, _res) do { if (!expect(context, _tok)) return _res; } while(0) -#define CONSUME_OR(_tok, _res) do { if (!expect(context, _tok)) return _res; advance(context); } while(0) -#define TRY_EXPECT_OR(_tok, _message, _type) do { if (context->tok.type != _tok) { SEMA_TOKEN_ERROR(context->tok, _message); return _type; } } while(0) -#define TRY_CONSUME_OR(_tok, _message, _type) do { if (!consume(context, _tok, _message)) return _type; } while(0) -#define TRY_CONSUME(_tok, _message) TRY_CONSUME_OR(_tok, _message, poisoned_ast) -#define TRY_CONSUME_EOS_OR(_res) do { if (!TOKEN_IS(TOKEN_EOS)) { sema_error_at_prev_end(context->tok, "Expected ';'"); return _res; } advance(context); } while(0) -#define TRY_CONSUME_EOS() TRY_CONSUME_EOS_OR(poisoned_ast) -#define RETURN_AFTER_EOS(_ast) extend_ast_with_prev_token(context, ast); TRY_CONSUME_EOS_OR(poisoned_ast); return _ast +#define EXPECT_IDENT_FOR_OR(_name, _res) do { if (!expect_ident(c, _name)) return _res; } while(0) +#define EXPECT_OR_RET(_tok, _res) do { if (!expect(c, _tok)) return _res; } while(0) +#define CONSUME_OR_RET(_tok, _res) do { if (!expect(c, _tok)) return _res; advance(c); } while(0) +#define CONSUME_EOS_OR_RET(_res) do { if (!tok_is(c, TOKEN_EOS)) { sema_error_at_after(c->prev_span, "Expected ';'"); return _res; } advance(c); } while(0) +#define RETURN_AFTER_EOS(_ast) RANGE_EXTEND_PREV(ast); CONSUME_EOS_OR_RET(poisoned_ast); return _ast +#define TRY_EXPECT_OR_RET(_tok, _message, _type) do { if (!tok_is(c, _tok)) { SEMA_ERROR_HERE(_message); return _type; } } while(0) +#define TRY_CONSUME_OR_RET(_tok, _message, _type) do { if (!consume(c, _tok, _message)) return _type; } while(0) +#define TRY_CONSUME(_tok, _message) TRY_CONSUME_OR_RET(_tok, _message, poisoned_ast) +#define CHECK_EXPR_OR_RET(_expr) do { if (!expr_ok(_expr)) return _expr; } while(0) -#define CHECK_EXPR(_expr) do { if (!expr_ok(_expr)) return _expr; } while(0) - -#define COMMA_RPAREN_OR(_res) \ -do { if (!try_consume(context, TOKEN_COMMA) && !TOKEN_IS(TOKEN_RPAREN)) { \ -SEMA_TOKEN_ERROR(context->tok, "Expected ',' or ')'"); return _res; } } while(0) - - -Decl *parse_top_level_statement(ParseContext *context); -Ast *parse_ct_assert_stmt(ParseContext *context); -Ast *parse_stmt(ParseContext *context); -Path *parse_path_prefix(ParseContext *context, bool *had_error); -Expr *parse_type_expression_with_path(ParseContext *context, Path *path); -Expr *parse_expr(ParseContext *context); -bool consume_ident(ParseContext *context, const char* name); -TypeInfo *parse_type(ParseContext *context); -TypeInfo *parse_failable_type(ParseContext *context); -TypeInfo *parse_type_with_base(ParseContext *context, TypeInfo *type_info); -Expr* parse_constant_expr(ParseContext *context); -Expr *parse_initializer(ParseContext *context); -void parse_imports(ParseContext *context); -Decl *parse_decl(ParseContext *context); -void recover_top_level(ParseContext *context); -Expr *parse_cond(ParseContext *context); -Expr *parse_assert_expr(ParseContext *context); -Ast* parse_compound_stmt(ParseContext *context); -Ast *parse_jump_stmt_no_eos(ParseContext *context); -bool parse_attributes(ParseContext *context, Attr ***attributes_ref); - -bool parse_switch_body(ParseContext *context, Ast ***cases, TokenType case_type, TokenType default_type, +Decl *parse_top_level_statement(ParseContext *c); +Ast *parse_ct_assert_stmt(ParseContext *c); +Ast *parse_stmt(ParseContext *c); +Path *parse_path_prefix(ParseContext *c, bool *had_error); +Expr *parse_type_expression_with_path(ParseContext *c, Path *path); +Expr *parse_expr(ParseContext *c); +bool consume_ident(ParseContext *c, const char* name); +TypeInfo *parse_type(ParseContext *c); +TypeInfo *parse_failable_type(ParseContext *c); +TypeInfo *parse_type_with_base(ParseContext *c, TypeInfo *type_info); +Expr* parse_constant_expr(ParseContext *c); +Expr *parse_initializer(ParseContext *c); +void parse_imports(ParseContext *c); +Decl *parse_decl(ParseContext *c); +Expr *parse_decl_or_expr(ParseContext *c, Decl **decl_ref); +void recover_top_level(ParseContext *c); +Expr *parse_cond(ParseContext *c); +Expr *parse_assert_expr(ParseContext *c); +Ast* parse_compound_stmt(ParseContext *c); +Ast *parse_jump_stmt_no_eos(ParseContext *c); +bool parse_attributes(ParseContext *c, Attr ***attributes_ref); +bool parse_switch_body(ParseContext *c, Ast ***cases, TokenType case_type, TokenType default_type, bool allow_multiple_values); -Expr *parse_ct_expression_list(ParseContext *context, bool allow_decl); -Expr *parse_expression_list(ParseContext *context, bool allow_decls); -Decl *parse_decl_after_type(ParseContext *context, TypeInfo *type); -Decl *parse_var_decl(ParseContext *context); +Expr *parse_ct_expression_list(ParseContext *c, bool allow_decl); +Expr *parse_expression_list(ParseContext *c, bool allow_decls); +Decl *parse_decl_after_type(ParseContext *c, TypeInfo *type); +Decl *parse_var_decl(ParseContext *c); +bool parse_parameters(ParseContext *c, Visibility visibility, Decl ***params_ref); +bool parse_arg_list(ParseContext *c, Expr ***result, TokenType param_end, bool *unsplat); +Expr *parse_type_compound_literal_expr_after_type(ParseContext *c, TypeInfo *type_info); -bool parse_parameters(ParseContext *context, Visibility visibility, Decl ***params_ref); -bool parse_arg_list(ParseContext *context, Expr ***result, TokenType param_end, bool *unsplat); -Expr *parse_type_compound_literal_expr_after_type(ParseContext *context, TypeInfo *type_info); +bool parse_module(ParseContext *c); -bool parse_next_is_decl(ParseContext *context); -bool parse_next_is_case_type(ParseContext *context); -bool parse_next_is_type(ParseContext *context); -bool parse_module(ParseContext *unit); +bool try_consume(ParseContext *c, TokenType type); +bool consume(ParseContext *c, TokenType type, const char *message, ...); +bool consume_const_name(ParseContext *c, const char* type); +Expr *parse_precedence_with_left_side(ParseContext *c, Expr *left_side, Precedence precedence); +Expr *parse_expr_or_type(ParseContext *c, TypeInfo **type_ref); -bool try_consume(ParseContext *context, TokenType type); -bool consume(ParseContext *context, TokenType type, const char *message, ...); -bool consume_const_name(ParseContext *context, const char* type); -Expr *parse_precedence_with_left_side(ParseContext *context, Expr *left_side, Precedence precedence); - -static inline bool tok_is(ParseContext *context, TokenType type) +INLINE const char *symstr(ParseContext *c) { - return context->tok.type == type; + return c->data.string; } -static inline bool expect(ParseContext *context, TokenType token_type) +INLINE TypeInfo *type_info_new_curr(ParseContext *c, TypeInfoKind type) { - if (token_type == context->tok.type) return true; + return type_info_new(type, c->span); +} - SEMA_TOKEN_ERROR(context->tok, "Expected '%s'.", token_type_to_string(token_type)); +INLINE TokenType peek(ParseContext *c) +{ + return c->lexer.token_type; +} + +INLINE Ast *ast_new_curr(ParseContext *c, AstKind kind) +{ + return new_ast(kind, c->span); +} + +INLINE bool tok_is(ParseContext *c, TokenType type) +{ + return c->tok == type; +} + +INLINE bool expect(ParseContext *c, TokenType token_type) +{ + if (tok_is(c, token_type)) return true; + + SEMA_ERROR_HERE("Expected '%s'.", token_type_to_string(token_type)); return false; } -static inline bool token_is_some_ident(TokenType token_type) +INLINE bool token_is_some_ident(TokenType token_type) { switch (token_type) { @@ -91,33 +100,62 @@ static inline bool token_is_some_ident(TokenType token_type) } } -static inline bool token_is_keyword(TokenType token_type) +INLINE bool token_is_keyword(TokenType token_type) { if (token_type >= TOKEN_VOID && token_type <= TOKEN_TYPEID) return true; if (token_type >= TOKEN_ALIAS && token_type <= TOKEN_WHILE) return true; return false; } -static inline bool expect_ident(ParseContext *context, const char* name) +static inline bool expect_ident(ParseContext *c, const char* name) { - switch (context->tok.type) + switch (c->tok) { case TOKEN_IDENT: return true; case TOKEN_TYPE_IDENT: case TOKEN_CONST_IDENT: - SEMA_TOKEN_ERROR(context->tok, "A %s cannot start with a capital letter.", name); + SEMA_ERROR_HERE("A %s cannot start with a capital letter.", name); return false; default: - SEMA_TOKEN_ERROR(context->tok, "A %s was expected.", name); + SEMA_ERROR_HERE("A %s was expected.", name); return false; } } -static inline Expr *parse_const_paren_expr(ParseContext *context) +static inline Expr *parse_const_paren_expr(ParseContext *c) { - CONSUME_OR(TOKEN_LPAREN, poisoned_expr); - ASSIGN_EXPR_ELSE(Expr *expr, parse_constant_expr(context), poisoned_expr); - CONSUME_OR(TOKEN_RPAREN, poisoned_expr); + CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); + ASSIGN_EXPR_OR_RET(Expr *expr, parse_constant_expr(c), poisoned_expr); + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); return expr; } + +INLINE bool parse_next_may_be_type(ParseContext *c) +{ + switch (c->tok) + { + case TOKEN_IDENT: + case TYPELIKE_TOKENS: + return true; + default: + return false; + } +} + +static inline bool parse_next_may_be_type_or_ident(ParseContext *c) +{ + switch (c->tok) + { + case TOKEN_CONST_IDENT: + case TOKEN_IDENT: + case TOKEN_HASH_CONST_IDENT: + case TOKEN_HASH: + case TOKEN_CT_IDENT: + case TOKEN_CT_CONST_IDENT: + case TYPELIKE_TOKENS: + return true; + default: + return false; + } +} diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 7bf4b2e81..345da4971 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -668,7 +668,7 @@ bool may_convert_float_const_implicit(Expr *expr, Type *to_type) if (!float_const_fits_type(&expr->const_expr, type_flatten(to_type)->type_kind)) { #if LONG_DOUBLE - SEMA_ERROR(expr, "The value '%Lg' is out of range for %s, so you need an explicit cast to truncate the value.", expr->const_expr.fxx.f, type_quoted_error_string(to_type)); + ERROR_NODE(expr, "The value '%Lg' is out of range for %s, so you need an explicit cast to truncate the value.", expr->const_expr.fxx.f, type_quoted_error_string(to_type)); #else SEMA_ERROR(expr, "The value '%g' is out of range for %s, so you need an explicit cast to truncate the value.", expr->const_expr.fxx.f, type_quoted_error_string(to_type)); #endif @@ -1012,7 +1012,7 @@ static void sema_error_const_int_out_of_range(Expr *expr, Expr *problem, Type *t } const char *error_value = expr->const_expr.is_hex ? int_to_str(expr->const_expr.ixx, 16) : expr_const_to_error_string(&expr->const_expr); SEMA_ERROR(problem, "The value '%s' is out of range for %s, so you need an explicit cast to truncate the value.", error_value, - type_quoted_error_string(to_type)); + type_quoted_error_string(to_type)); } static inline bool cast_maybe_string_lit_to_char_array(Expr *expr, Type *expr_canonical, Type *to_canonical) @@ -1082,7 +1082,7 @@ bool cast_implicit(Expr *expr, Type *to_type) if (problem) { SEMA_ERROR(problem, "The value '%s' is out of range for %s, so you need an explicit cast to truncate the value.", expr_const_to_error_string(&expr->const_expr), - type_quoted_error_string(to_type)); + type_quoted_error_string(to_type)); return false; } goto OK; @@ -1107,7 +1107,7 @@ bool cast_implicit(Expr *expr, Type *to_type) if (problem->expr_kind == EXPR_CONST) { SEMA_ERROR(problem, "The value '%s' is out of range for %s.", expr_const_to_error_string(&problem->const_expr), - type_quoted_error_string(to_type)); + type_quoted_error_string(to_type)); } else { diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 41eec4176..badd58d97 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -269,7 +269,7 @@ static bool sema_analyse_struct_members(SemaContext *context, Decl *decl, Decl * { Attr *attribute = attributes[j]; if (!sema_analyse_attribute(context, attribute, ATTR_GLOBAL)) return false; - if (TOKSTR(attribute->name) == kw_align) + if (attribute->name == kw_align) { member_alignment = attribute->alignment; // Update total alignment if we have a member that has bigger alignment. @@ -360,7 +360,7 @@ static bool sema_analyse_struct_union(SemaContext *context, Decl *decl) case DECL_UNION: domain = ATTR_UNION; break; - case DECL_ERRTYPE: + case DECL_OPTENUM: domain = ATTR_ERROR; break; default: @@ -395,7 +395,7 @@ static bool sema_analyse_struct_union(SemaContext *context, Decl *decl) #undef SET_ATTR if (had) { - SEMA_TOKID_ERROR(attr->name, "Attribute occurred twice, please remove one."); + sema_error_at(attr->name_span, "Attribute occurred twice, please remove one."); return decl_poison(decl); } } @@ -448,7 +448,7 @@ static inline bool sema_analyse_bitstruct_member(SemaContext *context, Decl *dec if (!type_is_integer(member_type) && member_type != type_bool) { SEMA_ERROR(member->var.type_info, "%s is not supported in a bitstruct, only enums, integer and boolean values may be used.", - type_quoted_error_string(member->var.type_info->type)); + type_quoted_error_string(member->var.type_info->type)); return false; } BitSize bits = type_size(decl->bitstruct.base_type->type) * 8; @@ -549,14 +549,14 @@ static bool sema_analyse_bitstruct(SemaContext *context, Decl *decl) case ATTRIBUTE_BIGENDIAN: if (decl->bitstruct.little_endian) { - SEMA_TOKID_ERROR(attr->name, "Attribute cannot be combined with @littleendian"); + sema_error_at(attr->name_span, "Attribute cannot be combined with @littleendian"); return decl_poison(decl); } SET_ATTR(big_endian); case ATTRIBUTE_LITTLEENDIAN: if (decl->bitstruct.big_endian) { - SEMA_TOKID_ERROR(attr->name, "Attribute cannot be combined with @bigendian"); + sema_error_at(attr->name_span, "Attribute cannot be combined with @bigendian"); return decl_poison(decl); } SET_ATTR(little_endian); @@ -566,7 +566,7 @@ static bool sema_analyse_bitstruct(SemaContext *context, Decl *decl) #undef SET_ATTR if (had) { - SEMA_TOKID_ERROR(attr->name, "Attribute occurred twice, please remove one."); + sema_error_at(attr->name_span, "Attribute occurred twice, please remove one."); return decl_poison(decl); } } @@ -800,7 +800,7 @@ static inline bool sema_analyse_enum(SemaContext *context, Decl *decl) // This will be evaluated later to catch the case if (!expr) { - expr = expr_new(EXPR_CONST, source_span_from_token_id(enum_value->name_token)); + expr = expr_new(EXPR_CONST, enum_value->span); expr->type = type; expr->resolve_status = RESOLVE_NOT_DONE; expr->const_expr.ixx = (Int) { value, canonical->type_kind }; @@ -862,7 +862,7 @@ static inline bool sema_analyse_error(SemaContext *context, Decl *decl) enum_value->enum_constant.ordinal = i; DEBUG_LOG("* Ordinal: %d", i); assert(enum_value->resolve_status == RESOLVE_NOT_DONE); - assert(enum_value->decl_kind == DECL_ERRVALUE); + assert(enum_value->decl_kind == DECL_OPTVALUE); // Start evaluating the constant enum_value->resolve_status = RESOLVE_DONE; @@ -921,13 +921,13 @@ static bool sema_analyse_operator_common(Decl *method, TypeInfo **rtype_ptr, Dec } if (param_count < parameters) { - SEMA_TOKID_ERROR(method->name_token, "Not enough parameters, '%s' requires %u.", method->name, (unsigned)parameters); + SEMA_ERROR(method, "Not enough parameters, '%s' requires %u.", method->name, (unsigned)parameters); return false; } if (*rtype_ptr == NULL) { - SEMA_TOKID_ERROR(method->name_token, "The return value must be explicitly typed for '%s'.", method->name); + SEMA_ERROR(method, "The return value must be explicitly typed for '%s'.", method->name); return false; } VECEACH(params, i) @@ -935,7 +935,7 @@ static bool sema_analyse_operator_common(Decl *method, TypeInfo **rtype_ptr, Dec Decl *param = params[i]; if (!params[i]->var.type_info) { - SEMA_TOKID_ERROR(param->name_token, "All parameters must be explicitly typed for '%s'.", method->name); + SEMA_ERROR(param, "All parameters must be explicitly typed for '%s'.", method->name); return false; } } @@ -1046,8 +1046,8 @@ static inline bool unit_add_method_like(CompilationUnit *unit, Type *parent_type Decl *method = sema_find_extension_method_in_module(unit->module, parent_type, name); if (method) { - SEMA_TOKID_ERROR(method_like->name_token, "This %s is already defined in this module.", name_by_decl(method_like)); - SEMA_TOKID_PREV(method->name_token, "The previous definition was here."); + SEMA_ERROR(method_like, "This %s is already defined in this module.", name_by_decl(method_like)); + SEMA_PREV(method, "The previous definition was here."); return false; } Decl *ambiguous = NULL; @@ -1055,8 +1055,8 @@ static inline bool unit_add_method_like(CompilationUnit *unit, Type *parent_type method = sema_resolve_method(unit, parent, name, &ambiguous, &private); if (method) { - SEMA_TOKID_ERROR(method_like->name_token, "This %s is already defined for '%s'.", name_by_decl(method_like), parent_type->name); - SEMA_TOKID_PREV(method->name_token, "The previous definition was here."); + SEMA_ERROR(method_like, "This %s is already defined for '%s'.", name_by_decl(method_like), parent_type->name); + SEMA_PREV(method, "The previous definition was here."); return false; } if (method_like->operator && !sema_check_operator_method_validity(method_like)) return false; @@ -1074,7 +1074,7 @@ static inline bool unit_add_method_like(CompilationUnit *unit, Type *parent_type scratch_buffer_append("__"); scratch_buffer_append(method_like->name); } - method_like->external_name = scratch_buffer_interned(); + method_like->external_name = scratch_buffer_copy(); DEBUG_LOG("Method-like '%s.%s' analysed.", parent->name, method_like->name); if (parent->module == unit->module) { @@ -1095,7 +1095,7 @@ static inline bool sema_analyse_method(SemaContext *context, Decl *decl) if (!type_may_have_sub_elements(parent_type->type)) { SEMA_ERROR(decl, - "Methods can not be associated with '%s'", + "Methods can not be associated with '%s'", type_to_error_string(decl->func_decl.type_parent->type)); return false; } @@ -1105,7 +1105,7 @@ static inline bool sema_analyse_method(SemaContext *context, Decl *decl) static inline AttributeType attribute_by_name(Attr *attr) { - const char *attribute = TOKSTR(attr->name); + const char *attribute = attr->name; for (unsigned i = 0; i < NUMBER_OF_ATTRIBUTES; i++) { if (attribute_list[i] == attribute) return (AttributeType)i; @@ -1154,7 +1154,7 @@ AttributeType sema_analyse_attribute(SemaContext *context, Attr *attr, Attribute AttributeType type = attribute_by_name(attr); if (type == ATTRIBUTE_NONE) { - SEMA_TOKID_ERROR(attr->name, "There is no attribute with the name '%s', did you mistype?", TOKSTR(attr->name)); + sema_error_at(attr->name_span, "There is no attribute with the name '%s', did you mistype?", attr->name); return ATTRIBUTE_NONE; } static AttributeDomain attribute_domain[NUMBER_OF_ATTRIBUTES] = { @@ -1185,7 +1185,7 @@ AttributeType sema_analyse_attribute(SemaContext *context, Attr *attr, Attribute if ((attribute_domain[type] & domain) != domain) { - SEMA_TOKID_ERROR(attr->name, "'%s' is not a valid %s attribute.", TOKSTR(attr->name), attribute_domain_to_string(domain)); + sema_error_at(attr->name_span, "'%s' is not a valid %s attribute.", attr->name, attribute_domain_to_string(domain)); return ATTRIBUTE_NONE; } switch (type) @@ -1201,8 +1201,7 @@ AttributeType sema_analyse_attribute(SemaContext *context, Attr *attr, Attribute Expr *expr = attr->expr; if (!expr || expr->expr_kind != EXPR_IDENTIFIER) goto FAILED_OP_TYPE; if (expr->identifier_expr.path) goto FAILED_OP_TYPE; - TokenId tok = expr->identifier_expr.identifier; - const char *kw = TOKSTR(tok); + const char *kw = expr->identifier_expr.ident; if (kw == kw_elementat) { attr->operator = OVERLOAD_ELEMENT_AT; @@ -1231,7 +1230,7 @@ AttributeType sema_analyse_attribute(SemaContext *context, Attr *attr, Attribute case ATTRIBUTE_ALIGN: if (!attr->expr) { - SEMA_TOKID_ERROR(attr->name, "'align' requires an power-of-2 argument, e.g. align(8)."); + sema_error_at(attr->name_span, "'align' requires an power-of-2 argument, e.g. align(8)."); return ATTRIBUTE_NONE; } if (!sema_analyse_expr(context, attr->expr)) return false; @@ -1265,12 +1264,12 @@ AttributeType sema_analyse_attribute(SemaContext *context, Attr *attr, Attribute case ATTRIBUTE_EXTNAME: if (context->unit->module->is_generic) { - SEMA_TOKID_ERROR(attr->name, "'extname' attributes are not allowed in generic modules."); + sema_error_at(attr->name_span, "'extname' attributes are not allowed in generic modules."); return false; } if (!attr->expr) { - SEMA_TOKID_ERROR(attr->name, "'%s' requires a string argument, e.g. %s(\"foo\").", TOKSTR(attr->name), TOKSTR(attr->name)); + sema_error_at(attr->name_span, "'%s' requires a string argument, e.g. %s(\"foo\").", attr->name, attr->name); return ATTRIBUTE_NONE; } if (!sema_analyse_expr(context, attr->expr)) return false; @@ -1283,7 +1282,7 @@ AttributeType sema_analyse_attribute(SemaContext *context, Attr *attr, Attribute default: if (attr->expr) { - SEMA_ERROR(attr->expr, "'%s' should not have any arguments.", TOKSTR(attr->name)); + SEMA_ERROR(attr->expr, "'%s' should not have any arguments.", attr->name); return ATTRIBUTE_NONE; } return type; @@ -1300,20 +1299,51 @@ static inline bool sema_analyse_doc_header(Ast *docs, Decl **params, Decl **extr { Ast *directive = doc_directives[i]; if (directive->doc_directive.kind != DOC_DIRECTIVE_PARAM) continue; - TokenId param = directive->doc_directive.param.param; - const char *param_name = TOKSTR(param); + const char *param_name = directive->doc_directive.param.name; + Decl *extra_param = NULL; + Decl *param = NULL; VECEACH(params, j) { - if (params[j]->name == param_name) goto NEXT; + param = params[j]; + if (param->name == param_name) goto NEXT; } VECEACH(extra_params, j) { - if (extra_params[j]->name == param_name) goto NEXT; + param = extra_params[j]; + if (param->name == param_name) goto NEXT; } - SEMA_TOKID_ERROR(param, "There is no parameter '%s', did you misspell it?", param_name); + SEMA_ERROR(&directive->doc_directive.param, "There is no parameter '%s', did you misspell it?", param_name); return false; - NEXT: - continue; + NEXT:; + bool may_be_pointer = !param->type || type_is_pointer(type_flatten(param->type)); + if (directive->doc_directive.param.by_ref) + { + if (!may_be_pointer) + { + SEMA_ERROR(directive, "'&' can only be added to pointer type parameters."); + return false; + } + param->var.not_null = true; + } + switch (directive->doc_directive.param.modifier) + { + case PARAM_ANY: + goto ADDED; + case PARAM_IN: + param->var.may_not_write = true; + break; + case PARAM_OUT: + param->var.may_not_read = true; + break; + case PARAM_INOUT: + break; + } + if (!may_be_pointer) + { + SEMA_ERROR(directive, "'in', 'out' and 'inout' may only be added to pointers."); + return false; + } +ADDED:; } return true; } @@ -1390,7 +1420,7 @@ static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl) decl->visibility = VISIBLE_EXTERN; return true; } - Decl *function = decl_new(DECL_FUNC, decl->name_token, VISIBLE_EXTERN); + Decl *function = decl_new(DECL_FUNC, NULL, decl->span, VISIBLE_EXTERN); function->name = kw_mainstub; function->extname = kw_main; function->func_decl.function_signature.returntype = type_info_new_base(type_cint, decl->span); @@ -1551,12 +1581,12 @@ static inline bool sema_analyse_func(SemaContext *context, Decl *decl) #undef SET_ATTR if (had) { - SEMA_TOKID_ERROR(attr->name, "Attribute occurred twice, please remove one."); + sema_error_at(attr->name_span, "Attribute occurred twice, please remove one."); return decl_poison(decl); } if (decl->func_decl.attr_inline && decl->func_decl.attr_noinline) { - SEMA_TOKID_ERROR(attr->name, "A function cannot be 'inline' and 'noinline' at the same time."); + sema_error_at(attr->name_span, "A function cannot be 'inline' and 'noinline' at the same time."); return decl_poison(decl); } } @@ -1647,7 +1677,7 @@ static inline bool sema_analyse_macro(SemaContext *context, Decl *decl) } if (had) { - SEMA_TOKID_ERROR(attr->name, "Attribute occurred twice, please remove one."); + sema_error_at(attr->name_span, "Attribute occurred twice, please remove one."); return decl_poison(decl); } } @@ -1796,7 +1826,7 @@ static bool sema_analyse_attributes_for_var(SemaContext *context, Decl *decl) #undef SET_ATTR if (had) { - SEMA_TOKID_ERROR(attr->name, "Attribute occurred twice, please remove one."); + sema_error_at(attr->name_span, "Attribute occurred twice, please remove one."); return decl_poison(decl); } } @@ -1807,14 +1837,14 @@ bool sema_analyse_decl_type(SemaContext *context, Type *type, SourceSpan span) { if (type == type_void) { - sema_error_range(span, "The use of 'void' as a variable type is not permitted."); + sema_error_at(span, "The use of 'void' as a variable type is not permitted."); return false; } if (!type_is_failable(type)) return true; if (type_is_failable_any(type) || type_flatten_distinct(type->failable) == type_void) { - sema_error_range(span, "The use of 'void!' as a variable type is not permitted, use %s instead.", + sema_error_at(span, "The use of 'void!' as a variable type is not permitted, use %s instead.", type_quoted_error_string(type_anyerr)); return false; } @@ -1957,7 +1987,7 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local) scratch_buffer_append(context->current_function->name); scratch_buffer_append_char('.'); scratch_buffer_append(decl->name); - decl->external_name = scratch_buffer_interned(); + decl->external_name = scratch_buffer_copy(); } if (decl->var.init_expr) @@ -2041,8 +2071,8 @@ static Module *module_instantiate_generic(Module *module, Path *path, TypeInfo * CompilationUnit *first_context = new_module->units[0]; VECEACH(module->parameters, i) { - TokenId param = module->parameters[i]; - Decl *decl = decl_new_with_type(param, DECL_TYPEDEF, VISIBLE_PUBLIC); + const char *param = module->parameters[i]; + Decl *decl = decl_new_with_type(param, parms[i]->span, DECL_TYPEDEF, VISIBLE_PUBLIC); decl->resolve_status = RESOLVE_DONE; TypeInfo *type_info = parms[i]; assert(type_info->resolve_status == RESOLVE_DONE); @@ -2057,12 +2087,14 @@ static Module *module_instantiate_generic(Module *module, Path *path, TypeInfo * static bool sema_analyse_parameterized_define(SemaContext *c, Decl *decl) { Path *decl_path; - TokenId name; + const char *name; + SourceSpan span; switch (decl->define_decl.define_kind) { case DEFINE_IDENT_GENERIC: decl_path = decl->define_decl.path; - name = decl->define_decl.identifier; + name = decl->define_decl.ident; + span = decl->define_decl.span; break; case DEFINE_TYPE_GENERIC: { @@ -2073,7 +2105,8 @@ static bool sema_analyse_parameterized_define(SemaContext *c, Decl *decl) return decl_poison(decl); } decl_path = define_type->unresolved.path; - name = define_type->unresolved.name_loc; + name = define_type->unresolved.name; + span = define_type->unresolved.span; break; } case DEFINE_ATTRIBUTE: @@ -2081,7 +2114,7 @@ static bool sema_analyse_parameterized_define(SemaContext *c, Decl *decl) default: UNREACHABLE } - Decl *alias = unit_resolve_parameterized_symbol(c->unit, name, decl_path); + Decl *alias = unit_resolve_parameterized_symbol(c->unit, name, span, decl_path); if (!decl_ok(alias)) { return decl_poison(decl); @@ -2093,8 +2126,10 @@ static bool sema_analyse_parameterized_define(SemaContext *c, Decl *decl) assert(parameter_count > 0); if (parameter_count != vec_size(params)) { - sema_error_range((SourceSpan) { params[0]->span.loc, VECLAST(params)->span.end_loc }, "The generic module expected %d arguments, but you only supplied %d, did you make a mistake?", - parameter_count, vec_size(decl->define_decl.generic_params)); + sema_error_at(extend_span_with_token(params[0]->span, VECLAST(params)->span), + "The generic module expected %d arguments, but you only supplied %d, did you make a mistake?", + parameter_count, + vec_size(decl->define_decl.generic_params)); return decl_poison(decl); } scratch_buffer_clear(); @@ -2121,8 +2156,7 @@ static bool sema_analyse_parameterized_define(SemaContext *c, Decl *decl) sema_analyze_stage(instantiated_module, c->unit->module->stage); } if (global_context.errors_found) return decl_poison(decl); - const char *name_str = TOKSTR(name); - Decl *symbol = module_find_symbol(instantiated_module, name_str); + Decl *symbol = module_find_symbol(instantiated_module, name); assert(symbol); unit_register_external_symbol(c->compilation_unit, symbol); switch (decl->define_decl.define_kind) @@ -2151,7 +2185,8 @@ static inline bool sema_analyse_define(SemaContext *c, Decl *decl) if (decl->define_decl.define_kind == DEFINE_IDENT_ALIAS) { Decl *symbol = sema_resolve_normal_symbol(c, - decl->define_decl.identifier, + decl->define_decl.ident, + decl->define_decl.span, decl->define_decl.path, true); if (!decl_ok(symbol)) return false; @@ -2214,7 +2249,7 @@ bool sema_analyse_decl(SemaContext *context, Decl *decl) if (!sema_analyse_enum(context, decl)) return decl_poison(decl); decl_set_external_name(decl); break; - case DECL_ERRTYPE: + case DECL_OPTENUM: if (!sema_analyse_error(context, decl)) return decl_poison(decl); decl_set_external_name(decl); break; @@ -2233,7 +2268,7 @@ bool sema_analyse_decl(SemaContext *context, Decl *decl) case DECL_CT_CASE: case DECL_CT_IF: case DECL_CT_ASSERT: - case DECL_ERRVALUE: + case DECL_OPTVALUE: case DECL_DECLARRAY: UNREACHABLE } diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 32bf81732..a88934368 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -122,7 +122,7 @@ Expr *expr_variable(Decl *decl) return expr; } Expr *expr = expr_new(EXPR_IDENTIFIER, decl->span); - expr->identifier_expr.identifier = decl->name_token; + expr->identifier_expr.ident = decl->name; expr->resolve_status = RESOLVE_NOT_DONE; return expr; @@ -273,8 +273,7 @@ static bool sema_bit_assignment_check(Expr *right, Decl *member) if (int_bits_needed(right->const_expr.ixx) > bits) { - SEMA_ERROR(right, - "This constant would be truncated if stored in the bitstruct, do you need a wider bit range?"); + SEMA_ERROR(right, "This constant would be truncated if stored in the bitstruct, do you need a wider bit range?"); return false; } return true; @@ -574,7 +573,7 @@ static inline bool sema_cast_ident_rvalue(SemaContext *context, Expr *expr) case DECL_GENERIC: SEMA_ERROR(expr, "Expected generic function followed by (...)."); return expr_poison(expr); - case DECL_ERRVALUE: + case DECL_OPTVALUE: SEMA_ERROR(expr, "Did you forget a '!' after '%s'?", decl->name); return expr_poison(expr); case DECL_ENUM_CONSTANT: @@ -603,7 +602,7 @@ static inline bool sema_cast_ident_rvalue(SemaContext *context, Expr *expr) case DECL_ENUM: SEMA_ERROR(expr, "Expected enum name followed by '.' and an enum value."); return expr_poison(expr); - case DECL_ERRTYPE: + case DECL_OPTENUM: SEMA_ERROR(expr, "Expected errtype name followed by '.' and an error value."); return expr_poison(expr); case DECL_IMPORT: @@ -683,8 +682,8 @@ static inline bool sema_type_error_on_binop(Expr *expr) { const char *c = token_type_to_string(binaryop_to_token(expr->binary_expr.operator)); SEMA_ERROR(expr, "%s is not defined in the expression %s %s %s.", - c, type_quoted_error_string(expr->binary_expr.left->type), - c, type_quoted_error_string(expr->binary_expr.right->type)); + c, type_quoted_error_string(expr->binary_expr.left->type), + c, type_quoted_error_string(expr->binary_expr.right->type)); return false; } @@ -760,7 +759,7 @@ static inline bool sema_expr_analyse_ternary(SemaContext *context, Expr *expr) if (!max) { SEMA_ERROR(expr, "Cannot find a common parent type of '%s' and '%s'", - type_to_error_string(left_canonical), type_to_error_string(right_canonical)); + type_to_error_string(left_canonical), type_to_error_string(right_canonical)); return false; } Type *no_fail_max = type_no_fail(max); @@ -778,9 +777,8 @@ static inline bool sema_expr_analyse_ternary(SemaContext *context, Expr *expr) -static inline Decl *decl_find_enum_constant(TokenId token, Decl *decl) +static inline Decl *decl_find_enum_constant(const char *name, Decl *decl) { - const char *name = TOKSTR(token); VECEACH(decl->enums.values, i) { Decl *enum_constant = decl->enums.values[i]; @@ -792,7 +790,7 @@ static inline Decl *decl_find_enum_constant(TokenId token, Decl *decl) return NULL; } -static inline bool sema_expr_analyse_enum_constant(Expr *expr, TokenId name, Decl *decl) +static inline bool sema_expr_analyse_enum_constant(Expr *expr, const char *name, Decl *decl) { Decl *enum_constant = decl_find_enum_constant(name, decl); if (!enum_constant) return false; @@ -821,8 +819,8 @@ static inline bool find_possible_inferred_identifier(Type *to, Expr *expr) switch (parent_decl->decl_kind) { case DECL_ENUM: - case DECL_ERRTYPE: - return sema_expr_analyse_enum_constant(expr, expr->identifier_expr.identifier, parent_decl); + case DECL_OPTENUM: + return sema_expr_analyse_enum_constant(expr, expr->identifier_expr.ident, parent_decl); case DECL_UNION: case DECL_STRUCT: case DECL_BITSTRUCT: @@ -838,9 +836,10 @@ static inline bool sema_expr_analyse_identifier(SemaContext *context, Type *to, Decl *ambiguous_decl = NULL; Decl *private_symbol = NULL; - DEBUG_LOG("Now resolving %s", TOKSTR(expr->identifier_expr.identifier)); + DEBUG_LOG("Now resolving %s", expr->identifier_expr.ident); Decl *decl = sema_resolve_normal_symbol(context, - expr->identifier_expr.identifier, + expr->identifier_expr.ident, + expr->span, expr->identifier_expr.path, false); if (!decl_ok(decl)) return false; @@ -851,7 +850,8 @@ static inline bool sema_expr_analyse_identifier(SemaContext *context, Type *to, if (!decl) { decl = sema_resolve_normal_symbol(context, - expr->identifier_expr.identifier, + expr->identifier_expr.ident, + expr->span, expr->identifier_expr.path, true); (void)decl; @@ -930,8 +930,8 @@ static inline bool sema_expr_analyse_macro_expansion(SemaContext *context, Expr Expr *inner = expr->macro_expansion_expr.inner; if (inner->expr_kind == EXPR_IDENTIFIER) { - TokenId body = context->current_macro ? context->current_macro->macro_decl.block_parameter : NO_TOKEN_ID ; - if (body.index && !inner->identifier_expr.path && TOKSTR(inner->identifier_expr.identifier) == TOKSTR(body)) + const char *body = context->current_macro ? context->current_macro->macro_decl.block_parameter : NULL; + if (body && !inner->identifier_expr.path && inner->identifier_expr.ident == body) { expr->expr_kind = EXPR_MACRO_BODY_EXPANSION; expr->body_expansion_expr.ast = NULL; @@ -966,9 +966,10 @@ static inline bool sema_expr_analyse_macro_expansion(SemaContext *context, Expr static inline bool sema_expr_analyse_ct_identifier(SemaContext *context, Expr *expr) { - DEBUG_LOG("Now resolving %s", TOKSTR(expr->ct_ident_expr.identifier)); + DEBUG_LOG("Now resolving %s", expr->ct_ident_expr.identifier); Decl *decl = sema_resolve_normal_symbol(context, expr->ct_ident_expr.identifier, + expr->span, NULL, true); // Already handled @@ -988,9 +989,10 @@ static inline bool sema_expr_analyse_ct_identifier(SemaContext *context, Expr *e static inline bool sema_expr_analyse_hash_identifier(SemaContext *context, Expr *expr) { - DEBUG_LOG("Now resolving %s", TOKSTR(expr->hash_ident_expr.identifier)); + DEBUG_LOG("Now resolving %s", expr->hash_ident_expr.identifier); Decl *decl = sema_resolve_normal_symbol(context, expr->hash_ident_expr.identifier, + expr->span, NULL, true); // Already handled @@ -1003,7 +1005,7 @@ static inline bool sema_expr_analyse_hash_identifier(SemaContext *context, Expr assert(decl->decl_kind == DECL_VAR); expr_replace(expr, copy_expr(decl->var.init_expr)); - if (!sema_analyse_expr(decl->var.context, expr)) + if (!sema_analyse_expr(decl->var.hash_var.context, expr)) { // Poison the decl so we don't evaluate twice. decl_poison(decl); @@ -1137,7 +1139,7 @@ typedef struct { bool macro; bool func_pointer; - TokenId block_parameter; + const char *block_parameter; Decl **params; Type **param_types; Expr *struct_var; @@ -1168,7 +1170,7 @@ static inline bool sema_check_invalid_body_arguments(SemaContext *context, Expr bool has_body_arguments = vec_size(body_arguments) > 0; // 1. Check if there are body arguments but no actual block. - if (has_body_arguments && !callee->block_parameter.index) + if (has_body_arguments && !callee->block_parameter) { if (callee->macro) { @@ -1191,7 +1193,7 @@ static inline bool sema_check_invalid_body_arguments(SemaContext *context, Expr return false; } // 2b. If we don't have a block parameter, then this is an error as well - if (!callee->block_parameter.index) + if (!callee->block_parameter) { SEMA_ERROR(call, "This macro does not support trailing statements, please remove it."); return false; @@ -1202,7 +1204,7 @@ static inline bool sema_check_invalid_body_arguments(SemaContext *context, Expr } // 3. If we don't have a body, then if it has a block parameter this is an error. - if (callee->block_parameter.index) + if (callee->block_parameter) { assert(callee->macro); SEMA_ERROR(call, "Expected call to have a trailing statement, did you forget to add it?"); @@ -1388,7 +1390,7 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr if (callee.variadic == VARIADIC_NONE) { SEMA_ERROR(call->call_expr.arguments[num_args - 1], - "Unpacking is only allowed for %s with variable parameters.", + "Unpacking is only allowed for %s with variable parameters.", callee.macro ? "macros" : "functions"); return false; } @@ -1520,7 +1522,8 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr break; case VARDECL_PARAM_EXPR: // #foo - param->var.context = context; + param->var.hash_var.context = context; + param->var.hash_var.span = arg->span; break; case VARDECL_PARAM_CT: // $foo @@ -1563,7 +1566,7 @@ static inline bool sema_expr_analyse_func_invocation(SemaContext *context, Funct CalledDecl callee = { .macro = false, .func_pointer = sig ? 0 : 1, - .block_parameter = NO_TOKEN_ID, + .block_parameter = NULL, .struct_var = struct_var, .params = sig ? sig->params : NULL, .param_types = prototype->params, @@ -1803,7 +1806,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s macro_context.yield_body = call_expr->call_expr.body; macro_context.yield_params = body_params; macro_context.yield_context = context; - macro_context.original_inline_line = context->original_inline_line ? context->original_inline_line : TOKLOC(call_expr->span.loc)->row; + macro_context.original_inline_line = context->original_inline_line ? context->original_inline_line : call_expr->span.row; VECEACH(params, i) { @@ -1828,8 +1831,8 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s if (rtype && type_no_fail(rtype) != type_void) { SEMA_ERROR(decl, - "Missing return in macro that should evaluate to %s.", - type_quoted_error_string(rtype)); + "Missing return in macro that should evaluate to %s.", + type_quoted_error_string(rtype)); return SCOPE_POP_ERROR(); } } @@ -1850,7 +1853,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s if (!cast_may_implicit(type, rtype, true, true)) { SEMA_ERROR(ret_expr, "Expected %s, not %s.", type_quoted_error_string(rtype), - type_quoted_error_string(type)); + type_quoted_error_string(type)); return SCOPE_POP_ERROR(); } bool success = cast_implicit(ret_expr, rtype); @@ -1967,6 +1970,7 @@ static inline bool sema_expr_analyse_generic_call(SemaContext *context, Expr *ca Decl *found = NULL; VECEACH(generic_cache, i) { + TODO if (generic_cache[i]->external_name == mangled_name) { found = generic_cache[i]; @@ -2000,7 +2004,7 @@ static bool sema_analyse_body_expansion(SemaContext *macro_context, Expr *call) { Decl *macro = macro_context->current_macro; assert(macro); - assert(macro->macro_decl.block_parameter.index); + assert(macro->macro_decl.block_parameter); ExprCall *call_expr = &call->call_expr; if (vec_size(call_expr->body_arguments)) @@ -2065,13 +2069,13 @@ static inline bool sema_analyse_call_attributes(SemaContext *context, Decl *decl case ATTRIBUTE_NOINLINE: if (decl && decl->decl_kind != DECL_FUNC) { - SEMA_TOKID_ERROR(attr->name, + sema_error_at(attr->name_span, "Inline / noinline attribute is only allowed for direct function/method calls"); return false; } if (force_inline != -1) { - SEMA_TOKID_ERROR(attr->name, "Only a single inline / noinline attribute is allowed on a call."); + sema_error_at(attr->name_span, "Only a single inline / noinline attribute is allowed on a call."); return false; } force_inline = attribute == ATTRIBUTE_INLINE ? 1 : 0; @@ -2783,7 +2787,7 @@ void expr_rewrite_to_int_const(Expr *expr_to_rewrite, Type *type, uint64_t value } -static inline void expr_rewrite_to_string(Expr *expr_to_rewrite, const char *string) +void expr_rewrite_to_string(Expr *expr_to_rewrite, const char *string) { expr_to_rewrite->expr_kind = EXPR_CONST; expr_to_rewrite->const_expr.const_kind = CONST_STRING; @@ -2857,40 +2861,41 @@ static Expr *enum_minmax_value(Decl *decl, BinaryOp comparison) * 4. .#bar -> It is an identifier to resolve as a member or a function * 5. .@#bar -> It is an identifier to resolve as a macro */ -static TokenId sema_expr_resolve_access_child(Expr *child) +static Expr *sema_expr_resolve_access_child(SemaContext *context, Expr *child) { switch (child->expr_kind) { case EXPR_IDENTIFIER: - // Not allowed obviously. + // A path is not allowed. if (child->identifier_expr.path) break; - return child->identifier_expr.identifier; - case EXPR_HASH_IDENT: - TODO + return child; case EXPR_MACRO_EXPANSION: child = child->macro_expansion_expr.inner; switch (child->expr_kind) { case EXPR_IDENTIFIER: case EXPR_HASH_IDENT: - return sema_expr_resolve_access_child(child); + return sema_expr_resolve_access_child(context, child); default: SEMA_ERROR(child, "Expected a macro name."); - return INVALID_TOKEN_ID; - } - case EXPR_TYPEINFO: - // Special case: .typeid - if (child->type_expr->resolve_status == RESOLVE_DONE && child->type_expr->type == type_typeid) - { - return child->type_expr->span.loc; + return NULL; } break; + case EXPR_HASH_IDENT: + { + Decl *decl = sema_resolve_normal_symbol(context, + child->hash_ident_expr.identifier, + child->span, + NULL, true); + if (!decl_ok(decl)) return NULL; + return sema_expr_resolve_access_child(context, decl->var.init_expr); + } default: break; } SEMA_ERROR(child, "Expected an identifier here."); - return INVALID_TOKEN_ID; + return NULL; } static inline void expr_replace_with_enum_array(Expr *enum_array_expr, Decl *enum_decl) @@ -2918,8 +2923,9 @@ static inline void expr_replace_with_enum_array(Expr *enum_array_expr, Decl *enu enum_array_expr->resolve_status = RESOLVE_NOT_DONE; } -static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *expr, TypeInfo *parent, bool was_group, bool is_macro, TokenId identifier_token) +static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *expr, TypeInfo *parent, bool was_group, bool is_macro, Expr *identifier) { + assert(identifier->expr_kind == EXPR_IDENTIFIER); // 1. Foo*.sizeof is not allowed, it must be (Foo*).sizeof if (!was_group && type_kind_is_derived(parent->type->type_kind)) { @@ -2927,24 +2933,12 @@ static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *exp return false; } - TokenType type = TOKTYPE(identifier_token); - - // 2. Handle Foo.typeid => return a typeid expression. - if (type == TOKEN_TYPEID) - { - expr->type = type_typeid; - expr->expr_kind = EXPR_CONST; - expr->const_expr.const_kind = CONST_TYPEID; - expr->const_expr.typeid = parent->type->canonical; - expr->resolve_status = RESOLVE_DONE; - return true; - } - Type *canonical = parent->type->canonical; - const char *name = TOKSTR(identifier_token); + const char *name = identifier->identifier_expr.ident; + bool is_const = identifier->identifier_expr.is_const; // 3. Handle float.nan, double.inf etc - if (type_is_float(canonical)) + if (!is_const && type_is_float(canonical)) { if (name == kw_nan) { @@ -2981,9 +2975,9 @@ static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *exp switch (decl->decl_kind) { case DECL_ENUM: - if (type == TOKEN_CONST_IDENT) + if (is_const) { - if (!sema_expr_analyse_enum_constant(expr, identifier_token, decl)) + if (!sema_expr_analyse_enum_constant(expr, name, decl)) { SEMA_ERROR(expr, "'%s' has no enumeration value '%s'.", decl->name, name); return false; @@ -3024,12 +3018,12 @@ static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *exp return true; } break; - case DECL_ERRTYPE: + case DECL_OPTENUM: unit_register_external_symbol(context->compilation_unit, decl); - if (type == TOKEN_CONST_IDENT) + if (is_const) { - if (!sema_expr_analyse_enum_constant(expr, identifier_token, decl)) + if (!sema_expr_analyse_enum_constant(expr, name, decl)) { SEMA_ERROR(expr, "'%s' has no error value '%s'.", decl->name, name); return false; @@ -3114,7 +3108,7 @@ static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *exp return false; } - expr->identifier_expr.identifier = identifier_token; + expr->identifier_expr.ident = name; expr->expr_kind = EXPR_IDENTIFIER; expr->identifier_expr.decl = member; expr->type = member->type; @@ -3136,14 +3130,38 @@ static inline bool sema_expr_analyse_access(SemaContext *context, Expr *expr) Expr *child = expr->access_expr.child; bool is_macro = child->expr_kind == EXPR_MACRO_EXPANSION; - // 3. Find the actual token. - TokenId identifier_token = sema_expr_resolve_access_child(child); - if (TOKEN_IS_INVALID(identifier_token)) return false; + // 3. Handle xxxxxx.typeid + if (child->expr_kind == EXPR_TYPEINFO) + { + if (child->type_expr->resolve_status != RESOLVE_DONE || child->type_expr->type != type_typeid) + { + SEMA_ERROR(child, "A type can't appear here."); + return false; + } - // 2. If our left hand side is a type, e.g. MyInt.abc, handle this here. + if (parent->expr_kind != EXPR_TYPEINFO) + { + SEMA_ERROR(expr, "'typeid' can only be used with types, not values"); + } + + expr->type = type_typeid; + expr->expr_kind = EXPR_CONST; + expr->const_expr.const_kind = CONST_TYPEID; + expr->const_expr.typeid = parent->type_expr->type->canonical; + expr->resolve_status = RESOLVE_DONE; + return true; + + } + + // 3. Find the actual token. + SourceSpan span; + Expr *identifier = sema_expr_resolve_access_child(context, child); + if (!identifier) return false; + + // 2. If our left-hand side is a type, e.g. MyInt.abc, handle this here. if (parent->expr_kind == EXPR_TYPEINFO) { - return sema_expr_analyse_type_access(context, expr, parent->type_expr, was_group, is_macro, identifier_token); + return sema_expr_analyse_type_access(context, expr, parent->type_expr, was_group, is_macro, identifier); } // 6. Copy failability @@ -3162,19 +3180,19 @@ static inline bool sema_expr_analyse_access(SemaContext *context, Expr *expr) } // 8. Depending on parent type, we have some hard coded types - TokenType token_type = TOKTYPE(identifier_token); Expr *current_parent = parent; Type *type = type_no_fail(parent->type)->canonical; Type *flat_type = type_flatten(type); - if (!is_macro && token_type == TOKEN_TYPEID && flat_type->type_kind == TYPE_ANY) + const char *kw = identifier->identifier_expr.ident; + + if (!is_macro && kw_type == kw && flat_type->type_kind == TYPE_ANY) { expr->expr_kind = EXPR_TYPEOFANY; expr->inner_expr = parent; expr->type = type_typeid; return true; } - const char *kw = TOKSTR(identifier_token); CHECK_DEEPER: @@ -4260,9 +4278,10 @@ static inline bool sema_expr_analyse_ct_identifier_lvalue(SemaContext *context, Decl *ambiguous_decl = NULL; Decl *private_symbol = NULL; - DEBUG_LOG("Now resolving %s", TOKSTR(expr->ct_ident_expr.identifier)); + DEBUG_LOG("Now resolving %s", expr->ct_ident_expr.identifier); Decl *decl = sema_resolve_normal_symbol(context, expr->ct_ident_expr.identifier, + expr->span, NULL, false); // Skip if poisoned. @@ -4270,7 +4289,7 @@ static inline bool sema_expr_analyse_ct_identifier_lvalue(SemaContext *context, if (!decl) { - SEMA_ERROR(expr, "The compile time variable '%s' was not defined in this scope.", TOKSTR(expr->ct_ident_expr.identifier)); + SEMA_ERROR(expr, "The compile time variable '%s' was not defined in this scope.", expr->ct_ident_expr.identifier); return expr_poison(expr); } @@ -4301,14 +4320,12 @@ static bool sema_expr_analyse_ct_type_identifier_assign(SemaContext *context, Ex { TypeInfo *info = left->type_expr; - if (info->kind != TYPE_INFO_IDENTIFIER || info->unresolved.path || TOKTYPE(info->unresolved.name_loc) != TOKEN_CT_TYPE_IDENT) + if (info->kind != TYPE_INFO_CT_IDENTIFIER) { SEMA_ERROR(left, "A type cannot be assigned to."); return false; } - TokenId token = info->unresolved.name_loc; - if (!sema_analyse_expr_lvalue(context, right)) return false; if (right->expr_kind != EXPR_TYPEINFO) @@ -4317,18 +4334,14 @@ static bool sema_expr_analyse_ct_type_identifier_assign(SemaContext *context, Ex return false; } - Decl *decl = sema_resolve_normal_symbol(context, token, NULL, false); + Decl *decl = sema_resolve_normal_symbol(context, info->unresolved.name, info->unresolved.span, NULL, false); if (!decl) { - decl = decl_new(DECL_VAR, token, VISIBLE_LOCAL); - decl->var.kind = VARDECL_LOCAL_CT_TYPE; - if (!sema_add_local(context, decl)) return false; + SEMA_ERROR(info, "'%s' is not defined in this scope yet.", info->unresolved.name); + return false; } - decl = sema_resolve_normal_symbol(context, token, NULL, true); - decl->var.init_expr = right; - expr->expr_kind = EXPR_NOP; expr->type = type_void; @@ -4566,8 +4579,8 @@ static bool sema_expr_analyse_add_sub_assign(SemaContext *context, Expr *expr, E if (!type_is_integer(right->type->canonical)) { SEMA_ERROR(right, "The right side was '%s' but only integers are valid on the right side of %s when the left side is a pointer.", - type_to_error_string(right->type), - token_type_to_string(binaryop_to_token(expr->binary_expr.operator))); + type_to_error_string(right->type), + token_type_to_string(binaryop_to_token(expr->binary_expr.operator))); return false; } return true; @@ -4804,8 +4817,8 @@ static bool sema_expr_analyse_add(SemaContext *context, Expr *expr, Expr *left, if (!type_is_integer(right_type)) { SEMA_ERROR(right, "A value of type '%s' cannot be added to '%s', an integer was expected here.", - type_to_error_string(right->type), - type_to_error_string(left->type)); + type_to_error_string(right->type), + type_to_error_string(left->type)); return false; } @@ -5296,7 +5309,7 @@ static bool sema_expr_analyse_comp(SemaContext *context, Expr *expr, Expr *left, if (type_is_integer(left_vec) && type_is_integer(right_vec)) goto DONE; } SEMA_ERROR(expr, "Vector types %s and %s cannot be compared.", - type_quoted_error_string(left->type), type_quoted_error_string(right->type)); + type_quoted_error_string(left->type), type_quoted_error_string(right->type)); return false; } @@ -5307,14 +5320,14 @@ static bool sema_expr_analyse_comp(SemaContext *context, Expr *expr, Expr *left, if (!max) { SEMA_ERROR(expr, "%s and %s are different types and cannot be compared.", - type_quoted_error_string(left->type), type_quoted_error_string(right->type)); + type_quoted_error_string(left->type), type_quoted_error_string(right->type)); return false; } if (!type_is_comparable(max)) { SEMA_ERROR(expr, "%s does not support comparisons, you need to manually implement a comparison if you need it.", - type_quoted_error_string(left->type)); + type_quoted_error_string(left->type)); return false; } if (!is_equality_type_op) @@ -5322,7 +5335,7 @@ static bool sema_expr_analyse_comp(SemaContext *context, Expr *expr, Expr *left, if (!type_is_ordered(max)) { SEMA_ERROR(expr, "%s can only be compared using '!=' and '==' it cannot be ordered, did you make a mistake?", - type_quoted_error_string(left->type)); + type_quoted_error_string(left->type)); return false; } if (type_flatten(max)->type_kind == TYPE_POINTER) @@ -6104,7 +6117,7 @@ static inline bool sema_expr_analyse_compound_literal(SemaContext *context, Expr if (type_is_failable(type)) { SEMA_ERROR(expr->expr_compound_literal.type_info, - "The type here should always be written as a plain type and not a failable, please remove the '!'."); + "The type here should always be written as a plain type and not a failable, please remove the '!'."); return false; } if (!sema_expr_analyse_initializer_list(context, type, expr->expr_compound_literal.initializer)) return false; @@ -6143,7 +6156,7 @@ static inline bool sema_expr_analyse_failable(SemaContext *context, Expr *expr) static inline bool sema_expr_analyse_compiler_const(SemaContext *context, Expr *expr) { - const char *string = TOKSTR(expr->builtin_expr.identifier); + const char *string = expr->builtin_expr.ident; if (string == kw_FILE) { expr_rewrite_to_string(expr, context->unit->file->name); @@ -6161,7 +6174,7 @@ static inline bool sema_expr_analyse_compiler_const(SemaContext *context, Expr * } if (string == kw_LINEREAL) { - expr_rewrite_to_int_const(expr, type_isize, TOKLOC(expr->placeholder_expr.identifier)->row, true); + expr_rewrite_to_int_const(expr, type_isize, expr->span.row, true); return true; } if (string == kw_LINE) @@ -6172,11 +6185,11 @@ static inline bool sema_expr_analyse_compiler_const(SemaContext *context, Expr * } else { - expr_rewrite_to_int_const(expr, type_isize, TOKLOC(expr->placeholder_expr.identifier)->row, true); + expr_rewrite_to_int_const(expr, type_isize, expr->span.row, true); } return true; } - Expr *value = stable_get(&global_context.compiler_defines, string); + Expr *value = htable_get(&global_context.compiler_defines, string); if (!value) { SEMA_ERROR(expr, "The compiler constant '%s' was not defined, did you mistype or forget to add it?", string); @@ -6289,7 +6302,7 @@ static inline bool sema_analyse_identifier_path_string(SemaContext *context, Sou uint32_t len = expr->const_expr.string.len; if (!len) { - sema_error_range(span, "Expected a name here."); + sema_error_at(span, "Expected a name here."); return false; } @@ -6352,7 +6365,7 @@ static inline bool sema_analyse_identifier_path_string(SemaContext *context, Sou } if (!global_context.scratch_buffer_len) { - sema_error_range(span, "A valid identifier was expected here, did you want to take the length of a string literal? If so use '.len'.", chars); + sema_error_at(span, "A valid identifier was expected here, did you want to take the length of a string literal? If so use '.len'.", chars); return false; } TokenType token_type; @@ -6364,7 +6377,7 @@ static inline bool sema_analyse_identifier_path_string(SemaContext *context, Sou { if (report_missing) { - sema_error_range(span, "'%s' could not be found, did you misspell it?", chars); + sema_error_at(span, "'%s' could not be found, did you misspell it?", chars); return false; } return true; @@ -6377,7 +6390,7 @@ static inline bool sema_analyse_identifier_path_string(SemaContext *context, Sou } else { - ASSIGN_DECL_ELSE(decl, sema_resolve_string_symbol(context, symbol, expr->span, path, report_missing), false); + ASSIGN_DECL_OR_RET(decl, sema_resolve_string_symbol(context, symbol, expr->span, path, report_missing), false); if (!decl) return true; if (!sema_analyse_decl(context, decl)) return false; if (decl->decl_kind == DECL_TYPEDEF) @@ -6415,7 +6428,7 @@ static inline bool sema_analyse_identifier_path_string(SemaContext *context, Sou array_size = array_size * 10 + (ArraySize)minilex_next(&lexer) - '0'; if (old_array_size > array_size || array_size > MAX_ARRAYINDEX) { - sema_error_range(span, "Array index out of bounds."); + sema_error_at(span, "Array index out of bounds."); return false; } } @@ -6429,7 +6442,7 @@ static inline bool sema_analyse_identifier_path_string(SemaContext *context, Sou { if (report_missing) { - sema_error_range(span, "The path to an existing member was expected after '%s', did you make a mistake?", symbol); + sema_error_at(span, "The path to an existing member was expected after '%s', did you make a mistake?", symbol); return false; } else @@ -6655,7 +6668,7 @@ static inline bool sema_expr_analyse_ct_nameof(SemaContext *context, Expr *expr) scratch_buffer_append(decl->module->name->module); scratch_buffer_append("::"); scratch_buffer_append(decl->name); - expr_rewrite_to_string(expr, scratch_buffer_interned()); + expr_rewrite_to_string(expr, scratch_buffer_copy()); return true; } @@ -6681,7 +6694,7 @@ static inline bool sema_expr_analyse_ct_nameof(SemaContext *context, Expr *expr) scratch_buffer_append(type->decl->module->name->module); scratch_buffer_append("::"); scratch_buffer_append(type->name); - expr_rewrite_to_string(expr, scratch_buffer_interned()); + expr_rewrite_to_string(expr, scratch_buffer_copy()); return true; } @@ -6706,7 +6719,7 @@ static Type *sema_expr_check_type_exists(SemaContext *context, TypeInfo *type_in if (!type_is_valid_for_vector(type)) { SEMA_ERROR(type_info->array.base, - "%s cannot be vectorized. Only integers, floats and booleans are allowed.", + "%s cannot be vectorized. Only integers, floats and booleans are allowed.", type_quoted_error_string(type)); return poisoned_type; } @@ -6721,9 +6734,10 @@ static Type *sema_expr_check_type_exists(SemaContext *context, TypeInfo *type_in if (!type_ok(type)) return type; return type_get_array(type, size); } + case TYPE_INFO_CT_IDENTIFIER: case TYPE_INFO_IDENTIFIER: { - Decl *decl = sema_resolve_normal_symbol(context, type_info->unresolved.name_loc, type_info->unresolved.path, false); + Decl *decl = sema_resolve_normal_symbol(context, type_info->unresolved.name, type_info->unresolved.span, type_info->unresolved.path, false); if (!decl) return NULL; if (!decl_ok(decl)) return poisoned_type; return decl->type->canonical; @@ -6771,7 +6785,7 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr { case EXPR_IDENTIFIER: // 2. An identifier does a lookup - decl = sema_resolve_normal_symbol(context, main_var->identifier_expr.identifier, main_var->identifier_expr.path, false); + decl = sema_resolve_normal_symbol(context, main_var->identifier_expr.ident, main_var->span, main_var->identifier_expr.path, false); // 2a. If it failed, then error if (!decl_ok(decl)) return false; // 2b. If it's missing, goto not defined @@ -6854,21 +6868,17 @@ NOT_DEFINED: static inline bool sema_expr_analyse_ct_stringify(SemaContext *context, Expr *expr) { Expr *inner = expr->inner_expr; - // Special handling of #foo - if (inner->expr_kind == EXPR_HASH_IDENT) + // Only hash ident style stringify reaches here. + assert(inner->expr_kind == EXPR_HASH_IDENT); + Decl *decl = sema_resolve_normal_symbol(context, inner->ct_ident_expr.identifier, inner->span, NULL, true); + if (!decl_ok(decl)) return false; + const char *desc = span_to_string(decl->var.hash_var.span); + if (!desc) { - Decl *decl = sema_resolve_normal_symbol(context, inner->ct_ident_expr.identifier, NULL, true); - if (!decl_ok(decl)) return false; - inner = decl->var.init_expr; + SEMA_ERROR(expr, "Failed to stringify hash variable contents - they must be a single line and not exceed 255 characters."); + return false; } - SourceLocation *start = TOKLOC(inner->span.loc); - SourceLocation *end = TOKLOC(inner->span.end_loc); - File *file = source_file_by_id(start->file_id); - const char *begin_char = &file->contents[start->start]; - const char *end_char = &file->contents[end->start + end->length]; - int len = (int)(end_char - begin_char); - const char *res = strformat("%.*s", len, begin_char); - expr_rewrite_to_string(expr, res); + expr_rewrite_to_string(expr, desc); return true; } @@ -6966,13 +6976,13 @@ static inline BuiltinFunction builtin_by_name(const char *name) static inline bool sema_expr_analyse_builtin(SemaContext *context, Expr *expr, bool throw_error) { - const char *builtin_char = TOKSTR(expr->builtin_expr.identifier); + const char *builtin_char = expr->builtin_expr.ident; BuiltinFunction func = builtin_by_name(builtin_char); if (func == BUILTIN_NONE) { - if (throw_error) SEMA_TOKEN_ERROR(expr->builtin_expr.identifier, "Unsupported builtin '%s'.", builtin_char); + if (throw_error) SEMA_ERROR(expr, "Unsupported builtin '%s'.", builtin_char); return false; } @@ -7142,7 +7152,7 @@ static inline bool sema_cast_rvalue(SemaContext *context, Expr *expr) case EXPR_MACRO_BODY_EXPANSION: if (!expr->body_expansion_expr.ast) { - SEMA_ERROR(expr, "'@%s' must be followed by ().", TOKSTR(context->current_macro->macro_decl.block_parameter)); + SEMA_ERROR(expr, "'@%s' must be followed by ().", context->current_macro->macro_decl.block_parameter); return false; } break; diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 60530a9b7..ee6bb23ed 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -73,6 +73,7 @@ bool sema_analyse_expr_lvalue(SemaContext *context, Expr *expr); bool sema_analyse_ct_expr(SemaContext *context, Expr *expr); bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *struct_var, Decl *decl, bool failable); void expr_rewrite_to_int_const(Expr *expr_to_rewrite, Type *type, uint64_t value, bool narrowable); +void expr_rewrite_to_string(Expr *expr_to_rewrite, const char *string); static inline bool expr_is_const(Expr *expr); static inline bool expr_is_const(Expr *expr) diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index 90c89272f..cc6422f41 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -207,7 +207,7 @@ static Decl *sema_resolve_no_path_symbol(SemaContext *context, const char *symbo CompilationUnit *unit = context->unit; // Search in file scope. - decl = stable_get(&unit->local_symbols, symbol); + decl = htable_get(&unit->local_symbols, symbol); if (decl) return decl; @@ -229,11 +229,11 @@ static void sema_report_error_on_decl(Path *path, const char *symbol_str, Source { if (path) { - sema_error_range(span, "The %s '%s::%s' is not visible from this module.", decl_to_name(private_decl), path->module, symbol_str); + sema_error_at(span, "The %s '%s::%s' is not visible from this module.", decl_to_name(private_decl), path->module, symbol_str); } else { - sema_error_range(span, "The %s '%s' is not visible from this module.", decl_to_name(private_decl), symbol_str); + sema_error_at(span, "The %s '%s' is not visible from this module.", decl_to_name(private_decl), symbol_str); } return; } @@ -243,7 +243,7 @@ static void sema_report_error_on_decl(Path *path, const char *symbol_str, Source const char *symbol_type = decl_to_name(found); if (path) { - sema_error_range(span, + sema_error_at(span, "The %s '%s::%s' is defined in both '%s' and '%s', please use either %s::%s or %s::%s to resolve the ambiguity.", symbol_type, path->module, @@ -257,7 +257,7 @@ static void sema_report_error_on_decl(Path *path, const char *symbol_str, Source } else { - sema_error_range(span, + sema_error_at(span, "The %s '%s' is defined in both '%s' and '%s', please use either %s::%s or %s::%s to resolve the ambiguity.", symbol_type, symbol_str, @@ -273,11 +273,11 @@ static void sema_report_error_on_decl(Path *path, const char *symbol_str, Source assert(!found); if (path) { - sema_error_range(span, "'%s::%s' could not be found, did you spell it right?", path->module, symbol_str); + sema_error_at(span, "'%s::%s' could not be found, did you spell it right?", path->module, symbol_str); } else { - sema_error_range(span, "'%s' could not be found, did you spell it right?", symbol_str); + sema_error_at(span, "'%s' could not be found, did you spell it right?", symbol_str); } } @@ -378,19 +378,18 @@ Decl *sema_resolve_method(CompilationUnit *unit, Decl *type, const char *method_ return result; } -Decl *unit_resolve_parameterized_symbol(CompilationUnit *unit, TokenId symbol, Path *path) +Decl *unit_resolve_parameterized_symbol(CompilationUnit *unit, const char *symbol, SourceSpan span, Path *path) { Decl *ambiguous_other_decl = NULL; Decl *private_decl = NULL; - const char *symbol_str = TOKSTR(symbol); bool path_found = false; - Decl *decl = sema_find_decl_in_imports(unit->imports, symbol_str, path, &ambiguous_other_decl, &private_decl, &path_found, true); + Decl *decl = sema_find_decl_in_imports(unit->imports, symbol, path, &ambiguous_other_decl, &private_decl, &path_found, true); if (!decl) { decl = sema_find_decl_in_global(&global_context.generic_symbols, global_context.generic_module_list, - symbol_str, path, + symbol, path, &ambiguous_other_decl, &private_decl, &path_found, true); @@ -398,21 +397,21 @@ Decl *unit_resolve_parameterized_symbol(CompilationUnit *unit, TokenId symbol, P // 14. Error report if (!decl || ambiguous_other_decl) { - sema_report_error_on_decl(path, symbol_str, source_span_from_token_id(symbol), decl, ambiguous_other_decl, private_decl); + sema_report_error_on_decl(path, symbol, span, decl, ambiguous_other_decl, private_decl); return poisoned_decl; } if (!decl_is_user_defined_type(decl) && !path) { - SEMA_TOKID_ERROR(symbol, "Function and variables must be prefixed with a path, e.g. 'foo::%s'.", symbol_str); + sema_error_at(span, "Function and variables must be prefixed with a path, e.g. 'foo::%s'.", symbol); return poisoned_decl; } return decl; } -Decl *sema_resolve_normal_symbol(SemaContext *context, TokenId symbol, Path *path, bool handle_error) +Decl *sema_resolve_normal_symbol(SemaContext *context, const char *symbol, SourceSpan span, Path *path, bool handle_error) { - return sema_resolve_symbol(context, TOKSTR(symbol), source_span_from_token_id(symbol), path, handle_error); + return sema_resolve_symbol(context, symbol, span, path, handle_error); } Decl *sema_resolve_string_symbol(SemaContext *context, const char *symbol, SourceSpan span, Path *path, bool report_error) @@ -450,9 +449,9 @@ bool sema_add_local(SemaContext *context, Decl *decl) { Module *current_module = decl->module = context->unit->module; // Ignore synthetic locals. - if (decl->name_token.index == NO_TOKEN_ID.index) return true; + if (!decl->name) return true; if (decl->decl_kind == DECL_VAR && decl->var.shadow) goto ADD_VAR; - Decl *other = sema_resolve_normal_symbol(context, decl->name_token, NULL, false); + Decl *other = sema_resolve_normal_symbol(context, decl->name, decl->span, NULL, false); assert(!other || other->module); if (other && other->module == current_module) { diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 2d32bbf02..42cc791fb 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -173,7 +173,7 @@ static inline bool sema_analyse_try_unwrap(SemaContext *context, Expr *expr) // 1. Check if we are doing an implicit declaration. if (!var_type && ident->expr_kind == EXPR_IDENTIFIER) { - Decl *decl = sema_resolve_normal_symbol(context, ident->identifier_expr.identifier, NULL, false); + Decl *decl = sema_resolve_normal_symbol(context, ident->identifier_expr.ident, ident->span, NULL, false); if (!decl) implicit_declaration = true; } @@ -244,13 +244,11 @@ static inline bool sema_analyse_try_unwrap(SemaContext *context, Expr *expr) if (ident->identifier_expr.path) { - sema_error_range(ident->identifier_expr.path->span, "The variable may not have a path."); + SEMA_ERROR(ident->identifier_expr.path, "The variable may not have a path."); return false; } - TokenId ident_token = ident->identifier_expr.identifier; - - if (TOKTYPE(ident_token) != TOKEN_IDENT) + if (ident->identifier_expr.is_const) { SEMA_ERROR(ident, "Expected a variable starting with a lower case letter."); return false; @@ -277,7 +275,7 @@ static inline bool sema_analyse_try_unwrap(SemaContext *context, Expr *expr) } // 4d. A new declaration is created. - Decl *decl = decl_new_var(ident_token, var_type, VARDECL_LOCAL, VISIBLE_LOCAL); + Decl *decl = decl_new_var(ident->identifier_expr.ident, ident->span, var_type, VARDECL_LOCAL, VISIBLE_LOCAL); // 4e. Analyse it if (!sema_analyse_var_decl(context, decl, true)) return false; @@ -329,7 +327,7 @@ static inline bool sema_analyse_catch_unwrap(SemaContext *context, Expr *expr) } if (!type && ident->expr_kind == EXPR_IDENTIFIER) { - Decl *decl = sema_resolve_normal_symbol(context, ident->identifier_expr.identifier, NULL, false); + Decl *decl = sema_resolve_normal_symbol(context, ident->identifier_expr.ident, ident->span, NULL, false); if (!decl) implicit_declaration = true; } @@ -348,7 +346,7 @@ static inline bool sema_analyse_catch_unwrap(SemaContext *context, Expr *expr) if (ident->type->canonical != type_anyerr) { SEMA_ERROR(ident, "Expected the variable to have the type %s, not %s.", type_quoted_error_string(type_anyerr), - type_quoted_error_string(type->type)); + type_quoted_error_string(type->type)); return false; } @@ -364,7 +362,7 @@ static inline bool sema_analyse_catch_unwrap(SemaContext *context, Expr *expr) if (type->type->canonical != type_anyerr) { SEMA_ERROR(type, "Expected the type to be %s, not %s.", type_quoted_error_string(type_anyerr), - type_quoted_error_string(type->type)); + type_quoted_error_string(type->type)); return false; } if (ident->expr_kind != EXPR_IDENTIFIER) @@ -375,20 +373,18 @@ static inline bool sema_analyse_catch_unwrap(SemaContext *context, Expr *expr) if (ident->identifier_expr.path) { - sema_error_range(ident->identifier_expr.path->span, "The variable may not have a path."); + SEMA_ERROR(ident->identifier_expr.path, "The variable may not have a path."); return false; } - TokenId ident_token = ident->identifier_expr.identifier; - - if (TOKTYPE(ident_token) != TOKEN_IDENT) + if (ident->identifier_expr.is_const) { SEMA_ERROR(ident, "Expected a variable starting with a lower case letter."); return false; } // 4d. A new declaration is created. - Decl *decl = decl_new_var(ident_token, type, VARDECL_LOCAL, VISIBLE_LOCAL); + Decl *decl = decl_new_var(ident->identifier_expr.ident, ident->span, type, VARDECL_LOCAL, VISIBLE_LOCAL); decl->var.init_expr = expr_new(EXPR_UNDEF, decl->span); // 4e. Analyse it @@ -470,7 +466,7 @@ static inline bool sema_analyse_last_cond(SemaContext *context, Expr *expr, Cond // Does the identifier exist in the parent scope? // then again it can't be a variant unwrap. - Decl *decl_for_ident = sema_resolve_normal_symbol(context, left->identifier_expr.identifier, NULL, false); + Decl *decl_for_ident = sema_resolve_normal_symbol(context, left->identifier_expr.ident, left->span, NULL, false); if (decl_for_ident) goto NORMAL_EXPR; Expr *right = expr->binary_expr.right; @@ -486,7 +482,8 @@ static inline bool sema_analyse_last_cond(SemaContext *context, Expr *expr, Cond if (right->type != type_any) goto NORMAL_EXPR; // Found an expansion here expr->expr_kind = EXPR_VARIANTSWITCH; - expr->variant_switch.new_ident = left->identifier_expr.identifier; + expr->variant_switch.new_ident = left->identifier_expr.ident; + expr->variant_switch.span = left->span; expr->variant_switch.variant_expr = right; expr->variant_switch.is_deref = is_deref; expr->variant_switch.is_assign = true; @@ -627,9 +624,7 @@ static inline bool sema_analyse_cond(SemaContext *context, Expr *expr, CondType static inline bool sema_analyse_stmt_placement(Expr *cond, Ast *stmt) { if (stmt->ast_kind == AST_COMPOUND_STMT) return true; - SourceLocation *end_of_cond = TOKLOC(cond->span.end_loc); - SourceLocation *start_of_then = TOKLOC(stmt->span.loc); - return end_of_cond->row == start_of_then->row; + return cond->span.row == stmt->span.row; } /** @@ -1077,8 +1072,8 @@ static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statemen if (!type_is_integer(type_flatten(index_var_type))) { SEMA_ERROR(index->var.type_info, - "Index must be an integer type, '%s' is not valid.", - type_to_error_string(index_var_type)); + "Index must be an integer type, '%s' is not valid.", + type_to_error_string(index_var_type)); return false; } } @@ -1129,7 +1124,7 @@ static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statemen ArraySize array_len = 0; if (len) { - ASSIGN_EXPR_ELSE(len_call, sema_insert_method_macro_call(context, enumerator->span, len, enum_val, NULL), false); + ASSIGN_EXPR_OR_RET(len_call, sema_insert_method_macro_call(context, enumerator->span, len, enum_val, NULL), false); } else { @@ -1248,10 +1243,16 @@ static inline bool sema_analyse_if_stmt(SemaContext *context, Ast *statement) success = sema_analyse_cond(context, cond, cond_type); Ast *then = statement->if_stmt.then_body; - bool then_has_braces = then->ast_kind == AST_COMPOUND_STMT || then->ast_kind == AST_IF_CATCH_SWITCH_STMT; - - if (statement->if_stmt.else_body) + if (success && !ast_ok(then)) { + SEMA_ERROR(statement->if_stmt.then_body, + "The 'then' part of a single line if-statement must start on the same line as the 'if' or use '{ }'"); + success = false; + } + + if (success && statement->if_stmt.else_body) + { + bool then_has_braces = then->ast_kind == AST_COMPOUND_STMT || then->ast_kind == AST_IF_CATCH_SWITCH_STMT; if (!then_has_braces) { SEMA_ERROR(then, "if-statements with an 'else' must use '{ }' even around a single statement."); @@ -1265,17 +1266,6 @@ static inline bool sema_analyse_if_stmt(SemaContext *context, Ast *statement) success = false; } } - if (success && !then_has_braces) - { - SourceLocation *end_of_cond = TOKLOC(cond->span.end_loc); - SourceLocation *start_of_then = TOKLOC(statement->if_stmt.then_body->span.loc); - if (end_of_cond->row != start_of_then->row) - { - SEMA_ERROR(statement->if_stmt.then_body, - "The 'then' part of a single line if-statement must start on the same line as the 'if' or use '{ }'"); - success = false; - } - } if (context->active_scope.jump_end && !context->active_scope.allow_dead_code) { SEMA_ERROR(then, "This code can never be executed."); @@ -1346,7 +1336,7 @@ static inline Decl *sema_analyse_label(SemaContext *context, Ast *stmt) { Decl *ambiguous; Decl *dummy; - Decl *target = sema_resolve_normal_symbol(context, stmt->contbreak_stmt.label.span, NULL, false); + Decl *target = sema_resolve_normal_symbol(context, stmt->contbreak_stmt.label.name, stmt->contbreak_stmt.label.span, NULL, false); if (!target) { SEMA_ERROR(stmt, "Cannot find a labelled statement with the name '%s'.", stmt->contbreak_stmt.label.name); @@ -1354,7 +1344,7 @@ static inline Decl *sema_analyse_label(SemaContext *context, Ast *stmt) } if (target->decl_kind != DECL_LABEL) { - SEMA_TOKID_ERROR(stmt->contbreak_stmt.label.span, "Expected the name to match a label, not a constant."); + SEMA_ERROR(&stmt->contbreak_stmt.label, "Expected the name to match a label, not a constant."); return poisoned_decl; } if (context->active_scope.in_defer) @@ -1415,7 +1405,7 @@ static bool sema_analyse_nextcase_stmt(SemaContext *context, Ast *statement) { context->active_scope.jump_end = true; - if (!context->next_target && !statement->nextcase_stmt.label.name && !statement->nextcase_stmt.expr_or_type_info) + if (!context->next_target && !statement->nextcase_stmt.label.name && !statement->nextcase_stmt.expr) { if (context->next_switch) { @@ -1431,7 +1421,7 @@ static bool sema_analyse_nextcase_stmt(SemaContext *context, Ast *statement) Ast *parent = context->next_switch; if (statement->nextcase_stmt.label.name) { - Decl *target = sema_resolve_normal_symbol(context, statement->nextcase_stmt.label.span, NULL, false); + Decl *target = sema_resolve_normal_symbol(context, statement->nextcase_stmt.label.name, statement->nextcase_stmt.label.span, NULL, false); if (!target) { SEMA_ERROR(statement, "Cannot find a switch statement with the name '%s'.", statement->nextcase_stmt.label.name); @@ -1439,14 +1429,14 @@ static bool sema_analyse_nextcase_stmt(SemaContext *context, Ast *statement) } if (target->decl_kind != DECL_LABEL) { - SEMA_TOKID_ERROR(statement->nextcase_stmt.label.span, "Expected the name to match a label, not a constant."); + SEMA_ERROR(&statement->nextcase_stmt.label, "Expected the name to match a label, not a constant."); return false; } parent = astptr(target->label.parent); AstKind kind = parent->ast_kind; if (kind != AST_SWITCH_STMT && kind != AST_IF_CATCH_SWITCH_STMT) { - SEMA_TOKID_ERROR(statement->nextcase_stmt.label.span, "Expected the label to match a 'switch' or 'if-catch' statement."); + SEMA_ERROR(&statement->nextcase_stmt.label, "Expected the label to match a 'switch' or 'if-catch' statement."); return false; } bool defer_mismatch = false; @@ -1456,14 +1446,14 @@ static bool sema_analyse_nextcase_stmt(SemaContext *context, Ast *statement) SEMA_ERROR(statement, "This 'nextcase' would jump out of a defer which is not allowed."); return false; } - assert(statement->nextcase_stmt.expr_or_type_info); + assert(statement->nextcase_stmt.expr); } statement->nextcase_stmt.defers.start = context->active_scope.defer_last; statement->nextcase_stmt.defers.end = parent->switch_stmt.defer; // Plain next. - if (!statement->nextcase_stmt.expr_or_type_info) + if (!statement->nextcase_stmt.expr) { assert(context->next_target); statement->nextcase_stmt.case_switch_stmt = context->next_target; @@ -1471,9 +1461,9 @@ static bool sema_analyse_nextcase_stmt(SemaContext *context, Ast *statement) } Expr *cond = parent->switch_stmt.cond; - if (statement->nextcase_stmt.is_type) + if (statement->nextcase_stmt.expr->expr_kind == EXPR_TYPEINFO) { - TypeInfo *type_info = statement->nextcase_stmt.expr_or_type_info; + TypeInfo *type_info = statement->nextcase_stmt.expr->type_expr; if (!sema_resolve_type_info(context, type_info)) return false; Ast **cases; statement->nextcase_stmt.defers.end = parent->switch_stmt.defer; @@ -1511,7 +1501,7 @@ static bool sema_analyse_nextcase_stmt(SemaContext *context, Ast *statement) return false; } - Expr *target = statement->nextcase_stmt.expr_or_type_info; + Expr *target = statement->nextcase_stmt.expr; Type *expected_type = parent->ast_kind == AST_SWITCH_STMT ? cond->type : type_anyerr; @@ -1715,11 +1705,11 @@ static inline bool sema_check_value_case(SemaContext *context, Type *switch_type { if (int_comp(const_expr->ixx, to_const_expr->ixx, BINARYOP_GT)) { - sema_error_range((SourceSpan){ expr->span.loc, to_expr->span.end_loc }, - "The range is not valid because the first value (%s) is greater than the second (%s). " - "It would work if you swapped their order.", - int_to_str(const_expr->ixx, 10), - int_to_str(to_const_expr->ixx, 10)); + sema_error_at(extend_span_with_token(expr->span, to_expr->span), + "The range is not valid because the first value (%s) is greater than the second (%s). " + "It would work if you swapped their order.", + int_to_str(const_expr->ixx, 10), + int_to_str(to_const_expr->ixx, 10)); return false; } Int128 range = int_sub(to_const_expr->ixx, const_expr->ixx).i; @@ -1750,7 +1740,7 @@ static bool sema_analyse_switch_body(SemaContext *context, Ast *statement, Sourc bool use_type_id = false; if (!type_is_comparable(switch_type)) { - sema_error_range(expr_span, "You cannot test '%s' for equality, and only values that supports '==' for comparison can be used in a switch.", type_to_error_string(switch_type)); + sema_error_at(expr_span, "You cannot test '%s' for equality, and only values that supports '==' for comparison can be used in a switch.", type_to_error_string(switch_type)); return false; } // We need an if chain if this isn't an integer type. @@ -1819,10 +1809,9 @@ static bool sema_analyse_switch_body(SemaContext *context, Ast *statement, Sourc if (variant->is_assign) { Type *real_type = type_get_ptr(stmt->case_stmt.expr->const_expr.typeid); - TokenId name = variant->new_ident; - Decl *new_var = decl_new_var(name, + Decl *new_var = decl_new_var(variant->new_ident, variant->span, type_info_new_base(variant->is_deref - ? real_type->pointer : real_type, source_span_from_token_id(name)), + ? real_type->pointer : real_type, variant->span), VARDECL_LOCAL, VISIBLE_LOCAL); Expr *var_result = expr_variable(var_holder); if (!cast(var_result, real_type)) return false; @@ -1838,7 +1827,7 @@ static bool sema_analyse_switch_body(SemaContext *context, Ast *statement, Sourc else { Type *type = type_get_ptr(stmt->case_stmt.expr->const_expr.typeid); - Decl *alias = decl_new_var(var_holder->name_token, + Decl *alias = decl_new_var(var_holder->name, var_holder->span, type_info_new_base(type, stmt->case_stmt.expr->span), VARDECL_LOCAL, VISIBLE_LOCAL); Expr *ident_converted = expr_variable(var_holder); @@ -1993,18 +1982,18 @@ static bool sema_analyse_ct_foreach_stmt(SemaContext *context, Ast *statement) } Expr **expression = collection->initializer_list; Decl *index = NULL; - TokenId index_token = statement->ct_foreach_stmt.index; + const char *index_name = statement->ct_foreach_stmt.index_name; AstId start = 0; SCOPE_START; - if (index_token.index) + if (index_name) { - index = decl_new_var(index_token, NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL); + index = decl_new_var(index_name, statement->ct_foreach_stmt.index_span, NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL); index->type = type_int; if (!sema_add_local(context, index)) return SCOPE_POP_ERROR(); } - Decl *value = decl_new_var(statement->ct_foreach_stmt.value, NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL); + Decl *value = decl_new_var(statement->ct_foreach_stmt.value_name, statement->ct_foreach_stmt.value_span, NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL); if (!sema_add_local(context, value)) return SCOPE_POP_ERROR(); // Get the body Ast *body = astptr(statement->ct_foreach_stmt.body); @@ -2325,7 +2314,7 @@ static inline bool sema_analyse_statement_inner(SemaContext *context, Ast *state context->active_scope.allow_dead_code = true; return true; } - //SEMA_ERROR(statement, "This code will never execute."); + //ERROR_NODE(statement, "This code will never execute."); context->active_scope.allow_dead_code = true; //return false; } @@ -2409,39 +2398,82 @@ bool sema_analyse_statement(SemaContext *context, Ast *statement) } -static bool sema_analyse_requires(SemaContext *context, Ast *docs, AstId **asserts) +static bool sema_analyse_require(SemaContext *context, Ast *directive, AstId **asserts) +{ + Expr *declexpr = directive->doc_directive.contract.decl_exprs; + assert(declexpr->expr_kind == EXPR_EXPRESSION_LIST); + + VECEACH(declexpr->expression_list, j) + { + Expr *expr = declexpr->expression_list[j]; + if (expr->expr_kind == EXPR_DECL) + { + SEMA_ERROR(expr, "Only expressions are allowed."); + return false; + } + if (!sema_analyse_cond_expr(context, expr)) return false; + Ast *assert = new_ast(AST_ASSERT_STMT, expr->span); + assert->assert_stmt.expr = expr; + const char *comment = directive->doc_directive.contract.comment; + if (!comment) comment = directive->doc_directive.contract.expr_string; + Expr *comment_expr = expr_new(EXPR_CONST, expr->span); + expr_rewrite_to_string(comment_expr, comment); + assert->assert_stmt.message = comment_expr; + ast_append(asserts, assert); + } + return true; +} + +static bool sema_analyse_checked(SemaContext *context, Ast *directive, AstId **asserts) +{ + Expr *declexpr = directive->doc_directive.contract.decl_exprs; + bool success = true; + SCOPE_START + VECEACH(declexpr->cond_expr, j) + { + Expr *expr = declexpr->cond_expr[j]; + if (!sema_analyse_cond_expr(context, expr)) + { + const char *comment = directive->doc_directive.contract.comment; + if (comment) + { + SEMA_ERROR(expr, comment); + } + success = false; + goto END; + } + } +END: + SCOPE_END; + return success; +} + +static bool sema_analyse_contracts(SemaContext *context, Ast *docs, AstId **asserts) { if (!docs) return true; Ast **directives = docs->directives; VECEACH(directives, i) { Ast *directive = directives[i]; - if (directive->doc_directive.kind != DOC_DIRECTIVE_REQUIRE) continue; - Expr *comment = directive->doc_directive.contract.comment; - Expr *declexpr = directive->doc_directive.contract.decl_exprs; - if (comment) + switch (directive->doc_directive.kind) { - if (comment->expr_kind != EXPR_CONST || comment->const_expr.const_kind != CONST_STRING) - { - SEMA_ERROR(comment, "Expected a string here."); - return false; - } - } - assert(declexpr->expr_kind == EXPR_COND); - - VECEACH(declexpr->cond_expr, j) - { - Expr *expr = declexpr->cond_expr[j]; - if (expr->expr_kind == EXPR_DECL) - { - SEMA_ERROR(expr, "Only expressions are allowed."); - return false; - } - if (!sema_analyse_cond_expr(context, expr)) return false; - Ast *assert = new_ast(AST_ASSERT_STMT, expr->span); - assert->assert_stmt.expr = expr; - assert->assert_stmt.message = comment; - ast_append(asserts, assert); + case DOC_DIRECTIVE_UNKNOWN: + break; + case DOC_DIRECTIVE_PURE: + context->current_function_pure = true; + break; + case DOC_DIRECTIVE_REQUIRE: + if (!sema_analyse_require(context, directive, asserts)) return false; + break; + case DOC_DIRECTIVE_CHECKED: + if (!sema_analyse_checked(context, directive, asserts)) return false; + break; + case DOC_DIRECTIVE_PARAM: + break; + case DOC_DIRECTIVE_ERRORS: + break; + case DOC_DIRECTIVE_ENSURE: + break; } } return true; @@ -2453,6 +2485,7 @@ bool sema_analyse_function_body(SemaContext *context, Decl *func) FunctionSignature *signature = &func->func_decl.function_signature; FunctionPrototype *prototype = func->type->func.prototype; context->current_function = func; + context->current_function_pure = false; context->rtype = prototype->rtype; context->active_scope = (DynamicScope) { .scope_id = 0, @@ -2478,7 +2511,7 @@ bool sema_analyse_function_body(SemaContext *context, Decl *func) } AstId assert_first = 0; AstId *next = &assert_first; - if (!sema_analyse_requires(context, func->docs, &next)) return false; + if (!sema_analyse_contracts(context, func->docs, &next)) return false; if (func->func_decl.attr_naked) { AstId current = func->func_decl.body->compound_stmt.first_stmt; @@ -2503,7 +2536,7 @@ bool sema_analyse_function_body(SemaContext *context, Decl *func) if (canonical_rtype != type_void) { // IMPROVE better pointer to end. - SEMA_TOKID_ERROR(func->name_token, "Missing return statement at the end of the function."); + SEMA_ERROR(func, "Missing return statement at the end of the function."); return false; } } diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index 9e3342660..1a37d7bdc 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -122,7 +122,8 @@ static inline bool sema_resolve_array_type(SemaContext *context, TypeInfo *type, static bool sema_resolve_type_identifier(SemaContext *context, TypeInfo *type_info) { Decl *decl = sema_resolve_normal_symbol(context, - type_info->unresolved.name_loc, + type_info->unresolved.name, + type_info->unresolved.span, type_info->unresolved.path, true); decl = decl_flatten(decl); // Already handled @@ -136,11 +137,11 @@ static bool sema_resolve_type_identifier(SemaContext *context, TypeInfo *type_in case DECL_STRUCT: case DECL_BITSTRUCT: case DECL_UNION: - case DECL_ERRTYPE: + case DECL_OPTENUM: case DECL_ENUM: type_info->type = decl->type; type_info->resolve_status = RESOLVE_DONE; - DEBUG_LOG("Resolved %s.", TOKSTR(type_info->unresolved.name_loc)); + DEBUG_LOG("Resolved %s.", type_info->unresolved.name); return true; case DECL_TYPEDEF: case DECL_DISTINCT: @@ -166,14 +167,14 @@ static bool sema_resolve_type_identifier(SemaContext *context, TypeInfo *type_in } FALLTHROUGH; case DECL_FUNC: - case DECL_ERRVALUE: + case DECL_OPTVALUE: case DECL_ENUM_CONSTANT: case DECL_IMPORT: case DECL_MACRO: case DECL_GENERIC: case DECL_LABEL: case DECL_ATTRIBUTE: - SEMA_TOKID_ERROR(type_info->unresolved.name_loc, "This is not a type."); + SEMA_ERROR(&type_info->unresolved, "This is not a type."); return type_info_poison(type_info); case DECL_CT_ELSE: case DECL_CT_IF: @@ -236,9 +237,9 @@ bool sema_resolve_type_shallow(SemaContext *context, TypeInfo *type_info, bool a if (type_info->resolve_status == RESOLVE_RUNNING) { // TODO this is incorrect for unresolved expressions - SEMA_TOKID_ERROR(type_info->unresolved.name_loc, - "Circular dependency resolving type '%s'.", - TOKSTR(type_info->unresolved.name_loc)); + SEMA_ERROR(&type_info->unresolved, + "Circular dependency resolving type '%s'.", + type_info->unresolved.name); return type_info_poison(type_info); } @@ -248,6 +249,7 @@ bool sema_resolve_type_shallow(SemaContext *context, TypeInfo *type_info, bool a { case TYPE_INFO_POISON: UNREACHABLE + case TYPE_INFO_CT_IDENTIFIER: case TYPE_INFO_IDENTIFIER: if (!sema_resolve_type_identifier(context, type_info)) return false; break; diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index b4de45ac7..a46923a0a 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -180,7 +180,7 @@ static void register_generic_decls(Module *module, Decl **decls) { case DECL_POISONED: case DECL_ENUM_CONSTANT: - case DECL_ERRVALUE: + case DECL_OPTVALUE: case DECL_IMPORT: case DECL_LABEL: case DECL_CT_ASSERT: @@ -208,7 +208,7 @@ static void register_generic_decls(Module *module, Decl **decls) case DECL_DISTINCT: case DECL_ENUM: case DECL_GENERIC: - case DECL_ERRTYPE: + case DECL_OPTENUM: case DECL_FUNC: case DECL_STRUCT: case DECL_TYPEDEF: @@ -217,7 +217,7 @@ static void register_generic_decls(Module *module, Decl **decls) case DECL_BITSTRUCT: break; } - stable_set(&module->symbols, decl->name, decl); + htable_set(&module->symbols, decl->name, decl); if (decl->visibility == VISIBLE_PUBLIC) global_context_add_generic_decl(decl); } @@ -268,13 +268,13 @@ void sema_analysis_run(void) if (has_error) exit_compiler(EXIT_FAILURE); // All global defines are added to the std module - global_context.std_module_path = (Path) { .module = kw_std, .span = INVALID_RANGE, .len = (uint32_t) strlen(kw_std) }; + global_context.std_module_path = (Path) { .module = kw_std, .span = INVALID_SPAN, .len = (uint32_t) strlen(kw_std) }; global_context.std_module = (Module){ .name = &global_context.std_module_path }; global_context.std_module.stage = ANALYSIS_LAST; global_context.locals_list = NULL; // Set a maximum of symbols in the std_module - stable_init(&global_context.std_module.symbols, 0x10000); + htable_init(&global_context.std_module.symbols, 0x10000); // Setup the func prototype hash map type_func_prototype_init(0x10000); diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index d05fe59d1..d7b0fadca 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -5,23 +5,26 @@ #include "compiler_internal.h" -typedef struct _SymEntry + +typedef struct SymtabEntry_ { + struct SymtabEntry_* next; const char *value; uint16_t key_len; TokenType type : 16; uint32_t hash; -} SymEntry; + uint32_t index; + const char* symbol; +} SymtabEntry; -typedef struct _SymTab +typedef struct { - uint32_t count; - uint32_t max_count; - uint32_t mask; - SymEntry *entries; - uint32_t capacity; + SymtabEntry **bucket; + size_t bucket_max; + size_t bucket_mask; } SymTab; + typedef struct _Entry { const char *key; @@ -36,12 +39,23 @@ static SymTab symtab; const char *attribute_list[NUMBER_OF_ATTRIBUTES]; const char *builtin_list[NUMBER_OF_BUILTINS]; +const char *kw_at_return; +const char *kw_at_checked; +const char *kw_at_ensure; +const char *kw_at_optreturn; +const char *kw_at_param; +const char *kw_at_require; + +const char *kw_in; +const char *kw_out; +const char *kw_inout; const char *kw_align; const char *kw_deprecated; const char *kw_distinct; const char *kw_ensure; const char *kw_elements; const char *kw_errors; +const char *kw_type; const char *kw_inf; const char *kw_inline; const char *kw_elementat; @@ -85,23 +99,25 @@ const char *kw_argc; const char *kw_argv; const char *kw_mainstub;; + void symtab_destroy() { - if (!symtab.entries) return; - - // This will only destroy the symtab, not the entry strings. - free(symtab.entries); - symtab = (SymTab) { .capacity = 0 }; + free(symtab.bucket); } + + void symtab_init(uint32_t capacity) { - assert (is_power_of_two(capacity) && "Must be a power of two"); - symtab.entries = calloc(sizeof(SymEntry), capacity); - symtab.count = 0; - symtab.capacity = capacity; - symtab.max_count = capacity * TABLE_MAX_LOAD; - symtab.mask = capacity - 1; + if (capacity < 0x100) error_exit("Too small symtab size."); + capacity = next_highest_power_of_2(capacity); + symtab.bucket_max = capacity; + symtab.bucket_mask = capacity - 1; + + size_t size = capacity * sizeof(SymtabEntry*); + symtab.bucket = malloc(size); + // Touch all pages to improve perf(!) + memset(symtab.bucket, 0, size); // Add keywords. for (int i = 0; i < TOKEN_LAST; i++) @@ -123,12 +139,16 @@ void symtab_init(uint32_t capacity) // Init some constant idents TokenType type = TOKEN_IDENT; #define KW_DEF(x) symtab_add(x, sizeof(x) - 1, fnv1a(x, sizeof(x) - 1), &type) + kw_in = KW_DEF("in"); + kw_out = KW_DEF("out"); + kw_inout = KW_DEF("inout"); kw_align = KW_DEF("align"); kw_deprecated = KW_DEF("deprecated"); kw_distinct = KW_DEF("distinct"); kw_elements = KW_DEF("elements"); kw_ensure = KW_DEF("ensure"); kw_errors = KW_DEF("errors"); + kw_type = KW_DEF("type"); kw_inf = KW_DEF("inf"); kw_inline = KW_DEF("inline"); kw_elementat = KW_DEF("elementat"); @@ -216,49 +236,57 @@ void symtab_init(uint32_t capacity) } -static inline SymEntry *entry_find(const char *key, uint32_t key_len, uint32_t hash) -{ - uint32_t index = hash & symtab.mask; - while (1) - { - SymEntry *entry = &symtab.entries[index]; - const char *entry_value = entry->value; - if (entry_value == NULL) return entry; - if (entry->key_len == key_len && (entry_value == key || memcmp(key, entry_value, key_len) == 0)) return entry; - index = (index + 1) & symtab.mask; - } -} - -const char *symtab_add(const char *symbol, uint32_t len, uint32_t fnv1hash, TokenType *type) -{ - SymEntry *entry = entry_find(symbol, len, fnv1hash); - if (entry->value) - { - *type = entry->type; - return entry->value; - } - - if (symtab.count >= symtab.max_count) - { - FATAL_ERROR("Symtab exceeded capacity, please increase --symtab."); - } - char *copy = copy_string(symbol, len); - entry->value = copy; - entry->key_len = len; - entry->hash = fnv1hash; - entry->type = *type; - symtab.count++; - return copy; -} const char *symtab_find(const char *symbol, uint32_t len, uint32_t fnv1hash, TokenType *type) { - SymEntry *entry = entry_find(symbol, len, fnv1hash); - if (!entry->value) return NULL; - *type = entry->type; - return entry->value; + size_t pos = fnv1hash & symtab.bucket_mask; + SymtabEntry *bucket = symtab.bucket[pos]; + while (bucket) + { + if (bucket->index == fnv1hash && len == bucket->key_len && memcmp(symbol, bucket->symbol, len) == 0) + { + *type = bucket->type; + return bucket->symbol; + } + bucket = bucket->next; + } + return NULL; } +const char *symtab_add(const char *data, uint32_t len, uint32_t fnv1hash, TokenType *type) +{ + size_t pos = fnv1hash & symtab.bucket_mask; + SymtabEntry *first_bucket = symtab.bucket[pos]; + if (!first_bucket) + { + SymtabEntry *node = malloc_arena(sizeof(SymtabEntry)); + symtab.bucket[pos] = node; + node->key_len = len; + node->next = NULL; + node->index = fnv1hash; + node->type = *type; + return node->symbol = copy_string(data, len); + } + SymtabEntry *bucket = first_bucket; + do + { + if (bucket->index == fnv1hash && len == bucket->key_len && memcmp(data, bucket->symbol, len) == 0) + { + *type = bucket->type; + return bucket->symbol; + } + bucket = bucket->next; + } while (bucket); + SymtabEntry *node = malloc_arena(sizeof(SymtabEntry)); + node->next = first_bucket; + symtab.bucket[pos] = node; + node->key_len = len; + node->index = fnv1hash; + node->type = *type; + return node->symbol = copy_string(data, len); +} + + void stable_init(STable *table, uint32_t initial_size) { assert(initial_size && "Size must be larger than 0"); @@ -341,3 +369,65 @@ void *stable_get(STable *table, const char *key) return entry->key == NULL ? NULL : entry->value; } + + +void htable_init(HTable *table, uint32_t initial_size) +{ + assert(initial_size && "Size must be larger than 0"); + size_t size = next_highest_power_of_2(initial_size); + + size_t mem_size = initial_size * sizeof(HTEntry); + table->entries = calloc_arena(mem_size); + + // Tap all pages + char *data = (char *)table->entries; + for (int i = 0; i < mem_size; i += 4096) + { + data[0] = 0; + } + table->mask = size - 1; +} + +void *htable_set(HTable *table, const char *key, void *value) +{ + assert(value && "Cannot insert NULL"); + uint32_t idx = (((uintptr_t)key) ^ ((uintptr_t)key) >> 8) & table->mask; + HTEntry **entry_ref = &table->entries[idx]; + HTEntry *entry = *entry_ref; + if (!entry) + { + entry = CALLOCS(HTEntry); + entry->key = key; + entry->value = value; + *entry_ref = entry; + return NULL; + } + HTEntry *first_entry = entry; + do + { + if (entry->key == key) return entry->value; + entry = entry->next; + } while (entry); + + entry = CALLOCS(HTEntry); + entry->key = key; + entry->value = value; + entry->next = first_entry; + *entry_ref = entry; + return NULL; +} + + +void *htable_get(HTable *table, const char *key) +{ + uint32_t idx = (((uintptr_t)key) ^ ((uintptr_t)key) >> 8) & table->mask; + HTEntry *entry = table->entries[idx]; + if (!entry) return NULL; + do + { + if (entry->key == key) return entry->value; + entry = entry->next; + } while (entry); + return NULL; +} + diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index 36a3e1d77..deab90fcb 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -176,8 +176,6 @@ const char *token_type_to_string(TokenType type) return "BYTES"; // Comments - case TOKEN_COMMENT: - return "COMMENT"; case TOKEN_DOC_COMMENT: return "DOC_COMMENT"; @@ -328,16 +326,26 @@ const char *token_type_to_string(TokenType type) return "uptrdiff"; case TOKEN_FLOAT16: return "float16"; - case TOKEN_DOCS_EOL: - return "EOL"; case TOKEN_DOCS_START: return "/**"; case TOKEN_DOCS_END: return "*/"; - case TOKEN_DOCS_DIRECTIVE: - return "DIRECTIVE"; - case TOKEN_DOCS_LINE: - return "DOCS_LINE"; + case TOKEN_DOCS_OPTRETURN: + return "@optreturn"; + case TOKEN_DOCS_PARAM: + return "@param"; + case TOKEN_DOCS_RETURN: + return "@return"; + case TOKEN_DOCS_ENSURE: + return "@ensure"; + case TOKEN_DOCS_REQUIRE: + return "@require"; + case TOKEN_DOCS_CHECKED: + return "@checked"; + case TOKEN_DOCS_PURE: + return "@pure"; + case TOKEN_DOC_DIRECTIVE: + return "DOC_DIRECTIVE"; case TOKEN_CT_ALIGNOF: return "$alignof"; diff --git a/src/compiler/types.c b/src/compiler/types.c index 036daecd3..026a7763f 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -4,7 +4,6 @@ #include "compiler_internal.h" -static STable function_types; static struct { Type u0, u1, i8, i16, i32, i64, i128, ixx; @@ -118,7 +117,7 @@ static void type_append_name_to_scratch(Type *type) case TYPE_UNION: case TYPE_DISTINCT: case TYPE_BITSTRUCT: - scratch_buffer_append(type->decl->external_name); + scratch_buffer_append(type->decl->name); break; case TYPE_POINTER: type_append_name_to_scratch(type->pointer); @@ -1136,7 +1135,6 @@ static inline void type_create_float(const char *name, Type *type, TypeKind kind void type_setup(PlatformTarget *target) { - stable_init(&function_types, 0x1000); max_alignment_vector = (AlignSize)target->align_max_vector; type_create_float("float16", &t.f16, TYPE_F16, BITS16); diff --git a/src/utils/common.h b/src/utils/common.h index 0581f54dc..8a9cb563c 100644 --- a/src/utils/common.h +++ b/src/utils/common.h @@ -18,6 +18,7 @@ #define MAX_VECTOR_WIDTH 65536 #define MAX_ARRAY_SIZE INT64_MAX #define MAX_IDENTIFIER_LENGTH 31 +#define MAX_SOURCE_LOCATION_LEN 255 #define PROJECT_JSON "project.c3p" #ifndef __unused #define __unused diff --git a/src/utils/errors.h b/src/utils/errors.h index 0546c1f20..2fdf6cb72 100644 --- a/src/utils/errors.h +++ b/src/utils/errors.h @@ -29,20 +29,22 @@ #if (defined(__GNUC__) && __GNUC__ >= 7) || defined(__clang__) #define FALLTHROUGH __attribute__ ((fallthrough)) #define UNUSED __attribute__((unused)) +#define NORETURN __attribute__((noreturn)) +#define INLINE __attribute__((always_inline)) static inline +#elif defined(_MSC_VER) +#define FALLTHROUGH ((void)0) +#define INLINE __forceinline static inline +#define NORETURN __declspec(noreturn) +#define UNUSED #else +#define INLINE static inline #define FALLTHROUGH ((void)0) #define UNUSED -#endif - - -#if defined(_MSC_VER) -#define NORETURN __declspec(noreturn) -#elif defined(__GNUC__) -#define NORETURN __attribute__((noreturn)) -#else #define NORETURN #endif + + #define TODO FATAL_ERROR("TODO reached"); #define TEST_ASSERT(condition_, string_) while (!(condition_)) { FATAL_ERROR(string_); } diff --git a/src/utils/lib.h b/src/utils/lib.h index b2fedda1c..36cff6d81 100644 --- a/src/utils/lib.h +++ b/src/utils/lib.h @@ -326,6 +326,7 @@ static inline bool is_alphanum_(char c) } } + static inline bool is_letter(char c) { switch (c) @@ -348,6 +349,11 @@ static inline bool is_letter(char c) } } +INLINE bool is_letter_(char c) +{ + return is_letter(c) || c == '_'; +} + static inline bool is_number(char c) { return c >= '0' && c <= '9'; diff --git a/test/test_suite/compile_time/ct_switch_top_level.c3t b/test/test_suite/compile_time/ct_switch_top_level.c3t index 9cdd66701..2ea2c1775 100644 --- a/test/test_suite/compile_time/ct_switch_top_level.c3t +++ b/test/test_suite/compile_time/ct_switch_top_level.c3t @@ -5,7 +5,7 @@ extern fn void printf(char*, ...); macro tester() { - $Type = int; + var $Type = int; $switch ($Type): $case int: printf("Hello\n"); diff --git a/test/test_suite/compile_time/stringify.c3t b/test/test_suite/compile_time/stringify.c3t index d68c37c8f..084ac4221 100644 --- a/test/test_suite/compile_time/stringify.c3t +++ b/test/test_suite/compile_time/stringify.c3t @@ -6,7 +6,7 @@ import std::io; macro timeit(#call) { long t = (long)libc::clock(); - $Type = $typeof(#call); + var $Type = $typeof(#call); var $is_void = $Type.typeid == void.typeid; $if ($is_void): #call; diff --git a/test/test_suite/errors/error_semantic_fails.c3 b/test/test_suite/errors/error_semantic_fails.c3 index fcc6161c2..051e1890d 100644 --- a/test/test_suite/errors/error_semantic_fails.c3 +++ b/test/test_suite/errors/error_semantic_fails.c3 @@ -1,5 +1,5 @@ -errtype Repeater +optenum Repeater { A, - A // #error: This enum constant is declared twice. + A // #error: This opt constant is declared twice. } diff --git a/test/test_suite/errors/try_with_weird_stuff.c3 b/test/test_suite/errors/try_with_weird_stuff.c3 index 371a8cb43..0561652e0 100644 --- a/test/test_suite/errors/try_with_weird_stuff.c3 +++ b/test/test_suite/errors/try_with_weird_stuff.c3 @@ -4,7 +4,7 @@ fn void test1() int! a; int b; int*! x; - if (try int &a = a) {} // #error: Expected a variable name after the type. + if (try int &a = a) {} // #error: A new variable was expected. } fn void test2() diff --git a/test/test_suite/expressions/assignability.c3 b/test/test_suite/expressions/assignability.c3 index ea5192f75..16e1e7396 100644 --- a/test/test_suite/expressions/assignability.c3 +++ b/test/test_suite/expressions/assignability.c3 @@ -34,5 +34,6 @@ fn void test21() fn void test22() { + var $Type = void; $Type = int; } \ No newline at end of file diff --git a/test/test_suite/initializer_lists/ranges_to_dynamic.c3t b/test/test_suite/initializer_lists/ranges_to_dynamic.c3t index c2087a1b3..170402c0e 100644 --- a/test/test_suite/initializer_lists/ranges_to_dynamic.c3t +++ b/test/test_suite/initializer_lists/ranges_to_dynamic.c3t @@ -22,7 +22,7 @@ fn void main() define void @test.test(i32 %0) #0 { entry: %y = alloca [10 x i32], align 16 - %"__idx$" = alloca i64, align 8 + %anon = alloca i64, align 8 %v = alloca i32, align 4 %1 = bitcast [10 x i32]* %y to i8* call void @llvm.memset.p0i8.i64(i8* align 16 %1, i8 0, i64 40, i1 false) @@ -40,24 +40,24 @@ entry: store i32 %0, i32* %7, align 4 %8 = getelementptr inbounds [10 x i32], [10 x i32]* %y, i64 0, i64 8 store i32 %0, i32* %8, align 4 - store i64 0, i64* %"__idx$", align 8 + store i64 0, i64* %anon, align 8 br label %loop.cond loop.cond: ; preds = %loop.body, %entry - %9 = load i64, i64* %"__idx$", align 8 + %9 = load i64, i64* %anon, align 8 %gt = icmp ugt i64 10, %9 br i1 %gt, label %loop.body, label %loop.exit loop.body: ; preds = %loop.cond - %10 = load i64, i64* %"__idx$", align 8 + %10 = load i64, i64* %anon, align 8 %11 = getelementptr inbounds [10 x i32], [10 x i32]* %y, i64 0, i64 %10 %12 = load i32, i32* %11, align 4 store i32 %12, i32* %v, align 4 %13 = load i32, i32* %v, align 4 call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i32 %13) - %14 = load i64, i64* %"__idx$", align 8 + %14 = load i64, i64* %anon, align 8 %add = add i64 %14, 1 - store i64 %add, i64* %"__idx$", align 8 + store i64 %add, i64* %anon, align 8 br label %loop.cond loop.exit: ; preds = %loop.cond diff --git a/test/test_suite/lexing/expected_directive.c3 b/test/test_suite/lexing/expected_directive.c3 index e0c62e665..57f71a053 100644 --- a/test/test_suite/lexing/expected_directive.c3 +++ b/test/test_suite/lexing/expected_directive.c3 @@ -1,3 +1,4 @@ /** -@1 // #error: Expected doc directive here +@hello +@param feij > 0 // #error: Expected end of line. */ diff --git a/test/test_suite/macros/macro_defer_with_body.c3t b/test/test_suite/macros/macro_defer_with_body.c3t index 7453a7269..8a2d87ba5 100644 --- a/test/test_suite/macros/macro_defer_with_body.c3t +++ b/test/test_suite/macros/macro_defer_with_body.c3t @@ -19,6 +19,43 @@ fn void main() printf("Done!\n"); } -/* expect: foo.ll +/* #expect: foo.ll + +define void @foo.main() #0 { +entry: + %x = alloca i32, align 4 + %a = alloca i32, align 4 + %y = alloca i32, align 4 + store i32 0, i32* %x, align 4 + store i32 1, i32* %a, align 4 + %0 = load i32, i32* %a, align 4 + store i32 %0, i32* %y, align 4 + %1 = load i32, i32* %x, align 4 + %add = add i32 %1, 1 + store i32 %add, i32* %x, align 4 + %2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i32 %1) + %3 = load i32, i32* %x, align 4 + %add1 = add i32 %3, 1 + store i32 %add1, i32* %x, align 4 + %4 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.1, i32 0, i32 0), i32 %3) + br label %exit + +exit: ; preds = %entry + %5 = load i32, i32* %a, align 4 + store i32 %5, i32* %y, align 4 + %6 = load i32, i32* %x, align 4 + %add2 = add i32 %6, 1 + store i32 %add2, i32* %x, align 4 + %7 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i32 0, i32 0), i32 %6) + %8 = load i32, i32* %x, align 4 + %add3 = add i32 %8, 1 + store i32 %add3, i32* %x, align 4 + %9 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.3, i32 0, i32 0), i32 %8) + br label %exit4 + +exit4: ; preds = %exit + %10 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.4, i32 0, i32 0)) + ret void +} + -feokfef \ No newline at end of file diff --git a/test/test_suite/scoping/general_scoping.c3t b/test/test_suite/scoping/general_scoping.c3t index 03aa9ed98..0133e76eb 100644 --- a/test/test_suite/scoping/general_scoping.c3t +++ b/test/test_suite/scoping/general_scoping.c3t @@ -43,16 +43,16 @@ define void @test.main() #0 { entry: %y = alloca i32, align 4 %x = alloca i32, align 4 - %.scope = alloca i32, align 4 - %.scope1 = alloca i32, align 4 + %anon = alloca i32, align 4 + %anon1 = alloca i32, align 4 store i32 7, i32* %y, align 4 store i32* %y, i32** getelementptr inbounds (%Foo, %Foo* @test.bob, i32 0, i32 0, i32 0), align 8 store i32 1, i32* %x, align 4 %0 = load i32, i32* %x, align 4 - store i32 %0, i32* %.scope, align 4 + store i32 %0, i32* %anon, align 4 %1 = load i32*, i32** getelementptr inbounds (%Foo, %Foo* @test.bob, i32 0, i32 0, i32 0), align 8 %2 = load i32, i32* %1, align 8 - store i32 %2, i32* %.scope1, align 4 + store i32 %2, i32* %anon1, align 4 store i32 3, i32* %x, align 4 %3 = load i32*, i32** getelementptr inbounds (%Foo, %Foo* @test.bob, i32 0, i32 0, i32 0), align 8 store i32 12, i32* %3, align 8 @@ -61,12 +61,12 @@ entry: %6 = load i32, i32* %5, align 8 call void (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str, i32 0, i32 0), i32 %4, i32 %6) %7 = load i32*, i32** getelementptr inbounds (%Foo, %Foo* @test.bob, i32 0, i32 0, i32 0), align 8 - %8 = load i32, i32* %.scope1, align 4 + %8 = load i32, i32* %anon1, align 4 store i32 %8, i32* %7, align 8 br label %exit exit: ; preds = %entry - %9 = load i32, i32* %.scope, align 4 + %9 = load i32, i32* %anon, align 4 store i32 %9, i32* %x, align 4 br label %exit2 diff --git a/test/test_suite/slices/slice_assign.c3t b/test/test_suite/slices/slice_assign.c3t index 38970579d..6554b81fd 100644 --- a/test/test_suite/slices/slice_assign.c3t +++ b/test/test_suite/slices/slice_assign.c3t @@ -25,7 +25,7 @@ declare void @printf(i8*, ...) #0 define void @test.main() #0 { entry: %x = alloca [8 x i32], align 16 - %"__idx$" = alloca i64, align 8 + %anon = alloca i64, align 8 %i = alloca i32, align 4 %0 = bitcast [8 x i32]* %x to i8* call void @llvm.memset.p0i8.i64(i8* align 16 %0, i8 0, i64 32, i1 false) @@ -43,24 +43,24 @@ entry: store i32 52, i32* %6, align 4 %7 = getelementptr inbounds [8 x i32], [8 x i32]* %x, i64 0, i64 7 store i32 52, i32* %7, align 4 - store i64 0, i64* %"__idx$", align 8 + store i64 0, i64* %anon, align 8 br label %loop.cond loop.cond: ; preds = %loop.body, %entry - %8 = load i64, i64* %"__idx$", align 8 + %8 = load i64, i64* %anon, align 8 %gt = icmp ugt i64 8, %8 br i1 %gt, label %loop.body, label %loop.exit loop.body: ; preds = %loop.cond - %9 = load i64, i64* %"__idx$", align 8 + %9 = load i64, i64* %anon, align 8 %10 = getelementptr inbounds [8 x i32], [8 x i32]* %x, i64 0, i64 %9 %11 = load i32, i32* %10, align 4 store i32 %11, i32* %i, align 4 %12 = load i32, i32* %i, align 4 call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i32 %12) - %13 = load i64, i64* %"__idx$", align 8 + %13 = load i64, i64* %anon, align 8 %add = add i64 %13, 1 - store i64 %add, i64* %"__idx$", align 8 + store i64 %add, i64* %anon, align 8 br label %loop.cond loop.exit: ; preds = %loop.cond diff --git a/test/test_suite/statements/call_missing_paren.c3 b/test/test_suite/statements/call_missing_paren.c3 index 1ccb83f99..3bcb40748 100644 --- a/test/test_suite/statements/call_missing_paren.c3 +++ b/test/test_suite/statements/call_missing_paren.c3 @@ -2,6 +2,6 @@ fn void foo(int a) {} fn int main() { - foo(10, foo(); // #error: Expected the ending ')' + foo(10, foo(); // #error: Did you forget a ')' before this return 0; } \ No newline at end of file diff --git a/test/test_suite/statements/custom_foreach_with_ref.c3t b/test/test_suite/statements/custom_foreach_with_ref.c3t index 3a32d7c7d..0a15ba7a3 100644 --- a/test/test_suite/statements/custom_foreach_with_ref.c3t +++ b/test/test_suite/statements/custom_foreach_with_ref.c3t @@ -133,52 +133,52 @@ entry: %a = alloca i32, align 4 %a1 = alloca i32, align 4 %a3 = alloca i32, align 4 - %"__idx$" = alloca i32, align 4 - %"__enum$" = alloca %Foo*, align 8 - %"__len$" = alloca i32, align 4 + %anon = alloca i32, align 4 + %anon5 = alloca %Foo*, align 8 + %anon6 = alloca i32, align 4 %i = alloca i32, align 4 %y = alloca i32, align 4 - %a5 = alloca i32, align 4 - %"__idx$7" = alloca i32, align 4 - %"__len$8" = alloca i32, align 4 - %i12 = alloca i32, align 4 - %y13 = alloca i32*, align 8 - %a14 = alloca i32, align 4 - %"__idx$19" = alloca i32, align 4 - %"__len$20" = alloca i32, align 4 - %i24 = alloca i32, align 4 - %y25 = alloca i32, align 4 - %a26 = alloca i32, align 4 - %"__idx$30" = alloca i32, align 4 - %"__len$31" = alloca i32, align 4 - %i35 = alloca i32, align 4 - %y36 = alloca i32, align 4 - %a37 = alloca i32, align 4 - %"__idx$41" = alloca i32, align 4 - %"__len$42" = alloca i32, align 4 - %i46 = alloca i32, align 4 - %y47 = alloca i32, align 4 - %a48 = alloca i32, align 4 - %"__idx$53" = alloca i64, align 8 - %"__enum$54" = alloca [5 x i32], align 16 - %i57 = alloca i64, align 8 - %y58 = alloca i32, align 4 - %"__idx$62" = alloca i64, align 8 - %"__enum$63" = alloca [5 x i32], align 16 + %a7 = alloca i32, align 4 + %anon9 = alloca i32, align 4 + %anon10 = alloca i32, align 4 + %i14 = alloca i32, align 4 + %y15 = alloca i32*, align 8 + %a16 = alloca i32, align 4 + %anon21 = alloca i32, align 4 + %anon22 = alloca i32, align 4 + %i26 = alloca i32, align 4 + %y27 = alloca i32, align 4 + %a28 = alloca i32, align 4 + %anon32 = alloca i32, align 4 + %anon33 = alloca i32, align 4 + %i37 = alloca i32, align 4 + %y38 = alloca i32, align 4 + %a39 = alloca i32, align 4 + %anon43 = alloca i32, align 4 + %anon44 = alloca i32, align 4 + %i48 = alloca i32, align 4 + %y49 = alloca i32, align 4 + %a50 = alloca i32, align 4 + %anon55 = alloca i64, align 8 + %anon56 = alloca [5 x i32], align 16 + %i59 = alloca i64, align 8 + %y60 = alloca i32, align 4 + %anon64 = alloca i64, align 8 + %anon65 = alloca [5 x i32], align 16 %sretparam = alloca [5 x i32], align 4 - %i67 = alloca i64, align 8 - %y68 = alloca i32, align 4 - %"__idx$71" = alloca i64, align 8 - %"__enum$72" = alloca [5 x i32]*, align 8 - %sretparam73 = alloca [5 x i32], align 4 - %i77 = alloca i64, align 8 - %y78 = alloca i32, align 4 - %a81 = alloca i32, align 4 + %i69 = alloca i64, align 8 + %y70 = alloca i32, align 4 + %anon73 = alloca i64, align 8 + %anon74 = alloca [5 x i32]*, align 8 + %sretparam75 = alloca [5 x i32], align 4 + %i79 = alloca i64, align 8 + %y80 = alloca i32, align 4 %a83 = alloca i32, align 4 - %y85 = alloca i32*, align 8 - %a86 = alloca i32, align 4 - %a89 = alloca i32, align 4 + %a85 = alloca i32, align 4 + %y87 = alloca i32*, align 8 + %a88 = alloca i32, align 4 %a91 = alloca i32, align 4 + %a93 = alloca i32, align 4 %0 = bitcast %Foo* %x to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %0, i8* align 4 bitcast (%Foo* @.__const to i8*), i32 12, i1 false) store i32 0, i32* %a, align 4 @@ -200,284 +200,284 @@ entry: %11 = getelementptr inbounds [3 x i32], [3 x i32]* %9, i64 0, i64 %sisiext4 %12 = load i32, i32* %11, align 4 call void (i8*, ...) @printf(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.2, i32 0, i32 0), i32 %4, i32 %8, i32 %12) - store i32 0, i32* %"__idx$", align 4 + store i32 0, i32* %anon, align 4 %13 = call %Foo* @foo.call(%Foo* %x) - store %Foo* %13, %Foo** %"__enum$", align 8 - store i32 3, i32* %"__len$", align 4 + store %Foo* %13, %Foo** %anon5, align 8 + store i32 3, i32* %anon6, align 4 br label %loop.cond loop.cond: ; preds = %loop.body, %entry - %14 = load i32, i32* %"__idx$", align 4 - %15 = load i32, i32* %"__len$", align 4 + %14 = load i32, i32* %anon, align 4 + %15 = load i32, i32* %anon6, align 4 %lt = icmp slt i32 %14, %15 br i1 %lt, label %loop.body, label %loop.exit loop.body: ; preds = %loop.cond - %16 = load i32, i32* %"__idx$", align 4 + %16 = load i32, i32* %anon, align 4 store i32 %16, i32* %i, align 4 - %17 = load %Foo*, %Foo** %"__enum$", align 8 - %18 = load i32, i32* %"__idx$", align 4 - store i32 %18, i32* %a5, align 4 + %17 = load %Foo*, %Foo** %anon5, align 8 + %18 = load i32, i32* %anon, align 4 + store i32 %18, i32* %a7, align 4 %19 = getelementptr inbounds %Foo, %Foo* %17, i32 0, i32 0 - %20 = load i32, i32* %a5, align 4 - %sisiext6 = sext i32 %20 to i64 - %21 = getelementptr inbounds [3 x i32], [3 x i32]* %19, i64 0, i64 %sisiext6 + %20 = load i32, i32* %a7, align 4 + %sisiext8 = sext i32 %20 to i64 + %21 = getelementptr inbounds [3 x i32], [3 x i32]* %19, i64 0, i64 %sisiext8 %22 = load i32, i32* %21, align 4 store i32 %22, i32* %y, align 4 %23 = load i32, i32* %i, align 4 %24 = load i32, i32* %y, align 4 call void (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str.3, i32 0, i32 0), i32 %23, i32 %24) - %25 = load i32, i32* %"__idx$", align 4 + %25 = load i32, i32* %anon, align 4 %add = add i32 %25, 1 - store i32 %add, i32* %"__idx$", align 4 + store i32 %add, i32* %anon, align 4 br label %loop.cond loop.exit: ; preds = %loop.cond - store i32 0, i32* %"__idx$7", align 4 - store i32 3, i32* %"__len$8", align 4 - br label %loop.cond9 + store i32 0, i32* %anon9, align 4 + store i32 3, i32* %anon10, align 4 + br label %loop.cond11 -loop.cond9: ; preds = %loop.body11, %loop.exit - %26 = load i32, i32* %"__idx$7", align 4 - %27 = load i32, i32* %"__len$8", align 4 - %lt10 = icmp slt i32 %26, %27 - br i1 %lt10, label %loop.body11, label %loop.exit18 +loop.cond11: ; preds = %loop.body13, %loop.exit + %26 = load i32, i32* %anon9, align 4 + %27 = load i32, i32* %anon10, align 4 + %lt12 = icmp slt i32 %26, %27 + br i1 %lt12, label %loop.body13, label %loop.exit20 -loop.body11: ; preds = %loop.cond9 - %28 = load i32, i32* %"__idx$7", align 4 - store i32 %28, i32* %i12, align 4 - %29 = load i32, i32* %"__idx$7", align 4 - store i32 %29, i32* %a14, align 4 +loop.body13: ; preds = %loop.cond11 + %28 = load i32, i32* %anon9, align 4 + store i32 %28, i32* %i14, align 4 + %29 = load i32, i32* %anon9, align 4 + store i32 %29, i32* %a16, align 4 %30 = getelementptr inbounds %Foo, %Foo* %x, i32 0, i32 0 - %31 = load i32, i32* %a14, align 4 - %sisiext15 = sext i32 %31 to i64 - %32 = getelementptr inbounds [3 x i32], [3 x i32]* %30, i64 0, i64 %sisiext15 - store i32* %32, i32** %y13, align 8 - %33 = load i32*, i32** %y13, align 8 + %31 = load i32, i32* %a16, align 4 + %sisiext17 = sext i32 %31 to i64 + %32 = getelementptr inbounds [3 x i32], [3 x i32]* %30, i64 0, i64 %sisiext17 + store i32* %32, i32** %y15, align 8 + %33 = load i32*, i32** %y15, align 8 %34 = load i32, i32* %33, align 8 - %add16 = add i32 %34, 1 - store i32 %add16, i32* %33, align 8 - %35 = load i32, i32* %i12, align 4 - %36 = load i32*, i32** %y13, align 8 + %add18 = add i32 %34, 1 + store i32 %add18, i32* %33, align 8 + %35 = load i32, i32* %i14, align 4 + %36 = load i32*, i32** %y15, align 8 %37 = load i32, i32* %36, align 8 call void (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str.4, i32 0, i32 0), i32 %35, i32 %37) - %38 = load i32, i32* %"__idx$7", align 4 - %add17 = add i32 %38, 1 - store i32 %add17, i32* %"__idx$7", align 4 - br label %loop.cond9 + %38 = load i32, i32* %anon9, align 4 + %add19 = add i32 %38, 1 + store i32 %add19, i32* %anon9, align 4 + br label %loop.cond11 -loop.exit18: ; preds = %loop.cond9 - store i32 0, i32* %"__idx$19", align 4 - store i32 3, i32* %"__len$20", align 4 - br label %loop.cond21 +loop.exit20: ; preds = %loop.cond11 + store i32 0, i32* %anon21, align 4 + store i32 3, i32* %anon22, align 4 + br label %loop.cond23 -loop.cond21: ; preds = %loop.body23, %loop.exit18 - %39 = load i32, i32* %"__idx$19", align 4 - %40 = load i32, i32* %"__len$20", align 4 - %lt22 = icmp slt i32 %39, %40 - br i1 %lt22, label %loop.body23, label %loop.exit29 +loop.cond23: ; preds = %loop.body25, %loop.exit20 + %39 = load i32, i32* %anon21, align 4 + %40 = load i32, i32* %anon22, align 4 + %lt24 = icmp slt i32 %39, %40 + br i1 %lt24, label %loop.body25, label %loop.exit31 -loop.body23: ; preds = %loop.cond21 - %41 = load i32, i32* %"__idx$19", align 4 - store i32 %41, i32* %i24, align 4 - %42 = load i32, i32* %"__idx$19", align 4 - store i32 %42, i32* %a26, align 4 +loop.body25: ; preds = %loop.cond23 + %41 = load i32, i32* %anon21, align 4 + store i32 %41, i32* %i26, align 4 + %42 = load i32, i32* %anon21, align 4 + store i32 %42, i32* %a28, align 4 %43 = getelementptr inbounds %Foo, %Foo* %x, i32 0, i32 0 - %44 = load i32, i32* %a26, align 4 - %sisiext27 = sext i32 %44 to i64 - %45 = getelementptr inbounds [3 x i32], [3 x i32]* %43, i64 0, i64 %sisiext27 + %44 = load i32, i32* %a28, align 4 + %sisiext29 = sext i32 %44 to i64 + %45 = getelementptr inbounds [3 x i32], [3 x i32]* %43, i64 0, i64 %sisiext29 %46 = load i32, i32* %45, align 4 - store i32 %46, i32* %y25, align 4 - %47 = load i32, i32* %i24, align 4 - %48 = load i32, i32* %y25, align 4 + store i32 %46, i32* %y27, align 4 + %47 = load i32, i32* %i26, align 4 + %48 = load i32, i32* %y27, align 4 call void (i8*, ...) @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @.str.5, i32 0, i32 0), i32 %47, i32 %48) - %49 = load i32, i32* %"__idx$19", align 4 - %add28 = add i32 %49, 1 - store i32 %add28, i32* %"__idx$19", align 4 - br label %loop.cond21 + %49 = load i32, i32* %anon21, align 4 + %add30 = add i32 %49, 1 + store i32 %add30, i32* %anon21, align 4 + br label %loop.cond23 -loop.exit29: ; preds = %loop.cond21 - store i32 0, i32* %"__idx$30", align 4 - store i32 3, i32* %"__len$31", align 4 - br label %loop.cond32 +loop.exit31: ; preds = %loop.cond23 + store i32 0, i32* %anon32, align 4 + store i32 3, i32* %anon33, align 4 + br label %loop.cond34 -loop.cond32: ; preds = %loop.body34, %loop.exit29 - %50 = load i32, i32* %"__idx$30", align 4 - %51 = load i32, i32* %"__len$31", align 4 - %lt33 = icmp slt i32 %50, %51 - br i1 %lt33, label %loop.body34, label %loop.exit40 +loop.cond34: ; preds = %loop.body36, %loop.exit31 + %50 = load i32, i32* %anon32, align 4 + %51 = load i32, i32* %anon33, align 4 + %lt35 = icmp slt i32 %50, %51 + br i1 %lt35, label %loop.body36, label %loop.exit42 -loop.body34: ; preds = %loop.cond32 - %52 = load i32, i32* %"__idx$30", align 4 - store i32 %52, i32* %i35, align 4 - %53 = load i32, i32* %"__idx$30", align 4 - store i32 %53, i32* %a37, align 4 +loop.body36: ; preds = %loop.cond34 + %52 = load i32, i32* %anon32, align 4 + store i32 %52, i32* %i37, align 4 + %53 = load i32, i32* %anon32, align 4 + store i32 %53, i32* %a39, align 4 %54 = getelementptr inbounds %Foo, %Foo* %x, i32 0, i32 0 - %55 = load i32, i32* %a37, align 4 - %sisiext38 = sext i32 %55 to i64 - %56 = getelementptr inbounds [3 x i32], [3 x i32]* %54, i64 0, i64 %sisiext38 + %55 = load i32, i32* %a39, align 4 + %sisiext40 = sext i32 %55 to i64 + %56 = getelementptr inbounds [3 x i32], [3 x i32]* %54, i64 0, i64 %sisiext40 %57 = load i32, i32* %56, align 4 - store i32 %57, i32* %y36, align 4 - %58 = load i32, i32* %i35, align 4 - %59 = load i32, i32* %y36, align 4 + store i32 %57, i32* %y38, align 4 + %58 = load i32, i32* %i37, align 4 + %59 = load i32, i32* %y38, align 4 call void (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* @.str.6, i32 0, i32 0), i32 %58, i32 %59) - %60 = load i32, i32* %"__idx$30", align 4 - %add39 = add i32 %60, 1 - store i32 %add39, i32* %"__idx$30", align 4 - br label %loop.cond32 + %60 = load i32, i32* %anon32, align 4 + %add41 = add i32 %60, 1 + store i32 %add41, i32* %anon32, align 4 + br label %loop.cond34 -loop.exit40: ; preds = %loop.cond32 - store i32 0, i32* %"__idx$41", align 4 - store i32 3, i32* %"__len$42", align 4 - br label %loop.cond43 +loop.exit42: ; preds = %loop.cond34 + store i32 0, i32* %anon43, align 4 + store i32 3, i32* %anon44, align 4 + br label %loop.cond45 -loop.cond43: ; preds = %loop.body45, %loop.exit40 - %61 = load i32, i32* %"__idx$41", align 4 - %62 = load i32, i32* %"__len$42", align 4 - %lt44 = icmp slt i32 %61, %62 - br i1 %lt44, label %loop.body45, label %loop.exit52 +loop.cond45: ; preds = %loop.body47, %loop.exit42 + %61 = load i32, i32* %anon43, align 4 + %62 = load i32, i32* %anon44, align 4 + %lt46 = icmp slt i32 %61, %62 + br i1 %lt46, label %loop.body47, label %loop.exit54 -loop.body45: ; preds = %loop.cond43 - %63 = load i32, i32* %"__idx$41", align 4 - store i32 %63, i32* %i46, align 4 - %64 = load i32, i32* %"__idx$41", align 4 - store i32 %64, i32* %a48, align 4 +loop.body47: ; preds = %loop.cond45 + %63 = load i32, i32* %anon43, align 4 + store i32 %63, i32* %i48, align 4 + %64 = load i32, i32* %anon43, align 4 + store i32 %64, i32* %a50, align 4 %65 = getelementptr inbounds %Foo, %Foo* %x, i32 0, i32 0 - %66 = load i32, i32* %a48, align 4 - %sisiext49 = sext i32 %66 to i64 - %67 = getelementptr inbounds [3 x i32], [3 x i32]* %65, i64 0, i64 %sisiext49 + %66 = load i32, i32* %a50, align 4 + %sisiext51 = sext i32 %66 to i64 + %67 = getelementptr inbounds [3 x i32], [3 x i32]* %65, i64 0, i64 %sisiext51 %68 = load i32, i32* %67, align 4 - store i32 %68, i32* %y47, align 4 - %69 = load i32, i32* %i46, align 4 - %70 = load i32, i32* %y47, align 4 + store i32 %68, i32* %y49, align 4 + %69 = load i32, i32* %i48, align 4 + %70 = load i32, i32* %y49, align 4 call void (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @.str.7, i32 0, i32 0), i32 %69, i32 %70) - %71 = load i32, i32* %i46, align 4 - %add50 = add i32 %71, 1 - store i32 %add50, i32* %i46, align 4 - %72 = load i32, i32* %"__idx$41", align 4 - %add51 = add i32 %72, 1 - store i32 %add51, i32* %"__idx$41", align 4 - br label %loop.cond43 + %71 = load i32, i32* %i48, align 4 + %add52 = add i32 %71, 1 + store i32 %add52, i32* %i48, align 4 + %72 = load i32, i32* %anon43, align 4 + %add53 = add i32 %72, 1 + store i32 %add53, i32* %anon43, align 4 + br label %loop.cond45 -loop.exit52: ; preds = %loop.cond43 - store i64 0, i64* %"__idx$53", align 8 - %73 = bitcast [5 x i32]* %"__enum$54" to i8* +loop.exit54: ; preds = %loop.cond45 + store i64 0, i64* %anon55, align 8 + %73 = bitcast [5 x i32]* %anon56 to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 16 %73, i8* align 16 bitcast ([5 x i32]* @.__const.8 to i8*), i32 20, i1 false) - br label %loop.cond55 + br label %loop.cond57 -loop.cond55: ; preds = %loop.body56, %loop.exit52 - %74 = load i64, i64* %"__idx$53", align 8 +loop.cond57: ; preds = %loop.body58, %loop.exit54 + %74 = load i64, i64* %anon55, align 8 %gt = icmp ugt i64 5, %74 - br i1 %gt, label %loop.body56, label %loop.exit61 + br i1 %gt, label %loop.body58, label %loop.exit63 -loop.body56: ; preds = %loop.cond55 - %75 = load i64, i64* %"__idx$53", align 8 - store i64 %75, i64* %i57, align 8 - %76 = load i64, i64* %"__idx$53", align 8 - %77 = getelementptr inbounds [5 x i32], [5 x i32]* %"__enum$54", i64 0, i64 %76 +loop.body58: ; preds = %loop.cond57 + %75 = load i64, i64* %anon55, align 8 + store i64 %75, i64* %i59, align 8 + %76 = load i64, i64* %anon55, align 8 + %77 = getelementptr inbounds [5 x i32], [5 x i32]* %anon56, i64 0, i64 %76 %78 = load i32, i32* %77, align 4 - store i32 %78, i32* %y58, align 4 - %79 = load i64, i64* %i57, align 8 - %80 = load i32, i32* %y58, align 4 + store i32 %78, i32* %y60, align 4 + %79 = load i64, i64* %i59, align 8 + %80 = load i32, i32* %y60, align 4 call void (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @.str.9, i32 0, i32 0), i64 %79, i32 %80) - %81 = load i64, i64* %i57, align 8 - %add59 = add i64 %81, 1 - store i64 %add59, i64* %i57, align 8 - %82 = load i64, i64* %"__idx$53", align 8 - %add60 = add i64 %82, 1 - store i64 %add60, i64* %"__idx$53", align 8 - br label %loop.cond55 + %81 = load i64, i64* %i59, align 8 + %add61 = add i64 %81, 1 + store i64 %add61, i64* %i59, align 8 + %82 = load i64, i64* %anon55, align 8 + %add62 = add i64 %82, 1 + store i64 %add62, i64* %anon55, align 8 + br label %loop.cond57 -loop.exit61: ; preds = %loop.cond55 - store i64 0, i64* %"__idx$62", align 8 +loop.exit63: ; preds = %loop.cond57 + store i64 0, i64* %anon64, align 8 call void @foo.getFields([5 x i32]* sret([5 x i32]) align 4 %sretparam) - %83 = bitcast [5 x i32]* %"__enum$63" to i8* + %83 = bitcast [5 x i32]* %anon65 to i8* %84 = bitcast [5 x i32]* %sretparam to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 16 %83, i8* align 4 %84, i32 20, i1 false) - br label %loop.cond64 + br label %loop.cond66 -loop.cond64: ; preds = %loop.body66, %loop.exit61 - %85 = load i64, i64* %"__idx$62", align 8 - %gt65 = icmp ugt i64 5, %85 - br i1 %gt65, label %loop.body66, label %loop.exit70 +loop.cond66: ; preds = %loop.body68, %loop.exit63 + %85 = load i64, i64* %anon64, align 8 + %gt67 = icmp ugt i64 5, %85 + br i1 %gt67, label %loop.body68, label %loop.exit72 -loop.body66: ; preds = %loop.cond64 - %86 = load i64, i64* %"__idx$62", align 8 - store i64 %86, i64* %i67, align 8 - %87 = load i64, i64* %"__idx$62", align 8 - %88 = getelementptr inbounds [5 x i32], [5 x i32]* %"__enum$63", i64 0, i64 %87 +loop.body68: ; preds = %loop.cond66 + %86 = load i64, i64* %anon64, align 8 + store i64 %86, i64* %i69, align 8 + %87 = load i64, i64* %anon64, align 8 + %88 = getelementptr inbounds [5 x i32], [5 x i32]* %anon65, i64 0, i64 %87 %89 = load i32, i32* %88, align 4 - store i32 %89, i32* %y68, align 4 - %90 = load i64, i64* %i67, align 8 - %91 = load i32, i32* %y68, align 4 + store i32 %89, i32* %y70, align 4 + %90 = load i64, i64* %i69, align 8 + %91 = load i32, i32* %y70, align 4 call void (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* @.str.10, i32 0, i32 0), i64 %90, i32 %91) - %92 = load i64, i64* %"__idx$62", align 8 - %add69 = add i64 %92, 1 - store i64 %add69, i64* %"__idx$62", align 8 - br label %loop.cond64 + %92 = load i64, i64* %anon64, align 8 + %add71 = add i64 %92, 1 + store i64 %add71, i64* %anon64, align 8 + br label %loop.cond66 -loop.exit70: ; preds = %loop.cond64 - store i64 0, i64* %"__idx$71", align 8 - call void @foo.getFields([5 x i32]* sret([5 x i32]) align 4 %sretparam73) - store [5 x i32]* %sretparam73, [5 x i32]** %"__enum$72", align 8 - br label %loop.cond74 +loop.exit72: ; preds = %loop.cond66 + store i64 0, i64* %anon73, align 8 + call void @foo.getFields([5 x i32]* sret([5 x i32]) align 4 %sretparam75) + store [5 x i32]* %sretparam75, [5 x i32]** %anon74, align 8 + br label %loop.cond76 -loop.cond74: ; preds = %loop.body76, %loop.exit70 - %93 = load i64, i64* %"__idx$71", align 8 - %gt75 = icmp ugt i64 5, %93 - br i1 %gt75, label %loop.body76, label %loop.exit80 +loop.cond76: ; preds = %loop.body78, %loop.exit72 + %93 = load i64, i64* %anon73, align 8 + %gt77 = icmp ugt i64 5, %93 + br i1 %gt77, label %loop.body78, label %loop.exit82 -loop.body76: ; preds = %loop.cond74 - %94 = load i64, i64* %"__idx$71", align 8 - store i64 %94, i64* %i77, align 8 - %95 = load [5 x i32]*, [5 x i32]** %"__enum$72", align 8 - %96 = load i64, i64* %"__idx$71", align 8 +loop.body78: ; preds = %loop.cond76 + %94 = load i64, i64* %anon73, align 8 + store i64 %94, i64* %i79, align 8 + %95 = load [5 x i32]*, [5 x i32]** %anon74, align 8 + %96 = load i64, i64* %anon73, align 8 %97 = getelementptr inbounds [5 x i32], [5 x i32]* %95, i64 0, i64 %96 %98 = load i32, i32* %97, align 4 - store i32 %98, i32* %y78, align 4 - %99 = load i64, i64* %i77, align 8 - %100 = load i32, i32* %y78, align 4 + store i32 %98, i32* %y80, align 4 + %99 = load i64, i64* %i79, align 8 + %100 = load i32, i32* %y80, align 4 call void (i8*, ...) @printf(i8* getelementptr inbounds ([27 x i8], [27 x i8]* @.str.11, i32 0, i32 0), i64 %99, i32 %100) - %101 = load i64, i64* %"__idx$71", align 8 - %add79 = add i64 %101, 1 - store i64 %add79, i64* %"__idx$71", align 8 - br label %loop.cond74 + %101 = load i64, i64* %anon73, align 8 + %add81 = add i64 %101, 1 + store i64 %add81, i64* %anon73, align 8 + br label %loop.cond76 -loop.exit80: ; preds = %loop.cond74 - store i32 0, i32* %a81, align 4 +loop.exit82: ; preds = %loop.cond76 + store i32 0, i32* %a83, align 4 %102 = getelementptr inbounds %Foo, %Foo* %x, i32 0, i32 0 - %103 = load i32, i32* %a81, align 4 - %sisiext82 = sext i32 %103 to i64 - %104 = getelementptr inbounds [3 x i32], [3 x i32]* %102, i64 0, i64 %sisiext82 + %103 = load i32, i32* %a83, align 4 + %sisiext84 = sext i32 %103 to i64 + %104 = getelementptr inbounds [3 x i32], [3 x i32]* %102, i64 0, i64 %sisiext84 %105 = load i32, i32* %104, align 4 - store i32 1, i32* %a83, align 4 + store i32 1, i32* %a85, align 4 %106 = getelementptr inbounds %Foo, %Foo* %x, i32 0, i32 0 - %107 = load i32, i32* %a83, align 4 - %sisiext84 = sext i32 %107 to i64 - %108 = getelementptr inbounds [3 x i32], [3 x i32]* %106, i64 0, i64 %sisiext84 + %107 = load i32, i32* %a85, align 4 + %sisiext86 = sext i32 %107 to i64 + %108 = getelementptr inbounds [3 x i32], [3 x i32]* %106, i64 0, i64 %sisiext86 %109 = load i32, i32* %108, align 4 call void (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.12, i32 0, i32 0), i32 %105, i32 %109) - store i32 1, i32* %a86, align 4 + store i32 1, i32* %a88, align 4 %110 = getelementptr inbounds %Foo, %Foo* %x, i32 0, i32 0 - %111 = load i32, i32* %a86, align 4 - %sisiext87 = sext i32 %111 to i64 - %112 = getelementptr inbounds [3 x i32], [3 x i32]* %110, i64 0, i64 %sisiext87 - store i32* %112, i32** %y85, align 8 - %113 = load i32*, i32** %y85, align 8 + %111 = load i32, i32* %a88, align 4 + %sisiext89 = sext i32 %111 to i64 + %112 = getelementptr inbounds [3 x i32], [3 x i32]* %110, i64 0, i64 %sisiext89 + store i32* %112, i32** %y87, align 8 + %113 = load i32*, i32** %y87, align 8 %114 = load i32, i32* %113, align 8 - %add88 = add i32 %114, 1 - store i32 %add88, i32* %113, align 8 - store i32 0, i32* %a89, align 4 + %add90 = add i32 %114, 1 + store i32 %add90, i32* %113, align 8 + store i32 0, i32* %a91, align 4 %115 = getelementptr inbounds %Foo, %Foo* %x, i32 0, i32 0 - %116 = load i32, i32* %a89, align 4 - %sisiext90 = sext i32 %116 to i64 - %117 = getelementptr inbounds [3 x i32], [3 x i32]* %115, i64 0, i64 %sisiext90 + %116 = load i32, i32* %a91, align 4 + %sisiext92 = sext i32 %116 to i64 + %117 = getelementptr inbounds [3 x i32], [3 x i32]* %115, i64 0, i64 %sisiext92 %118 = load i32, i32* %117, align 4 - store i32 1, i32* %a91, align 4 + store i32 1, i32* %a93, align 4 %119 = getelementptr inbounds %Foo, %Foo* %x, i32 0, i32 0 - %120 = load i32, i32* %a91, align 4 - %sisiext92 = sext i32 %120 to i64 - %121 = getelementptr inbounds [3 x i32], [3 x i32]* %119, i64 0, i64 %sisiext92 + %120 = load i32, i32* %a93, align 4 + %sisiext94 = sext i32 %120 to i64 + %121 = getelementptr inbounds [3 x i32], [3 x i32]* %119, i64 0, i64 %sisiext94 %122 = load i32, i32* %121, align 4 call void (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.13, i32 0, i32 0), i32 %118, i32 %122) ret void diff --git a/test/test_suite/statements/foreach_break.c3t b/test/test_suite/statements/foreach_break.c3t index 9844380d0..8ac18a01f 100644 --- a/test/test_suite/statements/foreach_break.c3t +++ b/test/test_suite/statements/foreach_break.c3t @@ -19,7 +19,7 @@ define void @test.test() #0 { entry: %x = alloca [3 x i32], align 4 %g = alloca i32, align 4 - %"__idx$" = alloca i64, align 8 + %anon = alloca i64, align 8 %z = alloca i32, align 4 %0 = getelementptr inbounds [3 x i32], [3 x i32]* %x, i64 0, i64 0 store i32 0, i32* %0, align 4 @@ -28,16 +28,16 @@ entry: %2 = getelementptr inbounds [3 x i32], [3 x i32]* %x, i64 0, i64 2 store i32 0, i32* %2, align 4 store i32 0, i32* %g, align 4 - store i64 0, i64* %"__idx$", align 8 + store i64 0, i64* %anon, align 8 br label %loop.cond loop.cond: ; preds = %loop.inc, %entry - %3 = load i64, i64* %"__idx$", align 8 + %3 = load i64, i64* %anon, align 8 %gt = icmp ugt i64 3, %3 br i1 %gt, label %loop.body, label %loop.exit loop.body: ; preds = %loop.cond - %4 = load i64, i64* %"__idx$", align 8 + %4 = load i64, i64* %anon, align 8 %5 = getelementptr inbounds [3 x i32], [3 x i32]* %x, i64 0, i64 %4 %6 = load i32, i32* %5, align 4 store i32 %6, i32* %z, align 4 @@ -64,9 +64,9 @@ if.exit3: ; preds = %if.exit br label %loop.inc loop.inc: ; preds = %if.exit3, %if.then2 - %11 = load i64, i64* %"__idx$", align 8 + %11 = load i64, i64* %anon, align 8 %add4 = add i64 %11, 1 - store i64 %add4, i64* %"__idx$", align 8 + store i64 %add4, i64* %anon, align 8 br label %loop.cond loop.exit: ; preds = %if.then, %loop.cond diff --git a/test/test_suite/statements/foreach_common.c3t b/test/test_suite/statements/foreach_common.c3t index 6711e80df..8f56f0ab2 100644 --- a/test/test_suite/statements/foreach_common.c3t +++ b/test/test_suite/statements/foreach_common.c3t @@ -37,54 +37,54 @@ fn void main() entry: %foo = alloca [3 x float], align 4 - %"__idx$" = alloca i64, align 8 + %anon = alloca i64, align 8 %a = alloca float, align 4 - %"__idx$1" = alloca i64, align 8 + %anon1 = alloca i64, align 8 %a5 = alloca float*, align 8 - %"__idx$9" = alloca i64, align 8 + %anon9 = alloca i64, align 8 %a13 = alloca i8*, align 8 - %"__idx$18" = alloca i64, align 8 + %anon18 = alloca i64, align 8 %i = alloca i64, align 8 %a22 = alloca float, align 4 - %"__idx$26" = alloca i64, align 8 + %anon26 = alloca i64, align 8 %i30 = alloca i8, align 1 %a31 = alloca double, align 8 - %"__idx$35" = alloca i64, align 8 + %anon35 = alloca i64, align 8 %a39 = alloca double, align 8 %0 = bitcast [3 x float]* %foo to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %0, i8* align 4 bitcast ([3 x float]* @.__const to i8*), i32 12, i1 false) - store i64 0, i64* %"__idx$", align 8 + store i64 0, i64* %anon, align 8 br label %loop.cond loop.cond: ; preds = %loop.body, %entry - %1 = load i64, i64* %"__idx$", align 8 + %1 = load i64, i64* %anon, align 8 %gt = icmp ugt i64 3, %1 br i1 %gt, label %loop.body, label %loop.exit loop.body: ; preds = %loop.cond - %2 = load i64, i64* %"__idx$", align 8 + %2 = load i64, i64* %anon, align 8 %3 = getelementptr inbounds [3 x float], [3 x float]* %foo, i64 0, i64 %2 %4 = load float, float* %3, align 4 store float %4, float* %a, align 4 %5 = load float, float* %a, align 4 %fpfpext = fpext float %5 to double call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str, i32 0, i32 0), double %fpfpext) - %6 = load i64, i64* %"__idx$", align 8 + %6 = load i64, i64* %anon, align 8 %add = add i64 %6, 1 - store i64 %add, i64* %"__idx$", align 8 + store i64 %add, i64* %anon, align 8 br label %loop.cond loop.exit: ; preds = %loop.cond - store i64 0, i64* %"__idx$1", align 8 + store i64 0, i64* %anon1, align 8 br label %loop.cond2 loop.cond2: ; preds = %loop.body4, %loop.exit - %7 = load i64, i64* %"__idx$1", align 8 + %7 = load i64, i64* %anon1, align 8 %gt3 = icmp ugt i64 3, %7 br i1 %gt3, label %loop.body4, label %loop.exit8 loop.body4: ; preds = %loop.cond2 - %8 = load i64, i64* %"__idx$1", align 8 + %8 = load i64, i64* %anon1, align 8 %9 = getelementptr inbounds [3 x float], [3 x float]* %foo, i64 0, i64 %8 store float* %9, float** %a5, align 8 %10 = load float*, float** %a5, align 8 @@ -95,22 +95,22 @@ loop.body4: ; preds = %loop.cond2 %13 = load float, float* %12, align 8 %fpfpext6 = fpext float %13 to double call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.1, i32 0, i32 0), double %fpfpext6) - %14 = load i64, i64* %"__idx$1", align 8 + %14 = load i64, i64* %anon1, align 8 %add7 = add i64 %14, 1 - store i64 %add7, i64* %"__idx$1", align 8 + store i64 %add7, i64* %anon1, align 8 br label %loop.cond2 loop.exit8: ; preds = %loop.cond2 - store i64 0, i64* %"__idx$9", align 8 + store i64 0, i64* %anon9, align 8 br label %loop.cond10 loop.cond10: ; preds = %loop.body12, %loop.exit8 - %15 = load i64, i64* %"__idx$9", align 8 + %15 = load i64, i64* %anon9, align 8 %gt11 = icmp ugt i64 3, %15 br i1 %gt11, label %loop.body12, label %loop.exit17 loop.body12: ; preds = %loop.cond10 - %16 = load i64, i64* %"__idx$9", align 8 + %16 = load i64, i64* %anon9, align 8 %17 = getelementptr inbounds [3 x float], [3 x float]* %foo, i64 0, i64 %16 %ptrptr = bitcast float* %17 to i8* store i8* %ptrptr, i8** %a13, align 8 @@ -119,24 +119,24 @@ loop.body12: ; preds = %loop.cond10 %19 = load float, float* %ptrptr14, align 8 %fpfpext15 = fpext float %19 to double call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.2, i32 0, i32 0), double %fpfpext15) - %20 = load i64, i64* %"__idx$9", align 8 + %20 = load i64, i64* %anon9, align 8 %add16 = add i64 %20, 1 - store i64 %add16, i64* %"__idx$9", align 8 + store i64 %add16, i64* %anon9, align 8 br label %loop.cond10 loop.exit17: ; preds = %loop.cond10 - store i64 0, i64* %"__idx$18", align 8 + store i64 0, i64* %anon18, align 8 br label %loop.cond19 loop.cond19: ; preds = %loop.body21, %loop.exit17 - %21 = load i64, i64* %"__idx$18", align 8 + %21 = load i64, i64* %anon18, align 8 %gt20 = icmp ugt i64 3, %21 br i1 %gt20, label %loop.body21, label %loop.exit25 loop.body21: ; preds = %loop.cond19 - %22 = load i64, i64* %"__idx$18", align 8 + %22 = load i64, i64* %anon18, align 8 store i64 %22, i64* %i, align 8 - %23 = load i64, i64* %"__idx$18", align 8 + %23 = load i64, i64* %anon18, align 8 %24 = getelementptr inbounds [3 x float], [3 x float]* %foo, i64 0, i64 %23 %25 = load float, float* %24, align 4 store float %25, float* %a22, align 4 @@ -144,25 +144,25 @@ loop.body21: ; preds = %loop.cond19 %27 = load float, float* %a22, align 4 %fpfpext23 = fpext float %27 to double call void (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @.str.3, i32 0, i32 0), i64 %26, double %fpfpext23) - %28 = load i64, i64* %"__idx$18", align 8 + %28 = load i64, i64* %anon18, align 8 %add24 = add i64 %28, 1 - store i64 %add24, i64* %"__idx$18", align 8 + store i64 %add24, i64* %anon18, align 8 br label %loop.cond19 loop.exit25: ; preds = %loop.cond19 - store i64 0, i64* %"__idx$26", align 8 + store i64 0, i64* %anon26, align 8 br label %loop.cond27 loop.cond27: ; preds = %loop.body29, %loop.exit25 - %29 = load i64, i64* %"__idx$26", align 8 + %29 = load i64, i64* %anon26, align 8 %gt28 = icmp ugt i64 3, %29 br i1 %gt28, label %loop.body29, label %loop.exit34 loop.body29: ; preds = %loop.cond27 - %30 = load i64, i64* %"__idx$26", align 8 + %30 = load i64, i64* %anon26, align 8 %uiuitrunc = trunc i64 %30 to i8 store i8 %uiuitrunc, i8* %i30, align 1 - %31 = load i64, i64* %"__idx$26", align 8 + %31 = load i64, i64* %anon26, align 8 %32 = getelementptr inbounds [3 x float], [3 x float]* %foo, i64 0, i64 %31 %33 = load float, float* %32, align 4 %fpfpext32 = fpext float %33 to double @@ -171,31 +171,31 @@ loop.body29: ; preds = %loop.cond27 %uisiext = zext i8 %34 to i32 %35 = load double, double* %a31, align 8 call void (i8*, ...) @printf(i8* getelementptr inbounds ([16 x i8], [16 x i8]* @.str.4, i32 0, i32 0), i32 %uisiext, double %35) - %36 = load i64, i64* %"__idx$26", align 8 + %36 = load i64, i64* %anon26, align 8 %add33 = add i64 %36, 1 - store i64 %add33, i64* %"__idx$26", align 8 + store i64 %add33, i64* %anon26, align 8 br label %loop.cond27 loop.exit34: ; preds = %loop.cond27 - store i64 0, i64* %"__idx$35", align 8 + store i64 0, i64* %anon35, align 8 br label %loop.cond36 loop.cond36: ; preds = %loop.body38, %loop.exit34 - %37 = load i64, i64* %"__idx$35", align 8 + %37 = load i64, i64* %anon35, align 8 %gt37 = icmp ugt i64 3, %37 br i1 %gt37, label %loop.body38, label %loop.exit42 loop.body38: ; preds = %loop.cond36 - %38 = load i64, i64* %"__idx$35", align 8 + %38 = load i64, i64* %anon35, align 8 %39 = getelementptr inbounds [3 x float], [3 x float]* %foo, i64 0, i64 %38 %40 = load float, float* %39, align 4 %fpfpext40 = fpext float %40 to double store double %fpfpext40, double* %a39, align 8 %41 = load double, double* %a39, align 8 call void (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str.5, i32 0, i32 0), double %41) - %42 = load i64, i64* %"__idx$35", align 8 + %42 = load i64, i64* %anon35, align 8 %add41 = add i64 %42, 1 - store i64 %add41, i64* %"__idx$35", align 8 + store i64 %add41, i64* %anon35, align 8 br label %loop.cond36 loop.exit42: ; preds = %loop.cond36 diff --git a/test/test_suite/statements/foreach_custom.c3t b/test/test_suite/statements/foreach_custom.c3t index 0e528e5ec..5b3cd3bf8 100644 --- a/test/test_suite/statements/foreach_custom.c3t +++ b/test/test_suite/statements/foreach_custom.c3t @@ -38,8 +38,8 @@ define void @foo.main() #0 { entry: %i = alloca [3 x i32], align 4 %x = alloca %Foo, align 8 - %"__idx$" = alloca i64, align 8 - %"__len$" = alloca i64, align 8 + %anon = alloca i64, align 8 + %anon1 = alloca i64, align 8 %f = alloca i32, align 4 %index = alloca i64, align 8 %0 = bitcast [3 x i32]* %i to i8* @@ -49,19 +49,21 @@ entry: %3 = insertvalue %"int[]" undef, i32* %2, 0 %4 = insertvalue %"int[]" %3, i64 3, 1 store %"int[]" %4, %"int[]"* %1, align 8 - store i64 0, i64* %"__idx$", align 8 + store i64 0, i64* %anon, align 8 %5 = getelementptr inbounds %Foo, %Foo* %x, i32 0, i32 0 %6 = getelementptr inbounds %"int[]", %"int[]"* %5, i32 0, i32 1 %7 = load i64, i64* %6, align 8 - store i64 %7, i64* %"__len$", align 8 + store i64 %7, i64* %anon1, align 8 br label %loop.cond + loop.cond: ; preds = %entry - %8 = load i64, i64* %"__idx$", align 8 - %9 = load i64, i64* %"__len$", align 8 + %8 = load i64, i64* %anon, align 8 + %9 = load i64, i64* %anon1, align 8 %lt = icmp ult i64 %8, %9 br i1 %lt, label %loop.body, label %loop.exit + loop.body: ; preds = %loop.cond - %10 = load i64, i64* %"__idx$", align 8 + %10 = load i64, i64* %anon, align 8 store i64 %10, i64* %index, align 8 %11 = getelementptr inbounds %Foo, %Foo* %x, i32 0, i32 0 %12 = getelementptr inbounds %"int[]", %"int[]"* %11, i32 0, i32 0 @@ -72,9 +74,11 @@ loop.body: ; preds = %loop.cond store i32 %15, i32* %f, align 4 %16 = load i32, i32* %f, align 4 %17 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i32 %16) - br label %loop.body1 -loop.body1: ; preds = %loop.body + br label %loop.body2 + +loop.body2: ; preds = %loop.body br label %loop.exit -loop.exit: ; preds = %loop.body1, %loop.cond + +loop.exit: ; preds = %loop.body2, %loop.cond ret void } \ No newline at end of file diff --git a/test/test_suite/statements/foreach_custom_macro.c3t b/test/test_suite/statements/foreach_custom_macro.c3t index 112cffd61..807e3dfc7 100644 --- a/test/test_suite/statements/foreach_custom_macro.c3t +++ b/test/test_suite/statements/foreach_custom_macro.c3t @@ -36,8 +36,8 @@ define void @foo.main() #0 { entry: %i = alloca [3 x i32], align 4 %x = alloca %Foo, align 8 - %"__idx$" = alloca i64, align 8 - %"__len$" = alloca i64, align 8 + %anon = alloca i64, align 8 + %anon1 = alloca i64, align 8 %f = alloca i32, align 4 %index = alloca i64, align 8 %0 = bitcast [3 x i32]* %i to i8* @@ -47,21 +47,21 @@ entry: %3 = insertvalue %"int[]" undef, i32* %2, 0 %4 = insertvalue %"int[]" %3, i64 3, 1 store %"int[]" %4, %"int[]"* %1, align 8 - store i64 0, i64* %"__idx$", align 8 + store i64 0, i64* %anon, align 8 %5 = getelementptr inbounds %Foo, %Foo* %x, i32 0, i32 0 %6 = getelementptr inbounds %"int[]", %"int[]"* %5, i32 0, i32 1 %7 = load i64, i64* %6, align 8 - store i64 %7, i64* %"__len$", align 8 + store i64 %7, i64* %anon1, align 8 br label %loop.cond loop.cond: ; preds = %entry - %8 = load i64, i64* %"__idx$", align 8 - %9 = load i64, i64* %"__len$", align 8 + %8 = load i64, i64* %anon, align 8 + %9 = load i64, i64* %anon1, align 8 %lt = icmp ult i64 %8, %9 br i1 %lt, label %loop.body, label %loop.exit loop.body: ; preds = %loop.cond - %10 = load i64, i64* %"__idx$", align 8 + %10 = load i64, i64* %anon, align 8 store i64 %10, i64* %index, align 8 %11 = getelementptr inbounds %Foo, %Foo* %x, i32 0, i32 0 %12 = getelementptr inbounds %"int[]", %"int[]"* %11, i32 0, i32 0 @@ -72,11 +72,11 @@ loop.body: ; preds = %loop.cond store i32 %15, i32* %f, align 4 %16 = load i32, i32* %f, align 4 %17 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i32 %16) - br label %loop.body1 + br label %loop.body2 -loop.body1: ; preds = %loop.body +loop.body2: ; preds = %loop.body br label %loop.exit -loop.exit: ; preds = %loop.body1, %loop.cond +loop.exit: ; preds = %loop.body2, %loop.cond ret void } diff --git a/test/test_suite/statements/various_switching.c3t b/test/test_suite/statements/various_switching.c3t index 314ec0c28..166b6f3eb 100644 --- a/test/test_suite/statements/various_switching.c3t +++ b/test/test_suite/statements/various_switching.c3t @@ -55,154 +55,3 @@ fn void main() test(); printf("Hello!\n"); } - -// #expect: mymodule.ll - -define void @mymodule.test() #0 { -entry: - %x = alloca i32, align 4 - %x.f = alloca i64, align 8 - %z = alloca i64, align 8 - %err = alloca i64, align 8 - %switch = alloca i64, align 8 - %switch4 = alloca i64, align 8 - %a = alloca i32, align 4 - %b = alloca i32, align 4 - %zy = alloca i32, align 4 - %switch17 = alloca i32, align 4 - store i64 ptrtoint ([2 x i8*]* @"mymodule.ByeErr$elements" to i64), i64* %x.f, align 8 - store i64 5, i64* %z, align 8 - br label %testblock - -testblock: ; preds = %entry - %0 = load i64, i64* %x.f, align 8 - %not_err = icmp eq i64 %0, 0 - br i1 %not_err, label %after_check, label %error - -error: ; preds = %testblock - store i64 %0, i64* %err, align 8 - br label %end_block - -after_check: ; preds = %testblock - store i64 0, i64* %err, align 8 - br label %end_block - -end_block: ; preds = %after_check, %error - %1 = load i64, i64* %err, align 8 - %neq = icmp ne i64 %1, 0 - br i1 %neq, label %if.then, label %if.exit - -if.then: ; preds = %end_block - store i64 %1, i64* %switch, align 8 - br label %switch.entry - -switch.entry: ; preds = %if.then - %2 = load i64, i64* %switch, align 8 - %eq = icmp eq i64 ptrtoint ([1 x i8*]* @"mymodule.HelloErr$elements" to i64), %2 - br i1 %eq, label %switch.case, label %next_if - -switch.case: ; preds = %switch.entry - call void (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str, i32 0, i32 0)) - br label %switch.exit - -next_if: ; preds = %switch.entry - %eq1 = icmp eq i64 ptrtoint ([2 x i8*]* @"mymodule.ByeErr$elements" to i64), %2 - br i1 %eq1, label %switch.case2, label %next_if3 - -switch.case2: ; preds = %next_if - call void (i8*, ...) @printf(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str.1, i32 0, i32 0)) - br label %switch.exit - -next_if3: ; preds = %next_if - br label %switch.default - -switch.default: ; preds = %next_if3 - call void (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str.2, i32 0, i32 0)) - br label %switch.exit - -switch.exit: ; preds = %switch.default, %switch.case2, %switch.case - br label %if.exit - -if.exit: ; preds = %switch.exit, %end_block - %3 = load i64, i64* %z, align 8 - store i64 %3, i64* %switch4, align 8 - br label %switch.entry5 - -switch.entry5: ; preds = %if.exit - %4 = load i64, i64* %switch4, align 8 - %eq6 = icmp eq i64 5, %4 - br i1 %eq6, label %switch.case7, label %next_if8 - -switch.case7: ; preds = %switch.entry5 - call void (i8*, ...) @printf(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.3, i32 0, i32 0)) - br label %switch.exit16 - -next_if8: ; preds = %switch.entry5 - %eq9 = icmp eq i64 2, %4 - br i1 %eq9, label %switch.case10, label %next_if11 - -switch.case10: ; preds = %next_if8 - br label %switch.case13 - -next_if11: ; preds = %next_if8 - %eq12 = icmp eq i64 15, %4 - br i1 %eq12, label %switch.case13, label %next_if14 - -switch.case13: ; preds = %next_if11, %switch.case10 - br label %switch.default15 - -next_if14: ; preds = %next_if11 - br label %switch.default15 - -switch.default15: ; preds = %next_if14, %switch.case13 - call void (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @.str.4, i32 0, i32 0)) - br label %switch.exit16 - -switch.exit16: ; preds = %switch.default15, %switch.case7 - store i32 1, i32* %a, align 4 - store i32 2, i32* %b, align 4 - %5 = load i32, i32* %b, align 4 - %6 = load i32, i32* %a, align 4 - %add = add i32 %5, %6 - store i32 %add, i32* %zy, align 4 - %7 = load i32, i32* %zy, align 4 - store i32 %7, i32* %switch17, align 4 - br label %switch.entry18 - -switch.entry18: ; preds = %switch.exit16 - %8 = load i32, i32* %switch17, align 4 - %9 = load i32, i32* %a, align 4 - %eq19 = icmp eq i32 %9, %8 - br i1 %eq19, label %switch.case20, label %next_if21 - -switch.case20: ; preds = %switch.entry18 - call void (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.5, i32 0, i32 0)) - br label %switch.exit26 - -next_if21: ; preds = %switch.entry18 - %10 = load i32, i32* %b, align 4 - %eq22 = icmp eq i32 %10, %8 - br i1 %eq22, label %switch.case23, label %next_if24 - -switch.case23: ; preds = %next_if21 - call void (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.6, i32 0, i32 0)) - br label %switch.exit26 - -next_if24: ; preds = %next_if21 - br label %switch.default25 - -switch.default25: ; preds = %next_if24 - call void (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* @.str.7, i32 0, i32 0)) - br label %switch.exit26 - -switch.exit26: ; preds = %switch.default25, %switch.case23, %switch.case20 - ret void -} - -; Function Attrs: nounwind -define void @mymodule.main() #0 { -entry: - call void @mymodule.test() - call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.8, i32 0, i32 0)) - ret void -} diff --git a/test/test_suite/variant/variant_assign.c3t b/test/test_suite/variant/variant_assign.c3t index 212ae47cf..44fa1ddf7 100644 --- a/test/test_suite/variant/variant_assign.c3t +++ b/test/test_suite/variant/variant_assign.c3t @@ -117,7 +117,7 @@ switch.exit: ; preds = %switch.default, %sw define void @foo.test2(i64 %0, i8* %1) #0 { entry: %y = alloca %variant, align 8 - %.variant = alloca %variant, align 8 + %anon = alloca %variant, align 8 %switch = alloca i64, align 8 %z = alloca i32*, align 8 %taddr = alloca i32, align 4 @@ -127,10 +127,10 @@ entry: store i64 %0, i64* %2, align 8 %3 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %pair, i32 0, i32 1 store i8* %1, i8** %3, align 8 - %4 = bitcast %variant* %.variant to i8* + %4 = bitcast %variant* %anon to i8* %5 = bitcast %variant* %y to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %4, i8* align 8 %5, i32 16, i1 false) - %6 = getelementptr inbounds %variant, %variant* %.variant, i32 0, i32 1 + %6 = getelementptr inbounds %variant, %variant* %anon, i32 0, i32 1 %7 = load i64, i64* %6, align 8 store i64 %7, i64* %switch, align 8 br label %switch.entry @@ -141,7 +141,7 @@ switch.entry: ; preds = %entry br i1 %eq, label %switch.case, label %next_if switch.case: ; preds = %switch.entry - %9 = getelementptr inbounds %variant, %variant* %.variant, i32 0, i32 0 + %9 = getelementptr inbounds %variant, %variant* %anon, i32 0, i32 0 %10 = bitcast i8** %9 to i32** %11 = load i32*, i32** %10, align 8 store i32* %11, i32** %z, align 8 @@ -160,7 +160,7 @@ next_if: ; preds = %switch.entry br i1 %eq1, label %switch.case2, label %next_if4 switch.case2: ; preds = %next_if - %17 = getelementptr inbounds %variant, %variant* %.variant, i32 0, i32 0 + %17 = getelementptr inbounds %variant, %variant* %anon, i32 0, i32 0 %18 = bitcast i8** %17 to double** %19 = load double*, double** %18, align 8 store double* %19, double** %z3, align 8 @@ -183,7 +183,7 @@ switch.exit: ; preds = %switch.default, %sw define void @foo.test3(i64 %0, i8* %1) #0 { entry: %y = alloca %variant, align 8 - %.variant = alloca %variant, align 8 + %anon = alloca %variant, align 8 %switch = alloca i64, align 8 %z = alloca i32, align 4 %z3 = alloca double, align 8 @@ -192,10 +192,10 @@ entry: store i64 %0, i64* %2, align 8 %3 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %pair, i32 0, i32 1 store i8* %1, i8** %3, align 8 - %4 = bitcast %variant* %.variant to i8* + %4 = bitcast %variant* %anon to i8* %5 = bitcast %variant* %y to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %4, i8* align 8 %5, i32 16, i1 false) - %6 = getelementptr inbounds %variant, %variant* %.variant, i32 0, i32 1 + %6 = getelementptr inbounds %variant, %variant* %anon, i32 0, i32 1 %7 = load i64, i64* %6, align 8 store i64 %7, i64* %switch, align 8 br label %switch.entry @@ -206,7 +206,7 @@ switch.entry: ; preds = %entry br i1 %eq, label %switch.case, label %next_if switch.case: ; preds = %switch.entry - %9 = getelementptr inbounds %variant, %variant* %.variant, i32 0, i32 0 + %9 = getelementptr inbounds %variant, %variant* %anon, i32 0, i32 0 %10 = bitcast i8** %9 to i32** %11 = load i32*, i32** %10, align 8 %12 = load i32, i32* %11, align 8 @@ -220,7 +220,7 @@ next_if: ; preds = %switch.entry br i1 %eq1, label %switch.case2, label %next_if4 switch.case2: ; preds = %next_if - %14 = getelementptr inbounds %variant, %variant* %.variant, i32 0, i32 0 + %14 = getelementptr inbounds %variant, %variant* %anon, i32 0, i32 0 %15 = bitcast i8** %14 to double** %16 = load double*, double** %15, align 8 %17 = load double, double* %16, align 8 diff --git a/test/test_suite/variant/variant_switch.c3t b/test/test_suite/variant/variant_switch.c3t index 8a671af13..d160b8306 100644 --- a/test/test_suite/variant/variant_switch.c3t +++ b/test/test_suite/variant/variant_switch.c3t @@ -15,7 +15,7 @@ fn void test(variant z) default: printf("Unknown type.\n"); } - if (z.typeid == int.typeid) + if (z.type == int.typeid) { printf("int: %d\n", *(int*)(z)); } @@ -28,6 +28,120 @@ fn int main() return 0; } -/* expect: foo.ll +/* #expect: foo.ll -foekf \ No newline at end of file +define void @foo.test(i64 %0, i8* %1) #0 { +entry: + %z = alloca %variant, align 8 + %switch = alloca i64, align 8 + %z1 = alloca i32*, align 8 + %z4 = alloca double*, align 8 + %pair = bitcast %variant* %z to { i64, i8* }* + %2 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %pair, i32 0, i32 0 + store i64 %0, i64* %2, align 8 + %3 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %pair, i32 0, i32 1 + store i8* %1, i8** %3, align 8 + %4 = getelementptr inbounds %variant, %variant* %z, i32 0, i32 1 + %5 = load i64, i64* %4, align 8 + store i64 %5, i64* %switch, align 8 + br label %switch.entry + +switch.entry: ; preds = %entry + %6 = load i64, i64* %switch, align 8 + %eq = icmp eq i64 5, %6 + br i1 %eq, label %switch.case, label %next_if + +switch.case: ; preds = %switch.entry + %7 = getelementptr inbounds %variant, %variant* %z, i32 0, i32 0 + %8 = bitcast i8** %7 to i32** + %9 = load i32*, i32** %8, align 8 + store i32* %9, i32** %z1, align 8 + %10 = load i32*, i32** %z1, align 8 + %11 = load i32, i32* %10, align 8 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str, i32 0, i32 0), i32 %11) + %12 = load i32*, i32** %z1, align 8 + store i32 3, i32* %12, align 8 + br label %switch.exit + +next_if: ; preds = %switch.entry + %eq2 = icmp eq i64 15, %6 + br i1 %eq2, label %switch.case3, label %next_if5 + +switch.case3: ; preds = %next_if + %13 = getelementptr inbounds %variant, %variant* %z, i32 0, i32 0 + %14 = bitcast i8** %13 to double** + %15 = load double*, double** %14, align 8 + store double* %15, double** %z4, align 8 + %16 = load double*, double** %z4, align 8 + %17 = load double, double* %16, align 8 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.1, i32 0, i32 0), double %17) + br label %switch.exit + +next_if5: ; preds = %next_if + br label %switch.default + +switch.default: ; preds = %next_if5 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @.str.2, i32 0, i32 0)) + br label %switch.exit + +switch.exit: ; preds = %switch.default, %switch.case3, %switch.case + %18 = getelementptr inbounds %variant, %variant* %z, i32 0, i32 1 + %19 = load i64, i64* %18, align 8 + %eq6 = icmp eq i64 %19, 5 + br i1 %eq6, label %if.then, label %if.exit + +if.then: ; preds = %switch.exit + %20 = getelementptr inbounds %variant, %variant* %z, i32 0, i32 0 + %21 = bitcast i8** %20 to i32** + %22 = load i32*, i32** %21, align 8 + %23 = load i32, i32* %22, align 8 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str.3, i32 0, i32 0), i32 %23) + br label %if.exit + +if.exit: ; preds = %if.then, %switch.exit + ret void +} + +; Function Attrs: nounwind +define i32 @main() #0 { +entry: + %taddr = alloca double, align 8 + %taddr1 = alloca %variant, align 8 + %taddr2 = alloca i32, align 4 + %taddr3 = alloca %variant, align 8 + %taddr6 = alloca i8, align 1 + %taddr7 = alloca %variant, align 8 + store double 1.230000e+02, double* %taddr, align 8 + %0 = bitcast double* %taddr to i8* + %1 = insertvalue %variant undef, i8* %0, 0 + %2 = insertvalue %variant %1, i64 15, 1 + store %variant %2, %variant* %taddr1, align 8 + %3 = bitcast %variant* %taddr1 to { i64, i8* }* + %4 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %3, i32 0, i32 0 + %lo = load i64, i64* %4, align 8 + %5 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %3, i32 0, i32 1 + %hi = load i8*, i8** %5, align 8 + call void @foo.test(i64 %lo, i8* %hi) + store i32 1, i32* %taddr2, align 4 + %6 = bitcast i32* %taddr2 to i8* + %7 = insertvalue %variant undef, i8* %6, 0 + %8 = insertvalue %variant %7, i64 5, 1 + store %variant %8, %variant* %taddr3, align 8 + %9 = bitcast %variant* %taddr3 to { i64, i8* }* + %10 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %9, i32 0, i32 0 + %lo4 = load i64, i64* %10, align 8 + %11 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %9, i32 0, i32 1 + %hi5 = load i8*, i8** %11, align 8 + call void @foo.test(i64 %lo4, i8* %hi5) + store i8 1, i8* %taddr6, align 1 + %12 = insertvalue %variant undef, i8* %taddr6, 0 + %13 = insertvalue %variant %12, i64 2, 1 + store %variant %13, %variant* %taddr7, align 8 + %14 = bitcast %variant* %taddr7 to { i64, i8* }* + %15 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %14, i32 0, i32 0 + %lo8 = load i64, i64* %15, align 8 + %16 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %14, i32 0, i32 1 + %hi9 = load i8*, i8** %16, align 8 + call void @foo.test(i64 %lo8, i8* %hi9) + ret i32 0 +} diff --git a/test/test_suite/variant/variant_test.c3t b/test/test_suite/variant/variant_test.c3t index 6e225e9f2..94436acfd 100644 --- a/test/test_suite/variant/variant_test.c3t +++ b/test/test_suite/variant/variant_test.c3t @@ -4,7 +4,7 @@ extern fn void printf(char*, ...); fn void test(variant x) { - switch (x.typeid) + switch (x.type) { case int: printf("Was int\n"); @@ -32,23 +32,23 @@ fn void main() variant x = &&1; int z; variant y = &z; - typeid g = y.typeid; - typeid h = x.typeid; - if (y.typeid == int.typeid) + typeid g = y.type; + typeid h = x.type; + if (y.type == int.typeid) { printf("y int match\n"); } - if (x.typeid == int.typeid) + if (x.type == int.typeid) { printf("x int match\n"); } y = &&1.0; x = &x; - if (y.typeid == int.typeid) + if (y.type == int.typeid) { printf("y int match\n"); } - if (x.typeid == int.typeid) + if (x.type == int.typeid) { printf("x int match\n"); } @@ -131,30 +131,30 @@ switch.exit: ; preds = %switch.default, %sw define void @foo.test_all(i8* %0, i64 %1) #0 { entry: %y = alloca %"variant[]", align 8 - %"__idx$" = alloca i64, align 8 - %"__len$" = alloca i64, align 8 + %anon = alloca i64, align 8 + %anon1 = alloca i64, align 8 %element = alloca %variant, align 8 %pair = bitcast %"variant[]"* %y to { i8*, i64 }* %2 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %pair, i32 0, i32 0 store i8* %0, i8** %2, align 8 %3 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %pair, i32 0, i32 1 store i64 %1, i64* %3, align 8 - store i64 0, i64* %"__idx$", align 8 + store i64 0, i64* %anon, align 8 %4 = getelementptr inbounds %"variant[]", %"variant[]"* %y, i32 0, i32 1 %5 = load i64, i64* %4, align 8 - store i64 %5, i64* %"__len$", align 8 + store i64 %5, i64* %anon1, align 8 br label %loop.cond loop.cond: ; preds = %loop.body, %entry - %6 = load i64, i64* %"__idx$", align 8 - %7 = load i64, i64* %"__len$", align 8 + %6 = load i64, i64* %anon, align 8 + %7 = load i64, i64* %anon1, align 8 %lt = icmp ult i64 %6, %7 br i1 %lt, label %loop.body, label %loop.exit loop.body: ; preds = %loop.cond %8 = getelementptr inbounds %"variant[]", %"variant[]"* %y, i32 0, i32 0 %9 = load %variant*, %variant** %8, align 8 - %10 = load i64, i64* %"__idx$", align 8 + %10 = load i64, i64* %anon, align 8 %ptroffset = getelementptr inbounds %variant, %variant* %9, i64 %10 %11 = bitcast %variant* %element to i8* %12 = bitcast %variant* %ptroffset to i8* @@ -165,9 +165,9 @@ loop.body: ; preds = %loop.cond %15 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %13, i32 0, i32 1 %hi = load i8*, i8** %15, align 8 call void @foo.test(i64 %lo, i8* %hi) - %16 = load i64, i64* %"__idx$", align 8 + %16 = load i64, i64* %anon, align 8 %add = add i64 %16, 1 - store i64 %add, i64* %"__idx$", align 8 + store i64 %add, i64* %anon, align 8 br label %loop.cond loop.exit: ; preds = %loop.cond