From a5ce7c47ba0e358e6b46ee26b444be62ac4c4cae Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Wed, 30 Jun 2021 11:46:02 +0200 Subject: [PATCH] Adding "require" precondition. Corrects inferred arrays and fixes so that it uses [*] everywhere. Distict type will now allow methods to be added to it. Added $alignof and $sizeof. --- src/compiler/ast.c | 6 + src/compiler/compiler_internal.h | 42 +- src/compiler/context.c | 4 +- src/compiler/copying.c | 4 + src/compiler/enums.h | 5 + src/compiler/lexer.c | 10 + src/compiler/llvm_codegen_expr.c | 2 + src/compiler/llvm_codegen_internal.h | 1 + src/compiler/llvm_codegen_stmt.c | 18 +- src/compiler/parse_expr.c | 75 ++ src/compiler/parse_global.c | 74 +- src/compiler/parse_stmt.c | 3 + src/compiler/sema_casts.c | 2 - src/compiler/sema_decls.c | 39 +- src/compiler/sema_expr.c | 751 +++++++++++++++--- src/compiler/sema_name_resolution.c | 37 +- src/compiler/sema_stmts.c | 59 +- src/compiler/symtab.c | 10 +- src/compiler/tokens.c | 6 + src/compiler/types.c | 61 +- src/utils/lib.h | 10 + .../compile_time_introspection/alignof.c3t | 90 +++ .../compile_time_introspection/sizeof.c3t | 66 ++ .../sizeof_errors.c3 | 84 ++ .../distinct/distinct_struct_array.c3 | 1 - .../expressions/casts/cast_unknown.c3 | 8 +- .../expressions/ternary_no_ident.c3 | 6 +- .../macro_methods/macro_method_fails.c3 | 2 +- test/test_suite/macros/hash_ident.c3 | 2 +- test/test_suite/macros/no_body.c3 | 2 +- test/test_suite/statements/switch_errors.c3 | 2 +- test/test_suite/types/enum_errors.c3 | 2 +- .../union/union_codegen_overwrite_call.c3t | 4 +- 33 files changed, 1254 insertions(+), 234 deletions(-) create mode 100644 test/test_suite/compile_time_introspection/alignof.c3t create mode 100644 test/test_suite/compile_time_introspection/sizeof.c3t create mode 100644 test/test_suite/compile_time_introspection/sizeof_errors.c3 diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 4117bdc09..633fcddb8 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -581,6 +581,12 @@ void fprint_expr_recursive(Context *context, FILE *file, Expr *expr, int indent) if (!expr) return; switch (expr->expr_kind) { + case EXPR_FLATPATH: + DUMP("(idents)"); + return; + case EXPR_CT_CALL: + DUMP("(ct-call)"); + return; case EXPR_MACRO_BODY_EXPANSION: DUMP("(macro-body-expansion)"); return; diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 97e3ad5c2..de5c6b47c 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -516,6 +516,7 @@ typedef struct Decl_ // Unions, Errtype and Struct use strukt StructDecl strukt; EnumDecl enums; + DistinctDecl distinct_decl; }; }; ImportDecl import; @@ -526,7 +527,6 @@ typedef struct Decl_ AttrDecl attr; TypedefDecl typedef_decl; InterfaceDecl interface_decl; - DistinctDecl distinct_decl; MacroDecl macro_decl; GenericDecl generic_decl; DefineDecl define_decl; @@ -720,6 +720,30 @@ typedef struct Decl *decl; } ExprIdentifierRaw; +typedef struct +{ + bool array : 1; + union + { + ArrayIndex index; + const char *ident; + }; +} ExprFlatElement; + +typedef struct +{ + TokenType token_type; + union + { + Expr **arguments; + struct { + Decl *decl; + Type *type; + ExprFlatElement *flatpath; + }; + }; +} ExprCtCall; + typedef struct { Expr *inner; @@ -805,6 +829,7 @@ typedef struct } ExprLen; + struct Expr_ { ExprKind expr_kind : 8; @@ -826,6 +851,7 @@ struct Expr_ ExprGuard guard_expr; Expr *trycatch_expr; ExprElse else_expr; + ExprFlatElement *flatpath_expr; ExprSliceAssign slice_assign_expr; ExprBinary binary_expr; ExprTernary ternary_expr; @@ -840,6 +866,7 @@ struct Expr_ ExprPlaceholder placeholder_expr; ExprIdentifier macro_identifier_expr; ExprIdentifierRaw ct_ident_expr; + ExprCtCall ct_call_expr; ExprIdentifierRaw ct_macro_ident_expr; ExprMacroExpansion macro_expansion_expr; ExprIdentifierRaw hash_ident_expr; @@ -1628,6 +1655,7 @@ static inline Decl *decl_raw(Decl *decl); static inline bool decl_ok(Decl *decl) { return !decl || decl->decl_kind != DECL_POISONED; } static inline bool decl_poison(Decl *decl) { decl->decl_kind = DECL_POISONED; decl->resolve_status = RESOLVE_DONE; return false; } static inline bool decl_is_struct_type(Decl *decl); +static inline bool decl_is_user_defined_type(Decl *decl); static inline DeclKind decl_from_token(TokenType type); static inline Decl *decl_flatten(Decl *decl) { @@ -1744,6 +1772,8 @@ bool sema_expr_analyse_assign_right_side(Context *context, Expr *expr, Type *lef Decl *sema_resolve_symbol_in_current_dynamic_scope(Context *context, const char *symbol); Decl *sema_resolve_parameterized_symbol(Context *context, TokenId symbol, Path *path); Decl *sema_resolve_normal_symbol(Context *context, TokenId symbol, Path *path, bool handle_error); +Decl *sema_resolve_string_symbol(Context *context, const char *symbol, SourceSpan span, Path *path); + bool sema_resolve_type_info(Context *context, TypeInfo *type_info); bool sema_resolve_type_info_maybe_inferred(Context *context, TypeInfo *type_info, bool allow_inferred_type); bool sema_resolve_type_shallow(Context *context, TypeInfo *type_info, bool allow_inferred_type, bool in_shallow); @@ -1784,7 +1814,7 @@ char *scratch_buffer_to_string(void); const char *scratch_buffer_interned(void); const char *symtab_add(const char *symbol, uint32_t len, uint32_t fnv1hash, TokenType *type); - +const char *symtab_find(const char *symbol, uint32_t len, uint32_t fnv1hash, TokenType *type); void *llvm_target_machine_create(void); void target_setup(BuildTarget *build_target); int target_alloca_addr_space(); @@ -1848,6 +1878,7 @@ static inline bool type_is_signed(Type *type); static inline bool type_is_structlike(Type *type); static inline size_t type_min_alignment(size_t a, size_t b); bool type_is_subtype(Type *type, Type *possible_subtype); +Type *type_from_token(TokenType type); bool type_is_union_struct(Type *type); bool type_is_user_defined(Type *type); bool type_is_structurally_equivalent(Type *type1, Type *type); @@ -2121,6 +2152,13 @@ static inline bool decl_is_struct_type(Decl *decl) return (kind == DECL_UNION) | (kind == DECL_STRUCT) | (kind == DECL_ERR); } +static inline bool decl_is_user_defined_type(Decl *decl) +{ + DeclKind kind = decl->decl_kind; + return (kind == DECL_UNION) | (kind == DECL_STRUCT) | (kind == DECL_ERR) + | (kind == DECL_ENUM) | (kind == DECL_DISTINCT); +} + static inline DeclKind decl_from_token(TokenType type) { if (type == TOKEN_STRUCT) return DECL_STRUCT; diff --git a/src/compiler/context.c b/src/compiler/context.c index 3f54697e9..573b20f61 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -144,10 +144,10 @@ void context_register_global_decl(Context *context, Decl *decl) vec_add(context->vars, decl); decl_set_external_name(decl); break; + case DECL_DISTINCT: case DECL_STRUCT: case DECL_UNION: case DECL_TYPEDEF: - case DECL_DISTINCT: case DECL_ERR: vec_add(context->types, decl); decl_set_external_name(decl); @@ -179,7 +179,7 @@ void context_register_global_decl(Context *context, Decl *decl) vec_add(context->ct_asserts, decl); return; } - DEBUG_LOG("Registering symbol '%s'.", decl->name); + DEBUG_LOG("Registering symbol '%s' in %s.", decl->name, context->module->name->module); Decl *old = stable_set(&context->local_symbols, decl->name, decl); if (!old) diff --git a/src/compiler/copying.c b/src/compiler/copying.c index 2a04d76e9..348369f2c 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -73,8 +73,12 @@ Expr *copy_expr(Expr *source_expr) case EXPR_ENUM_CONSTANT: case EXPR_MEMBER_ACCESS: UNREACHABLE + case EXPR_FLATPATH: case EXPR_UNDEF: return expr; + case EXPR_CT_CALL: + MACRO_COPY_EXPR_LIST(expr->ct_call_expr.arguments); + return expr; case EXPR_PLACEHOLDER: case EXPR_CONST_IDENTIFIER: case EXPR_CT_IDENT: diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 4c578dc97..f4edf8455 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -197,6 +197,7 @@ typedef enum EXPR_MACRO_EXPANSION, EXPR_MEMBER_ACCESS, EXPR_IDENTIFIER, + EXPR_FLATPATH, EXPR_INITIALIZER_LIST, EXPR_LEN, EXPR_PLACEHOLDER, @@ -212,6 +213,7 @@ typedef enum EXPR_TYPEOF, EXPR_UNARY, EXPR_UNDEF, + EXPR_CT_CALL, } ExprKind; typedef enum @@ -440,6 +442,7 @@ typedef enum TOKEN_VOLATILE, TOKEN_WHILE, + TOKEN_CT_ALIGNOF, // $alignof TOKEN_CT_ASSERT, // $assert TOKEN_CT_CASE, // $case TOKEN_CT_DEFAULT, // $default @@ -449,6 +452,8 @@ typedef enum TOKEN_CT_ENDIF, // $endif TOKEN_CT_ENDSWITCH, // $endswitch TOKEN_CT_IF, // $if + TOKEN_CT_OFFSETOF, // $offsetof + TOKEN_CT_SIZEOF, // $sizeof TOKEN_CT_SWITCH, // $switch TOKEN_CT_UNREACHABLE, // $unreachable diff --git a/src/compiler/lexer.c b/src/compiler/lexer.c index 2a990a8ba..c2fc27bf2 100644 --- a/src/compiler/lexer.c +++ b/src/compiler/lexer.c @@ -677,6 +677,14 @@ static void skip_doc_stars(Lexer *lexer) while (peek(lexer) == '*' && peek_next(lexer) != '/') next(lexer); } +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 @@ -732,6 +740,8 @@ static DocEnd parse_doc_remainder(Lexer *lexer) case '*': // Did we find the end of the directives? // If so return control. + if (!end_of_docs_found(lexer)) break; + if (characters_read > 0) { add_token(lexer, TOKEN_DOCS_LINE, 0); diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index ca1a9e34d..022a8add7 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -3134,6 +3134,8 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) case EXPR_CT_IDENT: case EXPR_HASH_IDENT: case EXPR_PLACEHOLDER: + case EXPR_CT_CALL: + case EXPR_FLATPATH: UNREACHABLE case EXPR_UNDEF: // Should never reach this. diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 33654c6a5..e0a4330df 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -248,6 +248,7 @@ void llvm_emit_stmt(GenContext *c, Ast *ast); static inline LLVMValueRef llvm_emit_store(GenContext *context, Decl *decl, LLVMValueRef value); void llvm_emit_panic_on_true(GenContext *c, LLVMValueRef value, const char *panic_name); void llvm_emit_ptr_from_array(GenContext *c, BEValue *value); +void llvm_emit_puts_output(GenContext *c, const char *message); void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failable); void llvm_emit_return_implicit(GenContext *c); LLVMValueRef llvm_emit_struct_gep_raw(GenContext *context, LLVMValueRef ptr, LLVMTypeRef struct_type, unsigned index, unsigned struct_alignment, unsigned offset, unsigned *alignment); diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index a65e087cb..c0aeb6cc2 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -1059,10 +1059,22 @@ static inline void gencontext_emit_assert_stmt(GenContext *c, Ast *ast) LLVMBasicBlockRef on_fail = llvm_basic_block_new(c, "assert_fail"); LLVMBasicBlockRef on_ok = llvm_basic_block_new(c, "assert_ok"); assert(value.kind == BE_BOOLEAN); - llvm_emit_cond_br(c, &value, on_fail, on_ok); + llvm_emit_cond_br(c, &value, on_ok, on_fail); llvm_emit_block(c, on_fail); - // TODO emit message + SourceLocation *loc = TOKLOC(ast->assert_stmt.expr->span.loc); + const char *error; + if (ast->assert_stmt.message) + { + error = ast->assert_stmt.message->const_expr.string.chars; + error = strformat("Assert violation '%s' on line %d, in file '%s'.", error, loc->line, loc->file->name); + } + else + { + error = strformat("Assert violation on line %d, in file '%s'.", loc->line, loc->file->name); + } + llvm_emit_puts_output(c, error); 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; } @@ -1071,7 +1083,7 @@ static inline void gencontext_emit_assert_stmt(GenContext *c, Ast *ast) static inline void gencontext_emit_unreachable_stmt(GenContext *context, Ast *ast) { - // TODO emit message + llvm_emit_puts_output(context, "Unreachable statement reached."); llvm_emit_call_intrinsic(context, intrinsic_id_trap, NULL, 0, NULL, 0); LLVMBuildUnreachable(context->builder); LLVMBasicBlockRef block = llvm_basic_block_new(context, "unreachable_block"); diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 4dda9a49a..06e38db3b 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -577,6 +577,78 @@ static Expr *parse_hash_ident(Context *context, Expr *left) return expr; } +static Expr *parse_ct_call(Context *context, Expr *left) +{ + assert(!left && "Unexpected left hand side"); + Expr *expr = EXPR_NEW_TOKEN(EXPR_CT_CALL, context->tok); + expr->ct_call_expr.token_type = context->tok.type; + advance(context); + CONSUME_OR(TOKEN_LPAREN, poisoned_expr); + Expr *element = TRY_EXPR_OR(parse_expr(context), poisoned_expr); + vec_add(expr->ct_call_expr.arguments, element); + if (try_consume(context, TOKEN_COMMA)) + { + if (context->tok.type == TOKEN_IDENT || context->tok.type == TOKEN_LBRACKET) + { + Expr *idents = EXPR_NEW_TOKEN(EXPR_FLATPATH, context->tok); + bool first = true; + while (1) + { + ExprFlatElement flat_element; + if (try_consume(context, TOKEN_LBRACKET)) + { + Expr *int_expr = TRY_EXPR_OR(parse_expr(context), poisoned_expr); + if (int_expr->expr_kind != EXPR_CONST || !type_kind_is_any_integer(int_expr->const_expr.kind)) + { + SEMA_TOKEN_ERROR(context->tok, "Expected an integer index."); + return poisoned_expr; + } + BigInt *value = &int_expr->const_expr.i; + BigInt limit; + bigint_init_unsigned(&limit, MAX_ARRAYINDEX); + if (bigint_cmp(value, &limit) == CMP_GT) + { + SEMA_ERROR(int_expr, "Array index out of range."); + return poisoned_expr; + } + if (bigint_cmp_zero(value) == CMP_LT) + { + SEMA_ERROR(int_expr, "Array index must be zero or greater."); + return poisoned_expr; + } + TRY_CONSUME_OR(TOKEN_RBRACKET, "Expected a ']' after the number.", poisoned_expr); + flat_element.array = true; + flat_element.index = (ArrayIndex)bigint_as_unsigned(value); + } + else if (try_consume(context, TOKEN_DOT) || first) + { + TRY_CONSUME_OR(TOKEN_IDENT, "Expected an identifier here.", poisoned_expr); + flat_element.array = false; + flat_element.ident = TOKSTR(context->prev_tok); + } + else + { + SEMA_TOKEN_ERROR(context->tok, "Expected '.' or '[' here."); + return poisoned_expr; + } + first = false; + vec_add(idents->flatpath_expr, flat_element); + if (TOKEN_IS(TOKEN_RPAREN)) break; + } + vec_add(expr->ct_call_expr.arguments, idents); + RANGE_EXTEND_PREV(expr); + } + else + { + Expr *idents = TRY_EXPR_OR(parse_expr(context), poisoned_expr); + vec_add(expr->ct_call_expr.arguments, idents); + } + } + CONSUME_OR(TOKEN_RPAREN, poisoned_expr); + RANGE_EXTEND_PREV(expr); + return expr; +} + static Expr *parse_identifier(Context *context, Expr *left) { assert(!left && "Unexpected left hand side"); @@ -1112,4 +1184,7 @@ 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_ALIGNOF] = { parse_ct_call, NULL, PREC_NONE }, + [TOKEN_CT_OFFSETOF] = { parse_ct_call, NULL, PREC_NONE } }; diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 586dbd204..c3ecab455 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -531,71 +531,9 @@ static inline TypeInfo *parse_base_type(Context *context) type_info = type_info_new(TYPE_INFO_IDENTIFIER, source_span_from_token_id(context->tok.id)); type_info->unresolved.name_loc = context->tok.id; break; + case TYPE_TOKENS: case TOKEN_ERR: - type_found = type_error; - break; - case TOKEN_VOID: - type_found = type_void; - break; - case TOKEN_BOOL: - type_found = type_bool; - break; - case TOKEN_CHAR: - type_found = type_char; - break; - case TOKEN_DOUBLE: - type_found = type_double; - break; - case TOKEN_FLOAT: - type_found = type_float; - break; - case TOKEN_I128: - type_found = type_i128; - break; - case TOKEN_ICHAR: - type_found = type_ichar; - break; - case TOKEN_INT: - type_found = type_int; - break; - case TOKEN_IPTR: - type_found = type_iptr; - break; - case TOKEN_IPTRDIFF: - type_found = type_iptrdiff; - break; - case TOKEN_ISIZE: - type_found = type_isize; - break; - case TOKEN_LONG: - type_found = type_long; - break; - case TOKEN_SHORT: - type_found = type_short; - break; - case TOKEN_U128: - type_found = type_u128; - break; - case TOKEN_UINT: - type_found = type_uint; - break; - case TOKEN_ULONG: - type_found = type_ulong; - break; - case TOKEN_UPTR: - type_found = type_uptr; - break; - case TOKEN_UPTRDIFF: - type_found = type_uptrdiff; - break; - case TOKEN_USHORT: - type_found = type_ushort; - break; - case TOKEN_USIZE: - type_found = type_usize; - break; - case TOKEN_TYPEID: - type_found = type_typeid; + type_found = type_from_token(context->tok.type); break; default: // Special case: "virtual *" @@ -2128,18 +2066,18 @@ static bool parse_docs(Context *context, Ast **docs) if (directive == kw_ensure) { doc_ast->doc_directive.kind = DOC_DIRECTIVE_ENSURE; - if (!parse_doc_contract(context, ast)) return false; + if (!parse_doc_contract(context, doc_ast)) return false; goto LINE_END; } if (directive == kw_require) { doc_ast->doc_directive.kind = DOC_DIRECTIVE_REQUIRE; - if (!parse_doc_contract(context, ast)) return false; + if (!parse_doc_contract(context, doc_ast)) return false; goto LINE_END; } if (directive == kw_errors) { - if (!parse_doc_errors(context, ast)) return false; + if (!parse_doc_errors(context, doc_ast)) return false; goto LINE_END; } doc_ast->doc_directive.kind = DOC_DIRECTIVE_UNKNOWN; @@ -2147,9 +2085,11 @@ static bool parse_docs(Context *context, Ast **docs) doc_ast->doc_directive.generic.rest_of_line = parse_doc_opt_rest_of_line(context); LINE_END: + vec_add(ast->directives, doc_ast); if (try_consume(context, TOKEN_DOCS_EOL)) continue; EXPECT_OR(TOKEN_DOCS_END, false); } + *docs = ast; return true; } diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index 35d220d00..91c196e43 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -1072,6 +1072,9 @@ Ast *parse_stmt(Context *context) case TOKEN_TRUE: case TOKEN_LBRAPIPE: case TOKEN_TYPEOF: + case TOKEN_CT_OFFSETOF: + case TOKEN_CT_ALIGNOF: + case TOKEN_CT_SIZEOF: return parse_expr_stmt(context); case TOKEN_ASSERT: return parse_assert_stmt(context); diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index c19142967..24fdc4b3b 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -553,8 +553,6 @@ bool cast_may_implicit(Type *from_type, Type *to_type) { return from->pointer->type_kind == TYPE_ARRAY && from->pointer->array.base == to->array.base; } - - return false; } diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 6b516a206..506e6fbc6 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -123,6 +123,12 @@ static bool sema_analyse_union_members(Context *context, Decl *decl, Decl **memb decl->strukt.union_rep = max_alignment_element; + // All members share the same alignment + VECEACH(members, i) + { + members[i]->alignment = decl->alignment; + } + if (!max_size) { decl->strukt.size = 0; @@ -222,6 +228,8 @@ static bool sema_analyse_struct_members(Context *context, Decl *decl, Decl **mem } } + member->alignment = member_alignment; + offset = align_offset; member->offset = offset; offset += type_size(member->type); @@ -404,7 +412,7 @@ static inline Type *sema_analyse_function_signature(Context *context, FunctionSi if (vec_size(signature->params) > MAX_PARAMS) { SEMA_ERROR(signature->params[MAX_PARAMS], "Number of params exceeds %d which is unsupported.", MAX_PARAMS); - return false; + return NULL; } STable *names = &global_context.scratch_table; stable_clear(names); @@ -770,6 +778,32 @@ static AttributeType sema_analyse_attribute(Context *context, Attr *attr, Attrib } +static inline bool sema_analyse_doc_header(Ast *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) + { + Ast *directive = doc_directives[i]; + if (directive->doc_directive.kind != DOC_DIRECTIVE_PARAM) continue; + TokenId param = directive->doc_directive.param.param; + const char *param_name = TOKSTR(param); + VECEACH(params, j) + { + if (params[j]->name == param_name) goto NEXT; + } + VECEACH(extra_params, j) + { + if (extra_params[j]->name == param_name) goto NEXT; + } + SEMA_TOKID_ERROR(param, "There is no parameter '%s', did you misspell it?", param_name); + return false; + NEXT: + continue; + } + return true; +} static inline bool sema_analyse_func(Context *context, Decl *decl) { DEBUG_LOG("----Analysing function %s", decl->name); @@ -793,6 +827,7 @@ static inline bool sema_analyse_func(Context *context, Decl *decl) } decl_set_external_name(decl); } + if (!sema_analyse_doc_header(decl->docs, decl->func_decl.function_signature.params, NULL)) return decl_poison(decl); VECEACH(decl->attributes, i) { Attr *attr = decl->attributes[i]; @@ -958,6 +993,7 @@ static inline bool sema_analyse_macro(Context *context, Decl *decl) } if (!sema_check_unique_parameters(decl->macro_decl.parameters)) return decl_poison(decl); if (!sema_check_unique_parameters(decl->macro_decl.body_parameters)) return decl_poison(decl); + if (!sema_analyse_doc_header(decl->docs, decl->macro_decl.parameters, decl->macro_decl.body_parameters)) return decl_poison(decl); if (decl->macro_decl.type_parent) { if (!sema_analyse_macro_method(context, decl)) return decl_poison(decl); @@ -1349,6 +1385,7 @@ bool sema_analyse_decl(Context *context, Decl *decl) break; case DECL_DISTINCT: if (!sema_analyse_distinct(context, decl)) return decl_poison(decl); + decl_set_external_name(decl); break; case DECL_TYPEDEF: if (!sema_analyse_typedef(context, decl)) return decl_poison(decl); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index ac4afdab8..791f50efb 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1762,6 +1762,8 @@ static inline void expr_rewrite_to_int_const(Expr *expr_to_rewrite, Type *type, expr_to_rewrite->expr_kind = EXPR_CONST; expr_const_set_int(&expr_to_rewrite->const_expr, value, type->canonical->type_kind); expr_set_type(expr_to_rewrite, type); + expr_to_rewrite->constant = true; + expr_to_rewrite->pure = true; expr_to_rewrite->resolve_status = RESOLVE_DONE; } @@ -1871,6 +1873,17 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr) static void add_members_to_context(Context *context, Decl *decl) { + VECEACH(decl->methods, i) + { + Decl *func = decl->methods[i]; + sema_add_member(context, func); + } + while (decl->decl_kind == DECL_DISTINCT) + { + Type *type = decl->distinct_decl.base_type->canonical; + if (!type_is_user_defined(type)) break; + decl = type->decl; + } if (decl_is_struct_type(decl)) { Decl **members = decl->strukt.members; @@ -1885,11 +1898,6 @@ static void add_members_to_context(Context *context, Decl *decl) sema_add_member(context, member); } } - VECEACH(decl->methods, i) - { - Decl *func = decl->methods[i]; - sema_add_member(context, func); - } } static Expr *enum_minmax_value(Decl *decl, BinaryOp comparison) @@ -1952,8 +1960,9 @@ static TokenId sema_expr_resolve_access_child(Expr *child) return INVALID_TOKEN_ID; } -static inline bool sema_expr_analyse_type_access(Expr *expr, TypeInfo *parent, bool was_group) +static inline bool sema_expr_analyse_type_access(Expr *expr, TypeInfo *parent, bool was_group, bool is_macro, TokenId identifier_token) { + // 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?"); @@ -1962,15 +1971,9 @@ static inline bool sema_expr_analyse_type_access(Expr *expr, TypeInfo *parent, b expr->constant = true; expr->pure = true; - - Type *canonical = parent->type->canonical; - Expr *child = expr->access_expr.child; - - TokenId identifier_token = sema_expr_resolve_access_child(child); - if (TOKEN_IS_INVALID(identifier_token)) return false; - TokenType type = TOKTYPE(identifier_token); - const char *name = TOKSTR(identifier_token); + + // 2. Handle Foo.typeid => return a typeid expression. if (type == TOKEN_TYPEID) { expr_set_type(expr, type_typeid); @@ -1979,26 +1982,36 @@ static inline bool sema_expr_analyse_type_access(Expr *expr, TypeInfo *parent, b expr->resolve_status = RESOLVE_DONE; return true; } + + Type *canonical = parent->type->canonical; + const char *name = TOKSTR(identifier_token); + + // Foo.sizeof => return the size in bytes, e.g. 11 if (name == kw_sizeof) { expr_rewrite_to_int_const(expr, type_compint, type_size(canonical)); return true; } + + // Foo.alignof => return the alignment, e.g. 4 if (name == kw_alignof) { expr_rewrite_to_int_const(expr, type_compint, type_abi_alignment(canonical)); return true; } + // Foo.nameof => return the name, e.g. "Foo" if (name == kw_nameof) { expr_rewrite_to_string(expr, canonical->name); return true; } + // Foo.qnameof => return the qualified name, e.g. "mylib::Foo" if (name == kw_qnameof) { expr_rewrite_to_string(expr, type_generate_qname(canonical)); return true; } + // if (!type_may_have_sub_elements(canonical)) { SEMA_ERROR(expr, "'%s' does not have a property '%s'.", type_to_error_string(parent->type), name); @@ -2081,7 +2094,7 @@ static inline bool sema_expr_analyse_type_access(Expr *expr, TypeInfo *parent, b return false; } -static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr) +static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr, bool is_macro, TokenId identifier_token) { Expr *parent = expr->access_expr.parent; @@ -2090,20 +2103,17 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr) Expr *child = expr->access_expr.child; - TokenId identifier_token = sema_expr_resolve_access_child(child); - if (TOKEN_IS_INVALID(identifier_token)) return false; TokenType type = TOKTYPE(identifier_token); const char *name = TOKSTR(identifier_token); Decl *ref = parent->access_expr.ref; - bool is_macro = child->expr_kind == EXPR_MACRO_EXPANSION; - bool is_plain_member = ref->decl_kind == DECL_VAR; + bool is_leaf_member_ref = ref->decl_kind == DECL_VAR; if (type == TOKEN_TYPEID && !is_macro) { expr_set_type(expr, type_typeid); expr->expr_kind = EXPR_TYPEID; - if (is_plain_member) + if (is_leaf_member_ref) { expr->typeid_expr = ref->var.type_info; } @@ -2116,22 +2126,23 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr) } if (name == kw_sizeof && !is_macro) { - expr_rewrite_to_int_const(expr, type_usize, type_size(ref->type)); + expr_rewrite_to_int_const(expr, type_compint, type_size(ref->type)); return true; } if (name == kw_alignof && !is_macro) { - expr_rewrite_to_int_const(expr, type_usize, type_abi_alignment(ref->type)); + expr_rewrite_to_int_const(expr, type_compint, type_abi_alignment(ref->type)); return true; } if (name == kw_ordinal && !is_macro) { if (ref->decl_kind == DECL_ENUM_CONSTANT) { - expr_rewrite_to_int_const(expr, type_usize, ref->enum_constant.ordinal); + expr_rewrite_to_int_const(expr, type_compint, ref->enum_constant.ordinal); return true; } } + if (name == kw_nameof && !is_macro) { expr_rewrite_to_string(expr, ref->name); @@ -2139,7 +2150,8 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr) } if (name == kw_qnameof && !is_macro) { - TODO + expr_rewrite_to_string(expr, ref->external_name ?: ref->name); + return true; } if (name == kw_kindof && !is_macro) { @@ -2148,7 +2160,7 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr) // If we have something like struct Foo { Bar b; int c; struct d { int e; } } // If we are inspecting Foo.c we're done here. Otherwise handle struct d / Bar b case // The same way. - if (is_plain_member) + if (is_leaf_member_ref) { if (!type_is_structlike(ref->type)) { @@ -2163,6 +2175,7 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr) // Pretend to be an inline struct. ref = ref->type->decl; } + VECEACH(ref->methods, i) { Decl *decl = ref->methods[i]; @@ -2186,125 +2199,129 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr) return true; } } - VECEACH(ref->strukt.members, i) + // We want a distinct to be able to access members. + Decl *flat_ref = ref; + while (flat_ref->decl_kind == DECL_DISTINCT) { - Decl *member = ref->strukt.members[i]; - if (name == member->name) - { - if (is_macro) + Type *flat_type = flat_ref->distinct_decl.base_type->canonical; + if (!type_is_user_defined(flat_type)) break; + flat_ref = flat_type->decl; + } + + switch (flat_ref->decl_kind) + { + case DECL_STRUCT: + case DECL_UNION: + case DECL_ERR: + VECEACH(flat_ref->strukt.members, i) { - SEMA_ERROR(child, "member '%s' should not be prefixed with '@'.", name); - return false; + Decl *member = ref->strukt.members[i]; + if (name == member->name) + { + if (is_macro) + { + SEMA_ERROR(child, "member '%s' should not be prefixed with '@'.", name); + return false; + } + expr->expr_kind = EXPR_MEMBER_ACCESS; + expr->access_expr.ref = member; + expr_set_type(expr, member->type); + return true; + } } - expr->expr_kind = EXPR_MEMBER_ACCESS; - expr->access_expr.ref = member; - expr_set_type(expr, member->type); - return true; - } + default: + break; } SEMA_ERROR(expr, "No function or member '%s.%s' found.", "todo", name); TODO return false; } - +/** + * Analyse "x.y" + */ static inline bool sema_expr_analyse_access(Context *context, Expr *expr) { Expr *parent = expr->access_expr.parent; bool was_group = parent->expr_kind == EXPR_GROUP; + + // 1. Resolve the left hand if (!sema_analyse_expr_value(context, NULL, parent)) return false; - if (parent->type == type_typeinfo) - { - return sema_expr_analyse_type_access(expr, parent->type_expr, was_group); - } - if (parent->expr_kind == EXPR_MEMBER_ACCESS) - { - return sema_expr_analyse_member_access(context, expr); - } - bool is_function_or_macro = false; - if (parent->expr_kind == EXPR_ACCESS) - { - is_function_or_macro = parent->access_expr.ref->decl_kind == DECL_FUNC || parent->access_expr.ref->decl_kind == DECL_MACRO; - } - if (parent->expr_kind == EXPR_IDENTIFIER) - { - is_function_or_macro = parent->identifier_expr.decl->decl_kind == DECL_FUNC || parent->identifier_expr.decl->decl_kind == DECL_MACRO; - } - - if (is_function_or_macro) - { - SEMA_ERROR(expr->access_expr.child, "No such member or function could be found."); - return false; - } + // 2. The right hand side may be a @ident or ident Expr *child = expr->access_expr.child; bool is_macro = child->expr_kind == EXPR_MACRO_EXPANSION; + // 3. Find the actual token. TokenId identifier_token = sema_expr_resolve_access_child(child); if (TOKEN_IS_INVALID(identifier_token)) return false; + // 2. If our left hand side is a type, e.g. MyInt.abc, handle this here. + Type *parent_type = parent->type; + if (parent->expr_kind == EXPR_TYPEINFO) + { + return sema_expr_analyse_type_access(expr, parent->type_expr, was_group, is_macro, identifier_token); + } + + // 3. The left hand side may already be indexing into a type: + // x.y.z. In this case "x.y" is the parent. + if (parent->expr_kind == EXPR_MEMBER_ACCESS) + { + return sema_expr_analyse_member_access(context, expr, is_macro, identifier_token); + } + + + // 6. Copy failability expr->failable = parent->failable; assert(expr->expr_kind == EXPR_ACCESS); assert(parent->resolve_status == RESOLVE_DONE); - Type *parent_type = parent->type; - Type *type = type_flatten(parent_type); - + // 7. Is this a pointer? If so we insert a deref. + Type *type = parent_type->canonical; bool is_pointer = type->type_kind == TYPE_POINTER; if (is_pointer) { - type = type_flatten(type->pointer); + type = type->pointer->canonical; + parent_type = type; if (!sema_cast_rvalue(context, NULL, parent)) return false; insert_access_deref(expr); parent = expr->access_expr.parent; } + + // 8. Depending on parent type, we have some hard coded types const char *kw = TOKSTR(identifier_token); Expr *current_parent = parent; + + Type *flat_type = type_flatten(type); CHECK_DEEPER: - switch (type->type_kind) + // 9. Fix hard coded function `len` on subarrays and arrays + if (!is_macro && kw == kw_len) { - case TYPE_SUBARRAY: - if (is_macro) goto NO_MATCH; - if (kw == kw_sizeof) - { - expr_rewrite_to_int_const(expr, type_usize, type_size(type)); - return true; - } - if (kw == kw_len) - { - expr->expr_kind = EXPR_LEN; - expr->len_expr.inner = parent; - expr_set_type(expr, type_void); - expr->resolve_status = RESOLVE_DONE; - return true; - } - goto NO_MATCH; - case TYPE_ARRAY: - if (is_macro) goto NO_MATCH; - if (kw == kw_sizeof) - { - expr_rewrite_to_int_const(expr, type_usize, type_size(type)); - return true; - } - if (kw == kw_len) - { - expr_rewrite_to_int_const(expr, type_usize, type->array.len); - return true; - } - goto NO_MATCH; - case TYPE_ENUM: - case TYPE_ERRTYPE: - case TYPE_STRUCT: - case TYPE_UNION: - break; - default: - NO_MATCH: - SEMA_ERROR(expr, "There is no member or method '%s' on '%s'", TOKSTR(identifier_token), type_to_error_string(parent_type)); - return false; + if (flat_type->type_kind == TYPE_SUBARRAY) + { + expr->expr_kind = EXPR_LEN; + expr->len_expr.inner = parent; + expr_set_type(expr, type_void); + expr->resolve_status = RESOLVE_DONE; + return true; + } + if (flat_type->type_kind == TYPE_ARRAY) + { + expr_rewrite_to_int_const(expr, type_usize, type->array.len); + return true; + } } + // 9. At this point we may only have distinct, struct, union, error, enum + if (!type_may_have_sub_elements(type)) + { + SEMA_ERROR(expr, "There is no member or method '%s' on '%s'", kw, type_to_error_string(parent_type)); + return false; + } + + // 10. Dump all members and methods into the scope. Decl *decl = type->decl; Decl *member; SCOPE_START @@ -2312,10 +2329,10 @@ CHECK_DEEPER: member = sema_resolve_symbol_in_current_dynamic_scope(context, kw); SCOPE_END; - + // 11. If we didn't find a match... if (!member) { - // We have a potential embedded struct check: + // 11a. We have a potential embedded struct check: if (type_is_substruct(type)) { Expr *embedded_struct = expr_access_inline_member(parent, type->decl); @@ -2323,23 +2340,29 @@ CHECK_DEEPER: type = embedded_struct->type->canonical; goto CHECK_DEEPER; } + + // 11b. Otherwise we give up. SEMA_ERROR(expr, "There is no field or method '%s.%s'.", decl->name, kw); return false; } + + // 12a. If the member was a macro and it isn't prefixed with `@` then that's an error. if (member->decl_kind == DECL_MACRO && !is_macro) { SEMA_ERROR(expr, "Expected '@' before the macro name."); 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) { SEMA_ERROR(expr, "'@' should only be placed in front of macro names."); return false; } + + // 13. Copy properties. expr->access_expr.parent = current_parent; expr->constant = expr->access_expr.parent->constant; expr->pure = expr->access_expr.parent->pure; - expr_set_type(expr, member->type); expr->access_expr.ref = member; return true; @@ -3197,10 +3220,29 @@ bool sema_expr_analyse_assign_right_side(Context *context, Expr *expr, Type *lef { return sema_expr_analyse_slice_assign(context, expr, left_type, right, lhs_is_failable); } - // 1. Evaluate right side to required type. - if (!sema_analyse_expr_of_required_type(context, left_type, right, lhs_is_failable != FAILABLE_NO)) return false; - // 2. Set the result to the type on the right side. + // 1. Evaluate right side to required type. + if (!sema_analyse_expr(context, left_type, right)) return false; + if (right->failable && lhs_is_failable == FAILABLE_NO) + { + if (!left_type) left_type = right->type; + SEMA_ERROR(right, "'%s!' cannot be converted into '%s'.", type_to_error_string(right->type), type_to_error_string(left_type)); + return false; + } + + // 2. Evaluate right hand side, making special concession for inferred arrays. + do + { + if (!left_type) break; + if (left_type->canonical->type_kind == TYPE_INFERRED_ARRAY && right->type->canonical->type_kind == TYPE_ARRAY) + { + if (left_type->canonical->array.base == right->type->canonical->array.base) break; + } + assert(right->type->canonical->type_kind != TYPE_INFERRED_ARRAY); + if (!cast_implicit(right, left_type)) return false; + } while (0); + + // 3. Set the result to the type on the right side. if (expr) expr_copy_types(expr, right); return true; @@ -5010,6 +5052,492 @@ static inline bool sema_expr_analyse_placeholder(Context *context, Type *to, Exp 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 + 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); + ByteSize value = 0; + while (is_number(minilex_peek(lexer, 0))) + { + ByteSize old_value = value; + value = value * 10 + minilex_next(lexer) - '0'; + if (old_value > value || value > max) + { + return UINT64_MAX; + } + } + return value; +} + +static inline bool sema_analyse_idents_string(Context *context, MiniLexer *lexer, ExprFlatElement **elements_ref) +{ + ExprFlatElement *elements = NULL; + + char c; + ExprFlatElement element; + while (1) + { + minilex_skip_whitespace(lexer); + 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 = (ArrayIndex)value; + } + else + { + 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); + minilex_skip_whitespace(lexer); + if (minilex_match(lexer, '\0')) break; + if (!minilex_match(lexer, '.') && minilex_peek(lexer, 0) != '[') return false; + } + *elements_ref = elements; + return true; +} + +static inline bool sema_analyse_identifier_path_string(Context *context, Expr *expr, Decl **decl_ref, Type **type_ref, ExprFlatElement **idents_ref) +{ + char *chars = expr->const_expr.string.chars; + uint32_t len = expr->const_expr.string.len; + if (!len) + { + SEMA_ERROR(expr, "Expected a name here."); + return false; + } + + // TODO HANDLE VIRTUAL! + + 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); + } + 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) goto FAIL; + 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) + { + SEMA_ERROR(expr, "'%s' could not be found, did you misspell it?", chars); + return false; + } + Type *type = NULL; + Decl *decl = NULL; + if (token_is_type(token_type)) + { + type = type_from_token(token_type); + } + else + { + decl = TRY_DECL_OR(sema_resolve_string_symbol(context, symbol, expr->span, path), 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; + } + ByteSize array_size = 0; + while (is_number(minilex_peek(&lexer, 0))) + { + ByteSize old_array_size = array_size; + array_size = array_size * 10 + minilex_next(&lexer) - '0'; + if (old_array_size > array_size || array_size > MAX_ARRAYINDEX) + { + SEMA_ERROR(expr, "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); + } + } + + minilex_skip_whitespace(&lexer); + if (minilex_match(&lexer, '\0')) goto EXIT; + + + minilex_skip_whitespace(&lexer); + if (!minilex_match(&lexer, '.')) + { + SEMA_ERROR(expr, "A '.' was expected after '%s'.", symbol); + return false; + } + + if (!sema_analyse_idents_string(context, &lexer, idents_ref)) + { + SEMA_ERROR(expr, "The path to an existing member was expected after '%s', did you make a mistake?", symbol); + return false; + } + +EXIT: + *decl_ref = decl; + *type_ref = type; + return true; + +FAIL: + SEMA_ERROR(expr, "'%s' is not a known identifier, did you misspell it?", chars); + return false; + +FAIL_PARSE: + SEMA_ERROR(expr, "'%s' could not be parsed as an identifier, did you make a mistake?", chars); + return false; + +} + + +static inline bool sema_analyse_ct_call_parameters(Context *context, Expr *expr) +{ + if (expr->resolve_status == RESOLVE_DONE) return true; + + Expr **elements = expr->ct_call_expr.arguments; + unsigned count = vec_size(elements); + if (count > 2) + { + SEMA_ERROR(elements[2], "The function can only take one or two arguments."); + return expr_poison(expr); + } + Expr *first_expr = elements[0]; + if (!sema_analyse_expr_value(context, NULL, first_expr)) return false; + ExprFlatElement *flatpath = NULL; + Expr *second_expr = NULL; + if (count == 2) + { + second_expr = elements[1]; + if (second_expr->expr_kind != EXPR_FLATPATH) + { + if (!sema_analyse_expr_value(context, NULL, second_expr)) return false; + if (second_expr->expr_kind != EXPR_CONST || second_expr->const_expr.kind != TYPE_STRLIT) + { + SEMA_ERROR(second_expr, "Expected a string here."); + return false; + } + MiniLexer lexer = { .chars = second_expr->const_expr.string.chars }; + if (!sema_analyse_idents_string(context, &lexer, &flatpath)) + { + SEMA_ERROR(expr, "The path to an existing member was expected, did you make a mistake?"); + return false; + } + } + else + { + flatpath = second_expr->flatpath_expr; + } + } + Decl *decl = NULL; + Type *type = NULL; + if (first_expr->expr_kind == EXPR_CONST && first_expr->const_expr.kind == TYPE_STRLIT) + { + ExprFlatElement *path_elements = NULL; + if (!sema_analyse_identifier_path_string(context, first_expr, &decl, &type, &path_elements)) return false; + if (path_elements) + { + if (flatpath) + { + SEMA_ERROR(elements[1], "You cannot combine a string with a member path with a separate path here."); + return false; + } + flatpath = path_elements; + } + } + else + { + switch (first_expr->expr_kind) + { + case EXPR_IDENTIFIER: + decl = first_expr->identifier_expr.decl; + break; + case EXPR_TYPEINFO: + type = first_expr->type_expr->type; + break; + case EXPR_TYPEOF: + type = first_expr->typeof_expr->type; + break; + default: + break; + } + } + if (!type && !decl) + { + SEMA_ERROR(first_expr, "A type or declaration was expected."); + return false; + } + if (!decl && type_is_user_defined(type)) decl = type->decl; + if (decl) + { + if (!sema_analyse_decl(context, decl)) return false; + if (decl) + { + switch (decl->decl_kind) + { + case DECL_DISTINCT: + case DECL_ENUM: + case DECL_ERR: + case DECL_FUNC: + case DECL_STRUCT: + case DECL_TYPEDEF: + case DECL_UNION: + break; + case DECL_VAR: + if (decl->type) break; + FALLTHROUGH; + default: + SEMA_ERROR(first_expr, "A type or typed declaration was expected."); + return false; + } + } + type = decl->type; + } + expr->ct_call_expr.flatpath = flatpath; + expr->ct_call_expr.decl = decl; + expr->ct_call_expr.type = type; + + return true; +} + +static inline bool sema_expr_analyse_ct_sizeof(Context *context, Type *to, Expr *expr) +{ + Expr *first = expr->ct_call_expr.arguments[0]; + if (!sema_analyse_ct_call_parameters(context, expr)) return false; + Type *type = expr->ct_call_expr.type->canonical; + ExprFlatElement *elements = expr->ct_call_expr.flatpath; + VECEACH(elements, i) + { + ExprFlatElement element = elements[i]; + type = type_flatten_distinct(type); + if (element.array) + { + if (type->type_kind != TYPE_ARRAY) + { + SEMA_ERROR(first, "%s is not a fixed size array.", type_quoted_error_string(expr->ct_call_expr.type)); + return false; + } + type = type->array.base; + continue; + } + if (!type_is_structlike(type)) + { + if (i == 0) + { + SEMA_ERROR(first, "%s has no members.", type_quoted_error_string(expr->ct_call_expr.type)); + } + else + { + SEMA_ERROR(first, "There is no such member in %s.", type_quoted_error_string(expr->ct_call_expr.type)); + } + return false; + } + Decl *decl = type->decl; + SCOPE_START + add_members_to_context(context, type->decl); + decl = sema_resolve_symbol_in_current_dynamic_scope(context, element.ident); + SCOPE_END; + if (!decl) + { + SEMA_ERROR(first, "There is no such member in %s.", type_quoted_error_string(expr->ct_call_expr.type)); + return false; + } + type = decl->type; + } + expr_rewrite_to_int_const(expr, type_compint, type_size(type)); + return true; +} + +static inline bool sema_expr_analyse_ct_alignof(Context *context, Type *to, Expr *expr) +{ + Expr *first = expr->ct_call_expr.arguments[0]; + if (!sema_analyse_ct_call_parameters(context, expr)) return false; + Type *type = expr->ct_call_expr.type->canonical; + ExprFlatElement *elements = expr->ct_call_expr.flatpath; + AlignSize align = expr->ct_call_expr.decl ? expr->ct_call_expr.decl->alignment : type_abi_alignment(type); + VECEACH(elements, i) + { + ExprFlatElement element = elements[i]; + type = type_flatten_distinct(type); + if (element.array) + { + if (type->type_kind != TYPE_ARRAY) + { + SEMA_ERROR(first, "%s is not a fixed size array.", type_quoted_error_string(expr->ct_call_expr.type)); + return false; + } + type = type->array.base; + ByteSize size = type_size(type); + align = (AlignSize)type_min_alignment(size * element.index, align); + continue; + } + if (!type_is_structlike(type)) + { + if (i == 0) + { + SEMA_ERROR(first, "%s has no members.", type_quoted_error_string(expr->ct_call_expr.type)); + } + else + { + SEMA_ERROR(first, "There is no such member in %s.", type_quoted_error_string(expr->ct_call_expr.type)); + } + return false; + } + Decl *decl = type->decl; + SCOPE_START + add_members_to_context(context, type->decl); + decl = sema_resolve_symbol_in_current_dynamic_scope(context, element.ident); + SCOPE_END; + if (!decl) + { + SEMA_ERROR(first, "There is no such member in %s.", type_quoted_error_string(expr->ct_call_expr.type)); + return false; + } + type = decl->type; + align = type_min_alignment(decl->offset, align); + } + + expr_rewrite_to_int_const(expr, type_compint, align); + + return true; +} + +static inline bool sema_expr_analyse_ct_call(Context *context, Type *to, Expr *expr) +{ + switch (expr->ct_call_expr.token_type) + { + case TOKEN_CT_SIZEOF: + return sema_expr_analyse_ct_sizeof(context, to, expr); + case TOKEN_CT_ALIGNOF: + return sema_expr_analyse_ct_alignof(context, to, expr); + case TOKEN_CT_OFFSETOF: + TODO + default: + UNREACHABLE + } +} static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr *expr) { @@ -5021,7 +5549,10 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * case EXPR_MEMBER_ACCESS: case EXPR_DESIGNATOR: case EXPR_MACRO_BODY_EXPANSION: + case EXPR_FLATPATH: UNREACHABLE + case EXPR_CT_CALL: + return sema_expr_analyse_ct_call(context, to, expr); case EXPR_HASH_IDENT: return sema_expr_analyse_hash_identifier(context, to, expr); case EXPR_CT_IDENT: diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index 03aebee95..6b31eb81b 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -170,20 +170,20 @@ static Decl *sema_resolve_no_path_symbol(Context *context, const char *symbol, } -static void sema_report_error_on_decl(const char *symbol_str, TokenId symbol, Decl *found, Decl *ambiguous_decl, +static void sema_report_error_on_decl(const char *symbol_str, SourceSpan span, Decl *found, Decl *ambiguous_decl, Decl *private_decl) { if (!found && private_decl) { - SEMA_TOKID_ERROR(symbol, "The %s '%s' is not visible from this module.", decl_to_name(private_decl), symbol_str); + sema_error_range(span, "The %s '%s' is not visible from this module.", decl_to_name(private_decl), symbol_str); return; } if (ambiguous_decl) { assert(found); const char *symbol_type = decl_to_name(found); - SEMA_TOKID_ERROR(symbol, - "The %s '%s' is defined in both '%s' and '%s', please use either %s::%s or %s::%s to resolve the ambiguity.", + sema_error_range(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, @@ -195,27 +195,15 @@ static void sema_report_error_on_decl(const char *symbol_str, TokenId symbol, De return; } assert(!found); - switch (TOKTYPE(symbol)) - { - case TOKEN_TYPE_IDENT: - SEMA_TOKID_ERROR(symbol, "The type '%s' could not be found, did you spell it right?", symbol_str); - break; - case TOKEN_CONST_IDENT: - SEMA_TOKID_ERROR(symbol, "The constant '%s' could not be found, did you spell it right?", symbol_str); - break; - default: - SEMA_TOKID_ERROR(symbol, "The identifier '%s' could not be found, did you spell it right?", symbol_str); - break; - } + sema_error_range(span, "'%s' could not be found, did you spell it right?", symbol_str); } -static Decl *sema_resolve_symbol(Context *context, TokenId symbol, Path *path, bool report_error) +static Decl *sema_resolve_symbol(Context *context, const char *symbol_str, SourceSpan span, Path *path, bool report_error) { Decl *ambiguous_other_decl = NULL; Decl *private_decl = NULL; bool path_found = false; Decl *decl; - const char *symbol_str = TOKSTR(symbol); if (path) { decl = sema_resolve_path_symbol(context, symbol_str, path, &ambiguous_other_decl, &private_decl, &path_found); @@ -233,7 +221,7 @@ static Decl *sema_resolve_symbol(Context *context, TokenId symbol, Path *path, b if (!decl || ambiguous_other_decl) { if (!report_error) return NULL; - sema_report_error_on_decl(symbol_str, symbol, decl, ambiguous_other_decl, private_decl); + sema_report_error_on_decl(symbol_str, span, decl, ambiguous_other_decl, private_decl); return poisoned_decl; } if (decl->module && decl->module != context->module) @@ -293,7 +281,7 @@ Decl *sema_resolve_parameterized_symbol(Context *context, TokenId symbol, Path * // 14. Error report if (!decl || ambiguous_other_decl) { - sema_report_error_on_decl(symbol_str, symbol, decl, ambiguous_other_decl, private_decl); + sema_report_error_on_decl(symbol_str, source_span_from_token_id(symbol), decl, ambiguous_other_decl, private_decl); return poisoned_decl; } return decl; @@ -335,7 +323,7 @@ Decl *sema_resolve_parameterized_symbol(Context *context, TokenId symbol, Path * // 14. Error report if (!decl || ambiguous_other_decl) { - sema_report_error_on_decl(symbol_str, symbol, decl, ambiguous_other_decl, private_decl); + sema_report_error_on_decl(symbol_str, source_span_from_token_id(symbol), decl, ambiguous_other_decl, private_decl); return poisoned_decl; } return decl; @@ -343,7 +331,12 @@ Decl *sema_resolve_parameterized_symbol(Context *context, TokenId symbol, Path * Decl *sema_resolve_normal_symbol(Context *context, TokenId symbol, Path *path, bool handle_error) { - return sema_resolve_symbol(context, symbol, path, handle_error); + return sema_resolve_symbol(context, TOKSTR(symbol), source_span_from_token_id(symbol), path, handle_error); +} + +Decl *sema_resolve_string_symbol(Context *context, const char *symbol, SourceSpan span, Path *path) +{ + return sema_resolve_symbol(context, symbol, span, path, true); } static inline bool sema_append_local(Context *context, Decl *decl) diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 42876401f..74ac547bd 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -376,17 +376,17 @@ static inline bool sema_analyse_local_decl(Context *context, Decl *decl) if (!sema_expr_analyse_assign_right_side(context, NULL, decl->type, init, decl->var.failable || decl->var.unwrap ? FAILABLE_YES : FAILABLE_NO)) return decl_poison(decl); - if (decl->type) - { - expr_set_type(decl->var.init_expr, decl->type); - } - if (type_is_inferred) { Type *right_side_type = init->type->canonical; assert(right_side_type->type_kind == TYPE_ARRAY); decl->type = type_get_array(decl->type->array.base, right_side_type->array.len); } + else if (decl->type) + { + expr_set_type(decl->var.init_expr, decl->type); + } + if (decl->var.unwrap && !init->failable) { @@ -1813,6 +1813,46 @@ bool sema_analyse_statement(Context *context, Ast *statement) } +static bool sema_analyse_requires(Context *context, Ast *docs, Ast ***asserts) +{ + if (!docs) return true; + Ast **directives = docs->directives; + Ast **output = NULL; + VECEACH(directives, i) + { + Ast *directive = directives[i]; + if (directive->doc_directive.kind != DOC_DIRECTIVE_REQUIRE) continue; + Expr *comment = directive->doc_directive.contract.comment; + Expr *declexpr = directive->doc_directive.contract.decl_exprs; + if (comment) + { + if (comment->expr_kind != EXPR_CONST || comment->const_expr.kind != TYPE_STRLIT) + { + SEMA_ERROR(comment, "Expected a string here."); + return false; + } + } + assert(declexpr->expr_kind == EXPR_DECL_LIST); + + VECEACH(declexpr->dexpr_list_expr, j) + { + Ast *ast = declexpr->dexpr_list_expr[j]; + if (ast->ast_kind != AST_EXPR_STMT) + { + SEMA_ERROR(ast, "Only expressions are allowed."); + return false; + } + Expr *expr = ast->expr_stmt; + if (!sema_analyse_expr_of_required_type(context, type_bool, expr, false)) return false; + Ast *assert = new_ast(AST_ASSERT_STMT, expr->span); + assert->assert_stmt.expr = expr; + assert->assert_stmt.message = comment; + vec_add(output, assert); + } + } + *asserts = output; + return true; +} bool sema_analyse_function_body(Context *context, Decl *func) { @@ -1846,6 +1886,8 @@ bool sema_analyse_function_body(Context *context, Decl *func) { if (!sema_add_local(context, params[i])) return false; } + Ast **asserts = NULL; + if (!sema_analyse_requires(context, func->docs, &asserts)) return false; if (!sema_analyse_compound_statement_no_scope(context, func->func_decl.body)) return false; assert(context->active_scope.depth == 1); if (!context->active_scope.jump_end) @@ -1858,6 +1900,13 @@ bool sema_analyse_function_body(Context *context, Decl *func) return false; } } + if (asserts) + { + Ast *ast = new_ast(AST_COMPOUND_STMT, func->func_decl.body->span); + ast->compound_stmt.stmts = asserts; + vec_add(ast->compound_stmt.stmts, func->func_decl.body); + func->func_decl.body = ast; + } SCOPE_END; return true; } diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index 6605e9c7d..3f780e913 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -117,7 +117,7 @@ void symtab_init(uint32_t capacity) kw_param = KW_DEF("param"); kw_pure = KW_DEF("pure"); kw_qnameof = KW_DEF("qnameof"); - kw_require = KW_DEF("required"); + kw_require = KW_DEF("require"); kw_sizeof = KW_DEF("sizeof"); kw_std = KW_DEF("std"); kw___ceil = KW_DEF("__ceil"); @@ -180,6 +180,14 @@ const char *symtab_add(const char *symbol, uint32_t len, uint32_t fnv1hash, Toke return entry->value; } +const char *symtab_find(const char *symbol, uint32_t len, uint32_t fnv1hash, TokenType *type) +{ + SymEntry *entry = entry_find(symbol, len, fnv1hash); + if (!entry->value) return NULL; + *type = entry->type; + return entry->value; +} + void stable_init(STable *table, uint32_t initial_size) { assert(initial_size && "Size must be larger than 0"); diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index afc7a9d34..a88b8186d 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -319,6 +319,8 @@ const char *token_type_to_string(TokenType type) case TOKEN_DOCS_LINE: return "DOCS_LINE"; + case TOKEN_CT_ALIGNOF: + return "$alignof"; case TOKEN_CT_ASSERT: return "$assert"; case TOKEN_CT_CASE: @@ -337,6 +339,10 @@ const char *token_type_to_string(TokenType type) return "$endswitch"; case TOKEN_CT_IF: return "$if"; + case TOKEN_CT_OFFSETOF: + return "$offsetof"; + case TOKEN_CT_SIZEOF: + return "$sizeof"; case TOKEN_CT_SWITCH: return "$switch"; case TOKEN_CT_UNREACHABLE: diff --git a/src/compiler/types.c b/src/compiler/types.c index 33a9481d1..201d58a75 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -50,7 +50,7 @@ Type *type_error = &t.error; Type *type_varheader = &t.varheader; static unsigned size_subarray; -static unsigned alignment_subarray; +static AlignSize alignment_subarray; unsigned size_error_code; unsigned alignment_error_code; @@ -147,7 +147,7 @@ const char *type_to_error_string(Type *type) asprintf(&buffer, "%s[%llu]", type_to_error_string(type->array.base), (unsigned long long)type->array.len); return buffer; case TYPE_INFERRED_ARRAY: - asprintf(&buffer, "%s[?]", type_to_error_string(type->array.base)); + asprintf(&buffer, "%s[*]", type_to_error_string(type->array.base)); return buffer; case TYPE_SUBARRAY: asprintf(&buffer, "%s[]", type_to_error_string(type->array.base)); @@ -753,7 +753,7 @@ static Type *type_generate_inferred_array(Type *arr_type, bool canonical) Type *arr = arr_type->type_cache[INFERRED_ARRAY_OFFSET]; if (arr == NULL) { - arr = type_new(TYPE_INFERRED_ARRAY, strformat("%s[_]", arr_type->name)); + arr = type_new(TYPE_INFERRED_ARRAY, strformat("%s[*]", arr_type->name)); arr->array.base = arr_type; arr_type->type_cache[INFERRED_ARRAY_OFFSET] = arr; if (arr_type == arr_type->canonical) @@ -887,6 +887,7 @@ bool type_is_user_defined(Type *type) case TYPE_UNION: case TYPE_ERRTYPE: case TYPE_TYPEDEF: + case TYPE_DISTINCT: return true; default: return false; @@ -1227,12 +1228,64 @@ bool type_is_subtype(Type *type, Type *possible_subtype) } - +Type *type_from_token(TokenType type) +{ + switch (type) + { + case TOKEN_ERR: + return type_error; + case TOKEN_VOID: + return type_void; + case TOKEN_BOOL: + return type_bool; + case TOKEN_CHAR: + return type_char; + case TOKEN_DOUBLE: + return type_double; + case TOKEN_FLOAT: + return type_float; + case TOKEN_I128: + return type_i128; + case TOKEN_ICHAR: + return type_ichar; + case TOKEN_INT: + return type_int; + case TOKEN_IPTR: + return type_iptr; + case TOKEN_IPTRDIFF: + return type_iptrdiff; + case TOKEN_ISIZE: + return type_isize; + case TOKEN_LONG: + return type_long; + case TOKEN_SHORT: + return type_short; + case TOKEN_U128: + return type_u128; + case TOKEN_UINT: + return type_uint; + case TOKEN_ULONG: + return type_ulong; + case TOKEN_UPTR: + return type_uptr; + case TOKEN_UPTRDIFF: + return type_uptrdiff; + case TOKEN_USHORT: + return type_ushort; + case TOKEN_USIZE: + return type_usize; + case TOKEN_TYPEID: + return type_typeid; + default: + UNREACHABLE + } +} bool type_may_have_sub_elements(Type *type) { // An alias is not ok. switch (type->type_kind) { + case TYPE_DISTINCT: case TYPE_UNION: case TYPE_STRUCT: case TYPE_ENUM: diff --git a/src/utils/lib.h b/src/utils/lib.h index b8bd51e16..cf8960cc3 100644 --- a/src/utils/lib.h +++ b/src/utils/lib.h @@ -59,6 +59,11 @@ static inline bool is_lower(char c) return c >= 'a' && c <= 'z'; } +static inline bool is_lower_(char c) +{ + return c == '_' || (c >= 'a' && c <= 'z'); +} + static inline bool is_upper(char c) { return c >= 'A' && c <= 'Z'; @@ -269,6 +274,11 @@ static inline bool is_letter(char c) } } +static inline bool is_number(char c) +{ + return c >= '0' && c <= '9'; +} + #define FNV1_PRIME 0x01000193u #define FNV1_SEED 0x811C9DC5u diff --git a/test/test_suite/compile_time_introspection/alignof.c3t b/test/test_suite/compile_time_introspection/alignof.c3t new file mode 100644 index 000000000..3d4502263 --- /dev/null +++ b/test/test_suite/compile_time_introspection/alignof.c3t @@ -0,0 +1,90 @@ +// #target: x64_darwin +module foo; + +int[100] zfe; +struct Bob +{ + Bob[] x; + char[100] y; + struct w { + int z; + } +} + +union Ex +{ + char[8] c; + int[2] y; + double z; +} + +union Br +{ + int y; + char x; +} +struct Ar +{ + long z; + Br[10] br; +} + +union Foob +{ + long a; + char[8] c; +} +Ar izzy; + + +long x = $alignof(zfe); +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 x2 = $alignof("Ex", y); +int x3 = $alignof(char[8]); +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 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]"); + + + +// #expect: foo.ll + +@foo.x = global i64 16, align 8 +@foo.y = global i16 8, align 2 +@foo.z = global i32 8, align 4 +@foo.w = global i32 8, align 4 +@foo.v = global i32 4, align 4 +@foo.x1 = global i32 8, align 4 +@foo.x2 = global i32 8, align 4 +@foo.x3 = global i32 1, align 4 +@foo.x4 = global i32 4, align 4 +@foo.x5 = global i32 4, align 4 +@foo.x6 = global i32 4, align 4 +@foo.x7 = global i32 4, align 4 +@foo.x8 = global i32 8, align 4 +@foo.x9 = global i32 4, align 4 +@foo.x10 = global i32 4, align 4 +@foo.x11 = global i32 4, align 4 +@foo.z0 = global i32 8, align 4 +@foo.z00 = global i32 8, align 4 +@foo.z01 = global i32 1, align 4 +@foo.z02 = global i32 2, align 4 +@foo.z03 = global i32 1, align 4 +@foo.z04 = global i32 4, align 4 +@foo.z05 = global i32 1, align 4 \ No newline at end of file diff --git a/test/test_suite/compile_time_introspection/sizeof.c3t b/test/test_suite/compile_time_introspection/sizeof.c3t new file mode 100644 index 000000000..536784a0e --- /dev/null +++ b/test/test_suite/compile_time_introspection/sizeof.c3t @@ -0,0 +1,66 @@ +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"); +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); + +module bar; + +struct Deep +{ + int x; + struct a + { + struct b + { + char[5] c; + } + } +} + +struct Baz +{ + int x; + char[60] y; +} + +module bar::abc; + +struct Foo +{ + char x; +} + +// #expect: foo.ll + +@foo.x = global i64 64, align 8 +@foo.y = global i16 64, align 2 +@foo.z = global i32 64, align 4 +@foo.w = global i32 64, align 4 +@foo.v = global i32 1, align 4 +@foo.x1 = global i32 8, align 4 +@foo.y1 = global i32 2, align 4 +@foo.a = global i32 60, align 4 +@foo.b = global i32 5, align 4 +@foo.c = global i32 5, align 4 +@foo.d = global i32 1600, align 4 +@foo.e = global i32 8, align 4 +@foo.a2 = global i32 60, align 4 +@foo.a3 = global i32 60, align 4 +@foo.a4 = global i32 60, align 4 +@foo.a5 = global i32 60, align 4 \ No newline at end of file diff --git a/test/test_suite/compile_time_introspection/sizeof_errors.c3 b/test/test_suite/compile_time_introspection/sizeof_errors.c3 new file mode 100644 index 000000000..e9f5ceeed --- /dev/null +++ b/test/test_suite/compile_time_introspection/sizeof_errors.c3 @@ -0,0 +1,84 @@ +module foo; +import bar; +import bar::abc; + +func void a() +{ + int x = $sizeof(Bazy); // #error: 'Bazy' could not be found, did you spell it right +} + +func void b() +{ + int x = $sizeof("Bazz"); // #error: 'Bazz' could not be found, did you misspell it +} + +func void c() +{ + int x = $sizeof("Baz#"); // #error: A '.' was expected after 'Baz' +} + +func void c2() +{ + int x = $sizeof("#Baz"); // #error: '#Baz' is not a known identifier, did you misspell it? +} + +func void d() +{ + int x = $sizeof("Baz."); // #error: The path to an existing member was expected after 'Baz', did you make a mistake? +} + +func void e() +{ + int x = $sizeof(bar::Baze); // #error: 'Baze' could not be found, did you spell it right +} + +func void f() +{ + int x = $sizeof("bar::Bazy"); // #error: 'Bazy' could not be found, did you spell it right +} + +func void g() +{ + int x = $sizeof("bar::"); // #error: 'bar::' is not a known identifier, did you misspell it? +} + +func void k() +{ + int v = $sizeof("int.x"); // #error: 'int' has no members +} + +func void l() +{ + int v = $sizeof("int[].len"); // #error: 'int[]' has no members +} + +func void m() +{ + int v = $sizeof("int[4].len"); // #error: 'int[4]' has no members +} + +func void n() +{ + int v = $sizeof("Baz.x1"); // #error: The path to an existing member was expected after +} + +func void o() +{ + int v = $sizeof("Baz.x", x); // #error: You cannot combine a string with a member path with a separate path here +} + +module bar; + +struct Baz +{ + int x; + char[60] y; +} + +module bar::abc; + +struct Foo +{ + char x; +} + diff --git a/test/test_suite/distinct/distinct_struct_array.c3 b/test/test_suite/distinct/distinct_struct_array.c3 index 867a0bdb3..e8b3a77df 100644 --- a/test/test_suite/distinct/distinct_struct_array.c3 +++ b/test/test_suite/distinct/distinct_struct_array.c3 @@ -14,7 +14,6 @@ define StructArr = distinct Struct2[3]; func void test(int x) { StructArr z = { { .x = 1 }, { .y = x }, { 1, 2 }}; - usize xr = z.sizeof; usize len = z.len; Foo zz = z[2].x; } diff --git a/test/test_suite/expressions/casts/cast_unknown.c3 b/test/test_suite/expressions/casts/cast_unknown.c3 index 39f3e7a19..73b9b7864 100644 --- a/test/test_suite/expressions/casts/cast_unknown.c3 +++ b/test/test_suite/expressions/casts/cast_unknown.c3 @@ -6,16 +6,16 @@ func void test1() int b = (Number)(a); - int c = (Foo)(a); // #error: type 'Foo' could not be found + int c = (Foo)(a); // #error: 'Foo' could not be found } func void test2() { - int d = (Number)(bar);; // #error: identifier 'bar' could not be found + int d = (Number)(bar);; // #error: 'bar' could not be found } func void test3() { - int e = (Bar)( // #error: type 'Bar' could not be found - faa); // #error: identifier 'faa' could not be found + int e = (Bar)( // #error: 'Bar' could not be found + faa); // #error: 'faa' could not be found } diff --git a/test/test_suite/expressions/ternary_no_ident.c3 b/test/test_suite/expressions/ternary_no_ident.c3 index e0f711a3b..4c7a4c44a 100644 --- a/test/test_suite/expressions/ternary_no_ident.c3 +++ b/test/test_suite/expressions/ternary_no_ident.c3 @@ -1,14 +1,14 @@ func void test1() { - int a = (i ? 1 : 1); // #error: identifier 'i' could not be found, did you spell it right + int a = (i ? 1 : 1); // #error: 'i' could not be found, did you spell it right } func void test2() { - int a = (1 ? i : 2); // #error: identifier 'i' could not be found, did you spell it right + int a = (1 ? i : 2); // #error: 'i' could not be found, did you spell it right } func void test3() { - int a = (1 ? 2 : i); // #error: identifier 'i' could not be found, did you spell it right + int a = (1 ? 2 : i); // #error: 'i' could not be found, did you spell it right } diff --git a/test/test_suite/macro_methods/macro_method_fails.c3 b/test/test_suite/macro_methods/macro_method_fails.c3 index 250cab8ba..be2bf0be0 100644 --- a/test/test_suite/macro_methods/macro_method_fails.c3 +++ b/test/test_suite/macro_methods/macro_method_fails.c3 @@ -40,7 +40,7 @@ func void test2() func void test3() { An2 a; - a.@helloWorld.b; // #error: No such member or function could be found. + a.@helloWorld.b; // #error: There is no member or method 'b' on 'void' } func void test4() diff --git a/test/test_suite/macros/hash_ident.c3 b/test/test_suite/macros/hash_ident.c3 index 9804b89df..f8b71e421 100644 --- a/test/test_suite/macros/hash_ident.c3 +++ b/test/test_suite/macros/hash_ident.c3 @@ -19,6 +19,6 @@ func void main() @cofefe(x += 1); @cofefe(xx()); @cofefe($x += 1); // #error: Cannot modify '$x' inside of a runtime scope. - @cofefe(y += 1); // #error: identifier 'y' could not be found + @cofefe(y += 1); // #error: 'y' could not be found } diff --git a/test/test_suite/macros/no_body.c3 b/test/test_suite/macros/no_body.c3 index c1f11d8e1..d871db878 100644 --- a/test/test_suite/macros/no_body.c3 +++ b/test/test_suite/macros/no_body.c3 @@ -40,7 +40,7 @@ func void test2() macro foo2(x) { - @body(x); // #error: The identifier 'body' could not be found, did you spell it right? + @body(x); // #error: 'body' could not be found, did you spell it right? } func void test4() diff --git a/test/test_suite/statements/switch_errors.c3 b/test/test_suite/statements/switch_errors.c3 index 89912b6fe..237b0a356 100644 --- a/test/test_suite/statements/switch_errors.c3 +++ b/test/test_suite/statements/switch_errors.c3 @@ -43,7 +43,7 @@ func void test_scope(int i) int a = 0; break; case 2: - test_scope(a + 1); // #error: identifier 'a' could not be found, did you spell it right + test_scope(a + 1); // #error: 'a' could not be found, did you spell it right } } diff --git a/test/test_suite/types/enum_errors.c3 b/test/test_suite/types/enum_errors.c3 index 63b0434ef..9b9feae83 100644 --- a/test/test_suite/types/enum_errors.c3 +++ b/test/test_suite/types/enum_errors.c3 @@ -1,7 +1,7 @@ enum EnumTestErrorType3 : int { - A = FOO // #error: constant 'FOO' could not be found, did you spell it + A = FOO // #error: 'FOO' could not be found, did you spell it } func int foo() diff --git a/test/test_suite/union/union_codegen_overwrite_call.c3t b/test/test_suite/union/union_codegen_overwrite_call.c3t index 1771d3f2c..16a76194c 100644 --- a/test/test_suite/union/union_codegen_overwrite_call.c3t +++ b/test/test_suite/union/union_codegen_overwrite_call.c3t @@ -23,9 +23,9 @@ entry: %b = alloca %UnionB, align 8 %0 = bitcast %UnionB* %b to i32* %1 = call i32 @bar() -store i32 %1, i32* %0, align 4 +store i32 %1, i32* %0, align 8 %2 = bitcast %UnionB* %b to %b* %3 = bitcast %b* %2 to i8* -call void @llvm.memset.p0i8.i64(i8* align 4 %3, i8 0, i64 4, i1 false) +call void @llvm.memset.p0i8.i64(i8* align 8 %3, i8 0, i64 4, i1 false) ret void