From 2802b2b96d6fa8f4dd3a9d7a4c86f9bcedc1913a Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Wed, 2 Mar 2022 23:01:43 +0100 Subject: [PATCH] Ensure, $eval and $evaltype, @unreachable() replaces $unreachable. .sizeof --- CMakeLists.txt | 1 - lib/std/builtin.c3 | 13 + lib/std/linkedlist.c3 | 2 +- lib/std/list.c3 | 2 +- lib/std/mem.c3 | 4 +- resources/examples/guess_number.c3 | 2 +- src/compiler/ast.c | 5 +- src/compiler/compiler_internal.h | 70 +- src/compiler/copying.c | 61 +- src/compiler/diagnostics.c | 2 +- src/compiler/enums.h | 25 +- src/compiler/hashtable.c | 3 - src/compiler/lexer.c | 37 - src/compiler/llvm_codegen_expr.c | 12 +- src/compiler/llvm_codegen_function.c | 2 + src/compiler/llvm_codegen_stmt.c | 51 +- src/compiler/parse_expr.c | 69 +- src/compiler/parse_global.c | 80 +- src/compiler/parse_stmt.c | 45 +- src/compiler/sema_casts.c | 2 + src/compiler/sema_decls.c | 43 +- src/compiler/sema_expr.c | 816 ++++++++---------- src/compiler/sema_internal.h | 9 + src/compiler/sema_name_resolution.c | 200 +++-- src/compiler/sema_stmts.c | 305 ++++--- src/compiler/sema_types.c | 38 +- src/compiler/semantic_analyser.c | 2 +- src/compiler/source_file.c | 3 +- src/compiler/symtab.c | 11 +- src/compiler/target.c | 2 +- src/compiler/tokens.c | 8 +- src/utils/errors.h | 2 - src/version.h | 2 +- test/test_suite/abi/darwinx64_2.c3t | 4 +- test/test_suite/assert/unreachable.c3t | 2 +- test/test_suite/compile_time/ct_eval.c3t | 22 + test/test_suite/compile_time/stringify.c3t | 12 +- .../compile_time_introspection/alignof.c3t | 26 +- .../compile_time_introspection/defined.c3t | 6 +- .../compile_time_introspection/defined_err.c3 | 2 +- .../compile_time_introspection/nameof.c3t | 18 +- .../compile_time_introspection/nameof_err.c3 | 9 +- .../compile_time_introspection/offsetof.c3t | 22 +- .../compile_time_introspection/sizeof.c3t | 30 +- .../sizeof_errors.c3 | 32 +- test/test_suite/contracts/simple_test.c3t | 98 +++ test/test_suite/enumerations/compile_time.c3t | 2 +- test/test_suite/macros/userland_bitcast.c3t | 2 +- test/test_suite/scoping/general_scoping.c3t | 79 -- .../struct/struct_const_construct_simple.c3t | 2 +- .../struct/struct_pack_and_align.c3t | 12 +- 51 files changed, 1146 insertions(+), 1163 deletions(-) delete mode 100644 src/compiler/hashtable.c create mode 100644 test/test_suite/compile_time/ct_eval.c3t create mode 100644 test/test_suite/contracts/simple_test.c3t delete mode 100644 test/test_suite/scoping/general_scoping.c3t diff --git a/CMakeLists.txt b/CMakeLists.txt index cd5ab2415..3211051ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,7 +136,6 @@ 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/builtin.c3 b/lib/std/builtin.c3 index ee5396538..46c94be29 100644 --- a/lib/std/builtin.c3 +++ b/lib/std/builtin.c3 @@ -2,6 +2,19 @@ // 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::builtin; + +macro scope(&variable; @body) @autoimport +{ + $typeof(variable) temp = variable; + defer variable = temp; + @body(); +} + +macro unreachable($string = "Unreachable statement reached.") @autoimport @noreturn +{ + assert(false, $string); +} + /* enum TypeKind { diff --git a/lib/std/linkedlist.c3 b/lib/std/linkedlist.c3 index cfd4e30ba..88f7e77cb 100644 --- a/lib/std/linkedlist.c3 +++ b/lib/std/linkedlist.c3 @@ -43,7 +43,7 @@ private fn void LinkedList.linkFirst(LinkedList *list, Type value) private fn void LinkedList.linkLast(LinkedList *list, Type value) { Node *last = list.last; - Node *new_node = mem::alloc($sizeof(Node)); + Node *new_node = mem::alloc(Node.sizeof); *new_node = { .prev = last, .value = value }; list.last = new_node; if (!last) diff --git a/lib/std/list.c3 b/lib/std/list.c3 index 1b2934970..c666c1d33 100644 --- a/lib/std/list.c3 +++ b/lib/std/list.c3 @@ -16,7 +16,7 @@ private fn void List.ensureCapacity(List *list) @inline if (list.capacity == list.size) { list.capacity = list.capacity ? 2 * list.capacity : 16; - list.entries = mem::realloc(list.entries, $sizeof(Type) * list.capacity); + list.entries = mem::realloc(list.entries, Type.sizeof * list.capacity); } } diff --git a/lib/std/mem.c3 b/lib/std/mem.c3 index 4441fb808..e202b6b3b 100644 --- a/lib/std/mem.c3 +++ b/lib/std/mem.c3 @@ -62,7 +62,7 @@ fn void! system_malloc_function(void *unused, void** pointer, usize bytes, usize *pointer = null; return; } - $unreachable; + @unreachable(); } @@ -159,7 +159,7 @@ Allocator main_allocator = { &system_malloc_function, null }; macro malloc($Type) { - return ($Type*)(mem::alloc($sizeof($Type))); + return ($Type*)(mem::alloc($Type.sizeof)); } fn void* alloc(usize size, usize count = 1) @inline diff --git a/resources/examples/guess_number.c3 b/resources/examples/guess_number.c3 index 799d0db43..c374b165e 100644 --- a/resources/examples/guess_number.c3 +++ b/resources/examples/guess_number.c3 @@ -54,7 +54,7 @@ fn int! askGuessMulti(int high) } return result; } - $unreachable; + @unreachable(); } fn void! Game.play(Game *game) diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 816217d08..6987a262c 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -202,7 +202,7 @@ Decl *decl_new_var(const char *name, SourceSpan loc, TypeInfo *type, VarDeclKind return decl; } -Decl *decl_new_generated_var(const char *name, Type *type, VarDeclKind kind, SourceSpan span) +Decl *decl_new_generated_var(Type *type, VarDeclKind kind, SourceSpan span) { Decl *decl = decl_calloc(); decl->decl_kind = DECL_VAR; @@ -211,6 +211,7 @@ Decl *decl_new_generated_var(const char *name, Type *type, VarDeclKind kind, Sou decl->visibility = VISIBLE_LOCAL; decl->var.kind = kind; decl->type = type; + decl->alignment = type ? type_alloca_alignment(type) : 0; decl->var.type_info = type_info_new_base(type, span); decl->resolve_status = RESOLVE_DONE; return decl; @@ -291,6 +292,7 @@ bool expr_is_pure(Expr *expr) } return true; case EXPR_TYPEOFANY: + case EXPR_CT_EVAL: return expr_is_pure(expr->inner_expr); case EXPR_LEN: return expr_is_pure(expr->len_expr.inner); @@ -314,6 +316,7 @@ bool expr_is_pure(Expr *expr) UNREACHABLE } + bool expr_is_simple(Expr *expr) { RETRY: diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index d26ddce85..70eab4def 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -459,6 +459,7 @@ typedef struct TypeInfo *type_parent; FunctionSignature function_signature; Ast *body; + Decl *ret_var; FuncAnnotations *annotations; } FuncDecl; @@ -491,10 +492,14 @@ typedef struct typedef struct { + struct + { + bool attr_noreturn : 1; + }; + struct Ast_ *body; Decl **parameters; TypeInfo *type_parent; // May be null TypeInfo *rtype; // May be null! - struct Ast_ *body; Decl **body_parameters; const char *block_parameter; struct CompilationUnit_ *unit; @@ -561,7 +566,7 @@ typedef struct Decl_ const char *name; SourceSpan span; const char *external_name; - Ast *docs; + struct AstDocDirective_ *docs; DeclKind decl_kind : 7; Visibility visibility : 3; ResolveStatus resolve_status : 3; @@ -769,11 +774,7 @@ typedef struct typedef struct { bool array : 1; - union - { - MemberIndex index; - const char *ident; - }; + Expr *inner; } ExprFlatElement; typedef struct @@ -1110,14 +1111,14 @@ typedef struct typedef struct AstCtIfStmt_ { Expr *expr; - struct Ast_ *then; - struct Ast_ *elif; + Ast *elif; + AstId then; } AstCtIfStmt; typedef struct { - Ast *stmt; + AstId stmt; DeferList defers; } AstScopedStmt; @@ -1192,11 +1193,6 @@ typedef struct Expr *body; } AstAsmStmt; -typedef struct -{ - Expr *scoped; - Ast *stmt; -} AstScopingStmt; typedef struct { Expr *message; @@ -1204,8 +1200,9 @@ typedef struct } AstAssertStmt; -typedef struct +typedef struct AstDocDirective_ { + SourceSpan span; DocDirectiveKind kind; union { @@ -1262,14 +1259,12 @@ typedef struct Ast_ AstForeachStmt foreach_stmt; AstCtIfStmt ct_if_stmt; // 24 AstCtIfStmt ct_elif_stmt; // 24 - Ast *ct_else_stmt; // 8 + AstId ct_else_stmt; // 8 AstCtForeachStmt ct_foreach_stmt; // 64 AstScopedStmt scoped_stmt; // 16 - AstScopingStmt scoping_stmt; AstAssertStmt ct_assert_stmt; AstAssertStmt assert_stmt; - Ast **directives; - AstDocDirective doc_directive; + AstDocDirective *directives; }; } Ast; @@ -1432,6 +1427,7 @@ typedef struct SemaContext_ // Reusable returns cache. Ast **returns_cache; }; + Decl *return_var; Type *rtype; struct SemaContext_ *yield_context; Decl** locals; @@ -1565,6 +1561,18 @@ typedef struct FunctionPrototype_ void *tb_prototype; } FunctionPrototype; +typedef struct +{ + Decl *ambiguous_other_decl; + Decl *private_decl; + Path *path; + SourceSpan span; + const char *symbol; + bool path_found; + bool suppress_error; +} NameResolve; + + extern GlobalContext global_context; extern BuildTarget active_target; extern Ast *poisoned_ast; @@ -1603,6 +1611,7 @@ extern const char *kw_min; extern const char *kw_elements; extern const char *kw_align; +extern const char *kw_sizeof; extern const char *kw_in; extern const char *kw_out; extern const char *kw_inout; @@ -1625,6 +1634,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_return; extern const char *kw_type; extern const char *kw_FILE; extern const char *kw_FUNC; @@ -1651,9 +1661,6 @@ extern const char *kw_argc; extern const char *kw_argv; extern const char *kw_mainstub; - - - ARENA_DEF(chars, char) ARENA_DEF(ast, Ast) ARENA_DEF(expr, Expr) @@ -1841,7 +1848,7 @@ Decl *decl_new(DeclKind decl_kind, const char *name, SourceSpan span, 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); +Decl *decl_new_generated_var(Type *type, VarDeclKind kind, SourceSpan span); void decl_set_external_name(Decl *decl); const char *decl_to_name(Decl *decl); @@ -1875,6 +1882,7 @@ void diag_verror_range(SourceSpan location, const char *message, va_list args); Expr *expr_new(ExprKind kind, SourceSpan start); bool expr_is_simple(Expr *expr); bool expr_is_pure(Expr *expr); + static inline bool expr_ok(Expr *expr) { return expr == NULL || expr->expr_kind != EXPR_POISONED; } static inline bool expr_poison(Expr *expr) { expr->expr_kind = EXPR_POISONED; expr->resolve_status = RESOLVE_DONE; return false; } static inline void expr_replace(Expr *expr, Expr *replacement) @@ -1956,11 +1964,13 @@ 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, const char *symbol, SourceSpan loc, Path *path); +Decl *unit_resolve_parameterized_symbol(CompilationUnit *unit, NameResolve *name_resolve); 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, 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); +Decl *sema_resolve_normal_symbol(SemaContext *context, NameResolve *name_resolve); +Decl *sema_find_symbol(SemaContext *context, const char *symbol); +Decl *sema_find_path_symbol(SemaContext *context, const char *symbol, Path *path); +Decl *sema_resolve_symbol(SemaContext *context, const char *symbol, Path *path, SourceSpan span); bool sema_resolve_type(SemaContext *context, Type *type); bool sema_resolve_array_like_len(SemaContext *context, TypeInfo *type_info, ArraySize *len_ref); @@ -2508,6 +2518,12 @@ static inline void ast_append(AstId **succ, Ast *next) *succ = &next->next; } +static inline Ast *ast_last(Ast *ast) +{ + while (ast->next) ast = astptr(ast->next); + return ast; +} + static inline void ast_prepend(AstId *first, Ast *ast) { Ast *end = ast; diff --git a/src/compiler/copying.c b/src/compiler/copying.c index 88d1841b2..178259319 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -136,6 +136,7 @@ Expr *copy_expr(Expr *source_expr) case EXPR_TYPEOFANY: case EXPR_PTR: case EXPR_STRINGIFY: + case EXPR_CT_EVAL: MACRO_COPY_EXPR(expr->inner_expr); return expr; case EXPR_COND: @@ -221,6 +222,29 @@ Expr *copy_expr(Expr *source_expr) UNREACHABLE } +AstDocDirective *doc_directive_copy(AstDocDirective *docs) +{ + AstDocDirective *directive_new = NULL; + VECEACH(docs, i) + { + AstDocDirective directive = docs[i]; + switch (directive.kind) + { + case DOC_DIRECTIVE_REQUIRE: + case DOC_DIRECTIVE_ENSURE: + case DOC_DIRECTIVE_CHECKED: + MACRO_COPY_EXPR(directive.contract.decl_exprs); + break; + case DOC_DIRECTIVE_PARAM: + case DOC_DIRECTIVE_ERRORS: + case DOC_DIRECTIVE_PURE: + case DOC_DIRECTIVE_UNKNOWN: + break; + } + vec_add(directive_new, directive); + } + return directive_new; +} Ast *ast_copy_deep(Ast *source) { @@ -229,28 +253,6 @@ Ast *ast_copy_deep(Ast *source) ast->next = astid_copy_deep(ast->next); switch (source->ast_kind) { - case AST_SCOPING_STMT: - MACRO_COPY_EXPR(ast->scoping_stmt.scoped); - MACRO_COPY_AST(ast->scoping_stmt.stmt); - return ast; - case AST_DOCS: - MACRO_COPY_AST_LIST(ast->directives); - return ast; - case AST_DOC_DIRECTIVE: - switch (ast->doc_directive.kind) - { - case DOC_DIRECTIVE_REQUIRE: - case DOC_DIRECTIVE_ENSURE: - case DOC_DIRECTIVE_CHECKED: - MACRO_COPY_EXPR(ast->doc_directive.contract.decl_exprs); - break; - case DOC_DIRECTIVE_PARAM: - case DOC_DIRECTIVE_ERRORS: - case DOC_DIRECTIVE_PURE: - case DOC_DIRECTIVE_UNKNOWN: - break; - } - return ast; case AST_POISONED: return ast; case AST_ASM_STMT: @@ -269,7 +271,6 @@ Ast *ast_copy_deep(Ast *source) MACRO_COPY_EXPR(ast->case_stmt.to_expr); return ast; case AST_COMPOUND_STMT: - case AST_CT_COMPOUND_STMT: ast->compound_stmt.first_stmt = astid_copy_deep(ast->compound_stmt.first_stmt); return ast; case AST_CT_ASSERT: @@ -279,16 +280,15 @@ Ast *ast_copy_deep(Ast *source) case AST_CT_IF_STMT: MACRO_COPY_EXPR(ast->ct_if_stmt.expr); MACRO_COPY_AST(ast->ct_if_stmt.elif); - MACRO_COPY_AST(ast->ct_if_stmt.then); + ast->ct_if_stmt.then = astid_copy_deep(ast->ct_if_stmt.then); return ast; case AST_CT_ELIF_STMT: MACRO_COPY_EXPR(ast->ct_elif_stmt.expr); - MACRO_COPY_AST(ast->ct_elif_stmt.then); + ast->ct_elif_stmt.then = astid_copy_deep(ast->ct_elif_stmt.then); MACRO_COPY_AST(ast->ct_elif_stmt.elif); return ast; - case AST_CT_ELSE_STMT: - MACRO_COPY_AST(ast->ct_else_stmt); + ast->ct_else_stmt = astid_copy_deep(ast->ct_else_stmt); return ast; case AST_CT_FOREACH_STMT: ast->ct_foreach_stmt.body = astid_copy_deep(ast->ct_foreach_stmt.body); @@ -346,7 +346,7 @@ Ast *ast_copy_deep(Ast *source) MACRO_COPY_EXPR(ast->return_stmt.expr); return ast; case AST_SCOPED_STMT: - MACRO_COPY_AST(ast->scoped_stmt.stmt); + ast->scoped_stmt.stmt = astid_copy_deep(ast->scoped_stmt.stmt); return ast; case AST_SWITCH_STMT: case AST_IF_CATCH_SWITCH_STMT: @@ -354,8 +354,6 @@ Ast *ast_copy_deep(Ast *source) MACRO_COPY_EXPR(ast->switch_stmt.cond); MACRO_COPY_AST_LIST(ast->switch_stmt.cases); return ast; - case AST_UNREACHABLE_STMT: - return ast; case AST_WHILE_STMT: copy_flow(ast); MACRO_COPY_EXPR(ast->while_stmt.cond); @@ -408,6 +406,7 @@ TypeInfo *copy_type_info(TypeInfo *source) case TYPE_INFO_CT_IDENTIFIER: case TYPE_INFO_IDENTIFIER: return copy; + case TYPE_INFO_EVALTYPE: case TYPE_INFO_EXPRESSION: assert(source->resolve_status == RESOLVE_NOT_DONE); copy->unresolved_type_expr = copy_expr(source->unresolved_type_expr); @@ -464,7 +463,7 @@ Decl *copy_decl(Decl *decl) { if (!decl) return NULL; Decl *copy = decl_copy(decl); - MACRO_COPY_AST(copy->docs); + copy->docs = doc_directive_copy(copy->docs); copy->attributes = copy_attributes(copy->attributes); switch (decl->decl_kind) { diff --git a/src/compiler/diagnostics.c b/src/compiler/diagnostics.c index bcdb38fff..ee49b3e81 100644 --- a/src/compiler/diagnostics.c +++ b/src/compiler/diagnostics.c @@ -85,7 +85,7 @@ static void print_error(SourceSpan location, const char *message, PrintType prin 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++) + for (unsigned i = 0; i < space_to - 1; i++) { switch (current[i]) { diff --git a/src/compiler/enums.h b/src/compiler/enums.h index b82180337..2b253de1b 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -51,7 +51,6 @@ typedef enum AST_COMPOUND_STMT, AST_CONTINUE_STMT, AST_CT_ASSERT, - AST_CT_COMPOUND_STMT, AST_CT_IF_STMT, AST_CT_ELIF_STMT, AST_CT_ELSE_STMT, @@ -62,8 +61,6 @@ typedef enum AST_DEFAULT_STMT, AST_DEFER_STMT, AST_DO_STMT, - AST_DOC_DIRECTIVE, - AST_DOCS, AST_EXPR_STMT, AST_FOR_STMT, AST_FOREACH_STMT, @@ -71,10 +68,8 @@ typedef enum AST_IF_STMT, AST_NOP_STMT, AST_RETURN_STMT, - AST_SCOPING_STMT, AST_SWITCH_STMT, AST_NEXT_STMT, - AST_UNREACHABLE_STMT, AST_WHILE_STMT, AST_SCOPED_STMT, } AstKind; @@ -157,6 +152,11 @@ typedef enum case DECL_CT_SWITCH: case DECL_CT_CASE: case DECL_ATTRIBUTE: case DECL_LABEL: \ case DECL_DEFINE: case DECL_CT_ASSERT: case DECL_GENERIC +#define NON_RUNTIME_EXPR EXPR_DESIGNATOR: case EXPR_POISONED: \ + case EXPR_TYPEINFO: case EXPR_CT_IDENT: case EXPR_HASH_IDENT: \ + case EXPR_COMPILER_CONST: case EXPR_CT_CALL: case EXPR_FLATPATH: \ + case EXPR_VARIANTSWITCH: case EXPR_STRINGIFY: case EXPR_CT_EVAL + typedef enum { DOC_DIRECTIVE_UNKNOWN, @@ -186,6 +186,7 @@ typedef enum EXPR_CONST, EXPR_CT_CALL, EXPR_CT_IDENT, + EXPR_CT_EVAL, EXPR_COND, EXPR_DECL, EXPR_DESIGNATOR, @@ -289,6 +290,7 @@ typedef enum TYPE_INFO_IDENTIFIER, TYPE_INFO_CT_IDENTIFIER, TYPE_INFO_EXPRESSION, + TYPE_INFO_EVALTYPE, TYPE_INFO_ARRAY, TYPE_INFO_VECTOR, TYPE_INFO_INFERRED_ARRAY, @@ -456,7 +458,6 @@ typedef enum TOKEN_NULL, TOKEN_PRIVATE, TOKEN_RETURN, - TOKEN_SCOPING, TOKEN_STATIC, TOKEN_STRUCT, TOKEN_SWITCH, @@ -480,6 +481,8 @@ typedef enum TOKEN_CT_FOREACH, // $foreach TOKEN_CT_ELIF, // $elif TOKEN_CT_ELSE, // $else + TOKEN_CT_EVAL, // $eval + TOKEN_CT_EVALTYPE, // $evaltype TOKEN_CT_ENDIF, // $endif TOKEN_CT_ENDSWITCH, // $endswitch TOKEN_CT_ENDFOR, // $endfor @@ -493,7 +496,6 @@ typedef enum TOKEN_CT_STRINGIFY, // $stringify TOKEN_CT_SWITCH, // $switch TOKEN_CT_TYPEOF, // $typeof - TOKEN_CT_UNREACHABLE, // $unreachable TOKEN_DOCS_START, // /** TOKEN_DOCS_END, // */ (may start with an arbitrary number of `*` @@ -747,15 +749,6 @@ typedef enum 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, diff --git a/src/compiler/hashtable.c b/src/compiler/hashtable.c deleted file mode 100644 index 3c334ffc9..000000000 --- a/src/compiler/hashtable.c +++ /dev/null @@ -1,3 +0,0 @@ -#include "../utils/lib.h" -#include "enums.h" - diff --git a/src/compiler/lexer.c b/src/compiler/lexer.c index 645023658..04dfa6ad2 100644 --- a/src/compiler/lexer.c +++ b/src/compiler/lexer.c @@ -39,13 +39,6 @@ static inline void begin_new_token(Lexer *lexer) lexer->start_row_start = lexer->line_start; } -static inline void backtrace_to_lexing_start(Lexer *lexer) -{ - lexer->current = lexer->lexing_start; - lexer->current_row = lexer->start_row; - lexer->line_start = lexer->start_row_start; -} - // Peek at the current character in the buffer. #define peek(lexer_) (*(lexer_)->current) @@ -1365,36 +1358,6 @@ static inline bool scan_base64(Lexer *lexer) // --- Lexer doc lexing -static bool end_of_docs_found(Lexer *lexer) -{ - int lookahead = 0; - // while we see '*' walk forward. - while (lexer->current[lookahead] == '*') lookahead++; - // And if it doesn't have a '/' at the last position it isn't either. - return lexer->current[lookahead] == '/'; -} -/** - * OPTIONALLY adds * / token. This allows any number of '*' to preceed it. - * @param lexer - * @return - */ -static bool parse_add_end_of_docs_if_present(Lexer *lexer) -{ - int lookahead = 0; - // while we see '*' walk forward. - while (lexer->current[lookahead] == '*') lookahead++; - // if we didn't see a '*' to begin with, then it's not an end - if (lookahead < 1) return false; - // And if it doesn't have a '/' at the last position it isn't either. - if (lexer->current[lookahead] != '/') return false; - // Otherwise, gladly skip ahead and store the end. - skip(lexer, lookahead + 1); - return_token(lexer, TOKEN_DOCS_END, lexer->lexing_start); - begin_new_token(lexer); - return true; -} - - INLINE void skip_to_doc_line_end(Lexer *lexer) { // Let's skip to either EOF, EOL or */ diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index b31b9ffb8..6faf92424 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -2170,6 +2170,7 @@ static void gencontext_emit_unary_expr(GenContext *c, BEValue *value, Expr *expr value->type = type_lowering(expr->type); return; case UNARYOP_DEREF: + REMINDER("insert a check for deref in debug mode"); llvm_emit_expr(c, value, inner); // Load the pointer value. llvm_value_rvalue(c, value); @@ -5452,18 +5453,9 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) EMIT_LOC(c, expr); switch (expr->expr_kind) { - case EXPR_DESIGNATOR: - case EXPR_POISONED: + case NON_RUNTIME_EXPR: case EXPR_COND: - case EXPR_TYPEINFO: case EXPR_MACRO_EXPANSION: - case EXPR_CT_IDENT: - case EXPR_HASH_IDENT: - case EXPR_COMPILER_CONST: - case EXPR_CT_CALL: - case EXPR_FLATPATH: - case EXPR_VARIANTSWITCH: - case EXPR_STRINGIFY: UNREACHABLE case EXPR_ARGV_TO_SUBARRAY: llvm_emit_argv_to_subarray(c, value, expr); diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index e16732a92..b0c073cf1 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -472,6 +472,8 @@ void llvm_emit_function_body(GenContext *context, Decl *decl) LLVMSetCurrentDebugLocation2(context->builder, NULL); + if (decl->func_decl.ret_var) llvm_emit_and_set_decl_alloca(context, decl->func_decl.ret_var); + AstId current = decl->func_decl.body->compound_stmt.first_stmt; while (current) { diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index ab1f8f9d1..c0cd11fc3 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -26,16 +26,6 @@ void llvm_emit_compound_stmt(GenContext *context, Ast *ast) } } -static void llvm_emit_ct_compound_stmt(GenContext *context, Ast *ast) -{ - assert(ast->ast_kind == AST_CT_COMPOUND_STMT); - AstId current = ast->compound_stmt.first_stmt; - while (current) - { - llvm_emit_stmt(context, ast_next(¤t)); - } -} - /** * This emits a local declaration. */ @@ -910,7 +900,7 @@ void gencontext_emit_next_stmt(GenContext *context, Ast *ast) void gencontext_emit_scoped_stmt(GenContext *context, Ast *ast) { - llvm_emit_stmt(context, ast->scoped_stmt.stmt); + llvm_emit_stmt(context, astptr(ast->scoped_stmt.stmt)); llvm_emit_defer(context, ast->scoped_stmt.defers.start, ast->scoped_stmt.defers.end); } @@ -957,10 +947,23 @@ static inline void llvm_emit_assume(GenContext *c, Expr *expr) } } - - static inline void llvm_emit_assert_stmt(GenContext *c, Ast *ast) { + Expr *expr = ast->assert_stmt.expr; + if (!expr) + { + File *file = source_file_by_id(ast->span.file_id); + unsigned row = ast->span.row; + llvm_emit_debug_output(c, "Unreachable statement reached.", file->name, c->cur_func_decl->external_name, row ? row : 1); + llvm_emit_call_intrinsic(c, intrinsic_id.trap, NULL, 0, NULL, 0); + LLVMBuildUnreachable(c->builder); + LLVMBasicBlockRef block = llvm_basic_block_new(c, "unreachable_block"); + c->current_block = NULL; + c->current_block_is_target = false; + llvm_emit_block(c, block); + return; + } + if (active_target.feature.safe_mode) { BEValue value; @@ -986,7 +989,6 @@ static inline void llvm_emit_assert_stmt(GenContext *c, Ast *ast) llvm_emit_call_intrinsic(c, intrinsic_id.trap, NULL, 0, NULL, 0); llvm_emit_br(c, on_ok); llvm_emit_block(c, on_ok); - return; } llvm_emit_assume(c, ast->assert_stmt.expr); } @@ -1030,18 +1032,6 @@ static inline void llvm_emit_asm_stmt(GenContext *c, Ast *ast) LLVMBuildCall2(c->builder, asm_fn_type, asm_fn, NULL, 0, ""); } -static inline void gencontext_emit_unreachable_stmt(GenContext *context, Ast *ast) -{ - 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"); - context->current_block = NULL; - context->current_block_is_target = false; - llvm_emit_block(context, block); -} void gencontext_emit_expr_stmt(GenContext *c, Ast *ast) { @@ -1239,11 +1229,8 @@ void llvm_emit_stmt(GenContext *c, Ast *ast) assert(!c->catch_block && "Did not expect a catch block here."); switch (ast->ast_kind) { - case AST_DOCS: - case AST_DOC_DIRECTIVE: case AST_POISONED: case AST_IF_CATCH_SWITCH_STMT: - case AST_SCOPING_STMT: case AST_FOREACH_STMT: case AST_WHILE_STMT: case AST_DO_STMT: @@ -1272,9 +1259,6 @@ void llvm_emit_stmt(GenContext *c, Ast *ast) case AST_COMPOUND_STMT: llvm_emit_compound_stmt(c, ast); break; - case AST_CT_COMPOUND_STMT: - llvm_emit_ct_compound_stmt(c, ast); - break; case AST_FOR_STMT: llvm_emit_for_stmt(c, ast); break; @@ -1303,9 +1287,6 @@ void llvm_emit_stmt(GenContext *c, Ast *ast) case AST_SWITCH_STMT: gencontext_emit_switch(c, ast); break; - case AST_UNREACHABLE_STMT: - gencontext_emit_unreachable_stmt(c, ast); - break; } } diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 241193bc7..ab93b31d5 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -288,7 +288,7 @@ static bool parse_param_path(ParseContext *c, DesignatorElement ***path) element->kind = DESIGNATOR_RANGE; } CONSUME_OR_RET(TOKEN_RBRACKET, false); - // Include ] in the expr + // Include right bracket in the expr vec_add(*path, element); continue; } @@ -438,7 +438,7 @@ static Expr *parse_type_identifier(ParseContext *context, Expr *left) return parse_type_expression_with_path(context, NULL); } -static Expr *parse_typeof_expr(ParseContext *c, Expr *left) +static Expr *parse_type_expr(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); Expr *expr = EXPR_NEW_TOKEN(EXPR_TYPEINFO); @@ -833,6 +833,38 @@ static Expr *parse_hash_ident(ParseContext *c, Expr *left) return expr; } +static Expr *parse_ct_eval(ParseContext *c, Expr *left) +{ + assert(!left && "Unexpected left hand side"); + Expr *expr = EXPR_NEW_TOKEN(EXPR_CT_EVAL); + advance(c); + CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); + ASSIGN_EXPR_OR_RET(expr->inner_expr, parse_expr(c), poisoned_expr); + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); + RANGE_EXTEND_PREV(expr); + return expr; +} + +static Expr *parse_ct_sizeof(ParseContext *c, Expr *left) +{ + assert(!left && "Unexpected left hand side"); + Expr *access = expr_new(EXPR_ACCESS, c->span); + advance(c); + CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); + ASSIGN_EXPR_OR_RET(Expr *inner, parse_expr(c), poisoned_expr); + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); + Expr *typeof_expr = expr_new(EXPR_TYPEINFO, inner->span); + TypeInfo *type_info = type_info_new(TYPE_INFO_EXPRESSION, inner->span); + type_info->unresolved_type_expr = inner; + typeof_expr->type_expr = type_info; + access->access_expr.parent = typeof_expr; + Expr *ident = expr_new(EXPR_IDENTIFIER, c->span); + ident->identifier_expr.ident = kw_sizeof; + access->access_expr.child = ident; + RANGE_EXTEND_PREV(access); + return access; +} + static Expr *parse_ct_call(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); @@ -840,7 +872,7 @@ static Expr *parse_ct_call(ParseContext *c, Expr *left) 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); + ASSIGN_EXPR_OR_RET(Expr* internal, parse_precedence(c, PREC_FIRST + 1), poisoned_expr); ExprFlatElement *flat_path = NULL; TokenType tok = c->tok; if (tok == TOKEN_DOT || tok == TOKEN_LBRACKET) @@ -850,31 +882,13 @@ static Expr *parse_ct_call(ParseContext *c, Expr *left) ExprFlatElement flat_element; if (try_consume(c, TOKEN_LBRACKET)) { - 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_ERROR_HERE("Expected an integer index."); - return poisoned_expr; - } - Int value = int_expr->const_expr.ixx; - if (!int_fits(value, TYPE_I64)) - { - SEMA_ERROR(int_expr, "Array index out of range."); - return poisoned_expr; - } - if (int_is_neg(value)) - { - SEMA_ERROR(int_expr, "Array index must be zero or greater."); - return poisoned_expr; - } - TRY_CONSUME_OR_RET(TOKEN_RBRACKET, "Expected a ']' after the number.", poisoned_expr); + ASSIGN_EXPR_OR_RET(flat_element.inner, parse_expr(c), poisoned_expr); + TRY_CONSUME_OR_RET(TOKEN_RBRACKET, "Expected a ']' after the index.", poisoned_expr); flat_element.array = true; - flat_element.index = (MemberIndex) value.i.low; } else if (try_consume(c, TOKEN_DOT)) { - flat_element.ident = symstr(c); - TRY_CONSUME_OR_RET(TOKEN_IDENT, "Expected an identifier here.", poisoned_expr); + ASSIGN_EXPR_OR_RET(flat_element.inner, parse_precedence(c, PREC_FIRST), poisoned_expr); flat_element.array = false; } else @@ -1678,7 +1692,6 @@ Expr *parse_type_expression_with_path(ParseContext *c, Path *path) - /** * function_block * : '{|' stmt_list '|}' @@ -1789,14 +1802,16 @@ ParseRule rules[TOKEN_EOF + 1] = { [TOKEN_HASH_IDENT] = { parse_hash_ident, NULL, PREC_NONE }, //[TOKEN_HASH_TYPE_IDENT] = { parse_type_identifier, NULL, PREC_NONE } - [TOKEN_CT_SIZEOF] = { parse_ct_call, NULL, PREC_NONE }, + [TOKEN_CT_SIZEOF] = { parse_ct_sizeof, NULL, PREC_NONE }, [TOKEN_CT_ALIGNOF] = { parse_ct_call, NULL, PREC_NONE }, [TOKEN_CT_DEFINED] = { parse_ct_call, NULL, PREC_NONE }, + [TOKEN_CT_EVAL] = { parse_ct_eval, NULL, PREC_NONE }, [TOKEN_CT_EXTNAMEOF] = { parse_ct_call, NULL, PREC_NONE }, [TOKEN_CT_OFFSETOF] = { parse_ct_call, NULL, PREC_NONE }, [TOKEN_CT_NAMEOF] = { parse_ct_call, NULL, PREC_NONE }, [TOKEN_CT_QNAMEOF] = { parse_ct_call, NULL, PREC_NONE }, - [TOKEN_CT_TYPEOF] = { parse_typeof_expr, NULL, PREC_NONE }, + [TOKEN_CT_TYPEOF] = { parse_type_expr, NULL, PREC_NONE }, [TOKEN_CT_STRINGIFY] = { parse_ct_stringify, NULL, PREC_NONE }, + [TOKEN_CT_EVALTYPE] = { parse_type_expr, NULL, PREC_NONE }, [TOKEN_LBRACE] = { parse_initializer_list, NULL, PREC_NONE }, }; diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 873a6c853..00ee02e69 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -463,6 +463,7 @@ Path *parse_path_prefix(ParseContext *c, bool *had_error) * | CT_TYPE_IDENT * | VIRTUAL (ident_scope TYPE_IDENT)? * | CT_TYPEOF '(' expr ')' + * | CT_EVALTYPE '(' expr ')' * ; * * Assume prev_token is the type. @@ -479,6 +480,15 @@ static inline TypeInfo *parse_base_type(ParseContext *c) RANGE_EXTEND_PREV(type_info); return type_info; } + if (try_consume(c, TOKEN_CT_EVALTYPE)) + { + TypeInfo *type_info = type_info_new(TYPE_INFO_EVALTYPE, 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 = c->span; bool had_error; Path *path = parse_path_prefix(c, &had_error); @@ -1770,8 +1780,9 @@ static inline bool parse_enum_spec(ParseContext *c, TypeInfo **type_ref, Decl*** while (!try_consume(c, TOKEN_RPAREN)) { if (!parse_param_decl(c, parent_visibility, parameters_ref, true)) return false; - assert(VECLAST(*parameters_ref)); - if (VECLAST(*parameters_ref)->var.vararg) + Decl *last_parameter = VECLAST(*parameters_ref); + assert(last_parameter); + if (last_parameter->var.vararg) { SEMA_ERROR_LAST("Vararg parameters are not allowed as enum parameters."); return false; @@ -1980,6 +1991,7 @@ static inline bool parse_import(ParseContext *c) } is_not_first = true; Path *path = parse_module_path(c); + if (!path) return false; unit_add_import(c->unit, path, private); if (!try_consume(c, TOKEN_COMMA)) break; } @@ -2002,17 +2014,27 @@ void parse_imports(ParseContext *c) -static inline bool parse_doc_contract(ParseContext *c, Ast ***docs_ref, DocDirectiveKind kind) +static inline bool parse_doc_contract(ParseContext *c, AstDocDirective **docs_ref, DocDirectiveKind kind) { - Ast *doc = new_ast(AST_DOC_DIRECTIVE, c->span); + AstDocDirective directive = { .span = c->span, .kind = kind }; 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); + ASSIGN_EXPR_OR_RET(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 \""); + switch (kind) + { + case DOC_DIRECTIVE_CHECKED: + scratch_buffer_append("@require \""); + break; + case DOC_DIRECTIVE_ENSURE: + scratch_buffer_append("@ensure \""); + break; + default: + scratch_buffer_append("@require \""); + break; + } scratch_buffer_append_len(start, end - start); scratch_buffer_append("\" violated"); if (tok_is(c, TOKEN_STRING)) @@ -2020,21 +2042,21 @@ static inline bool parse_doc_contract(ParseContext *c, Ast ***docs_ref, DocDirec scratch_buffer_append(": '"); scratch_buffer_append(symstr(c)); scratch_buffer_append("'."); - doc->doc_directive.contract.comment = scratch_buffer_copy(); + directive.contract.comment = scratch_buffer_copy(); advance(c); } else { scratch_buffer_append("."); - doc->doc_directive.contract.expr_string = scratch_buffer_copy(); + directive.contract.expr_string = scratch_buffer_copy(); } - vec_add(*docs_ref, doc); + vec_add(*docs_ref, directive); return true; } -static inline bool parse_doc_param(ParseContext *c, Ast ***docs_ref) +static inline bool parse_doc_param(ParseContext *c, AstDocDirective **docs_ref) { - Ast *doc = new_ast(AST_DOC_DIRECTIVE, c->span); + AstDocDirective directive = { .span = c->span, .kind = DOC_DIRECTIVE_PARAM }; advance(c); // [&inout] [in] [out] @@ -2082,18 +2104,17 @@ static inline bool parse_doc_param(ParseContext *c, Ast ***docs_ref) return false; } - 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; + directive.param.name = symstr(c); + directive.param.span = c->span; + directive.param.modifier = mod; + directive.param.by_ref = is_ref; advance(c); try_consume(c, TOKEN_STRING); - vec_add(*docs_ref, doc); + vec_add(*docs_ref, directive); return true; } -static inline bool parse_doc_errors(ParseContext *c, Ast *docs) +static inline bool parse_doc_errors(ParseContext *c, AstDocDirective **docs) { TODO while (1) @@ -2110,19 +2131,19 @@ static inline bool parse_doc_errors(ParseContext *c, Ast *docs) default: return false; } + /* docs->doc_directive.kind = DOC_DIRECTIVE_PARAM; docs->doc_directive.param.name = symstr(c); - docs->doc_directive.param.span = c->span; + docs->doc_directive.param.span = c->span;*/ advance(c); return true; } -static bool parse_docs(ParseContext *c, Ast **docs) +static bool parse_docs(ParseContext *c, AstDocDirective **docs_ref) { - *docs = NULL; + *docs_ref = NULL; if (!try_consume(c, TOKEN_DOCS_START)) return true; - Ast *ast = ast_new_curr(c, AST_DOCS); uint32_t row_last_row = c->span.row; while (1) @@ -2132,23 +2153,23 @@ static bool parse_docs(ParseContext *c, Ast **docs) switch (c->tok) { case TOKEN_DOCS_PARAM: - if (!parse_doc_param(c, &ast->directives)) return false; + if (!parse_doc_param(c, docs_ref)) 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; + if (!parse_doc_contract(c, docs_ref, DOC_DIRECTIVE_REQUIRE)) return false; break; case TOKEN_DOCS_CHECKED: - if (!parse_doc_contract(c, &ast->directives, DOC_DIRECTIVE_CHECKED)) return false; + if (!parse_doc_contract(c, docs_ref, DOC_DIRECTIVE_CHECKED)) return false; break; case TOKEN_DOCS_ENSURE: - if (!parse_doc_contract(c, &ast->directives, DOC_DIRECTIVE_ENSURE)) return false; + if (!parse_doc_contract(c, docs_ref, DOC_DIRECTIVE_ENSURE)) return false; break; case TOKEN_DOCS_OPTRETURN: - if (!parse_doc_errors(c, ast)) return false; + if (!parse_doc_errors(c, docs_ref)) return false; break; case TOKEN_DOCS_PURE: advance(c); @@ -2159,7 +2180,6 @@ static bool parse_docs(ParseContext *c, Ast **docs) // Ignore break; case TOKEN_DOCS_END: - *docs = ast; advance(c); return true; default: @@ -2196,7 +2216,7 @@ static bool parse_docs(ParseContext *c, Ast **docs) */ Decl *parse_top_level_statement(ParseContext *c) { - Ast *docs = NULL; + AstDocDirective *docs = NULL; if (!parse_docs(c, &docs)) return poisoned_decl; Visibility visibility = VISIBLE_PUBLIC; switch (c->tok) diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index dbb38d30e..8afcab972 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -6,10 +6,6 @@ #include "parser_internal.h" -Ast *parse_unreachable_stmt(ParseContext *c); - -Ast *parse_scoping_stmt(ParseContext *c); - // --- Internal functions @@ -537,19 +533,17 @@ static inline Ast *parse_var_stmt(ParseContext *c) return ast; } -static inline Ast* parse_ct_compound_stmt(ParseContext *c) +static inline bool parse_ct_compound_stmt(ParseContext *c, AstId *start) { - Ast *stmts = new_ast(AST_CT_COMPOUND_STMT, c->span); - AstId *next = &stmts->compound_stmt.first_stmt; + AstId *next = start; while (1) { 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); + ASSIGN_AST_OR_RET(Ast *stmt, parse_stmt(c), false); ast_append(&next, stmt); - RANGE_EXTEND_PREV(stmts); } - return stmts; + return true; } /** @@ -561,7 +555,7 @@ static inline Ast* parse_ct_else_stmt(ParseContext *c) 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_OR_RET(ast->ct_else_stmt, parse_ct_compound_stmt(c), poisoned_ast); + if (!parse_ct_compound_stmt(c, &ast->ct_else_stmt)) return poisoned_ast; return ast; } @@ -575,7 +569,7 @@ static inline Ast *parse_ct_elif_stmt(ParseContext *c) 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_OR_RET(ast->ct_elif_stmt.then, parse_ct_compound_stmt(c), poisoned_ast); + if (!parse_ct_compound_stmt(c, &ast->ct_elif_stmt.then)) return poisoned_ast; if (tok_is(c, TOKEN_CT_ELIF)) { @@ -599,7 +593,7 @@ static inline Ast* parse_ct_if_stmt(ParseContext *c) 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_OR_RET(ast->ct_if_stmt.then, parse_ct_compound_stmt(c), poisoned_ast); + if (!parse_ct_compound_stmt(c, &ast->ct_if_stmt.then)) return poisoned_ast; if (tok_is(c, TOKEN_CT_ELIF)) { @@ -856,9 +850,8 @@ Ast *parse_stmt(ParseContext *c) case TOKEN_HASH_IDENT: case TOKEN_IDENT: case TOKEN_CONST_IDENT: + case TOKEN_CT_EVALTYPE: return parse_decl_or_expr_stmt(c); - case TOKEN_SCOPING: - return parse_scoping_stmt(c); case TOKEN_VAR: return parse_var_stmt(c); case TOKEN_TLOCAL: // Global means declaration! @@ -921,8 +914,6 @@ Ast *parse_stmt(ParseContext *c) return parse_ct_foreach_stmt(c); case TOKEN_CT_FOR: return parse_ct_for_stmt(c); - case TOKEN_CT_UNREACHABLE: - return parse_unreachable_stmt(c); case TOKEN_STAR: case TOKEN_AMP: case TOKEN_INTEGER: @@ -953,6 +944,7 @@ Ast *parse_stmt(ParseContext *c) case TOKEN_CT_NAMEOF: case TOKEN_CT_DEFINED: case TOKEN_CT_STRINGIFY: + case TOKEN_CT_EVAL: case TOKEN_TRY: case TOKEN_CATCH: case TOKEN_BYTES: @@ -1065,25 +1057,6 @@ Ast *parse_stmt(ParseContext *c) UNREACHABLE } -Ast *parse_scoping_stmt(ParseContext *c) -{ - 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 *c) -{ - 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 *c) { switch (c->tok) diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 345da4971..483e56109 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -817,6 +817,7 @@ Expr *recursive_may_narrow_float(Expr *expr, Type *type) case EXPR_ARGV_TO_SUBARRAY: case EXPR_COMPILER_CONST: case EXPR_STRINGIFY: + case EXPR_CT_EVAL: UNREACHABLE case EXPR_POST_UNARY: return recursive_may_narrow_float(expr->unary_expr.expr, type); @@ -971,6 +972,7 @@ Expr *recursive_may_narrow_int(Expr *expr, Type *type) case EXPR_VARIANTSWITCH: case EXPR_COMPILER_CONST: case EXPR_STRINGIFY: + case EXPR_CT_EVAL: UNREACHABLE case EXPR_POST_UNARY: return recursive_may_narrow_int(expr->unary_expr.expr, type); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index badd58d97..39f6f23f0 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -1162,7 +1162,7 @@ AttributeType sema_analyse_attribute(SemaContext *context, Attr *attr, Attribute [ATTRIBUTE_EXTNAME] = (AttributeDomain)~(ATTR_CALL | ATTR_BITSTRUCT | ATTR_MACRO), [ATTRIBUTE_SECTION] = ATTR_FUNC | ATTR_CONST | ATTR_GLOBAL, [ATTRIBUTE_PACKED] = ATTR_STRUCT | ATTR_UNION, - [ATTRIBUTE_NORETURN] = ATTR_FUNC, + [ATTRIBUTE_NORETURN] = ATTR_FUNC | ATTR_MACRO, [ATTRIBUTE_ALIGN] = ATTR_FUNC | ATTR_CONST | ATTR_LOCAL | ATTR_GLOBAL | ATTR_STRUCT | ATTR_UNION | ATTR_MEMBER, [ATTRIBUTE_INLINE] = ATTR_FUNC | ATTR_CALL, [ATTRIBUTE_NOINLINE] = ATTR_FUNC | ATTR_CALL, @@ -1290,16 +1290,14 @@ AttributeType sema_analyse_attribute(SemaContext *context, Attr *attr, Attribute } -static inline bool sema_analyse_doc_header(Ast *docs, Decl **params, Decl **extra_params) +static inline bool sema_analyse_doc_header(AstDocDirective *docs, Decl **params, Decl **extra_params) { if (!docs) return true; - assert(docs->ast_kind == AST_DOCS); - Ast **doc_directives = docs->directives; - VECEACH(doc_directives, i) + VECEACH(docs, i) { - Ast *directive = doc_directives[i]; - if (directive->doc_directive.kind != DOC_DIRECTIVE_PARAM) continue; - const char *param_name = directive->doc_directive.param.name; + AstDocDirective *directive = &docs[i]; + if (directive->kind != DOC_DIRECTIVE_PARAM) continue; + const char *param_name = directive->param.name; Decl *extra_param = NULL; Decl *param = NULL; VECEACH(params, j) @@ -1312,11 +1310,11 @@ static inline bool sema_analyse_doc_header(Ast *docs, Decl **params, Decl **extr param = extra_params[j]; if (param->name == param_name) goto NEXT; } - SEMA_ERROR(&directive->doc_directive.param, "There is no parameter '%s', did you misspell it?", param_name); + SEMA_ERROR(&directive->param, "There is no parameter '%s', did you misspell it?", param_name); return false; NEXT:; bool may_be_pointer = !param->type || type_is_pointer(type_flatten(param->type)); - if (directive->doc_directive.param.by_ref) + if (directive->param.by_ref) { if (!may_be_pointer) { @@ -1325,7 +1323,7 @@ static inline bool sema_analyse_doc_header(Ast *docs, Decl **params, Decl **extr } param->var.not_null = true; } - switch (directive->doc_directive.param.modifier) + switch (directive->param.modifier) { case PARAM_ANY: goto ADDED; @@ -1424,8 +1422,8 @@ static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl) function->name = kw_mainstub; function->extname = kw_main; function->func_decl.function_signature.returntype = type_info_new_base(type_cint, decl->span); - Decl *param1 = decl_new_generated_var(kw_argv, type_cint, VARDECL_PARAM, decl->span); - Decl *param2 = decl_new_generated_var(kw_argc, type_get_ptr(type_get_ptr(type_char)), VARDECL_PARAM, decl->span); + Decl *param1 = decl_new_generated_var(type_cint, VARDECL_PARAM, decl->span); + Decl *param2 = decl_new_generated_var(type_get_ptr(type_get_ptr(type_char)), VARDECL_PARAM, decl->span); Decl **main_params = NULL; vec_add(main_params, param1); vec_add(main_params, param2); @@ -1568,8 +1566,8 @@ static inline bool sema_analyse_func(SemaContext *context, Decl *decl) call_abi = CALL_X86_REG; } break; - case ATTRIBUTE_INLINE: SET_ATTR(attr_inline); case ATTRIBUTE_NORETURN: SET_ATTR(attr_noreturn); + case ATTRIBUTE_INLINE: SET_ATTR(attr_inline); case ATTRIBUTE_WEAK: SET_ATTR(attr_weak); case ATTRIBUTE_NAKED: SET_ATTR(attr_naked); case ATTRIBUTE_AUTOIMPORT: @@ -1672,6 +1670,10 @@ static inline bool sema_analyse_macro(SemaContext *context, Decl *decl) case ATTRIBUTE_AUTOIMPORT: decl->is_autoimport = true; break; + case ATTRIBUTE_NORETURN: + had = decl->macro_decl.attr_noreturn; + decl->macro_decl.attr_noreturn = true; + break; default: UNREACHABLE } @@ -2114,7 +2116,12 @@ static bool sema_analyse_parameterized_define(SemaContext *c, Decl *decl) default: UNREACHABLE } - Decl *alias = unit_resolve_parameterized_symbol(c->unit, name, span, decl_path); + NameResolve name_resolve = { + .path = decl_path, + .span = span, + .symbol = name + }; + Decl *alias = unit_resolve_parameterized_symbol(c->unit, &name_resolve); if (!decl_ok(alias)) { return decl_poison(decl); @@ -2184,11 +2191,7 @@ static inline bool sema_analyse_define(SemaContext *c, Decl *decl) // 1. The plain define if (decl->define_decl.define_kind == DEFINE_IDENT_ALIAS) { - Decl *symbol = sema_resolve_normal_symbol(c, - decl->define_decl.ident, - decl->define_decl.span, - decl->define_decl.path, - true); + Decl *symbol = sema_resolve_symbol(c, decl->define_decl.ident, decl->define_decl.path, decl->define_decl.span); if (!decl_ok(symbol)) return false; decl->type = symbol->type; decl->define_decl.alias = symbol; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index a88934368..5f03a66c6 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -9,6 +9,7 @@ * - Disallow jumping in and out of an expression block. */ +static inline bool sema_expr_analyse_ct_eval(SemaContext *context, Expr *expr); static bool sema_take_addr_of(Expr *inner); static inline bool sema_expr_analyse_binary(SemaContext *context, Expr *expr); static inline bool sema_cast_rvalue(SemaContext *context, Expr *expr); @@ -56,6 +57,67 @@ int BINOP_PREC_REQ[BINARYOP_LAST + 1] = }; +const char *ct_eval_error = "EVAL_ERROR"; + +const char *ct_eval_expr(SemaContext *c, const char *expr_type, Expr *inner, TokenType *type, Path **path_ref, bool report_missing) +{ + if (!sema_analyse_expr(c, inner)) return false; + if (!expr_is_const_string(inner)) + { + SEMA_ERROR(inner, "'%s' expects a constant string as the argument.", expr_type); + return ct_eval_error; + } + const char *string = inner->const_expr.string.chars; + ArraySize len = inner->const_expr.string.len; + + ArraySize path_end = 0; + for (ArraySize i = 0; i < len; i++) + { + char ch = string[i]; + if (!is_alphanum_(ch)) + { + if (ch == ':' && i > 0 && string[i + 1] == ':') + { + path_end = i; + i++; + } + else + { + SEMA_ERROR(inner, "A valid name was expected here."); + return ct_eval_error; + } + } + } + if (path_end > 0) + { + *path_ref = path_create_from_string(string, path_end, inner->span); + string += path_end + 2; + len -= path_end + 2; + } + if (len == 0) + { + SEMA_ERROR(inner, "A valid name was expected here."); + return ct_eval_error; + } + const char *interned_version = symtab_find(string, len, fnv1a(string, len), type); + if (!interned_version) + { + if (report_missing) + { + if (*path_ref) + { + SEMA_ERROR(inner, "'%s::%.*s' could not be found, did you spell it right?", (*path_ref)->module, (int)len, string); + } + else + { + SEMA_ERROR(inner, "'%.*s' could not be found, did you spell it right?", (int)len, string); + } + return ct_eval_error; + } + return NULL; + } + return interned_version; +} static Expr *expr_access_inline_member(Expr *parent, Decl *parent_decl) { @@ -285,6 +347,7 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) switch (expr->expr_kind) { case EXPR_BUILTIN: + case EXPR_CT_EVAL: return false; case EXPR_BITACCESS: case EXPR_ACCESS: @@ -837,31 +900,25 @@ static inline bool sema_expr_analyse_identifier(SemaContext *context, Type *to, Decl *private_symbol = NULL; DEBUG_LOG("Now resolving %s", expr->identifier_expr.ident); - Decl *decl = sema_resolve_normal_symbol(context, - expr->identifier_expr.ident, - expr->span, - expr->identifier_expr.path, - false); + Decl *decl = sema_find_path_symbol(context, expr->identifier_expr.ident, expr->identifier_expr.path); + // Is this a broken decl? if (!decl_ok(decl)) return false; + + // Just no real way to find it, try inference if (!decl && !expr->identifier_expr.path && to) { if (find_possible_inferred_identifier(to, expr)) return true; } + + // Rerun if we can't do inference. if (!decl) { - decl = sema_resolve_normal_symbol(context, - expr->identifier_expr.ident, - expr->span, - expr->identifier_expr.path, - true); + decl = sema_resolve_symbol(context, expr->identifier_expr.ident, expr->identifier_expr.path, expr->span); (void)decl; assert(!decl_ok(decl)); return false; } - // Already handled - if (!decl_ok(decl)) return false; - if (decl->decl_kind == DECL_FUNC || decl->decl_kind == DECL_MACRO || decl->decl_kind == DECL_GENERIC) { if (decl->module != context->unit->module && !decl->is_autoimport && !expr->identifier_expr.path) @@ -967,10 +1024,7 @@ 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", expr->ct_ident_expr.identifier); - Decl *decl = sema_resolve_normal_symbol(context, - expr->ct_ident_expr.identifier, - expr->span, - NULL, true); + Decl *decl = sema_resolve_symbol(context, expr->ct_ident_expr.identifier, NULL, expr->span); // Already handled if (!decl_ok(decl)) @@ -990,10 +1044,7 @@ 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", expr->hash_ident_expr.identifier); - Decl *decl = sema_resolve_normal_symbol(context, - expr->hash_ident_expr.identifier, - expr->span, - NULL, true); + Decl *decl = sema_resolve_symbol(context, expr->hash_ident_expr.identifier, NULL, expr->span); // Already handled if (!decl_ok(decl)) @@ -1697,7 +1748,6 @@ static bool sema_check_stmt_compile_time(SemaContext *context, Ast *ast) return expr_is_constant_eval(ast->return_stmt.expr, CONSTANT_EVAL_ANY); case AST_EXPR_STMT: return sema_check_expr_compile_time(context, ast->expr_stmt); - case AST_CT_COMPOUND_STMT: case AST_COMPOUND_STMT: { AstId current = ast->compound_stmt.first_stmt; @@ -1726,7 +1776,6 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s }; if (!sema_expr_analyse_call_invocation(context, call_expr, callee, &failable)) return false; - Decl **func_params = decl->macro_decl.parameters; Expr **args = call_expr->call_expr.arguments; VECEACH(params, i) { @@ -1820,6 +1869,8 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s if (!sema_analyse_statement(¯o_context, ast_next(¤t))) goto EXIT_FAIL; } + bool is_no_return = decl->macro_decl.attr_noreturn; + if (no_scope) { call_expr->type = type_void; @@ -1836,6 +1887,11 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s return SCOPE_POP_ERROR(); } } + else if (is_no_return) + { + SEMA_ERROR(context->returns[0], "Return used despite macro being marked '@noreturn'."); + return SCOPE_POP_ERROR(); + } if (rtype) { @@ -1899,6 +1955,7 @@ EXIT: assert(context->active_scope.defer_last == context->active_scope.defer_start); context->active_scope = old_scope; } + if (is_no_return) context->active_scope.jump_end = true; return true; EXIT_FAIL: return SCOPE_POP_ERROR(); @@ -2860,9 +2917,11 @@ static Expr *enum_minmax_value(Decl *decl, BinaryOp comparison) * 3. .@foo -> It is a macro. * 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 + * 6. .$eval(...) -> resolve the eval and retry. */ -static Expr *sema_expr_resolve_access_child(SemaContext *context, Expr *child) +static Expr *sema_expr_resolve_access_child(SemaContext *context, Expr *child, bool *missing) { +RETRY: switch (child->expr_kind) { case EXPR_IDENTIFIER: @@ -2875,7 +2934,7 @@ static Expr *sema_expr_resolve_access_child(SemaContext *context, Expr *child) { case EXPR_IDENTIFIER: case EXPR_HASH_IDENT: - return sema_expr_resolve_access_child(context, child); + return sema_expr_resolve_access_child(context, child, missing); default: SEMA_ERROR(child, "Expected a macro name."); return NULL; @@ -2883,12 +2942,36 @@ static Expr *sema_expr_resolve_access_child(SemaContext *context, Expr *child) break; case EXPR_HASH_IDENT: { - Decl *decl = sema_resolve_normal_symbol(context, - child->hash_ident_expr.identifier, - child->span, - NULL, true); + Decl *decl = sema_resolve_symbol(context, child->hash_ident_expr.identifier, NULL, child->span); if (!decl_ok(decl)) return NULL; - return sema_expr_resolve_access_child(context, decl->var.init_expr); + return sema_expr_resolve_access_child(context, decl->var.init_expr, missing); + } + case EXPR_CT_EVAL: + { + Expr *inner = child->inner_expr; + TokenType type; + // Only report missing if missing var is NULL + Path *path = NULL; + const char *ident = ct_eval_expr(context, "$eval", inner, &type, &path, missing == NULL); + if (!ident && missing) + { + *missing = true; + return NULL; + } + if (ident == ct_eval_error) return NULL; + switch (type) + { + case TOKEN_IDENT: + case TOKEN_CONST_IDENT: + child->expr_kind = EXPR_IDENTIFIER; + child->identifier_expr.ident = ident; + child->identifier_expr.path = path; + child->identifier_expr.is_const = type == TOKEN_CONST_IDENT; + goto RETRY; + default: + SEMA_ERROR(inner, "Only function, variable and constant names may be resolved with $eval."); + return NULL; + } } default: break; @@ -2926,17 +3009,17 @@ static inline void expr_replace_with_enum_array(Expr *enum_array_expr, Decl *enu 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)) - { - SEMA_ERROR(expr->access_expr.parent, "Array and pointer types must be enclosed in (), did you forget it?"); - return false; - } Type *canonical = parent->type->canonical; const char *name = identifier->identifier_expr.ident; bool is_const = identifier->identifier_expr.is_const; + if (name == kw_sizeof) + { + expr_rewrite_to_int_const(expr, type_usize, type_size(canonical), true); + return true; + } + // 3. Handle float.nan, double.inf etc if (!is_const && type_is_float(canonical)) { @@ -3079,11 +3162,6 @@ static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *exp return false; } - if (member->decl_kind == DECL_VAR) - { - SEMA_ERROR(expr, "You cannot use '.' on normal members, only nested structs and unions."); - return false; - } // 12b. If the member was *not* a macro but was prefixed with `@` then that's an error. if (member->decl_kind != DECL_MACRO && is_macro) @@ -3092,6 +3170,15 @@ static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *exp return false; } + if (member->decl_kind == DECL_VAR) + { + expr->expr_kind = EXPR_TYPEINFO; + expr->type_expr->type = member->type; + expr->type_expr->resolve_status = RESOLVE_DONE; + expr->type = type_typeinfo; + return true; + } + if (member->decl_kind == DECL_UNION || member->decl_kind == DECL_STRUCT || member->decl_kind == DECL_BITSTRUCT) { expr->expr_kind = EXPR_TYPEINFO; @@ -3155,7 +3242,7 @@ static inline bool sema_expr_analyse_access(SemaContext *context, Expr *expr) // 3. Find the actual token. SourceSpan span; - Expr *identifier = sema_expr_resolve_access_child(context, child); + Expr *identifier = sema_expr_resolve_access_child(context, child, NULL); if (!identifier) return false; // 2. If our left-hand side is a type, e.g. MyInt.abc, handle this here. @@ -3790,7 +3877,6 @@ static bool sema_expr_analyse_designated_initializer(SemaContext *context, Type if (!result) return false; Expr *value = expr->designator_expr.value; if (!sema_analyse_expr_rhs(context, result, value, true)) return false; - DesignatorElement *element = VECLAST(expr->designator_expr.path); if (member && member->decl_kind == DECL_VAR && member->var.kind == VARDECL_BITMEMBER) { if (!sema_bit_assignment_check(value, member)) return false; @@ -4279,13 +4365,7 @@ static inline bool sema_expr_analyse_ct_identifier_lvalue(SemaContext *context, Decl *ambiguous_decl = NULL; Decl *private_symbol = NULL; 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. - if (!decl_ok(decl)) return false; + Decl *decl = sema_find_symbol(context, expr->ct_ident_expr.identifier); if (!decl) { @@ -4334,7 +4414,7 @@ static bool sema_expr_analyse_ct_type_identifier_assign(SemaContext *context, Ex return false; } - Decl *decl = sema_resolve_normal_symbol(context, info->unresolved.name, info->unresolved.span, NULL, false); + Decl *decl = sema_find_symbol(context, info->unresolved.name); if (!decl) { @@ -6062,7 +6142,6 @@ static inline bool sema_expr_analyse_expr_block(SemaContext *context, Type *infe bool success = true; expr->type = type_void; bool saved_expr_failable_return = context->expr_failable_return; - Type *prev_expected_block_type = context->expected_block_type; Ast **saved_returns = context_push_returns(context); Type *stored_block_type = context->expected_block_type; context->expected_block_type = infer_type; @@ -6199,268 +6278,7 @@ static inline bool sema_expr_analyse_compiler_const(SemaContext *context, Expr * return true; } -typedef struct MiniLexer_ -{ - const char *chars; - uint32_t index; -} MiniLexer; -static inline char minilex_next(MiniLexer *lexer) -{ - return lexer->chars[lexer->index++]; -} - -static inline char minilex_peek(MiniLexer *lexer, int32_t offset) -{ - return lexer->chars[lexer->index + (uint32_t)offset]; -} - -static inline bool minilex_match(MiniLexer *lexer, char c) -{ - if (lexer->chars[lexer->index] != c) return false; - lexer->index++; - return true; -} - -static inline void minilex_skip_whitespace(MiniLexer *lexer) -{ - char c; - while (1) - { - c = lexer->chars[lexer->index]; - if ((c != ' ') && (c != '\t')) break; - lexer->index++; - } -} - -uint64_t minilex_parse_number(MiniLexer *lexer, uint64_t max) -{ - assert(max < UINT64_MAX); - uint64_t value = 0; - while (is_number(minilex_peek(lexer, 0))) - { - uint64_t old_value = value; - value = value * 10 + (uint64_t)minilex_next(lexer) - '0'; - if (old_value > value || value > max) - { - return UINT64_MAX; - } - } - return value; -} - -static inline bool sema_analyse_idents_string(SemaContext *context, MiniLexer *lexer, ExprFlatElement **elements_ref) -{ - ExprFlatElement *elements = NULL; - - char c; - ExprFlatElement element; - while (1) - { - minilex_skip_whitespace(lexer); - if (minilex_match(lexer, '\0')) break; - if (minilex_match(lexer, '[')) - { - minilex_skip_whitespace(lexer); - if (!is_number(minilex_peek(lexer, 0))) return false; - uint64_t value = minilex_parse_number(lexer, MAX_ARRAYINDEX); - if (value == UINT64_MAX) return false; - if (!minilex_match(lexer, ']')) return false; - element.array = true; - element.index = (MemberIndex)value; - vec_add(elements, element); - continue; - } - if (minilex_match(lexer, '.')) - { - scratch_buffer_clear(); - while (is_alphanum_(minilex_peek(lexer, 0))) - { - scratch_buffer_append_char(minilex_next(lexer)); - } - if (!global_context.scratch_buffer_len) return false; - TokenType token_type; - const char *ident = symtab_find(global_context.scratch_buffer, - global_context.scratch_buffer_len, - fnv1a(global_context.scratch_buffer, global_context.scratch_buffer_len), - &token_type); - if (!ident || token_type != TOKEN_IDENT) return false; - element.array = false; - element.ident = ident; - vec_add(elements, element); - continue; - } - return false; - } - *elements_ref = elements; - return true; -} - -static inline bool sema_analyse_identifier_path_string(SemaContext *context, SourceSpan span, Expr *expr, Decl **decl_ref, Type **type_ref, ExprFlatElement **idents_ref, bool report_missing) -{ - const char *chars = expr->const_expr.string.chars; - uint32_t len = expr->const_expr.string.len; - if (!len) - { - sema_error_at(span, "Expected a name here."); - return false; - } - - MiniLexer lexer = { .chars = chars }; - - // Do we parse a path? - Path *path = NULL; - - // Skip past the start. - while (is_lower_(minilex_peek(&lexer, 0))) minilex_next(&lexer); - - minilex_skip_whitespace(&lexer); - - // Yes, parse a path. - if (minilex_match(&lexer, ':')) - { - if (!minilex_match(&lexer, ':')) goto FAIL_PARSE; - lexer.index = 0; - NEXT_PART: - scratch_buffer_clear(); - minilex_skip_whitespace(&lexer); - while (is_lower(minilex_peek(&lexer, 0))) - { - scratch_buffer_append_char(minilex_next(&lexer)); - } - minilex_skip_whitespace(&lexer); - - if (!(minilex_match(&lexer, ':') && minilex_match(&lexer, ':'))) - { - UNREACHABLE; - } - uint32_t start_index = lexer.index; - - minilex_skip_whitespace(&lexer); - - while (is_lower_(minilex_peek(&lexer, 0))) minilex_next(&lexer); - - minilex_skip_whitespace(&lexer); - if (minilex_match(&lexer, ':')) - { - if (!minilex_match(&lexer, ':')) goto FAIL_PARSE; - scratch_buffer_append_len("::", 2); - lexer.index = start_index; - goto NEXT_PART; - } - lexer.index = start_index; - path = path_create_from_string(scratch_buffer_to_string(), global_context.scratch_buffer_len, expr->span); - if (!path) return false; - } - else - { - lexer.index = 0; - } - - scratch_buffer_clear(); - minilex_skip_whitespace(&lexer); - while (is_alphanum_(minilex_peek(&lexer, 0))) - { - scratch_buffer_append_char(minilex_next(&lexer)); - } - if (!global_context.scratch_buffer_len) - { - 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; - const char *symbol = symtab_find(global_context.scratch_buffer, - global_context.scratch_buffer_len, - fnv1a(global_context.scratch_buffer, global_context.scratch_buffer_len), - &token_type); - if (!symbol) - { - if (report_missing) - { - sema_error_at(span, "'%s' could not be found, did you misspell it?", chars); - return false; - } - return true; - } - Type *type = NULL; - Decl *decl = NULL; - if (token_is_type(token_type)) - { - type = type_from_token(token_type); - } - else - { - 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) - { - type = decl->typedef_decl.type_info->type; - decl = NULL; - } - } - if (type || decl_is_user_defined_type(decl)) - { - if (decl) - { - type = decl->type; - decl = NULL; - } - while (1) - { - minilex_skip_whitespace(&lexer); - if (minilex_match(&lexer, '*')) - { - type = type_get_ptr(type); - continue; - } - if (!minilex_match(&lexer, '[')) break; - minilex_skip_whitespace(&lexer); - if (minilex_match(&lexer, ']')) - { - type = type_get_subarray(type); - continue; - } - ArraySize array_size = 0; - while (is_number(minilex_peek(&lexer, 0))) - { - ByteSize old_array_size = array_size; - array_size = array_size * 10 + (ArraySize)minilex_next(&lexer) - '0'; - if (old_array_size > array_size || array_size > MAX_ARRAYINDEX) - { - sema_error_at(span, "Array index out of bounds."); - return false; - } - } - minilex_skip_whitespace(&lexer); - if (!minilex_match(&lexer, ']')) goto FAIL_PARSE; - type = type_get_array(type, array_size); - } - } - - if (!sema_analyse_idents_string(context, &lexer, idents_ref)) - { - if (report_missing) - { - sema_error_at(span, "The path to an existing member was expected after '%s', did you make a mistake?", symbol); - return false; - } - else - { - return true; - } - } - - *decl_ref = decl; - *type_ref = type; - return true; - - -FAIL_PARSE: - SEMA_ERROR(expr, "'%s' could not be parsed as an identifier, did you make a mistake?", chars); - return false; - -} static inline bool decl_is_local(Decl *decl) @@ -6499,13 +6317,6 @@ RETRY: case EXPR_TYPEINFO: type = current->type_expr->type; break; - case EXPR_CONST: - if (expr->const_expr.const_kind == CONST_STRING) - { - if (!sema_analyse_identifier_path_string(context, expr->span, current, &decl, &type, elements, true)) return false; - break; - } - FALLTHROUGH; default: SEMA_ERROR(expr, "A type or a variable was expected here."); return false; @@ -6520,6 +6331,107 @@ RETRY: return true; } +static inline bool sema_expr_analyse_flat_element(SemaContext *context, ExprFlatElement *element, Type *type, Decl **member_ref, ArraySize *index_ref, Type **return_type, unsigned i, SourceSpan loc, + bool *is_missing) +{ + Expr *inner = element->inner; + Type *actual_type = type_flatten_distinct(type); + if (element->array) + { + if (!type_is_arraylike(actual_type)) + { + if (is_missing) + { + *is_missing = true; + return false; + } + SEMA_ERROR(inner, "It's not possible to index into something that is not an array nor vector."); + return false; + } + if (!sema_analyse_expr(context, inner)) return false; + if (!type_is_integer(inner->type)) + { + SEMA_ERROR(inner, "Expected an integer index."); + return false; + } + if (!expr_is_const(inner)) + { + SEMA_ERROR(inner, "Expected a constant index."); + return false; + } + Int value = inner->const_expr.ixx; + if (!int_fits(value, type_isize->canonical->type_kind)) + { + SEMA_ERROR(inner, "The index is out of range for a %s.", type_quoted_error_string(type_isize)); + return false; + } + if (int_is_neg(value)) + { + SEMA_ERROR(inner, "The index must be zero or greater."); + return false; + } + type = actual_type->array.base; + ArraySize len = actual_type->array.len; + int64_t index = int_to_i64(value); + if (len && index >= len) + { + if (is_missing) + { + *is_missing = true; + return false; + } + SEMA_ERROR(element->inner, "Index exceeds array bounds."); + return false; + } + *return_type = type; + *index_ref = index; + *member_ref = NULL; + return true; + } + inner = sema_expr_resolve_access_child(context, inner, is_missing); + if (!inner) return false; + if (inner->expr_kind != EXPR_IDENTIFIER) + { + SEMA_ERROR(inner, "Expected an identifier here."); + return false; + } + if (!type_is_structlike(actual_type)) + { + if (is_missing) + { + *is_missing = true; + return false; + } + if (i == 0) + { + sema_error_at(loc, "%s has no members.", type_quoted_error_string(type)); + } + else + { + sema_error_at(loc, "There is no such member in %s.", type_quoted_error_string(type)); + } + return false; + } + Decl *member; + SCOPE_START + add_members_to_context(context, actual_type->decl); + member = sema_resolve_symbol_in_current_dynamic_scope(context, element->inner->identifier_expr.ident); + SCOPE_END; + if (!member) + { + if (is_missing) + { + *is_missing = true; + return false; + } + sema_error_at(loc, "There is no such member in %s.", type_quoted_error_string(type)); + return false; + } + *member_ref = member; + *return_type = member->type; + return true; +} + static inline bool sema_expr_analyse_ct_alignof(SemaContext *context, Expr *expr) { Expr *main_var = expr->ct_call_expr.main_var; @@ -6532,44 +6444,21 @@ static inline bool sema_expr_analyse_ct_alignof(SemaContext *context, Expr *expr AlignSize align = decl && !decl_is_user_defined_type(decl) ? decl->alignment : type_abi_alignment(type); VECEACH(path, i) { - ExprFlatElement element = path[i]; - Type *actual_type = type_flatten_distinct(type); - if (element.array) - { - if (actual_type->type_kind != TYPE_ARRAY) - { - SEMA_ERROR(expr, "It's possible to index into a non fixed size array."); - return false; - } - type = actual_type->array.base; - TypeSize size = type_size(type); - align = type_min_alignment(size * (AlignSize)element.index, align); - continue; - } - if (!type_is_structlike(actual_type)) - { - if (i == 0) - { - SEMA_ERROR(main_var, "%s has no members.", type_quoted_error_string(type)); - } - else - { - SEMA_ERROR(expr, "There is no such member in %s.", type_quoted_error_string(type)); - } - return false; - } + ExprFlatElement *element = &path[i]; Decl *member; - SCOPE_START - add_members_to_context(context, actual_type->decl); - member = sema_resolve_symbol_in_current_dynamic_scope(context, element.ident); - SCOPE_END; - if (!member) + ArraySize index; + Type *result_type; + if (!sema_expr_analyse_flat_element(context, element, type, &member, &index, &result_type, i, i == 0 ? main_var->span : expr->span, NULL)) return false; + if (member) { - SEMA_ERROR(expr, "There is no such member in %s.", type_quoted_error_string(type)); - return false; + align = type_min_alignment(member->offset, align); } - type = member->type; - align = type_min_alignment(member->offset, align); + else + { + TypeSize size = type_size(result_type); + align = type_min_alignment(size * index, align); + } + type = result_type; } expr_rewrite_to_int_const(expr, type_isize, align, true); @@ -6589,41 +6478,12 @@ static inline bool sema_expr_analyse_ct_sizeof(SemaContext *context, Expr *expr) VECEACH(path, i) { - ExprFlatElement element = path[i]; - Type *actual_type = type_flatten_distinct(type); - if (element.array) - { - if (actual_type->type_kind != TYPE_ARRAY) - { - SEMA_ERROR(expr, "It's possible to index into a non fixed size array."); - return false; - } - type = actual_type->array.base; - continue; - } - if (!type_is_structlike(actual_type)) - { - if (i == 0) - { - SEMA_ERROR(main_var, "%s has no members.", type_quoted_error_string(type)); - } - else - { - SEMA_ERROR(expr, "There is no such member in %s.", type_quoted_error_string(type)); - } - return false; - } + ExprFlatElement *element = &path[i]; Decl *member; - SCOPE_START - add_members_to_context(context, actual_type->decl); - member = sema_resolve_symbol_in_current_dynamic_scope(context, element.ident); - SCOPE_END; - if (!member) - { - SEMA_ERROR(expr, "There is no such member in %s.", type_quoted_error_string(type)); - return false; - } - type = member->type; + ArraySize index; + Type *result_type; + if (!sema_expr_analyse_flat_element(context, element, type, &member, &index, &result_type, i, i == 0 ? main_var->span : expr->span, NULL)) return false; + type = result_type; } expr_rewrite_to_int_const(expr, type_isize, type_size(type), true); @@ -6705,6 +6565,7 @@ static Type *sema_expr_check_type_exists(SemaContext *context, TypeInfo *type_in { return type_info->type; } +RETRY: switch (type_info->kind) { case TYPE_INFO_POISON: @@ -6737,15 +6598,37 @@ static Type *sema_expr_check_type_exists(SemaContext *context, TypeInfo *type_in case TYPE_INFO_CT_IDENTIFIER: case TYPE_INFO_IDENTIFIER: { - Decl *decl = sema_resolve_normal_symbol(context, type_info->unresolved.name, type_info->unresolved.span, type_info->unresolved.path, false); + Decl *decl = sema_find_path_symbol(context, type_info->unresolved.name, type_info->unresolved.path); if (!decl) return NULL; if (!decl_ok(decl)) return poisoned_type; return decl->type->canonical; } - break; case TYPE_INFO_EXPRESSION: if (!sema_resolve_type_info(context, type_info)) return poisoned_type; return type_info->type; + case TYPE_INFO_EVALTYPE: + { + Expr *expr = type_info->unresolved_type_expr; + TokenType type; + Path *path = NULL; + const char *ident = ct_eval_expr(context, "$eval", expr, &type, &path, false); + if (!ident) return NULL; + if (ident == ct_eval_error) return poisoned_type; + switch (type) + { + case TOKEN_TYPE_IDENT: + type_info->unresolved.name = ident; + type_info->unresolved.span = expr->span; + type_info->unresolved.path = path; + type_info->kind = TYPE_INFO_IDENTIFIER; + goto RETRY; + case TYPE_TOKENS: + return type_info->type = type_from_token(type); + default: + SEMA_ERROR(expr, "Only type names may be resolved with $evaltype."); + return poisoned_type; + } + } case TYPE_INFO_SUBARRAY: { // If it's an array, make sure we can resolve the length @@ -6780,12 +6663,13 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr Expr *main_var = expr->ct_call_expr.main_var; Type *type = NULL; Decl *decl = NULL; - ExprFlatElement *path = expr->ct_call_expr.flat_path; + ExprFlatElement *flat_path = expr->ct_call_expr.flat_path; +RETRY: switch (main_var->expr_kind) { case EXPR_IDENTIFIER: // 2. An identifier does a lookup - decl = sema_resolve_normal_symbol(context, main_var->identifier_expr.ident, main_var->span, main_var->identifier_expr.path, false); + decl = sema_find_path_symbol(context, main_var->identifier_expr.ident, main_var->identifier_expr.path); // 2a. If it failed, then error if (!decl_ok(decl)) return false; // 2b. If it's missing, goto not defined @@ -6802,53 +6686,47 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr case EXPR_BUILTIN: if (!sema_expr_analyse_builtin(context, main_var, false)) goto NOT_DEFINED; break; + case EXPR_CT_EVAL: + { + Expr *inner = main_var->inner_expr; + TokenType token_type; + Path *path = NULL; + const char *ident = ct_eval_expr(context, "$eval", inner, &token_type, &path, false); + if (ident == ct_eval_error) return false; + if (!ident) goto NOT_DEFINED; + switch (token_type) + { + case TOKEN_IDENT: + case TOKEN_CONST_IDENT: + main_var->expr_kind = EXPR_IDENTIFIER; + main_var->resolve_status = RESOLVE_NOT_DONE; + main_var->identifier_expr.ident = ident; + main_var->identifier_expr.path = path; + main_var->identifier_expr.is_const = token_type == TOKEN_CONST_IDENT; + goto RETRY; + default: + SEMA_ERROR(inner, "Only function, variable and constant names may be resolved with $eval."); + return false; + } + } default: - if (!sema_analyse_expr_lvalue(context, main_var)) return false; - if (main_var->expr_kind == EXPR_TYPEINFO) - { - type = expr->type_expr->type; - break; - } - if (main_var->expr_kind != EXPR_CONST || main_var->const_expr.const_kind != CONST_STRING) - { - SEMA_ERROR(main_var, "A constant string containing an identifier or type was expected here."); - return false; - } - if (!sema_analyse_identifier_path_string(context, - expr->span, - main_var, - &decl, - &type, - &path, - true)) - { - return false; - } + SEMA_ERROR(main_var, "Expected an identifier here."); break; } - VECEACH(path, i) + VECEACH(flat_path, i) { - ExprFlatElement element = path[i]; - Type *actual_type = type_flatten_distinct(type); - if (element.array) + ExprFlatElement *element = &flat_path[i]; + Decl *member = NULL; + ArraySize index; + Type *ret_type; + bool missing = false; + if (!sema_expr_analyse_flat_element(context, element, type, &member, &index, &ret_type, i, i == 0 ? main_var->span : expr->span, &missing)) { - if (actual_type->type_kind != TYPE_ARRAY) - { - SEMA_ERROR(expr, "It's possible to index into a non fixed size array."); - return false; - } - type = actual_type->array.base; - continue; + if (missing) goto NOT_DEFINED; + return false; } - if (!type_is_structlike(actual_type)) goto NOT_DEFINED; - Decl *member; - SCOPE_START - add_members_to_context(context, actual_type->decl); - member = sema_resolve_symbol_in_current_dynamic_scope(context, element.ident); - SCOPE_END; - if (!member) goto NOT_DEFINED; - type = member->type; + type = ret_type; } expr->type = type_bool; @@ -6870,7 +6748,7 @@ static inline bool sema_expr_analyse_ct_stringify(SemaContext *context, Expr *ex Expr *inner = expr->inner_expr; // 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); + Decl *decl = sema_resolve_symbol(context, inner->ct_ident_expr.identifier, NULL, inner->span); if (!decl_ok(decl)) return false; const char *desc = span_to_string(decl->var.hash_var.span); if (!desc) @@ -6882,6 +6760,29 @@ static inline bool sema_expr_analyse_ct_stringify(SemaContext *context, Expr *ex return true; } +static inline bool sema_expr_analyse_ct_eval(SemaContext *context, Expr *expr) +{ + Expr *inner = expr->inner_expr; + TokenType type; + Path *path = NULL; + const char *ident = ct_eval_expr(context, "$eval", inner, &type, &path, true); + if (ident == ct_eval_error) return false; + switch (type) + { + case TOKEN_IDENT: + case TOKEN_CONST_IDENT: + expr->expr_kind = EXPR_IDENTIFIER; + expr->resolve_status = RESOLVE_NOT_DONE; + expr->identifier_expr.ident = ident; + expr->identifier_expr.path = path; + expr->identifier_expr.is_const = type == TOKEN_CONST_IDENT; + return sema_analyse_expr(context, expr); + default: + SEMA_ERROR(inner, "Only function, variable and constant names may be resolved with $eval."); + return false; + } +} + static inline bool sema_expr_analyse_ct_offsetof(SemaContext *context, Expr *expr) { @@ -6899,43 +6800,20 @@ static inline bool sema_expr_analyse_ct_offsetof(SemaContext *context, Expr *exp ByteSize offset = 0; VECEACH(path, i) { - ExprFlatElement element = path[i]; - Type *actual_type = type_flatten_distinct(type); - if (element.array) - { - if (actual_type->type_kind != TYPE_ARRAY) - { - SEMA_ERROR(expr, "It's possible to index into a non fixed size array."); - return false; - } - type = actual_type->array.base; - offset += type_size(type) * (ArraySize)element.index; - continue; - } - if (!type_is_structlike(actual_type)) - { - if (i == 0) - { - SEMA_ERROR(main_var, "%s has no members.", type_quoted_error_string(type)); - } - else - { - SEMA_ERROR(expr, "There is no such member in %s.", type_quoted_error_string(type)); - } - return false; - } + ExprFlatElement *element = &path[i]; Decl *member; - SCOPE_START - add_members_to_context(context, actual_type->decl); - member = sema_resolve_symbol_in_current_dynamic_scope(context, element.ident); - SCOPE_END; - if (!member) + ArraySize index; + Type *result_type; + if (!sema_expr_analyse_flat_element(context, element, type, &member, &index, &result_type, i, i == 0 ? main_var->span : expr->span, NULL)) return false; + if (member) { - SEMA_ERROR(expr, "There is no such member in %s.", type_quoted_error_string(type)); - return false; + offset += member->offset; } - type = member->type; - offset += member->offset; + else + { + offset += type_size(result_type) * index; + } + type = result_type; } expr_rewrite_to_int_const(expr, type_iptrdiff, offset, true); @@ -7059,6 +6937,8 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr) return sema_expr_analyse_rethrow(context, expr); case EXPR_CONST: return true; + case EXPR_CT_EVAL: + return sema_expr_analyse_ct_eval(context, expr); case EXPR_BITASSIGN: return sema_expr_analyse_bitassign(context, expr); case EXPR_BINARY: diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index ee6bb23ed..f5c6997f7 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -74,9 +74,18 @@ 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); +const char *ct_eval_expr(SemaContext *c, const char *expr_type, Expr *inner, TokenType *type, Path **path_ref, bool report_missing); +extern const char *ct_eval_error; + static inline bool expr_is_const(Expr *expr); static inline bool expr_is_const(Expr *expr) { return expr->expr_kind == EXPR_CONST; } + +static inline bool expr_is_const_string(Expr *expr) +{ + return expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_STRING; +} + diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index cc6422f41..9f9f2b44b 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -2,7 +2,7 @@ // Use of this source code is governed by a LGPLv3.0 // a copy of which can be found in the LICENSE file. -#include "compiler_internal.h" +#include "sema_internal.h" #if defined(_MSC_VER) // This isn't standard apparently, so MSVC doesn't have it built in... @@ -50,18 +50,19 @@ static inline Decl *sema_find_decl_in_module(Module *module, Path *path, const c return module_find_symbol(module, symbol); } -static Decl *sema_find_decl_in_imports(Decl **imports, const char *symbol, Path *path, Decl **ambiguous_other_decl, - Decl **private_decl, bool *path_found, bool want_generic) +static Decl *sema_find_decl_in_imports(Decl **imports, NameResolve *name_resolve, bool want_generic) { Decl *decl = NULL; // 1. Loop over imports. + Path *path = name_resolve->path; + const char *symbol = name_resolve->symbol; VECEACH(imports, i) { Decl *import = imports[i]; if (import->module->is_generic != want_generic) continue; // Is the decl in the import. - Decl *found = sema_find_decl_in_module(import->module, path, symbol, path_found); + Decl *found = sema_find_decl_in_module(import->module, path, symbol, &name_resolve->path_found); // No match, so continue if (!found) continue; @@ -70,7 +71,7 @@ static Decl *sema_find_decl_in_imports(Decl **imports, const char *symbol, Path if (found->visibility <= VISIBLE_MODULE && !import->import.private && !decl) { // Register this as a possible private decl. - *private_decl = found; + name_resolve->private_decl = found; continue; } @@ -78,13 +79,13 @@ static Decl *sema_find_decl_in_imports(Decl **imports, const char *symbol, Path if (decl) { // 11. Then set an ambiguous match. - *ambiguous_other_decl = found; + name_resolve->ambiguous_other_decl = found; continue; } // We've found a match. decl = found; - *private_decl = NULL; + name_resolve->private_decl = NULL; } return decl; } @@ -103,16 +104,17 @@ static inline bool sema_is_path_found(Module **modules, Path *path, bool want_ge return false; } -static Decl *sema_find_decl_in_global(DeclTable *table, Module **module_list, const char *symbol, Path *path, Decl **ambiguous_other_decl, - Decl **private_decl, bool *path_found, bool want_generic) +static Decl *sema_find_decl_in_global(DeclTable *table, Module **module_list, NameResolve *name_resolve, bool want_generic) { + const char *symbol = name_resolve->symbol; + Path *path = name_resolve->path; Decl *decls = decltable_get(table, symbol); // We might have no match at all. if (!decls) { // Update the path found - if (path && !*path_found) *path_found = sema_is_path_found(module_list, path, want_generic); + if (path && !name_resolve->path_found) name_resolve->path_found = sema_is_path_found(module_list, path, want_generic); return NULL; } @@ -120,7 +122,7 @@ static Decl *sema_find_decl_in_global(DeclTable *table, Module **module_list, co if (decls->decl_kind != DECL_DECLARRAY) { if (path && !matches_subpath(decls->module->name, path)) return false; - *private_decl = NULL; + name_resolve->private_decl = NULL; return decls; } @@ -137,19 +139,20 @@ static Decl *sema_find_decl_in_global(DeclTable *table, Module **module_list, co decl = candidate; } } - if (ambiguous) *ambiguous_other_decl = ambiguous; - *private_decl = NULL; + if (ambiguous) name_resolve->ambiguous_other_decl = ambiguous; + name_resolve->private_decl = NULL; return decl; } -static Decl *sema_resolve_path_symbol(SemaContext *context, const char *symbol, Path *path, Decl **ambiguous_other_decl, - Decl **private_decl, bool *path_found) +static Decl *sema_resolve_path_symbol(SemaContext *context, NameResolve *name_resolve) { - assert(path && "Expected path."); - *ambiguous_other_decl = NULL; + Path *path = name_resolve->path; + name_resolve->ambiguous_other_decl = NULL; Decl *decl = NULL; - *path_found = false; + name_resolve->path_found = false; + assert(name_resolve->path && "Expected path."); + const char *symbol = name_resolve->symbol; // 0. std module special handling. if (path->module == global_context.std_module_path.module) { @@ -166,18 +169,19 @@ static Decl *sema_resolve_path_symbol(SemaContext *context, const char *symbol, } // 3. Loop over imports. - decl = sema_find_decl_in_imports(unit->imports, symbol, path, ambiguous_other_decl, private_decl, path_found, false); + decl = sema_find_decl_in_imports(unit->imports, name_resolve, false); // 4. Go to global search - return decl ? decl : sema_find_decl_in_global(&global_context.symbols, global_context.module_list, symbol, - path, ambiguous_other_decl, private_decl, path_found, false); + return decl ? decl : sema_find_decl_in_global(&global_context.symbols, global_context.module_list, name_resolve, false); } -static Decl *sema_resolve_no_path_symbol(SemaContext *context, const char *symbol, - Decl **ambiguous_other_decl, Decl **private_decl) +static Decl *sema_resolve_no_path_symbol(SemaContext *context, NameResolve *name_resolve) { Decl *decl = NULL; + const char *symbol = name_resolve->symbol; + assert(name_resolve->path == NULL); + Decl **locals = context->locals; if (context->active_scope.current_local > 0) { @@ -217,95 +221,94 @@ static Decl *sema_resolve_no_path_symbol(SemaContext *context, const char *symbo if (decl) return decl; - decl = sema_find_decl_in_imports(unit->imports, symbol, NULL, ambiguous_other_decl, private_decl, NULL, false); - return decl ? decl : sema_find_decl_in_global(&global_context.symbols, NULL, symbol, NULL, ambiguous_other_decl, private_decl, NULL, false); + decl = sema_find_decl_in_imports(unit->imports, name_resolve, false); + return decl ? decl : sema_find_decl_in_global(&global_context.symbols, NULL, name_resolve, false); } -static void sema_report_error_on_decl(Path *path, const char *symbol_str, SourceSpan span, Decl *found, Decl *ambiguous_decl, - Decl *private_decl) +static void sema_report_error_on_decl(Decl *found, NameResolve *name_resolve) { - if (!found && private_decl) + assert(!name_resolve->suppress_error); + const char *symbol = name_resolve->symbol; + SourceSpan span = name_resolve->span; + const char *path_name = name_resolve->path ? name_resolve->path->module : NULL; + if (!found && name_resolve->private_decl) { - if (path) + const char *private_name = decl_to_name(name_resolve->private_decl); + if (path_name) { - sema_error_at(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.", + private_name, path_name, + symbol); } else { - sema_error_at(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.", + private_name, symbol); } return; } - if (ambiguous_decl) + + if (name_resolve->ambiguous_other_decl) { assert(found); const char *symbol_type = decl_to_name(found); - if (path) + const char *found_path = found->module->name->module; + const char *other_path = name_resolve->ambiguous_other_decl->module->name->module; + if (path_name) { 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, - symbol_str, - found->module->name->module, - ambiguous_decl->module->name->module, - found->module->name->module, - symbol_str, - ambiguous_decl->module->name->module, - symbol_str); + "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_name, symbol, found_path, other_path, + found_path, symbol, other_path, symbol); } else { 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, - found->module->name->module, - ambiguous_decl->module->name->module, - found->module->name->module, - symbol_str, - ambiguous_decl->module->name->module, - symbol_str); + "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, found_path, other_path, + found_path, symbol, other_path, symbol); } return; } assert(!found); - if (path) + if (path_name) { - sema_error_at(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_name, symbol); } else { - sema_error_at(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); } } -static inline Decl *sema_resolve_symbol(SemaContext *context, const char *symbol_str, SourceSpan span, Path *path, bool report_error) +INLINE Decl *sema_resolve_symbol_common(SemaContext *context, NameResolve *name_resolve) { - Decl *ambiguous_other_decl = NULL; - Decl *private_decl = NULL; - bool path_found = false; + name_resolve->ambiguous_other_decl = NULL; + name_resolve->private_decl = NULL; + name_resolve->path_found = false; Decl *decl; - if (path) + if (name_resolve->path) { - decl = sema_resolve_path_symbol(context, symbol_str, path, &ambiguous_other_decl, &private_decl, &path_found); - if (!decl && !path_found) + decl = sema_resolve_path_symbol(context, name_resolve); + if (!decl && !name_resolve->path_found) { - if (!report_error) return NULL; - SEMA_ERROR(path, "Unknown module '%.*s', did you type it right?", path->len, path->module); + if (!name_resolve->suppress_error) return NULL; + SEMA_ERROR(name_resolve->path, "Unknown module '%.*s', did you type it right?", name_resolve->path->len, name_resolve->path->module); return poisoned_decl; } } else { - decl = sema_resolve_no_path_symbol(context, symbol_str, &ambiguous_other_decl, &private_decl); + decl = sema_resolve_no_path_symbol(context, name_resolve); } - if (!decl || ambiguous_other_decl) + if (!decl || name_resolve->ambiguous_other_decl) { - if (!report_error) return NULL; - sema_report_error_on_decl(path, symbol_str, span, decl, ambiguous_other_decl, private_decl); + if (name_resolve->suppress_error) return NULL; + sema_report_error_on_decl(decl, name_resolve); return poisoned_decl; } unit_register_external_symbol(context->compilation_unit, decl); @@ -378,47 +381,72 @@ Decl *sema_resolve_method(CompilationUnit *unit, Decl *type, const char *method_ return result; } -Decl *unit_resolve_parameterized_symbol(CompilationUnit *unit, const char *symbol, SourceSpan span, Path *path) +Decl *unit_resolve_parameterized_symbol(CompilationUnit *unit, NameResolve *name_resolve) { - Decl *ambiguous_other_decl = NULL; - Decl *private_decl = NULL; - bool path_found = false; + name_resolve->ambiguous_other_decl = NULL; + name_resolve->private_decl = NULL; + name_resolve->path_found = false; - Decl *decl = sema_find_decl_in_imports(unit->imports, symbol, path, &ambiguous_other_decl, &private_decl, &path_found, true); + Decl *decl = sema_find_decl_in_imports(unit->imports, name_resolve, true); if (!decl) { decl = sema_find_decl_in_global(&global_context.generic_symbols, global_context.generic_module_list, - symbol, path, - &ambiguous_other_decl, - &private_decl, - &path_found, true); + name_resolve, true); } // 14. Error report - if (!decl || ambiguous_other_decl) + if (!decl || name_resolve->ambiguous_other_decl) { - sema_report_error_on_decl(path, symbol, span, decl, ambiguous_other_decl, private_decl); + if (name_resolve->suppress_error) return poisoned_decl; + sema_report_error_on_decl(decl, name_resolve); return poisoned_decl; } - if (!decl_is_user_defined_type(decl) && !path) + if (!decl_is_user_defined_type(decl) && !name_resolve->path) { - sema_error_at(span, "Function and variables must be prefixed with a path, e.g. 'foo::%s'.", symbol); + if (name_resolve->suppress_error) return poisoned_decl; + sema_error_at(name_resolve->span, "Function and variables must be prefixed with a path, e.g. 'foo::%s'.", name_resolve->symbol); return poisoned_decl; } return decl; } -Decl *sema_resolve_normal_symbol(SemaContext *context, const char *symbol, SourceSpan span, Path *path, bool handle_error) + +Decl *sema_find_symbol(SemaContext *context, const char *symbol) { - return sema_resolve_symbol(context, symbol, span, path, handle_error); + NameResolve resolve = { + .suppress_error = true, + .symbol = symbol, + }; + return sema_resolve_symbol_common(context, &resolve); } -Decl *sema_resolve_string_symbol(SemaContext *context, const char *symbol, SourceSpan span, Path *path, bool report_error) +Decl *sema_find_path_symbol(SemaContext *context, const char *symbol, Path *path) { - return sema_resolve_symbol(context, symbol, span, path, report_error); + NameResolve resolve = { + .suppress_error = true, + .symbol = symbol, + .path = path + }; + return sema_resolve_symbol_common(context, &resolve); } +Decl *sema_resolve_symbol(SemaContext *context, const char *symbol, Path *path, SourceSpan span) +{ + NameResolve resolve = { + .symbol = symbol, + .path = path, + .span = span + }; + return sema_resolve_symbol_common(context, &resolve); +} + +Decl *sema_resolve_normal_symbol(SemaContext *context, NameResolve *name_resolve) +{ + return sema_resolve_symbol_common(context, name_resolve); +} + + static inline bool sema_append_local(SemaContext *context, Decl *decl) { Decl ***locals = &context->locals; @@ -451,7 +479,7 @@ bool sema_add_local(SemaContext *context, Decl *decl) // Ignore synthetic locals. 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, decl->span, NULL, false); + Decl *other = sema_find_symbol(context, decl->name); 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 42cc791fb..4bc012ee9 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -6,6 +6,7 @@ // --- Helper functions + static bool sema_analyse_compound_stmt(SemaContext *context, Ast *statement); typedef enum @@ -54,14 +55,40 @@ static void sema_unwrappable_from_catch_in_else(SemaContext *c, Expr *cond) // --- Sema analyse stmts +static inline bool assert_create_from_contract(SemaContext *context, AstDocDirective *directive, AstId **asserts) +{ + Expr *declexpr = directive->contract.decl_exprs; + assert(declexpr->expr_kind == EXPR_EXPRESSION_LIST); + + Expr **exprs = declexpr->expression_list; + VECEACH(exprs, j) + { + Expr *expr = exprs[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->contract.comment; + if (!comment) comment = 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 inline bool sema_analyse_block_return_stmt(SemaContext *context, Ast *statement) { assert(context->active_scope.flags & (SCOPE_EXPR_BLOCK | SCOPE_MACRO)); context->active_scope.jump_end = true; + Type *block_type = context->expected_block_type; if (statement->return_stmt.expr) { - Type *block_type = context->expected_block_type; if (block_type) { if (!sema_analyse_expr_rhs(context, block_type, statement->return_stmt.expr, type_is_failable(block_type))) return false; @@ -71,6 +98,14 @@ static inline bool sema_analyse_block_return_stmt(SemaContext *context, Ast *sta if (!sema_analyse_expr(context, statement->return_stmt.expr)) return false; } } + else + { + if (block_type && block_type != type_void) + { + SEMA_ERROR(statement, "Expected a return value of type %s here.", type_quoted_error_string(block_type)); + return false; + } + } vec_add(context->returns, statement); return true; } @@ -103,8 +138,11 @@ static inline bool sema_analyse_return_stmt(SemaContext *context, Ast *statement Expr *return_expr = statement->return_stmt.expr; statement->return_stmt.defer = context->active_scope.defer_last; - // 2. First handle the plain return. - if (return_expr == NULL) + if (return_expr) + { + if (!sema_analyse_expr_rhs(context, expected_rtype, return_expr, type_is_failable(expected_rtype))) return false; + } + else { if (type_no_fail(expected_rtype)->canonical != type_void) { @@ -114,19 +152,52 @@ static inline bool sema_analyse_return_stmt(SemaContext *context, Ast *statement return true; } - // 3. Evaluate the return value to be the expected return type. - if (!sema_analyse_expr_rhs(context, expected_rtype, return_expr, type_is_failable(expected_rtype))) return false; + // Process any ensures. + if (context->return_var) + { + AstId next_id = 0; + AstId *append_id = &next_id; + // Creating an assign statement + if (return_expr) + { + Expr *assign = expr_new(EXPR_BINARY, return_expr->span); + assign->binary_expr.operator = BINARYOP_ASSIGN; + assign->binary_expr.widen = true; + assign->binary_expr.left = expr_variable(context->return_var); + assign->binary_expr.right = return_expr; + Ast *new_ret = new_ast(AST_EXPR_STMT, assign->span); + new_ret->expr_stmt = assign; + if (!sema_analyse_statement(context, new_ret)) return false; + ast_append(&append_id, new_ret); + } + AstDocDirective *directives = context->current_function->docs; + VECEACH(directives, i) + { + AstDocDirective *directive = &directives[i]; + if (directive->kind != DOC_DIRECTIVE_ENSURE) continue; + if (!assert_create_from_contract(context, directive, &append_id)) return false; + } + if (next_id) + { + Ast *new_return = new_ast(AST_RETURN_STMT, statement->span); + ast_append(&append_id, new_return); + if (return_expr) + { + new_return->return_stmt.expr = expr_variable(context->return_var); + } + new_return->return_stmt.defer = statement->return_stmt.defer; + new_return->next = statement->next; + statement->next = next_id; + statement->ast_kind = AST_NOP_STMT; + } + } + assert(type_no_fail(statement->return_stmt.expr->type)->canonical == type_no_fail(expected_rtype)->canonical); return true; } -static inline bool sema_analyse_unreachable_stmt(SemaContext *context) -{ - context->active_scope.jump_end = true; - return true; -} static inline bool sema_analyse_try_unwrap(SemaContext *context, Expr *expr) { @@ -173,8 +244,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.ident, ident->span, NULL, false); - if (!decl) implicit_declaration = true; + implicit_declaration = !sema_find_symbol(context, ident->identifier_expr.ident); } // 2. If we have a type for the variable, resolve it. @@ -327,8 +397,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.ident, ident->span, NULL, false); - if (!decl) implicit_declaration = true; + implicit_declaration = !sema_find_symbol(context, ident->identifier_expr.ident); } if (!type && !implicit_declaration) @@ -466,7 +535,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.ident, left->span, NULL, false); + Decl *decl_for_ident = sema_find_symbol(context, left->identifier_expr.ident); if (decl_for_ident) goto NORMAL_EXPR; Expr *right = expr->binary_expr.right; @@ -1079,7 +1148,7 @@ static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statemen } // IndexType __idx$ = 0 - Decl *idx_decl = decl_new_generated_var("__idx$", index_type, VARDECL_LOCAL, index ? index->span : enumerator->span); + Decl *idx_decl = decl_new_generated_var(index_type, VARDECL_LOCAL, index ? index->span : enumerator->span); Expr *idx_init = expr_new(EXPR_CONST, idx_decl->span); expr_rewrite_to_int_const(idx_init, index_type, 0, true); vec_add(expressions, expr_generate_decl(idx_decl, idx_init)); @@ -1112,7 +1181,7 @@ static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statemen else { // Store either "Foo* __enum$ = &some_var;" or "Foo __enum$ = some_call()" - temp = decl_new_generated_var("__enum$", enumerator->type, VARDECL_LOCAL, enumerator->span); + temp = decl_new_generated_var(enumerator->type, VARDECL_LOCAL, enumerator->span); vec_add(expressions, expr_generate_decl(temp, enumerator)); } @@ -1147,7 +1216,7 @@ static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statemen Decl *len_decl = NULL; if (len_call) { - len_decl = decl_new_generated_var("__len$", idx_init->type, VARDECL_LOCAL, enumerator->span); + len_decl = decl_new_generated_var(idx_init->type, VARDECL_LOCAL, enumerator->span); if (!cast_implicit(len_call, idx_init->type)) return false; vec_add(expressions, expr_generate_decl(len_decl, len_call)); } @@ -1334,9 +1403,7 @@ static bool sema_analyse_asm_stmt(SemaContext *context, Ast *stmt) 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.name, stmt->contbreak_stmt.label.span, NULL, false); + Decl *target = sema_find_symbol(context, stmt->contbreak_stmt.label.name); if (!target) { SEMA_ERROR(stmt, "Cannot find a labelled statement with the name '%s'.", stmt->contbreak_stmt.label.name); @@ -1421,7 +1488,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.name, statement->nextcase_stmt.label.span, NULL, false); + Decl *target = sema_find_symbol(context, statement->nextcase_stmt.label.name); if (!target) { SEMA_ERROR(statement, "Cannot find a switch statement with the name '%s'.", statement->nextcase_stmt.label.name); @@ -1579,14 +1646,30 @@ static bool sema_analyse_continue_stmt(SemaContext *context, Ast *statement) return true; } -static inline bool sema_analyse_then_overwrite(SemaContext *context, Ast *statement, Ast *replacement) + +static inline bool sema_analyse_then_overwrite(SemaContext *context, Ast *statement, AstId replacement) { - if (!sema_analyse_statement(context, replacement)) return false; + if (!replacement) + { + statement->ast_kind = AST_NOP_STMT; + return true; + } + AstId current = replacement; + Ast *last; + while (1) + { + Ast *curr_ast = astptr(current); + if (!sema_analyse_statement(context, curr_ast)) return false; + current = curr_ast->next; + if (!current) + { + last = curr_ast; + break; + } + } // Overwrite but store link. - AstId next = statement->next; - assert(!replacement->next); - *statement = *replacement; - statement->next = next; + last->next = statement->next; + *statement = *astptr(replacement); return true; } @@ -2047,7 +2130,7 @@ static bool sema_analyse_switch_stmt(SemaContext *context, Ast *statement) if (var_switch.is_assign) { inner = expr_new(EXPR_DECL, last->span); - variant_decl = decl_new_generated_var(".variant", type_any, VARDECL_LOCAL, last->span); + variant_decl = decl_new_generated_var(type_any, VARDECL_LOCAL, last->span); variant_decl->var.init_expr = var_switch.variant_expr; inner->decl_expr = variant_decl; if (!sema_analyse_expr(context, inner)) return false; @@ -2124,51 +2207,6 @@ bool sema_analyse_ct_assert_stmt(SemaContext *context, Ast *statement) return true; } -static inline bool sema_analyse_scoping_stmt(SemaContext *context, Ast *statement) -{ - Expr **exprs = statement->scoping_stmt.scoped->expression_list; - unsigned scoped_count = vec_size(exprs); - AstId first; - AstId *succ = &first; - for (unsigned i = 0; i < scoped_count; i++) - { - Expr *expr = exprs[i]; - if (!sema_analyse_expr_lvalue(context, expr)) return false; - if (!expr_is_ltype(expr)) - { - SEMA_ERROR(expr, "Expected an assignable value."); - return false; - } - if (!expr_is_pure(expr)) - { - SEMA_ERROR(expr, "A value with side effects (e.g. function calls) is not allowed here."); - return false; - } - Decl *new_decl = decl_new_generated_var(".scope", expr->type, VARDECL_LOCAL, expr->span); - new_decl->var.init_expr = expr; - Ast *declare = new_ast(AST_DECLARE_STMT, expr->span); - declare->declare_stmt = new_decl; - ast_append(&succ, declare); - Ast *defer_restore = new_ast(AST_DEFER_STMT, expr->span); - - Expr *restore_expr = expr_new(EXPR_BINARY, expr->span); - Expr *rhs = expr_new(EXPR_IDENTIFIER, expr->span); - rhs->resolve_status = RESOLVE_DONE; - rhs->identifier_expr.decl = new_decl; - rhs->type = expr->type; - - restore_expr->binary_expr = (ExprBinary) { .left = MACRO_COPY_EXPR(expr), .right = rhs, .operator = BINARYOP_ASSIGN }; - Ast *restore_stmt = new_ast(AST_EXPR_STMT, expr->span); - restore_stmt->expr_stmt = restore_expr; - - defer_restore->defer_stmt.body = restore_stmt; - ast_append(&succ, defer_restore); - } - ast_append(&succ, statement->scoping_stmt.stmt); - statement->ast_kind = AST_COMPOUND_STMT; - statement->compound_stmt = (AstCompoundStmt) { .first_stmt = first }; - return sema_analyse_compound_stmt(context, statement); -} bool sema_analyse_assert_stmt(SemaContext *context, Ast *statement) { @@ -2189,6 +2227,16 @@ bool sema_analyse_assert_stmt(SemaContext *context, Ast *statement) else { if (!sema_analyse_cond_expr(context, expr)) return false; + if (expr_is_const(expr)) + { + if (expr->const_expr.b) + { + statement->ast_kind = AST_NOP_STMT; + return true; + } + statement->assert_stmt.expr = NULL; + context->active_scope.jump_end = true; + } } return true; } @@ -2284,22 +2332,6 @@ static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement return success; } -static bool sema_analyse_ct_compound_stmt(SemaContext *context, Ast *statement) -{ - bool all_ok = ast_ok(statement); - AstId current = statement->compound_stmt.first_stmt; - while (current) - { - Ast *stmt = ast_next(¤t); - if (!sema_analyse_statement(context, stmt)) - { - ast_poison(stmt); - all_ok = false; - } - } - return all_ok; -} - static inline bool sema_analyse_statement_inner(SemaContext *context, Ast *statement) { @@ -2309,7 +2341,7 @@ static inline bool sema_analyse_statement_inner(SemaContext *context, Ast *state } if (context->active_scope.jump_end && !context->active_scope.allow_dead_code) { - if (statement->ast_kind == AST_UNREACHABLE_STMT) + if (statement->ast_kind == AST_ASSERT_STMT) { context->active_scope.allow_dead_code = true; return true; @@ -2322,12 +2354,8 @@ static inline bool sema_analyse_statement_inner(SemaContext *context, Ast *state { case AST_POISONED: case AST_SCOPED_STMT: - case AST_DOCS: - case AST_DOC_DIRECTIVE: case AST_IF_CATCH_SWITCH_STMT: UNREACHABLE - case AST_SCOPING_STMT: - return sema_analyse_scoping_stmt(context, statement); case AST_ASM_STMT: return sema_analyse_asm_stmt(context, statement); case AST_ASSERT_STMT: @@ -2343,8 +2371,6 @@ static inline bool sema_analyse_statement_inner(SemaContext *context, Ast *state return sema_analyse_continue_stmt(context, statement); case AST_CT_ASSERT: return sema_analyse_ct_assert_stmt(context, statement); - case AST_CT_COMPOUND_STMT: - return sema_analyse_ct_compound_stmt(context, statement); case AST_CT_IF_STMT: return sema_analyse_ct_if_stmt(context, statement); case AST_DECLARE_STMT: @@ -2372,8 +2398,6 @@ static inline bool sema_analyse_statement_inner(SemaContext *context, Ast *state return sema_analyse_switch_stmt(context, statement); case AST_NEXT_STMT: return sema_analyse_nextcase_stmt(context, statement); - case AST_UNREACHABLE_STMT: - return sema_analyse_unreachable_stmt(context); case AST_WHILE_STMT: return sema_analyse_while_stmt(context, statement); case AST_CT_SWITCH_STMT: @@ -2398,9 +2422,14 @@ bool sema_analyse_statement(SemaContext *context, Ast *statement) } -static bool sema_analyse_require(SemaContext *context, Ast *directive, AstId **asserts) +static bool sema_analyse_require(SemaContext *context, AstDocDirective *directive, AstId **asserts) { - Expr *declexpr = directive->doc_directive.contract.decl_exprs; + return assert_create_from_contract(context, directive, asserts); +} + +static bool sema_analyse_ensure(SemaContext *context, AstDocDirective *directive) +{ + Expr *declexpr = directive->contract.decl_exprs; assert(declexpr->expr_kind == EXPR_EXPRESSION_LIST); VECEACH(declexpr->expression_list, j) @@ -2411,22 +2440,13 @@ static bool sema_analyse_require(SemaContext *context, Ast *directive, AstId **a 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) +static bool sema_analyse_checked(SemaContext *context, AstDocDirective *directive, AstId **asserts) { - Expr *declexpr = directive->doc_directive.contract.decl_exprs; + Expr *declexpr = directive->contract.decl_exprs; bool success = true; SCOPE_START VECEACH(declexpr->cond_expr, j) @@ -2434,7 +2454,7 @@ static bool sema_analyse_checked(SemaContext *context, Ast *directive, AstId **a Expr *expr = declexpr->cond_expr[j]; if (!sema_analyse_cond_expr(context, expr)) { - const char *comment = directive->doc_directive.contract.comment; + const char *comment = directive->contract.comment; if (comment) { SEMA_ERROR(expr, comment); @@ -2448,14 +2468,13 @@ END: return success; } -static bool sema_analyse_contracts(SemaContext *context, Ast *docs, AstId **asserts) +static bool sema_analyse_contracts(SemaContext *context, AstDocDirective *directives, AstId **asserts, bool *ensure_found) { - if (!docs) return true; - Ast **directives = docs->directives; + if (!directives) return true; VECEACH(directives, i) { - Ast *directive = directives[i]; - switch (directive->doc_directive.kind) + AstDocDirective *directive = &directives[i]; + switch (directive->kind) { case DOC_DIRECTIVE_UNKNOWN: break; @@ -2473,6 +2492,8 @@ static bool sema_analyse_contracts(SemaContext *context, Ast *docs, AstId **asse case DOC_DIRECTIVE_ERRORS: break; case DOC_DIRECTIVE_ENSURE: + if (!sema_analyse_ensure(context, directive)) return false; + *ensure_found = true; break; } } @@ -2501,7 +2522,11 @@ bool sema_analyse_function_body(SemaContext *context, Decl *func) context->next_target = 0; context->next_switch = 0; context->break_target = 0; + context->return_var = NULL; func->func_decl.annotations = CALLOCS(FuncAnnotations); + bool ensure_found = false; + func->func_decl.ret_var = NULL; + Ast *body = func->func_decl.body; SCOPE_START assert(context->active_scope.depth == 1); Decl **params = signature->params; @@ -2511,7 +2536,7 @@ bool sema_analyse_function_body(SemaContext *context, Decl *func) } AstId assert_first = 0; AstId *next = &assert_first; - if (!sema_analyse_contracts(context, func->docs, &next)) return false; + if (!sema_analyse_contracts(context, func->docs, &next, &ensure_found)) return false; if (func->func_decl.attr_naked) { AstId current = func->func_decl.body->compound_stmt.first_stmt; @@ -2528,22 +2553,46 @@ bool sema_analyse_function_body(SemaContext *context, Decl *func) } else { - if (!sema_analyse_compound_statement_no_scope(context, func->func_decl.body)) return false; + if (ensure_found) + { + Decl *ret_val = decl_new_generated_var(context->rtype, + VARDECL_LOCAL, + func->func_decl.function_signature.returntype->span); + ret_val->name = kw_return; + context->return_var = ret_val; + func->func_decl.ret_var = ret_val; + if (!sema_add_local(context, ret_val)) return false; + } + Type *canonical_rtype = type_no_fail(prototype->rtype)->canonical; + // Insert an implicit return + if (canonical_rtype == type_void) + { + func->func_decl.ret_var = NULL; + AstId *next_id = &body->compound_stmt.first_stmt; + SourceSpan span = body->span; + if (*next_id) + { + Ast *last = ast_last(astptr(*next_id)); + // Cleanup later + if (last->ast_kind == AST_RETURN_STMT) goto SKIP_NEW_RETURN; + span = last->span; + next_id = &last->next; + } + Ast *ret = new_ast(AST_RETURN_STMT, span); + ast_append(&next_id, ret); + SKIP_NEW_RETURN:; + } + if (!sema_analyse_compound_statement_no_scope(context, body)) return false; assert(context->active_scope.depth == 1); if (!context->active_scope.jump_end) { - Type *canonical_rtype = type_no_fail(prototype->rtype)->canonical; - if (canonical_rtype != type_void) - { - // IMPROVE better pointer to end. - SEMA_ERROR(func, "Missing return statement at the end of the function."); - return false; - } + SEMA_ERROR(func, "Missing return statement at the end of the function."); + return false; } } if (assert_first) { - Ast *ast = new_ast(AST_COMPOUND_STMT, func->func_decl.body->span); + Ast *ast = new_ast(AST_COMPOUND_STMT, body->span); ast->compound_stmt.first_stmt = assert_first; ast_prepend(&func->func_decl.body->compound_stmt.first_stmt, ast); } diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index 1a37d7bdc..0a3961bf2 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -121,17 +121,15 @@ 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, - type_info->unresolved.span, - type_info->unresolved.path, true); - decl = decl_flatten(decl); + Decl *decl = sema_resolve_symbol(context, type_info->unresolved.name, type_info->unresolved.path, type_info->unresolved.span); + // Already handled if (!decl_ok(decl)) { return type_info_poison(type_info); } + decl = decl_flatten(decl); switch (decl->decl_kind) { case DECL_STRUCT: @@ -244,7 +242,7 @@ bool sema_resolve_type_shallow(SemaContext *context, TypeInfo *type_info, bool a } type_info->resolve_status = RESOLVE_RUNNING; - +RETRY: switch (type_info->kind) { case TYPE_INFO_POISON: @@ -253,6 +251,34 @@ bool sema_resolve_type_shallow(SemaContext *context, TypeInfo *type_info, bool a case TYPE_INFO_IDENTIFIER: if (!sema_resolve_type_identifier(context, type_info)) return false; break; + case TYPE_INFO_EVALTYPE: + { + Expr *expr = type_info->unresolved_type_expr; + TokenType type; + Path *path = NULL; + const char *ident = ct_eval_expr(context, "$eval", expr, &type, &path, true); + if (ident == ct_eval_error) return type_info_poison(type_info); + switch (type) + { + case TOKEN_TYPE_IDENT: + type_info->unresolved.name = ident; + type_info->unresolved.span = expr->span; + type_info->unresolved.path = path; + type_info->kind = TYPE_INFO_IDENTIFIER; + goto RETRY; + case TYPE_TOKENS: + if (path) + { + SEMA_ERROR(path, "Built in types cannot have a path prefix."); + return false; + } + type_info->type = type_from_token(type); + return true; + default: + SEMA_ERROR(expr, "Only type names may be resolved with $evaltype."); + return type_info_poison(type_info); + } + } case TYPE_INFO_EXPRESSION: { Expr *expr = type_info->unresolved_type_expr; diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index a46923a0a..1d27073e2 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -127,7 +127,7 @@ void context_pop_defers_and_replace_ast(SemaContext *context, Ast *ast) assert(ast->ast_kind != AST_COMPOUND_STMT); Ast *replacement = ast_copy(ast); ast->ast_kind = AST_SCOPED_STMT; - ast->scoped_stmt.stmt = replacement; + ast->scoped_stmt.stmt = astid(replacement); ast->scoped_stmt.defers = defers; } diff --git a/src/compiler/source_file.c b/src/compiler/source_file.c index 3375acbe1..a0bd2cf71 100644 --- a/src/compiler/source_file.c +++ b/src/compiler/source_file.c @@ -11,7 +11,6 @@ #include #endif #include "compiler_internal.h" -#include "../build/build_options.h" static const size_t LEXER_FILES_START_CAPACITY = 128; @@ -38,7 +37,7 @@ File *source_file_load(const char *filename, bool *already_loaded) { if (strcmp(global_context.loaded_sources[index]->full_path, full_path) == 0) { - *already_loaded = true; + if (already_loaded) *already_loaded = true; return global_context.loaded_sources[index]; } } diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index d7b0fadca..143bc2f1d 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -55,6 +55,7 @@ const char *kw_distinct; const char *kw_ensure; const char *kw_elements; const char *kw_errors; +const char *kw_return; const char *kw_type; const char *kw_inf; const char *kw_inline; @@ -73,6 +74,7 @@ const char *kw_pure; const char *kw_reqparse; const char *kw_require; const char *kw_std; +const char *kw_sizeof; const char *kw_values; const char *kw_FILE; const char *kw_FUNC; @@ -131,7 +133,7 @@ void symtab_init(uint32_t capacity) uint32_t len = (uint32_t)strlen(name); TokenType type = (TokenType)i; const char* interned = symtab_add(name, (uint32_t)strlen(name), fnv1a(name, len), &type); - (void)interned; + if (type == TOKEN_RETURN) kw_return = interned; assert(type == (TokenType)i); assert(symtab_add(name, (uint32_t)strlen(name), fnv1a(name, len), &type) == interned); } @@ -139,6 +141,7 @@ 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_sizeof = KW_DEF("sizeof"); kw_in = KW_DEF("in"); kw_out = KW_DEF("out"); kw_inout = KW_DEF("inout"); @@ -159,7 +162,6 @@ void symtab_init(uint32_t capacity) kw_max = KW_DEF("max"); kw_min = KW_DEF("min"); kw_nan = KW_DEF("nan"); - KW_DEF("next"); kw_ordinal = KW_DEF("ordinal"); kw_param = KW_DEF("param"); kw_ptr = KW_DEF("ptr"); @@ -167,16 +169,11 @@ void symtab_init(uint32_t capacity) kw_require = KW_DEF("require"); kw_std = KW_DEF("std"); kw_values = KW_DEF("values"); - KW_DEF("__ceil"); - KW_DEF("__round"); - KW_DEF("__sqrt"); - KW_DEF("__trunc"); kw_LINE = KW_DEF("LINE"); kw_LINEREAL = KW_DEF("LINEREAL"); kw_FILE = KW_DEF("FILE"); kw_FUNC = KW_DEF("FUNC"); kw_incr = KW_DEF("incr"); - KW_DEF("default_iterator"); kw_check_assign = KW_DEF("check_assign"); kw_argc = KW_DEF("_$argc"); diff --git a/src/compiler/target.c b/src/compiler/target.c index 533cb37ef..cf356e17b 100644 --- a/src/compiler/target.c +++ b/src/compiler/target.c @@ -1159,7 +1159,7 @@ void *llvm_target_machine_create(void) error_exit("Could not create target: %s for triple '%s'", err, platform_target.target_triple); // Usually we would dispose of err, but no need to do it due to exit. } - LLVMRelocMode reloc_mode; + LLVMRelocMode reloc_mode = LLVMRelocDefault; switch (platform_target.reloc_model) { diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index deab90fcb..ddcd314b0 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -246,8 +246,6 @@ const char *token_type_to_string(TokenType type) return "private"; case TOKEN_RETURN: return "return"; - case TOKEN_SCOPING: - return "scoping"; case TOKEN_STATIC: return "static"; case TOKEN_STRUCT: @@ -357,6 +355,10 @@ const char *token_type_to_string(TokenType type) return "$default"; case TOKEN_CT_DEFINED: return "$defined"; + case TOKEN_CT_EVAL: + return "$eval"; + case TOKEN_CT_EVALTYPE: + return "$evaltype"; case TOKEN_CT_FOR: return "$for"; case TOKEN_CT_FOREACH: @@ -391,8 +393,6 @@ const char *token_type_to_string(TokenType type) return "$typeof"; case TOKEN_CT_STRINGIFY: return "$stringify"; - case TOKEN_CT_UNREACHABLE: - return "$unreachable"; case TOKEN_EOF: return "EOF"; diff --git a/src/utils/errors.h b/src/utils/errors.h index 2fdf6cb72..eea0efa6e 100644 --- a/src/utils/errors.h +++ b/src/utils/errors.h @@ -54,8 +54,6 @@ do { long long __tempval1 = _value; long long __tempval2 = _expected; \ TEST_ASSERT(__tempval1 == __tempval2, "Checking " _string ": expected %lld but was %lld.", __tempval2, __tempval1); } while(0) -#define LOG_FUNC DEBUG_LOG("ENTER %s.", __func__); - void evprintf(const char *format, va_list list); void eprintf(const char *format, ...); NORETURN void error_exit(const char *format, ...) ; diff --git a/src/version.h b/src/version.h index ba7cddc90..32aa4662f 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "PRE.23" \ No newline at end of file +#define COMPILER_VERSION "PRE.24" \ No newline at end of file diff --git a/test/test_suite/abi/darwinx64_2.c3t b/test/test_suite/abi/darwinx64_2.c3t index 239d81250..cdc7a2d6a 100644 --- a/test/test_suite/abi/darwinx64_2.c3t +++ b/test/test_suite/abi/darwinx64_2.c3t @@ -5,13 +5,13 @@ struct St12 { int a @align(16); } -fn St12 f12_0(void) { while (1) {}; $unreachable; } +fn St12 f12_0(void) { while (1) {}; @unreachable(); } fn void f12_1(St12 a0) {} struct St13_0 { long[3] f0; } struct St13_1 { long[2] f0; } fn St13_0 f13(int a, int b, int c, int d, - St13_1 e, int f) { while (1) {}; $unreachable; } + St13_1 e, int f) { while (1) {}; @unreachable(); } fn void f14(int a, int b, int c, int d, int e, int f, ichar x) {} diff --git a/test/test_suite/assert/unreachable.c3t b/test/test_suite/assert/unreachable.c3t index 197b6c65c..0ea68332d 100644 --- a/test/test_suite/assert/unreachable.c3t +++ b/test/test_suite/assert/unreachable.c3t @@ -8,7 +8,7 @@ fn void test() { int x = foo(); if (x > 0) return; - $unreachable; + @unreachable(); x++; } diff --git a/test/test_suite/compile_time/ct_eval.c3t b/test/test_suite/compile_time/ct_eval.c3t new file mode 100644 index 000000000..aec86461a --- /dev/null +++ b/test/test_suite/compile_time/ct_eval.c3t @@ -0,0 +1,22 @@ +// #target: x64-darwin +module test; +extern fn void printf(char*, ...); + +fn void main() +{ + var $x = "abc"; + int abc = 20; + $eval($x) = 444; + printf("Val: %d\n", $eval("abc")); +} +/* #expect: test.ll + +define void @test.main() #0 { +entry: + %abc = alloca i32, align 4 + store i32 20, i32* %abc, align 4 + store i32 444, i32* %abc, align 4 + %0 = load i32, i32* %abc, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str, i32 0, i32 0), i32 %0) + ret void +} diff --git a/test/test_suite/compile_time/stringify.c3t b/test/test_suite/compile_time/stringify.c3t index 084ac4221..75d0d3fc4 100644 --- a/test/test_suite/compile_time/stringify.c3t +++ b/test/test_suite/compile_time/stringify.c3t @@ -71,7 +71,6 @@ entry: %diff = alloca i64, align 8 %a = alloca i32, align 4 %x = alloca i32, align 4 - %blockret = alloca i32, align 4 %t1 = alloca i64, align 8 %result = alloca i32, align 4 %diff2 = alloca i64, align 8 @@ -97,14 +96,9 @@ entry: %9 = load i64, i64* %diff2, align 8 %10 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* @.str.3, i32 0, i32 0), [9 x i8]* bitcast ([10 x i8]* @.str.4 to [9 x i8]*), i64 %9) %11 = load i32, i32* %result, align 4 - store i32 %11, i32* %blockret, align 4 - br label %expr_block.exit - -expr_block.exit: ; preds = %entry - %12 = load i32, i32* %blockret, align 4 - store i32 %12, i32* %x, align 4 - %13 = load i32, i32* %x, align 4 - %14 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @.str.5, i32 0, i32 0), i32 %13) + store i32 %11, i32* %x, align 4 + %12 = load i32, i32* %x, align 4 + %13 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @.str.5, i32 0, i32 0), i32 %12) ret void } diff --git a/test/test_suite/compile_time_introspection/alignof.c3t b/test/test_suite/compile_time_introspection/alignof.c3t index f3e73add6..cc5258cc1 100644 --- a/test/test_suite/compile_time_introspection/alignof.c3t +++ b/test/test_suite/compile_time_introspection/alignof.c3t @@ -38,28 +38,28 @@ Ar izzy; long x = $alignof(zfe); -short y = $alignof("Bob.y"); -int z = $alignof("Bob.y"); +short y = $alignof(Bob.y); +int z = $alignof(Bob.y); int w = $alignof(Bob.y); int v = $alignof(v); -int x1 = $alignof("Ex.c"); +int x1 = $alignof(Ex.c); int x2 = $alignof(Ex.y); int x3 = $alignof(char[8]); -int x4 = $alignof("Ar.br[1]"); +int x4 = $alignof(Ar.br[1]); int x5 = $alignof(Ar.br[1]); int x6 = $alignof(Ar.br[1]); int x7 = $alignof(Ar.br[1]); int x8 = $alignof(Ar.br[2]); -int x9 = $alignof("izzy.br[1]"); -int x10 = $alignof("izzy.br[1]"); +int x9 = $alignof(izzy.br[1]); +int x10 = $alignof(izzy.br[1]); int x11 = $alignof(izzy.br[1]); -int z0 = $alignof("Foob.c"); -int z00 = $alignof("Foob.c[0]"); -int z01 = $alignof("Foob.c[1]"); -int z02 = $alignof("Foob.c[2]"); -int z03 = $alignof("Foob.c[3]"); -int z04 = $alignof("Foob.c[4]"); -int z05 = $alignof("Foob.c[5]"); +int z0 = $alignof(Foob.c); +int z00 = $alignof(Foob.c[0]); +int z01 = $alignof(Foob.c[1]); +int z02 = $alignof(Foob.c[2]); +int z03 = $alignof(Foob.c[3]); +int z04 = $alignof(Foob.c[4]); +int z05 = $alignof(Foob.c[5]); diff --git a/test/test_suite/compile_time_introspection/defined.c3t b/test/test_suite/compile_time_introspection/defined.c3t index 1bb88877f..c32c8b361 100644 --- a/test/test_suite/compile_time_introspection/defined.c3t +++ b/test/test_suite/compile_time_introspection/defined.c3t @@ -34,15 +34,15 @@ fn void main() x++; $counter++; $endif; - $if ($defined("x")): + $if ($defined($eval("x"))): x++; $counter++; $endif; - $if ($defined("Foo.ab")): + $if ($defined($evaltype("Foo").$eval("ab"))): x++; $counter++; $endif; - $if ($defined("mymodule::Foo.bob")): + $if ($defined($evaltype("mymodule::Foo").$eval("bob"))): x++; $counter++; $endif; diff --git a/test/test_suite/compile_time_introspection/defined_err.c3 b/test/test_suite/compile_time_introspection/defined_err.c3 index 27d9bbf03..ae06b4be9 100644 --- a/test/test_suite/compile_time_introspection/defined_err.c3 +++ b/test/test_suite/compile_time_introspection/defined_err.c3 @@ -1,6 +1,6 @@ fn void test1() { - bool x = $defined(1); // #error: constant string containing an identifier or type was expected + bool x = $defined(1); // #error: Expected an identifier here } struct Foo diff --git a/test/test_suite/compile_time_introspection/nameof.c3t b/test/test_suite/compile_time_introspection/nameof.c3t index a1b39d10c..96e49bf85 100644 --- a/test/test_suite/compile_time_introspection/nameof.c3t +++ b/test/test_suite/compile_time_introspection/nameof.c3t @@ -10,29 +10,29 @@ int b; fn void main() { printf("%s\n", $qnameof(Foo)); - printf("%s\n", $qnameof("Foo")); + printf("%s\n", $qnameof($evaltype("Foo"))); printf("%s\n", $nameof(Foo)); - printf("%s\n", $nameof("mymodule::Foo")); + printf("%s\n", $nameof($evaltype("mymodule::Foo"))); printf("%s\n", $extnameof(Foo)); - printf("%s\n", $extnameof("Foo")); + printf("%s\n", $extnameof($evaltype("Foo"))); printf("%s\n", $qnameof(b)); - printf("%s\n", $qnameof("b")); + printf("%s\n", $qnameof($eval("b"))); printf("%s\n", $nameof(b)); - printf("%s\n", $nameof("mymodule::b")); + printf("%s\n", $nameof($eval("mymodule::b"))); printf("%s\n", $extnameof(b)); - printf("%s\n", $extnameof("b")); + printf("%s\n", $extnameof($eval("b"))); int a; printf("%s\n", $qnameof(a)); - printf("%s\n", $qnameof("a")); + printf("%s\n", $qnameof($eval("a"))); printf("%s\n", $nameof(a)); - printf("%s\n", $nameof("a")); + printf("%s\n", $nameof($eval("a"))); } -// #expect: mymodule.ll +/* #expect: mymodule.ll @.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1 @.str.1 = private unnamed_addr constant [14 x i8] c"mymodule::Foo\00", align 1 diff --git a/test/test_suite/compile_time_introspection/nameof_err.c3 b/test/test_suite/compile_time_introspection/nameof_err.c3 index b23c18902..bcfacfaef 100644 --- a/test/test_suite/compile_time_introspection/nameof_err.c3 +++ b/test/test_suite/compile_time_introspection/nameof_err.c3 @@ -6,10 +6,15 @@ fn void main() fn void main2() { int a; - $extnameof("a"); // #error: 'a' does not have an external name. + $extnameof($eval("a")); // #error: 'a' does not have an external name. } fn void main3() { - $extnameof("int"); // #error: Only user defined types have an external name. + $extnameof($evaltype("int")); // #error: Only user defined types have an external name. +} + +fn void main4() +{ + $extnameof($evaltype("foo::int")); // #error: Built in types cannot have a path prefix. } diff --git a/test/test_suite/compile_time_introspection/offsetof.c3t b/test/test_suite/compile_time_introspection/offsetof.c3t index aff15a0a9..1114086fe 100644 --- a/test/test_suite/compile_time_introspection/offsetof.c3t +++ b/test/test_suite/compile_time_introspection/offsetof.c3t @@ -36,21 +36,21 @@ union Foob } -short y = $offsetof("Bob.y"); -int z = $offsetof("Bob.y"); +short y = $offsetof(Bob.y); +int z = $offsetof(Bob.y); int w = $offsetof(Bob.y); -int x1 = $offsetof("Ex.c[3]"); -int x2 = $offsetof("Ex.y[1]"); -int x4 = $offsetof("Ar.br[1]"); +int x1 = $offsetof(Ex.c[3]); +int x2 = $offsetof(Ex.y[1]); +int x4 = $offsetof(Ar.br[1]); int x6 = $offsetof(Ar.br[1].x); int x5 = $offsetof(Ar.br[1]); -int x7 = $offsetof("Ar.br[2].x"); +int x7 = $offsetof(Ar.br[2].x); int x8 = $offsetof(Ar.br[2]); -int z0 = $offsetof("Foob.c"); -int z00 = $offsetof("Foob.c[0]"); -int z01 = $offsetof("Foob.c[1]"); -int z02 = $offsetof("Foob.c[5]"); -int z03 = $offsetof("Foob.a"); +int z0 = $offsetof(Foob.c); +int z00 = $offsetof(Foob.c[0]); +int z01 = $offsetof(Foob.c[1]); +int z02 = $offsetof(Foob.c[5]); +int z03 = $offsetof(Foob.a); // #expect: foo.ll @foo.y = local_unnamed_addr global i16 16, align 2 diff --git a/test/test_suite/compile_time_introspection/sizeof.c3t b/test/test_suite/compile_time_introspection/sizeof.c3t index ea33fa5f5..772d36ae2 100644 --- a/test/test_suite/compile_time_introspection/sizeof.c3t +++ b/test/test_suite/compile_time_introspection/sizeof.c3t @@ -2,22 +2,22 @@ module foo; import bar; import bar::abc; -long x = $sizeof(Baz); -short y = $sizeof("Baz"); -int z = $sizeof(bar::Baz); -int w = $sizeof("bar::Baz"); -int v = $sizeof("bar::abc::Foo"); +long x = Baz.sizeof; +short y = $evaltype("Baz").sizeof; +int z = bar::Baz.sizeof; +int w = bar::Baz.sizeof; +int v = bar::abc::Foo.sizeof; int x1 = $sizeof(x); -int y1 = $sizeof("y"); -int a = $sizeof("Baz.y"); -int b = $sizeof("Deep.a.b"); -int c = $sizeof("Deep.a.b.c"); -int d = $sizeof("Deep[][100]"); -int e = $sizeof("Deep[][100]**[100][]*"); -int a2 = $sizeof("Baz.y"); -int a3 = $sizeof(Baz.y); -int a4 = $sizeof(Baz.y); -int a5 = $sizeof(Baz.y); +int y1 = $sizeof($eval("y")); +int a = Baz.y.sizeof; +int b = $evaltype("Deep").a.$eval("b").sizeof; +int c = Deep.a.b.c.sizeof; +int d = Deep[][100].sizeof; +int e = $evaltype("Deep")[][100]**[100][]*.sizeof; +int a2 = Baz.y.sizeof; +int a3 = Baz.y.sizeof; +int a4 = Baz.y.sizeof; +int a5 = Baz.y.sizeof; module bar; diff --git a/test/test_suite/compile_time_introspection/sizeof_errors.c3 b/test/test_suite/compile_time_introspection/sizeof_errors.c3 index 6f1d79997..a86de52ed 100644 --- a/test/test_suite/compile_time_introspection/sizeof_errors.c3 +++ b/test/test_suite/compile_time_introspection/sizeof_errors.c3 @@ -4,62 +4,48 @@ import bar::abc; fn void a() { - int x = $sizeof(Bazy); // #error: 'Bazy' could not be found, did you spell it right + int x = Bazy.sizeof; // #error: 'Bazy' could not be found, did you spell it right } fn void b() { - int x = $sizeof("Bazz"); // #error: 'Bazz' could not be found, did you misspell it + int x = $evaltype("Bazz").sizeof; // #error: 'Bazz' could not be found, did you spell it rig } -fn void c() -{ - int x = $sizeof("Baz#"); // #error: The path to an existing member was expected after 'Baz', did you make a mistake? -} - -fn void c2() -{ - int x = $sizeof("#Baz"); // #error: A valid identifier was expected here, did you want to take the length of a string literal? If so use '.len'. -} - -fn void d() -{ - int x = $sizeof("Baz."); // #error: The path to an existing member was expected after 'Baz', did you make a mistake? -} fn void e() { - int x = $sizeof(bar::Baze); // #error: + int x = bar::Baze.sizeof; // #error: 'bar::Baze' could not be found, did you spell it right } fn void f() { - int x = $sizeof("bar::Bazy"); // #error: 'bar::Bazy' could not be found, did you spell it right + int x = $evaltype("bar::Bazy").sizeof; // #error: 'bar::Bazy' could not be found } fn void g() { - int x = $sizeof("bar::"); // #error: A valid identifier was expected here, did you want to take the length of a string literal? If so use '.len'. + int x = $evaltype("bar::").sizeof; // #error: A valid name was expected here. } fn void k() { - int v = $sizeof("int.x"); // #error: 'int' has no members + int v = $evaltype("int").x.sizeof; // #error: 'int' does not have a property 'x'. } fn void l() { - int v = $sizeof("int[].len"); // #error: 'int[]' has no members + int v = $sizeof(int[].len); // #error: 'int[]' does not have a property 'len' } fn void m() { - int v = $sizeof("int[4].len"); // #error: 'int[4]' has no members + int v = $sizeof(int[4].len); // #error: 'int[4]' does not have a property 'len' } fn void n() { - int v = $sizeof("Baz.x1"); // #error: The path to an existing member was expected after + int v = Baz.x1.sizeof; // #error: No method or inner struct/union 'Baz.x1' found. } diff --git a/test/test_suite/contracts/simple_test.c3t b/test/test_suite/contracts/simple_test.c3t new file mode 100644 index 000000000..30fcb97c4 --- /dev/null +++ b/test/test_suite/contracts/simple_test.c3t @@ -0,0 +1,98 @@ +// #target: x64-darwin + +/** + * @param [inout] foo `test` + * @require baz > 100 `whatever` + * @ensure *foo > 231 +*/ +fn void test(int *foo, int baz) +{ + *foo = 444; +} + +/** + * @param [inout] foo `test` + * @require baz > 100 `whatever` + * @ensure return < 200 +*/ +fn int test2(int *foo, int baz) +{ + *foo = 444; + return baz; +} + +/** + * @require x > 0 + * @ensure return > 0 + **/ +fn int test3(int x) +{ + return x + 1; +} + +extern fn void printf(char*, ...); + +fn void main() +{ + int fooofke; + test(&fooofke, 330); + test2(&fooofke, 150); + test3(123); + printf("Foo: %d\n", fooofke); +} + +/* #expect: simple_test.ll + +define void @simple_test.test(i32* %0, i32 %1) #0 { +entry: + %gt = icmp sgt i32 %1, 100 + call void @llvm.assume(i1 %gt) + store i32 444, i32* %0, align 8 + ret void +} + +define i32 @simple_test.test2(i32* %0, i32 %1) #0 { +entry: + %return = alloca i32, align 4 + %gt = icmp sgt i32 %1, 100 + call void @llvm.assume(i1 %gt) + store i32 444, i32* %0, align 8 + store i32 %1, i32* %return, align 4 + %2 = load i32, i32* %return, align 4 + %lt = icmp slt i32 %2, 200 + call void @llvm.assume(i1 %lt) + %3 = load i32, i32* %return, align 4 + ret i32 %3 +} + +define i32 @simple_test.test3(i32 %0) #0 { +entry: + %return = alloca i32, align 4 + %gt = icmp sgt i32 %0, 0 + call void @llvm.assume(i1 %gt) + %add = add i32 %0, 1 + store i32 %add, i32* %return, align 4 + %1 = load i32, i32* %return, align 4 + %gt1 = icmp sgt i32 %1, 0 + call void @llvm.assume(i1 %gt1) + %2 = load i32, i32* %return, align 4 + ret i32 %2 +} + +define void @simple_test.main() #0 { +entry: + %fooofke = alloca i32, align 4 + store i32 0, i32* %fooofke, align 4 + call void @simple_test.test(i32* %fooofke, i32 330) + %0 = call i32 @simple_test.test2(i32* %fooofke, i32 150) + %1 = call i32 @simple_test.test3(i32 123) + %2 = load i32, i32* %fooofke, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str, i32 0, i32 0), i32 %2) + ret void +} + +define i32 @main(i32 %0, i8** %1) #0 { +entry: + call void @simple_test.main() + ret i32 0 +} diff --git a/test/test_suite/enumerations/compile_time.c3t b/test/test_suite/enumerations/compile_time.c3t index 195dac759..bc26ed669 100644 --- a/test/test_suite/enumerations/compile_time.c3t +++ b/test/test_suite/enumerations/compile_time.c3t @@ -9,7 +9,7 @@ int myenum_max = MyEnum.max; int myenum_min = MyEnum.min; int myenum_elements = MyEnum.elements; int myenum_alignof = $alignof(MyEnum); -int myenum_sizeof = $sizeof(MyEnum); +int myenum_sizeof = MyEnum.sizeof; // #expect: compile_time.ll diff --git a/test/test_suite/macros/userland_bitcast.c3t b/test/test_suite/macros/userland_bitcast.c3t index 89f7ae97a..c224696f7 100644 --- a/test/test_suite/macros/userland_bitcast.c3t +++ b/test/test_suite/macros/userland_bitcast.c3t @@ -2,7 +2,7 @@ macro testbitcast(expr, $Type) { - $assert($sizeof(expr) == $sizeof($Type), "Cannot bitcast between types of different size."); + $assert($sizeof(expr) == $Type.sizeof, "Cannot bitcast between types of different size."); $Type x = void; var $size = (usize)($sizeof(expr)); $if ($alignof(expr) >= 8 && $alignof($Type) >= 8): diff --git a/test/test_suite/scoping/general_scoping.c3t b/test/test_suite/scoping/general_scoping.c3t deleted file mode 100644 index 0133e76eb..000000000 --- a/test/test_suite/scoping/general_scoping.c3t +++ /dev/null @@ -1,79 +0,0 @@ -module test; -extern fn void printf(char*, ...); - -struct Foo -{ - struct goo - { - int* z; - } -} - -Foo bob; -fn void main() -{ - int y = 7; - bob.goo.z = &y; - int x = 1; - scoping (x, *bob.goo.z) - { - x = 3; - *bob.goo.z = 12; - printf("%d %d\n", x, *bob.goo.z); - } - printf("%d %d\n", x, *bob.goo.z); -} - -/* #expect: test.ll - -%Foo = type { %goo } -%goo = type { i32* } - -@goo = linkonce_odr constant i8 1 -@Foo = linkonce_odr constant i8 1 -@test.bob = local_unnamed_addr global %Foo zeroinitializer, align 8 -@.str = private unnamed_addr constant [7 x i8] c"%d %d\0A\00", align 1 -@.str.1 = private unnamed_addr constant [7 x i8] c"%d %d\0A\00", align 1 - -; Function Attrs: nounwind -declare void @printf(i8*, ...) #0 - -; Function Attrs: nounwind -define void @test.main() #0 { -entry: - %y = alloca i32, align 4 - %x = 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* %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* %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 - %4 = load i32, i32* %x, align 4 - %5 = load i32*, i32** getelementptr inbounds (%Foo, %Foo* @test.bob, i32 0, i32 0, i32 0), align 8 - %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* %anon1, align 4 - store i32 %8, i32* %7, align 8 - br label %exit - -exit: ; preds = %entry - %9 = load i32, i32* %anon, align 4 - store i32 %9, i32* %x, align 4 - br label %exit2 - -exit2: ; preds = %exit - %10 = load i32, i32* %x, align 4 - %11 = load i32*, i32** getelementptr inbounds (%Foo, %Foo* @test.bob, i32 0, i32 0, i32 0), align 8 - %12 = load i32, i32* %11, align 8 - call void (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.1, i32 0, i32 0), i32 %10, i32 %12) - ret void -} diff --git a/test/test_suite/struct/struct_const_construct_simple.c3t b/test/test_suite/struct/struct_const_construct_simple.c3t index 41e07bc5e..743989d0f 100644 --- a/test/test_suite/struct/struct_const_construct_simple.c3t +++ b/test/test_suite/struct/struct_const_construct_simple.c3t @@ -6,7 +6,7 @@ struct Foo long bar; } -private usize x = $sizeof(Foo); +private usize x = Foo.sizeof; private Foo foo1 = { 1, 2 }; private Foo foo2 = { .foo = 2 }; diff --git a/test/test_suite/struct/struct_pack_and_align.c3t b/test/test_suite/struct/struct_pack_and_align.c3t index ec7b0c579..1546ca928 100644 --- a/test/test_suite/struct/struct_pack_and_align.c3t +++ b/test/test_suite/struct/struct_pack_and_align.c3t @@ -7,7 +7,7 @@ struct Foo1 @packed @align(4) char foo; } -$assert($sizeof(Foo1) == 12); +$assert(Foo1.sizeof == 12); Foo1 foo1 = { 1, 2 }; // <{ i8, i64, [3 x i8] }> @@ -17,7 +17,7 @@ struct Foo2 @packed @align(4) long bar; } -$assert($sizeof(Foo2) == 12); +$assert(Foo2.sizeof == 12); Foo2 foo2 = { 1, 2 }; // <{ i8, i64, [7 x i8] }> @@ -28,7 +28,7 @@ struct Foo3 @packed @align(8) } Foo3 foo3 = { 1, 2 }; -$assert($sizeof(Foo3) == 16); +$assert(Foo3.sizeof == 16); // <{ i8, i64 }> struct Foo4 @packed @@ -37,7 +37,7 @@ struct Foo4 @packed long bar; } -$assert($sizeof(Foo4) == 9); +$assert(Foo4.sizeof == 9); Foo4 foo4 = { 1, 2 }; // { i32, [12 x i8], i8, [15 x i8] } @@ -47,7 +47,7 @@ struct Foo5 ichar foo @align(16); } -$assert($sizeof(Foo5) == 32); +$assert(Foo5.sizeof == 32); Foo5 foo5 = { 1, 2 }; fn int test5(ichar x) @@ -64,7 +64,7 @@ struct Foo6 @packed short c; } -$assert($sizeof(Foo6) == 8); +$assert(Foo6.sizeof == 8); Foo6 foo6 = { 1, 2, 3 }; // #expect: struct2.ll