diff --git a/releasenotes.md b/releasenotes.md index 96750ae33..1d6bfd63e 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -4,6 +4,7 @@ ### Changes / improvements - New generic syntax. +- Added `$embed` to embed binary data. - Ad hoc generics are now allowed. - Allow inferred type on method first argument. - Fix to void expression blocks diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 4bb656ff2..934ca22b2 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -194,16 +194,11 @@ typedef struct uint64_t ptr; bool b; struct - { - const char *chars; - ArraySize len; - } string; - Decl *enum_err_val; - struct { const char *ptr; - TypeSize len; + ArraySize len; } bytes; + Decl *enum_err_val; Type *typeid; ConstInitializer *initializer; Expr **untyped_list; @@ -222,6 +217,7 @@ typedef struct { FileId file_id; const char *contents; + size_t content_len; char *name; char *dir_path; const char *full_path; @@ -593,7 +589,7 @@ typedef struct typedef enum { - DEFINE_TYPE_GENERIC, + DEFINE_TYPE_GENERIC_OLD, DEFINE_IDENT_ALIAS, DEFINE_IDENT_GENERIC, } DefineType; @@ -846,6 +842,11 @@ typedef struct ExprId inner; } ExprBuiltinAccess; +typedef struct +{ + Expr *filename; + Expr *default_value; +} ExprEmbedExpr; typedef struct { ExprId parent; @@ -1146,6 +1147,7 @@ struct Expr_ ExprTryUnwrap try_unwrap_expr; // 24 ExprCall call_expr; // 32 Expr *inner_expr; // 8 + ExprEmbedExpr embed_expr; ExprBuiltinAccess builtin_access_expr; ExprGenericIdent generic_ident_expr; ExprCatchUnwrap catch_unwrap_expr; // 24 @@ -2213,7 +2215,7 @@ Path *path_create_from_string(const char *string, uint32_t len, SourceSpan span) #define SEMA_ERROR(_node, ...) sema_error_at((_node)->span, __VA_ARGS__) #define RETURN_SEMA_ERROR(_node, ...) do { sema_error_at((_node)->span, __VA_ARGS__); return false; } while (0) #define SEMA_NOTE(_node, ...) sema_error_prev_at((_node)->span, __VA_ARGS__) -#define EXPAND_EXPR_STRING(str_) (str_)->const_expr.string.len, (str_)->const_expr.string.chars +#define EXPAND_EXPR_STRING(str_) (str_)->const_expr.bytes.len, (str_)->const_expr.bytes.ptr #define TABLE_MAX_LOAD 0.5 void sema_analysis_run(void); @@ -2939,7 +2941,7 @@ INLINE bool decl_is_user_defined_type(Decl *decl) INLINE Decl *decl_flatten(Decl *decl) { - if (decl->decl_kind == DECL_DEFINE && decl->define_decl.define_kind != DEFINE_TYPE_GENERIC) + if (decl->decl_kind == DECL_DEFINE && decl->define_decl.define_kind != DEFINE_TYPE_GENERIC_OLD) { return decl->define_decl.alias; } @@ -3318,11 +3320,21 @@ INLINE bool expr_is_const_pointer(Expr *expr) return expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_POINTER; } +INLINE bool expr_is_const_bool(Expr *expr) +{ + return expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_BOOL; +} + INLINE bool expr_is_const_initializer(Expr *expr) { return expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_INITIALIZER; } +INLINE bool expr_is_const_bytes(Expr *expr) +{ + return expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_BYTES; +} + INLINE bool expr_is_const_untyped_list(Expr *expr) { return expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_UNTYPED_LIST; diff --git a/src/compiler/copying.c b/src/compiler/copying.c index 14e502162..c7d6a42af 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -296,6 +296,10 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr) { case EXPR_ANYSWITCH: UNREACHABLE + case EXPR_EMBED: + MACRO_COPY_EXPR(expr->embed_expr.default_value); + MACRO_COPY_EXPR(expr->embed_expr.filename); + return expr; case EXPR_GENERIC_IDENT: MACRO_COPY_EXPRID(expr->generic_ident_expr.parent); MACRO_COPY_EXPR_LIST(expr->generic_ident_expr.parmeters); @@ -971,7 +975,7 @@ Decl *copy_decl(CopyStruct *c, Decl *decl) case DECL_DEFINE: switch (decl->define_decl.define_kind) { - case DEFINE_TYPE_GENERIC: + case DEFINE_TYPE_GENERIC_OLD: case DEFINE_IDENT_GENERIC: MACRO_COPY_EXPR_LIST(decl->define_decl.generic_params); break; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 686638db8..b84406dda 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -224,6 +224,7 @@ typedef enum EXPR_DECL, EXPR_DESIGNATED_INITIALIZER_LIST, EXPR_DESIGNATOR, + EXPR_EMBED, EXPR_EXPRESSION_LIST, EXPR_EXPR_BLOCK, EXPR_OPTIONAL, @@ -556,6 +557,7 @@ typedef enum TOKEN_CT_DEFINED, // $defined TOKEN_CT_ECHO, // $echo TOKEN_CT_ELSE, // $else + TOKEN_CT_EMBED, // $embed TOKEN_CT_ENDFOR, // $endfor TOKEN_CT_ENDFOREACH, // $endforeach TOKEN_CT_ENDIF, // $endif diff --git a/src/compiler/expr.c b/src/compiler/expr.c index 2caff80f3..79628f8ed 100644 --- a/src/compiler/expr.c +++ b/src/compiler/expr.c @@ -134,6 +134,7 @@ bool expr_may_addr(Expr *expr) case EXPR_SWIZZLE: case EXPR_LAMBDA: case EXPR_GENERIC_IDENT: + case EXPR_EMBED: return false; } UNREACHABLE @@ -203,6 +204,7 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) case EXPR_STRINGIFY: case EXPR_CT_CHECKS: case EXPR_LAMBDA: + case EXPR_EMBED: return true; case EXPR_COND: return expr_list_is_constant_eval(expr->cond_expr, eval_kind); @@ -662,6 +664,7 @@ bool expr_is_pure(Expr *expr) case EXPR_OPERATOR_CHARS: case EXPR_CT_CHECKS: case EXPR_LAMBDA: + case EXPR_EMBED: return true; case EXPR_VASPLAT: return true; @@ -932,9 +935,9 @@ void expr_rewrite_to_string(Expr *expr_to_rewrite, const char *string) { expr_to_rewrite->expr_kind = EXPR_CONST; expr_to_rewrite->const_expr.const_kind = CONST_STRING; - expr_to_rewrite->const_expr.string.chars = (char *)string; + expr_to_rewrite->const_expr.bytes.ptr = (char *)string; ArraySize len = (ArraySize)strlen(string); - expr_to_rewrite->const_expr.string.len = len; + expr_to_rewrite->const_expr.bytes.len = len; expr_to_rewrite->resolve_status = RESOLVE_DONE; expr_to_rewrite->type = type_string; } diff --git a/src/compiler/headers.c b/src/compiler/headers.c index 7baf0a1c7..a1657546e 100644 --- a/src/compiler/headers.c +++ b/src/compiler/headers.c @@ -481,7 +481,7 @@ static void header_gen_global_var(FILE *file, FILE *file_type, HTable *table, De Expr *init = decl->var.init_expr; if (type_is_arraylike(type) || type_is_user_defined(type) || !init) return; OUTPUT("#define %s ", decl_get_extname(decl)); - assert(init->expr_kind == EXPR_CONST); + assert(expr_is_const(init)); switch (init->const_expr.const_kind) { case CONST_INTEGER: @@ -503,9 +503,9 @@ static void header_gen_global_var(FILE *file, FILE *file_type, HTable *table, De return; case CONST_STRING: putc('\"', file); - for (unsigned i = 0; i < init->const_expr.string.len; i++) + for (unsigned i = 0; i < init->const_expr.bytes.len; i++) { - char ch = init->const_expr.string.chars[i]; + char ch = init->const_expr.bytes.ptr[i]; if (ch >= ' ' && ch <= 127 && ch != '"') { fputc(ch, file); diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index df412872b..f672ac348 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -395,7 +395,7 @@ void llvm_emit_global_variable_init(GenContext *c, Decl *decl) } if (init_expr) { - if (init_expr->expr_kind == EXPR_CONST && init_expr->const_expr.const_kind == CONST_INITIALIZER) + if (expr_is_const_initializer(init_expr)) { ConstInitializer *list = init_expr->const_expr.initializer; init_value = llvm_emit_const_initializer(c, list); @@ -403,7 +403,7 @@ void llvm_emit_global_variable_init(GenContext *c, Decl *decl) else { BEValue value; - if (init_expr->expr_kind == EXPR_CONST && init_expr->const_expr.const_kind == CONST_BYTES) + if (expr_is_const_bytes(init_expr)) { init_value = llvm_get_bytes(c, init_expr->const_expr.bytes.ptr, init_expr->const_expr.bytes.len); } @@ -1071,7 +1071,7 @@ LLVMValueRef llvm_get_ref(GenContext *c, Decl *decl) } return backend_ref; case DECL_DEFINE: - if (decl->define_decl.define_kind != DEFINE_TYPE_GENERIC) return llvm_get_ref(c, decl->define_decl.alias); + if (decl->define_decl.define_kind != DEFINE_TYPE_GENERIC_OLD) return llvm_get_ref(c, decl->define_decl.alias); UNREACHABLE case DECL_FAULTVALUE: if (!decl->backend_ref) diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index e01197d01..81c874b63 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -138,7 +138,7 @@ BEValue llvm_emit_assign_expr(GenContext *c, BEValue *ref, Expr *expr, LLVMValue llvm_emit_expr(c, &value, expr); llvm_store(c, ref, &value); } - else if (expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_INITIALIZER) + else if (expr_is_const_initializer(expr)) { llvm_emit_const_initialize_reference(c, ref, expr); value = *ref; @@ -675,8 +675,8 @@ static inline void gencontext_emit_subscript(GenContext *c, BEValue *value, Expr else if (parent_type_kind == TYPE_ARRAY) { // From back should always be folded. - assert(expr->expr_kind != EXPR_CONST || !expr->subscript_expr.range.start_from_end); - needs_len = (active_target.feature.safe_mode && expr->expr_kind != EXPR_CONST) || expr->subscript_expr.range.start_from_end; + assert(!expr_is_const(expr) || !expr->subscript_expr.range.start_from_end); + needs_len = (active_target.feature.safe_mode && expr_is_const(expr)) || expr->subscript_expr.range.start_from_end; } if (needs_len) { @@ -1689,7 +1689,7 @@ void llvm_emit_initialize_reference_temporary_const(GenContext *c, BEValue *ref, Type *canonical = expr->type->canonical; - assert(expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_INITIALIZER); + assert(expr_is_const_initializer(expr)); LLVMValueRef value = llvm_emit_const_initializer(c, expr->const_expr.initializer); // Create a global const. @@ -1804,7 +1804,7 @@ static void llvm_emit_inititialize_reference_const(GenContext *c, BEValue *ref, } static inline void llvm_emit_initialize_reference_const(GenContext *c, BEValue *ref, Expr *expr) { - assert(expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_INITIALIZER); + assert(expr_is_const_initializer(expr)); ConstInitializer *initializer = expr->const_expr.initializer; // Make sure we have an address. @@ -1918,7 +1918,7 @@ static inline void llvm_emit_initialize_reference_list(GenContext *c, BEValue *r llvm_value_set_address(&pointer, value, element->type, ref->alignment); } // If this is an initializer, we want to actually run the initialization recursively. - if (element->expr_kind == EXPR_CONST && element->const_expr.const_kind == CONST_INITIALIZER) + if (expr_is_const_initializer(element)) { llvm_emit_const_initialize_reference(c, &pointer, element); continue; @@ -1969,7 +1969,7 @@ static void llvm_emit_initialize_designated(GenContext *c, BEValue *ref, AlignSi llvm_store(c, ref, emitted_value); return; } - if (expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_INITIALIZER) + if (expr_is_const_initializer(expr)) { llvm_emit_const_initialize_reference(c, ref, expr); return; @@ -2141,7 +2141,7 @@ LLVMValueRef llvm_emit_const_bitstruct_array(GenContext *c, ConstInitializer *in // Special case for bool if (member_type == type_bool) { - assert(expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_BOOL); + assert(expr_is_const_bool(expr)); assert(start_bit == end_bit); // Completely skip zero. @@ -2262,7 +2262,7 @@ static inline void llvm_emit_const_initialize_bitstruct_ref(GenContext *c, BEVal */ static inline void llvm_emit_const_initialize_reference(GenContext *c, BEValue *ref, Expr *expr) { - assert(expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_INITIALIZER); + assert(expr_is_const_initializer(expr)); ConstInitializer *initializer = expr->const_expr.initializer; if (initializer->type->type_kind == TYPE_VECTOR) { @@ -4571,23 +4571,9 @@ static inline void llvm_emit_const_initializer_list_expr(GenContext *c, BEValue static void llvm_emit_const_expr(GenContext *c, BEValue *be_value, Expr *expr) { Type *type = type_reduced_from_expr(expr)->canonical; + bool is_bytes = false; switch (expr->const_expr.const_kind) { - case CONST_BYTES: - assert(type->array.base == type_char); - { - LLVMValueRef global_name = llvm_add_global_raw(c, - ".bytes", - LLVMArrayType(llvm_get_type(c, type_char), - expr->const_expr.bytes.len), - 1); - llvm_set_private_linkage(global_name); - LLVMSetGlobalConstant(global_name, 1); - - LLVMSetInitializer(global_name, llvm_get_bytes(c, expr->const_expr.bytes.ptr, expr->const_expr.bytes.len)); - llvm_value_set_address_abi_aligned(be_value, global_name, type); - return; - } case CONST_INTEGER: { LLVMValueRef value; @@ -4627,6 +4613,9 @@ static void llvm_emit_const_expr(GenContext *c, BEValue *be_value, Expr *expr) case CONST_BOOL: llvm_value_set(be_value, LLVMConstInt(c->bool_type, expr->const_expr.b ? 1 : 0, 0), type_bool); return; + case CONST_BYTES: + is_bytes = true; + FALLTHROUGH; case CONST_STRING: { Type *str_type = type_lowering(expr->type); @@ -4635,20 +4624,23 @@ static void llvm_emit_const_expr(GenContext *c, BEValue *be_value, Expr *expr) { // In the global alloc case, create the byte array. ArraySize array_len = str_type->array.len; - ArraySize size = expr->const_expr.string.len + 1; + ArraySize size = expr->const_expr.bytes.len; + if (!is_bytes) size += 1; LLVMValueRef string; if (array_len == size) { - string = llvm_get_zstring(c, expr->const_expr.string.chars, expr->const_expr.string.len); + string = is_bytes + ? llvm_get_bytes(c, expr->const_expr.bytes.ptr, size) + : llvm_get_zstring(c, expr->const_expr.bytes.ptr, expr->const_expr.bytes.len); } else if (array_len < size) { - string = llvm_get_bytes(c, expr->const_expr.string.chars, array_len); + string = llvm_get_bytes(c, expr->const_expr.bytes.ptr, array_len); } else { char *buffer = ccalloc(1, array_len); - memcpy(buffer, expr->const_expr.string.chars, expr->const_expr.string.len); + memcpy(buffer, expr->const_expr.bytes.ptr, expr->const_expr.bytes.len); string = llvm_get_bytes(c, buffer, array_len); } llvm_value_set(be_value, string, type); @@ -4656,26 +4648,40 @@ static void llvm_emit_const_expr(GenContext *c, BEValue *be_value, Expr *expr) } // local case or creating a pointer / subarray. // In this case we first create the constant. - ArraySize strlen = expr->const_expr.string.len; - if (strlen == 0 && str_type->type_kind == TYPE_SUBARRAY) + ArraySize len = expr->const_expr.bytes.len; + if (len == 0 && str_type->type_kind == TYPE_SUBARRAY) { llvm_value_set(be_value, llvm_get_zero(c, expr->type), expr->type); return; } - ArraySize size = expr->const_expr.string.len + 1; + ArraySize size = expr->const_expr.bytes.len; + if (!is_bytes) size++; if (is_array && type->array.len > size) size = type->array.len; - LLVMValueRef global_name = llvm_add_global_raw(c, ".str", LLVMArrayType(llvm_get_type(c, type_char), size), 1); + LLVMValueRef global_name = llvm_add_global_raw(c, is_bytes ? ".bytes" : ".str", LLVMArrayType(llvm_get_type(c, type_char), size), 1); llvm_set_private_linkage(global_name); LLVMSetUnnamedAddress(global_name, LLVMGlobalUnnamedAddr); LLVMSetGlobalConstant(global_name, 1); - LLVMValueRef string = llvm_get_zstring(c, expr->const_expr.string.chars, expr->const_expr.string.len); - if (size > strlen + 1) + LLVMValueRef data = is_bytes + ? llvm_get_bytes(c, expr->const_expr.bytes.ptr, expr->const_expr.bytes.len) + : llvm_get_zstring(c, expr->const_expr.bytes.ptr, expr->const_expr.bytes.len); + LLVMValueRef trailing_zeros = NULL; + if (is_bytes) + { + if (size > len) + { + trailing_zeros = llvm_get_zero_raw(LLVMArrayType(c->byte_type, size - len)); + } + } + else if (size > len + 1) + { + trailing_zeros = llvm_get_zero_raw(LLVMArrayType(c->byte_type, size - len - 1)); + } + if (trailing_zeros) { - LLVMValueRef trailing_zeros = llvm_get_zero_raw(LLVMArrayType(c->byte_type, size - strlen - 1)); - LLVMValueRef values[2] = { string, trailing_zeros }; - string = llvm_get_packed_struct(c, values, 2); + LLVMValueRef values[2] = { data, trailing_zeros }; + data = llvm_get_packed_struct(c, values, 2); } - LLVMSetInitializer(global_name, string); + LLVMSetInitializer(global_name, data); if (is_array) { llvm_value_set_address(be_value, global_name, type, 1); @@ -4684,8 +4690,8 @@ static void llvm_emit_const_expr(GenContext *c, BEValue *be_value, Expr *expr) { if (str_type->type_kind == TYPE_SUBARRAY) { - LLVMValueRef len = llvm_const_int(c, type_usz, strlen); - llvm_value_aggregate_two(c, be_value, str_type, global_name, len); + LLVMValueRef len_value = llvm_const_int(c, type_usz, len); + llvm_value_aggregate_two(c, be_value, str_type, global_name, len_value); } else { @@ -6544,6 +6550,7 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) case EXPR_ASM: case EXPR_VASPLAT: case EXPR_GENERIC_IDENT: + case EXPR_EMBED: UNREACHABLE case EXPR_LAMBDA: llvm_emit_lambda(c, value, expr); diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index a977f6518..4760a6c70 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -420,7 +420,7 @@ static inline LoopType loop_type_for_cond(Expr *cond, bool do_while) } // Do we have a constant cond? - if (cond->expr_kind == EXPR_CONST) + if (expr_is_const(cond)) { assert(cond->const_expr.const_kind == CONST_BOOL); // The result is either infinite or no loop @@ -686,7 +686,7 @@ static void llvm_emit_switch_jump_table(GenContext *c, Ast *case_ast = cases[i]; Expr *from = case_ast->case_stmt.expr; Expr *to = case_ast->case_stmt.to_expr; - assert(type_is_integer(from->type) && from->expr_kind == EXPR_CONST); + assert(type_is_integer(from->type) && expr_is_const(from)); Int value = from->const_expr.ixx; Int to_value = to ? to->const_expr.ixx : value; if (min.type == TYPE_VOID) @@ -828,7 +828,7 @@ static void llvm_emit_switch_body(GenContext *c, BEValue *switch_value, Ast *swi LLVMValueRef case_value; BEValue be_value; Expr *from = case_stmt->case_stmt.expr; - assert(from->expr_kind == EXPR_CONST); + assert(expr_is_const(from)); llvm_emit_expr(c, &be_value, case_stmt->case_stmt.expr); llvm_value_rvalue(c, &be_value); case_value = be_value.value; @@ -1007,7 +1007,7 @@ static inline void llvm_emit_assert_stmt(GenContext *c, Ast *ast) BEValue *values = NULL; if (message_expr) { - const char *err_msg = exprptr(ast->assert_stmt.message)->const_expr.string.chars; + const char *err_msg = exprptr(ast->assert_stmt.message)->const_expr.bytes.ptr; Expr **args = ast->assert_stmt.args; if (vec_size(args)) { @@ -1086,7 +1086,7 @@ static inline void llvm_emit_asm_block_stmt(GenContext *c, Ast *ast) AsmInlineBlock *block = ast->asm_block_stmt.block; if (ast->asm_block_stmt.is_string) { - data = exprptr(ast->asm_block_stmt.asm_string)->const_expr.string.chars; + data = exprptr(ast->asm_block_stmt.asm_string)->const_expr.bytes.ptr; } else { diff --git a/src/compiler/number.c b/src/compiler/number.c index ee2fd8c80..eb35e77b5 100644 --- a/src/compiler/number.c +++ b/src/compiler/number.c @@ -69,17 +69,17 @@ bool expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp return int_comp(a, b, op); } case CONST_STRING: - if (left->string.len != right->string.len) + if (left->bytes.len != right->bytes.len) { is_eq = false; goto RETURN; } - if (right->string.chars == left->string.chars) + if (right->bytes.ptr == left->bytes.ptr) { is_eq = true; goto RETURN; } - is_eq = !strncmp(left->string.chars, right->string.chars, left->string.len); + is_eq = !strncmp(left->bytes.ptr, right->bytes.ptr, left->bytes.len); goto RETURN; case CONST_TYPEID: is_eq = left->typeid == right->typeid; @@ -219,7 +219,7 @@ const char *expr_const_to_error_string(const ExprConst *expr) case CONST_FLOAT: return str_printf("%g", expr->fxx.f); case CONST_STRING: - return str_printf("\"%*.s\"", expr->string.len, expr->string.chars); + return str_printf("\"%*.s\"", expr->bytes.len, expr->bytes.ptr); case CONST_BYTES: return ""; case CONST_ENUM: diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 5787a9e62..d6c69f9e9 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -622,8 +622,8 @@ static Expr *parse_ct_stringify(ParseContext *c, Expr *left) const char *content = str_copy(start, len); Expr *expr = expr_new(EXPR_CONST, start_span); expr->const_expr.const_kind = CONST_STRING; - expr->const_expr.string.chars = content; - expr->const_expr.string.len = len; + expr->const_expr.bytes.ptr = content; + expr->const_expr.bytes.len = len; expr->type = type_string; return expr; } @@ -1085,6 +1085,25 @@ static Expr *parse_ct_checks(ParseContext *c, Expr *left) return checks; } +/** + * ct_checks ::= CT_EMBED '(' constant_expr (',' constant_expr)? ')' + */ +static Expr *parse_ct_embed(ParseContext *c, Expr *left) +{ + assert(!left && "Unexpected left hand side"); + Expr *embed = expr_new(EXPR_EMBED, c->span); + advance_and_verify(c, TOKEN_CT_EMBED); + CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); + ASSIGN_EXPR_OR_RET(embed->embed_expr.filename, parse_constant_expr(c), poisoned_expr); + if (try_consume(c, TOKEN_COMMA)) + { + ASSIGN_EXPR_OR_RET(embed->embed_expr.default_value, parse_constant_expr(c), poisoned_expr); + } + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); + RANGE_EXTEND_PREV(embed); + return embed; +} + /** * ct_call ::= (ALIGNOF | DEFINED | EXTNAMEOF | OFFSETOF | NAMEOF | QNAMEOF) '(' flat_path ')' * flat_path ::= expr ('.' primary) | '[' expr ']')* @@ -1692,8 +1711,8 @@ static Expr *parse_string_literal(ParseContext *c, Expr *left) return poisoned_expr; } assert(str); - expr_string->const_expr.string.chars = str; - expr_string->const_expr.string.len = (uint32_t)len; + expr_string->const_expr.bytes.ptr = str; + expr_string->const_expr.bytes.len = (uint32_t)len; expr_string->type = type_string; expr_string->const_expr.const_kind = CONST_STRING; expr_string->resolve_status = RESOLVE_DONE; @@ -1889,6 +1908,7 @@ ParseRule rules[TOKEN_EOF + 1] = { [TOKEN_CT_ALIGNOF] = { parse_ct_call, NULL, PREC_NONE }, [TOKEN_CT_DEFINED] = { parse_ct_call, NULL, PREC_NONE }, [TOKEN_CT_CHECKS] = { parse_ct_checks, NULL, PREC_NONE }, + [TOKEN_CT_EMBED] = { parse_ct_embed, 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 }, diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 7ec543a71..0411bd421 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -295,7 +295,7 @@ bool parse_module(ParseContext *c, AstId contracts) { RETURN_SEMA_ERROR(attr, "External name for the module may only be declared in one location."); } - c->unit->module->extname = expr->const_expr.string.chars; + c->unit->module->extname = expr->const_expr.bytes.ptr; continue; } default: @@ -1755,25 +1755,22 @@ static inline Decl *parse_def_type(ParseContext *c) // 2. Now parse the type which we know is here. ASSIGN_TYPE_OR_RET(TypeInfo *type_info, parse_type(c), poisoned_decl); - bool old_style_encountered = try_consume(c, TOKEN_LESS); + assert(!tok_is(c, TOKEN_LGENPAR)); - // 3. Do we have '(<' if so it's a parameterized type e.g. foo::bar::Type(). - if (old_style_encountered || try_consume(c, TOKEN_LGENPAR)) + // 3. Do we have '<' if so it's an old style parameterized type e.g. foo::bar::Type. + if (try_consume(c, TOKEN_LESS)) { - Expr **params = parse_generic_parameters(c, old_style_encountered); + Expr **params = parse_generic_parameters(c, true); if (!params) return poisoned_decl; decl->decl_kind = DECL_DEFINE; decl_add_type(decl, TYPE_TYPEDEF); - decl->define_decl.define_kind = DEFINE_TYPE_GENERIC; + decl->define_decl.define_kind = DEFINE_TYPE_GENERIC_OLD; decl->define_decl.type_info = type_info; decl->define_decl.generic_params = params; if (!parse_attributes_for_global(c, decl)) return poisoned_decl; RANGE_EXTEND_PREV(decl); - if (old_style_encountered) - { - sema_warning_at(decl->span, "Use of <...> for generics is deprecated, please use (<...>) instead."); - } + sema_warning_at(decl->span, "Use of <...> for generics is deprecated, please use (<...>) instead."); CONSUME_EOS_OR_RET(poisoned_decl); return decl; } diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index 755c410fc..94c6ef09e 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -1243,6 +1243,7 @@ Ast *parse_stmt(ParseContext *c) case TOKEN_AT_CONST_IDENT: case TOKEN_AT: case TOKEN_AT_IDENT: + case TOKEN_CT_EMBED: return parse_expr_stmt(c); case TOKEN_ASSERT: return parse_assert_stmt(c); diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index cc1bffdbd..f7816870c 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -143,7 +143,7 @@ Type *type_infer_len_from_actual_type(Type *to_infer, Type *actual_type) */ INLINE bool insert_runtime_cast_unless_const(Expr *expr, CastKind kind, Type *type) { - if (expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind != CONST_TYPEID) return false; + if (expr_is_const(expr) && expr->const_expr.const_kind != CONST_TYPEID) return false; return insert_cast(expr, kind, type); } @@ -409,7 +409,7 @@ static bool integer_to_pointer(Expr *expr, Type *type) assert(type_bit_size(type_uptr) <= 64 && "For > 64 bit pointers, this code needs updating."); // Handle const: - if (expr->expr_kind == EXPR_CONST) + if (expr_is_const(expr)) { // For if the type doesn't fit, insert an error. if (!int_fits(expr->const_expr.ixx, type_uptr->canonical->type_kind)) @@ -435,7 +435,7 @@ static void enum_to_int_lowering(Expr* expr) { assert(type_flatten(expr->type)->type_kind == TYPE_ENUM); Type *underlying_type = type_base(expr->type); - if (expr->expr_kind == EXPR_CONST) + if (expr_is_const(expr)) { assert(expr->const_expr.const_kind == CONST_ENUM); expr_rewrite_const_int(expr, underlying_type, expr->const_expr.enum_err_val->enum_constant.ordinal); @@ -541,7 +541,7 @@ static bool vector_to_array(Expr *expr, Type *to_type) static bool vector_to_vector(Expr *expr, Type *to_type) { // - if (expr->expr_kind != EXPR_CONST) + if (!expr_is_const(expr)) { // Extract indexed types. Type *from_type = type_flatten(expr->type); @@ -1015,7 +1015,7 @@ static bool cast_from_pointer(SemaContext *context, Expr *expr, Type *from, Type static void sema_error_const_int_out_of_range(Expr *expr, Expr *problem, Type *to_type) { - assert(expr->expr_kind == EXPR_CONST); + assert(expr_is_const(expr)); if (expr->const_expr.is_character && expr->type->type_kind != TYPE_U128) { SEMA_ERROR(problem, "The unicode character U+%04x cannot fit in a %s.", (uint32_t)expr->const_expr.ixx.i.low, type_quoted_error_string(to_type)); @@ -1034,12 +1034,26 @@ static void sema_error_const_int_out_of_range(Expr *expr, Expr *problem, Type *t } -static inline bool cast_maybe_string_lit(Expr *expr, Type *to_canonical, Type *to_original) +static inline bool cast_maybe_string_byte_lit(Expr *expr, Type *to_canonical, Type *to_original) { - if (expr->expr_kind != EXPR_CONST || expr->const_expr.const_kind != CONST_STRING || expr->type != type_string) return false; + if (!expr_is_const(expr)) return false; + bool is_bytes = false; + switch (expr->const_expr.const_kind) + { + case CONST_BYTES: + if (expr->type->type_kind != TYPE_ARRAY) return false; + is_bytes = true; + break; + case CONST_STRING: + if (expr->type != type_string) return false; + break; + default: + return false; + } Type *flat = type_flatten(to_canonical); Type *indexed_type = type_get_indexed_type(flat); if (indexed_type) indexed_type = type_flatten(indexed_type); + size_t len = is_bytes ? expr->const_expr.bytes.len : expr->const_expr.bytes.len; switch (flat->type_kind) { case TYPE_SUBARRAY: @@ -1048,13 +1062,11 @@ static inline bool cast_maybe_string_lit(Expr *expr, Type *to_canonical, Type *t expr->type = to_original; case TYPE_INFERRED_ARRAY: if (indexed_type != type_char && indexed_type != type_ichar) return false; - expr->type = type_infer_len_from_actual_type(to_original, type_get_array(indexed_type, expr->const_expr.string.len)); + expr->type = type_infer_len_from_actual_type(to_original, type_get_array(indexed_type, len)); return true; - break; case TYPE_ARRAY: if (indexed_type != type_char && indexed_type != type_ichar) return false; { - ArraySize len = expr->const_expr.string.len; ArraySize to_len = flat->array.len; if (len > to_len) return false; expr->type = to_original; @@ -1599,7 +1611,7 @@ static bool cast_expr_inner(SemaContext *context, Expr *expr, Type *to_type, boo // Handle strings, these don't actually mess with the underlying data, // just the type. - if (cast_maybe_string_lit(expr, to, to_type)) return true; + if (cast_maybe_string_byte_lit(expr, to, to_type)) return true; // For constant pointers cast into anything pointer-like: if (expr_is_const_pointer(expr) && from == type_voidptr && type_flatten(to)->type_kind == TYPE_POINTER) @@ -1794,7 +1806,7 @@ static bool err_to_bool(Expr *expr, Type *to_type) static inline bool subarray_to_bool(Expr *expr, Type *type) { - if (expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_INITIALIZER) + if (expr_is_const_initializer(expr)) { ConstInitializer *list = expr->const_expr.initializer; switch (list->kind) @@ -1962,7 +1974,7 @@ bool cast(Expr *expr, Type *to_type) if (from_type == to) { expr->type = type_add_optional(to_type, from_is_optional); - if (expr->expr_kind == EXPR_CONST) + if (expr_is_const(expr)) { expr->const_expr.is_hex = false; } diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 69cf75af4..da1ed7e37 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -62,7 +62,7 @@ static inline bool sema_analyse_error(SemaContext *context, Decl *decl); static bool sema_check_section(SemaContext *context, Attr *attr) { - const char *section_string = attr->exprs[0]->const_expr.string.chars; + const char *section_string = attr->exprs[0]->const_expr.bytes.ptr; // No restrictions except for MACH-O if (platform_target.object_format != OBJ_FORMAT_MACHO) { @@ -567,7 +567,7 @@ static inline bool sema_analyse_bitstruct_member(SemaContext *context, Decl *dec if (!sema_analyse_expr(context, start)) return false; // Check for negative, non integer or non const values. - if (start->expr_kind != EXPR_CONST || !type_is_integer(start->type) || int_is_neg(start->const_expr.ixx)) + if (!expr_is_const(start) || !type_is_integer(start->type) || int_is_neg(start->const_expr.ixx)) { SEMA_ERROR(start, "This must be a constant non-negative integer value."); return false; @@ -588,7 +588,7 @@ static inline bool sema_analyse_bitstruct_member(SemaContext *context, Decl *dec { // Analyse the end if (!sema_analyse_expr(context, start)) return false; - if (end->expr_kind != EXPR_CONST || !type_is_integer(end->type) || int_is_neg(end->const_expr.ixx)) + if (!expr_is_const(end) || !type_is_integer(end->type) || int_is_neg(end->const_expr.ixx)) { SEMA_ERROR(end, "This must be a constant non-negative integer value."); return false; @@ -891,7 +891,7 @@ static inline bool sema_analyse_signature(SemaContext *context, Signature *sig, if (param->var.init_expr) { Expr *expr = param->var.init_expr; - if (expr->expr_kind == EXPR_CONST) + if (expr_is_const(expr)) { if (!sema_analyse_expr_rhs(context, param->type, expr, true)) return decl_poison(param); } @@ -1768,7 +1768,7 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, return false; } decl->has_extname = true; - decl->extname = expr->const_expr.string.chars; + decl->extname = expr->const_expr.bytes.ptr; } decl->is_export = true; return true; @@ -1806,11 +1806,11 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, { case ATTRIBUTE_SECTION: if (!sema_check_section(context, attr)) return false; - decl->section = expr->const_expr.string.chars; + decl->section = expr->const_expr.bytes.ptr; break; case ATTRIBUTE_EXTERN: decl->has_extname = true; - decl->extname = expr->const_expr.string.chars; + decl->extname = expr->const_expr.bytes.ptr; break; default: UNREACHABLE; @@ -2957,7 +2957,7 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local) return decl_poison(decl); } } - if (init_expr->expr_kind == EXPR_CONST) + if (expr_is_const(init_expr)) { init_expr->const_expr.is_hex = false; } @@ -3184,7 +3184,7 @@ static bool sema_analyse_parameterized_define(SemaContext *c, Decl *decl) name = decl->define_decl.ident; span = decl->define_decl.span; break; - case DEFINE_TYPE_GENERIC: + case DEFINE_TYPE_GENERIC_OLD: { TypeInfo *define_type = decl->define_decl.type_info; if (define_type->resolve_status == RESOLVE_DONE && type_is_user_defined(define_type->type)) @@ -3209,7 +3209,7 @@ static bool sema_analyse_parameterized_define(SemaContext *c, Decl *decl) decl->define_decl.alias = symbol; decl->type = symbol->type; return true; - case DEFINE_TYPE_GENERIC: + case DEFINE_TYPE_GENERIC_OLD: { Type *type = type_new(TYPE_TYPEDEF, decl->name); decl->type = type; @@ -3443,7 +3443,7 @@ void sema_display_deprecated_warning_on_use(SemaContext *context, Decl *decl, So { if (attr->exprs) { - const char *comment_string = attr->exprs[0]->const_expr.string.chars; + const char *comment_string = attr->exprs[0]->const_expr.bytes.ptr; sema_warning_at(span, "'%s' is deprecated: %s.", decl->name, comment_string); return; } diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 25aa56836..43cb5c43a 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -191,7 +191,7 @@ static inline bool sema_expr_fold_to_member(Expr *expr, Expr *parent, Decl *memb static inline bool sema_constant_fold_ops(Expr *expr) { - if (expr->expr_kind != EXPR_CONST) return false; + if (!expr_is_const(expr)) return false; switch (expr->const_expr.const_kind) { case CONST_INTEGER: @@ -253,7 +253,7 @@ Expr *sema_ct_eval_expr(SemaContext *c, bool is_type_eval, Expr *inner, bool rep return NULL; } const char *interned_version = NULL; - TokenType token = sema_splitpathref(inner->const_expr.string.chars, inner->const_expr.string.len, &path, &interned_version); + TokenType token = sema_splitpathref(inner->const_expr.bytes.ptr, inner->const_expr.bytes.len, &path, &interned_version); switch (token) { case TOKEN_CONST_IDENT: @@ -264,7 +264,7 @@ Expr *sema_ct_eval_expr(SemaContext *c, bool is_type_eval, Expr *inner, bool rep { if (report_missing) { - SEMA_ERROR(inner, "'%.*s' could not be found, did you spell it right?", (int)inner->const_expr.string.len, inner->const_expr.string.chars); + SEMA_ERROR(inner, "'%.*s' could not be found, did you spell it right?", (int)inner->const_expr.bytes.len, inner->const_expr.bytes.ptr); } return NULL; } @@ -319,7 +319,7 @@ Expr *expr_access_inline_member(Expr *parent, Decl *parent_decl) static inline bool expr_both_const(Expr *left, Expr *right) { - return left->expr_kind == EXPR_CONST && right->expr_kind == EXPR_CONST; + return expr_is_const(left) && expr_is_const(right); } static inline bool expr_both_any_integer_or_integer_vector(Expr *left, Expr *right) @@ -392,6 +392,7 @@ static bool sema_binary_is_expr_lvalue(Expr *top_expr, Expr *expr) { case EXPR_SWIZZLE: case EXPR_LAMBDA: + case EXPR_EMBED: return false; case EXPR_SUBSCRIPT_ASSIGN: case EXPR_CT_IDENT: @@ -524,6 +525,7 @@ static bool expr_may_ref(Expr *expr) case EXPR_SWIZZLE: case EXPR_LAMBDA: case EXPR_CT_IDENT: + case EXPR_EMBED: return false; case EXPR_SUBSCRIPT_ASSIGN: return true; @@ -762,7 +764,7 @@ static inline bool sema_expr_analyse_ternary(SemaContext *context, Expr *expr) { if (!sema_analyse_cond_expr(context, cond)) return expr_poison(expr); if (!sema_analyse_expr(context, left)) return expr_poison(expr); - if (cond->expr_kind == EXPR_CONST) + if (expr_is_const(cond)) { path = cond->const_expr.b ? 1 : 0; } @@ -781,7 +783,7 @@ static inline bool sema_expr_analyse_ternary(SemaContext *context, Expr *expr) { Expr *copy = copy_expr_single(cond); cast(copy, type_bool); - assert(cond->expr_kind == EXPR_CONST); + assert(expr_is_const(cond)); path = cond->const_expr.b ? 1 : 0; } left = cond; @@ -2251,7 +2253,7 @@ static inline bool sema_expr_analyse_call(SemaContext *context, Expr *expr) static bool sema_slice_len_is_in_range(SemaContext *context, Type *type, Expr *len_expr, bool from_end, bool *remove_from_end) { assert(type == type->canonical); - if (len_expr->expr_kind != EXPR_CONST) return true; + if (!expr_is_const(len_expr)) return true; Int const_len = len_expr->const_expr.ixx; if (!int_fits(const_len, TYPE_I64)) @@ -2309,7 +2311,7 @@ static bool sema_slice_len_is_in_range(SemaContext *context, Type *type, Expr *l static bool sema_slice_index_is_in_range(SemaContext *context, Type *type, Expr *index_expr, bool end_index, bool from_end, bool *remove_from_end) { assert(type == type->canonical); - if (index_expr->expr_kind != EXPR_CONST) return true; + if (!expr_is_const(index_expr)) return true; Int index = index_expr->const_expr.ixx; if (!int_fits(index, TYPE_I64)) @@ -2401,7 +2403,7 @@ static Type *sema_subscript_find_indexable_type_recursively(Type **type, Expr ** static bool sema_subscript_rewrite_index_const_list(Expr *const_list, Expr *index, Expr *result) { - assert(index->expr_kind == EXPR_CONST && index->const_expr.const_kind == CONST_INTEGER); + assert(expr_is_const_int(index)); if (!int_fits(index->const_expr.ixx, TYPE_U32)) return false; uint32_t idx = index->const_expr.ixx.i.low; @@ -2759,7 +2761,7 @@ static inline bool sema_expr_analyse_slice(SemaContext *context, Expr *expr) end_from_end = expr->subscript_expr.range.end_from_end = false; } - if (start && end && start->expr_kind == EXPR_CONST && end->expr_kind == EXPR_CONST) + if (start && end && expr_is_const(start) && expr_is_const(end)) { if (!is_lenrange && start_from_end && end_from_end) { @@ -3797,7 +3799,7 @@ static inline bool sema_expr_analyse_access(SemaContext *context, Expr *expr) sema_expr_flatten_const(context, parent); if (expr_is_const_string(parent)) { - expr_rewrite_const_int(expr, type_isz, parent->const_expr.string.len); + expr_rewrite_const_int(expr, type_isz, parent->const_expr.bytes.len); return true; } expr_rewrite_to_builtin_access(expr, current_parent, ACCESS_LEN, type_usz); @@ -3888,7 +3890,7 @@ static inline bool sema_expr_analyse_access(SemaContext *context, Expr *expr) } if (flat_type->type_kind == TYPE_FAULTTYPE || flat_type->type_kind == TYPE_ANYFAULT) { - if (current_parent->expr_kind == EXPR_CONST) + if (expr_is_const(current_parent)) { expr_rewrite_to_string(expr, current_parent->const_expr.enum_err_val->name); return true; @@ -3931,7 +3933,7 @@ static inline bool sema_expr_analyse_access(SemaContext *context, Expr *expr) Decl *decl = type->decl; Decl *member = sema_decl_stack_find_decl_member(decl, kw); - if (member && decl_is_enum_kind(decl) && member->decl_kind == DECL_VAR && parent->expr_kind == EXPR_CONST) + if (member && decl_is_enum_kind(decl) && member->decl_kind == DECL_VAR && expr_is_const(parent)) { assert(parent->const_expr.const_kind == CONST_ENUM); Expr *copy_init = copy_expr_single(current_parent->const_expr.enum_err_val->enum_constant.args[member->var.index]); @@ -4455,7 +4457,7 @@ static bool sema_binary_analyse_ct_common_assign(SemaContext *context, Expr *exp if (!sema_expr_analyse_binary(context, expr)) return false; - if (expr->expr_kind != EXPR_CONST) + if (!expr_is_const(expr)) { SEMA_ERROR(exprptr(expr->binary_expr.right), "Expected a constant expression."); return false; @@ -4507,7 +4509,7 @@ static bool sema_expr_analyse_op_assign(SemaContext *context, Expr *expr, Expr * if (!sema_analyse_expr(context, right)) return false; if (!cast_implicit_maybe_optional(context, right, no_fail, IS_OPTIONAL(left))) return false; // 6. Check for zero in case of div or mod. - if (right->expr_kind == EXPR_CONST) + if (expr_is_const(right)) { if (expr->binary_expr.operator == BINARYOP_DIV_ASSIGN) { @@ -5080,7 +5082,7 @@ static bool sema_expr_analyse_div(SemaContext *context, Expr *expr, Expr *left, if (!sema_binary_analyse_arithmetic_subexpr(context, expr, "Cannot divide %s by %s.", false)) return false; // 2. Check for a constant 0 on the rhs. - if (IS_CONST(right)) + if (expr_is_const(right)) { switch (right->const_expr.const_kind) { @@ -5135,7 +5137,7 @@ static bool sema_expr_analyse_mod(SemaContext *context, Expr *expr, Expr *left, if (!sema_binary_analyse_arithmetic_subexpr(context, expr, NULL, false)) return false; // 3. a % 0 is not valid, so detect it. - if (IS_CONST(right) && int_is_zero(right->const_expr.ixx)) + if (expr_is_const(right) && int_is_zero(right->const_expr.ixx)) { SEMA_ERROR(right, "Cannot perform %% with a constant zero."); return false; @@ -5239,7 +5241,7 @@ static bool sema_expr_analyse_shift(SemaContext *context, Expr *expr, Expr *left if (!cast_implicit(context, left, cast_numeric_arithmetic_promotion(type_no_optional(left->type)))) return false; // 4. For a constant rhs side we will make a series of checks. - if (IS_CONST(right)) + if (expr_is_const(right)) { // 4a. Make sure the value does not exceed the bitsize of // the left hand side. We ignore this check for lhs being a constant. @@ -5259,7 +5261,7 @@ static bool sema_expr_analyse_shift(SemaContext *context, Expr *expr, Expr *left } // 5. Fold constant expressions. - if (IS_CONST(left)) + if (expr_is_const(left)) { bool shr = expr->binary_expr.operator == BINARYOP_SHR; expr_replace(expr, left); @@ -5302,7 +5304,7 @@ static bool sema_expr_analyse_shift_assign(SemaContext *context, Expr *expr, Exp if (!expr_both_any_integer_or_integer_vector(left, right)) return sema_type_error_on_binop(expr); // 4. For a constant right hand side we will make a series of checks. - if (IS_CONST(right)) + if (expr_is_const(right)) { // 4a. Make sure the value does not exceed the bitsize of // the left hand side. @@ -5553,7 +5555,7 @@ static inline bool sema_expr_analyse_deref(SemaContext *context, Expr *expr) } // 3. This could be a constant, in which case it is a null which is an error. - if (inner->expr_kind == EXPR_CONST) + if (expr_is_const(inner)) { SEMA_ERROR(inner, "Dereferencing null is not allowed, did you do it by mistake?"); return false; @@ -5864,7 +5866,7 @@ static inline bool sema_expr_analyse_not(SemaContext *context, Expr *expr) expr->type = type_add_optional(type_bool, IS_OPTIONAL(inner)); - if (inner->expr_kind == EXPR_CONST) + if (expr_is_const(inner)) { bool success = cast_explicit(context, inner, expr->type); assert(success); @@ -5884,7 +5886,7 @@ static inline bool sema_expr_analyse_ct_incdec(SemaContext *context, Expr *expr, Decl *var = inner->ct_ident_expr.decl; Expr *start_value = var->var.init_expr; - assert(start_value->expr_kind == EXPR_CONST); + assert(expr_is_const(start_value)); switch (start_value->const_expr.const_kind) { @@ -6980,6 +6982,11 @@ static inline Decl *sema_find_cached_lambda(SemaContext *context, Type *func_typ return NULL; } +static inline bool sema_expr_analyse_embed(SemaContext *context, Expr *expr) +{ + return sema_load_embed_expr(context, expr); +} + static inline bool sema_expr_analyse_generic_ident(SemaContext *context, Expr *expr) { Expr *parent = exprptr(expr->generic_ident_expr.parent); @@ -7473,6 +7480,8 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr) case EXPR_TEST_HOOK: case EXPR_SWIZZLE: UNREACHABLE + case EXPR_EMBED: + return sema_expr_analyse_embed(context, expr); case EXPR_VASPLAT: SEMA_ERROR(expr, "'$vasplat' can only be used inside of macros."); return false; @@ -7603,7 +7612,7 @@ bool sema_analyse_expr_rhs(SemaContext *context, Type *to, Expr *expr, bool allo if (to && allow_optional && to->canonical != expr->type->canonical && expr->type->canonical->type_kind == TYPE_FAULTTYPE) { Type *flat = type_flatten(to); - if (flat != type_anyfault && flat->type_kind != TYPE_FAULTTYPE && expr->expr_kind == EXPR_CONST) + if (flat != type_anyfault && flat->type_kind != TYPE_FAULTTYPE && expr_is_const(expr)) { sema_error_at_after(expr->span, "You need to add a trailing '?' here to make this an optional."); return false; @@ -7981,7 +7990,7 @@ bool sema_insert_method_call(SemaContext *context, Expr *method_call, Decl *meth bool sema_bit_assignment_check(Expr *right, Decl *member) { // Don't check non-consts and non integers. - if (!IS_CONST(right) || !type_is_integer(right->type)) return true; + if (!expr_is_const(right) || !type_is_integer(right->type)) return true; unsigned bits = member->var.end_bit - member->var.start_bit + 1; diff --git a/src/compiler/sema_initializers.c b/src/compiler/sema_initializers.c index 6ee14cf5f..364bc4cbe 100644 --- a/src/compiler/sema_initializers.c +++ b/src/compiler/sema_initializers.c @@ -182,7 +182,7 @@ static inline bool sema_expr_analyse_struct_plain_initializer(SemaContext *conte { Expr *expr = elements[0]; const_init->init_union.index = 0; - if (expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_INITIALIZER) + if (expr_is_const_initializer(expr)) { const_init->init_union.element = expr->const_expr.initializer; } @@ -199,7 +199,7 @@ static inline bool sema_expr_analyse_struct_plain_initializer(SemaContext *conte VECEACH(elements, i) { Expr *expr = elements[i]; - if (expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_INITIALIZER) + if (expr_is_const_initializer(expr)) { inits[i] = expr->const_expr.initializer; continue; @@ -361,7 +361,7 @@ static inline bool sema_expr_analyse_array_plain_initializer(SemaContext *contex VECEACH(elements, i) { Expr *expr = elements[i]; - if (expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_INITIALIZER) + if (expr_is_const_initializer(expr)) { vec_add(inits, expr->const_expr.initializer); continue; @@ -631,7 +631,7 @@ static void sema_create_const_initializer_value(ConstInitializer *const_init, Ex { // Possibly this is already a const initializers, in that case // overwrite what is inside, eg [1] = { .a = 1 } - if (value->expr_kind == EXPR_CONST && value->const_expr.const_kind == CONST_INITIALIZER) + if (expr_is_const_initializer(value)) { *const_init = *value->const_expr.initializer; value->const_expr.initializer = const_init; @@ -917,8 +917,7 @@ static Type *sema_expr_analyse_designator(SemaContext *context, Type *current, E INLINE bool sema_initializer_list_is_empty(Expr *value) { - return value->expr_kind == EXPR_CONST && value->const_expr.const_kind == CONST_INITIALIZER - && value->const_expr.initializer->kind == CONST_INIT_ZERO; + return expr_is_const_initializer(value) && value->const_expr.initializer->kind == CONST_INIT_ZERO; } static Type *sema_find_type_of_element(SemaContext *context, Type *type, DesignatorElement ***elements_ref, unsigned *curr_index, bool *is_constant, bool *did_report_error, MemberIndex *max_index, Decl **member_ptr) @@ -1001,7 +1000,7 @@ static Type *sema_find_type_of_element(SemaContext *context, Type *type, Designa MemberIndex sema_get_initializer_const_array_size(SemaContext *context, Expr *initializer, bool *may_be_array, bool *is_const_size) { - if (initializer->expr_kind == EXPR_CONST) + if (expr_is_const(initializer)) { assert(initializer->const_expr.const_kind == CONST_INITIALIZER); ConstInitializer *init = initializer->const_expr.initializer; diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 903566c13..b899352d5 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -25,7 +25,6 @@ #define POP_NEXT() POP_X(next); context->next_switch = _old_next_switch #define PUSH_BREAKCONT(ast) PUSH_CONTINUE(ast); PUSH_BREAK(ast) #define POP_BREAKCONT() POP_CONTINUE(); POP_BREAK() -#define IS_CONST(_x) ((_x)->expr_kind == EXPR_CONST) extern const char *ct_eval_error; @@ -88,6 +87,7 @@ void cast_to_int_to_max_bit_size(SemaContext *context, Expr *lhs, Expr *rhs, Typ bool sema_decl_if_cond(SemaContext *context, Decl *decl); bool sema_flattened_expr_is_const(SemaContext *context, Expr *expr); Decl *sema_analyse_parameterized_identifier(SemaContext *c, Path *decl_path, const char *name, SourceSpan span, Expr **params); +bool sema_load_embed_expr(SemaContext *context, Expr *expr); bool sema_analyse_checked(SemaContext *context, Ast *directive, SourceSpan span); diff --git a/src/compiler/sema_liveness.c b/src/compiler/sema_liveness.c index a824475dd..e4115ed00 100644 --- a/src/compiler/sema_liveness.c +++ b/src/compiler/sema_liveness.c @@ -257,6 +257,7 @@ static void sema_trace_expr_liveness(Expr *expr) case EXPR_CT_IDENT: case EXPR_ANYSWITCH: case EXPR_GENERIC_IDENT: + case EXPR_EMBED: UNREACHABLE case EXPR_DESIGNATOR: sema_trace_expr_liveness(expr->designator_expr.value); diff --git a/src/compiler/sema_passes.c b/src/compiler/sema_passes.c index c85fbf5bd..14719ce8a 100644 --- a/src/compiler/sema_passes.c +++ b/src/compiler/sema_passes.c @@ -119,27 +119,14 @@ INLINE void register_global_decls(CompilationUnit *unit, Decl **decls) vec_resize(decls, 0); } -Decl **sema_load_include(CompilationUnit *unit, Decl *decl) +INLINE File *sema_load_file(CompilationUnit *unit, SourceSpan span, Expr *filename, const char *type, File *no_file) { - SemaContext context; - sema_context_init(&context, unit); - FOREACH_BEGIN(Attr *attr, decl->attributes) - if (attr->attr_kind != ATTRIBUTE_IF) - { - SEMA_ERROR(attr, "Invalid attribute for '$include'."); - return NULL; - } - FOREACH_END(); - Expr *filename = decl->include.filename; - bool success = sema_analyse_ct_expr(&context, filename); - sema_context_destroy(&context); - if (!success) return NULL; if (!expr_is_const_string(filename)) { - SEMA_ERROR(decl->include.filename, "A compile time string was expected."); + SEMA_ERROR(filename, "A compile time string was expected."); return NULL; } - const char *string = filename->const_expr.string.chars; + const char *string = filename->const_expr.bytes.ptr; bool loaded; const char *error; char *path; @@ -151,19 +138,69 @@ Decl **sema_load_include(CompilationUnit *unit, Decl *decl) File *file = source_file_load(string, &loaded, &error); if (!file) { - SEMA_ERROR(decl, "Failed to load file %s: %s", string, error); + if (no_file) return no_file; + sema_error_at(span, "Failed to load file %s: %s", string, error); return NULL; } if (global_context.errors_found) return NULL; + return file; +} + +Decl **sema_load_include(CompilationUnit *unit, Decl *decl) +{ + SemaContext context; + sema_context_init(&context, unit); + FOREACH_BEGIN(Attr *attr, decl->attributes) + if (attr->attr_kind != ATTRIBUTE_IF) + { + SEMA_ERROR(attr, "Invalid attribute for '%include'."); + return NULL; + } + FOREACH_END(); + bool success = sema_analyse_ct_expr(&context, decl->include.filename); + sema_context_destroy(&context); + if (success) return NULL; + File *file = sema_load_file(unit, decl->span, decl->include.filename, "$include", NULL); + if (!file) return NULL; if (global_context.includes_used++ > MAX_INCLUDES) { SEMA_ERROR(decl, "This $include would cause the maximum number of includes (%d) to be exceeded.", MAX_INCLUDES); return NULL; } - return parse_include_file(file, unit); } +bool sema_load_embed_expr(SemaContext *context, Expr *expr) +{ + static File no_file; + Expr *filename = expr->embed_expr.filename; + if (!sema_analyse_ct_expr(context, filename)) return false; + Expr *default_expr = expr->embed_expr.default_value; + File *file = sema_load_file(context->unit, expr->span, filename, "$embed", default_expr ? &no_file : NULL); + if (!file) return false; + if (file == &no_file) + { + if (!sema_analyse_ct_expr(context, default_expr)) return NULL; + if (!expr_is_const(default_expr) || default_expr->const_expr.const_kind != CONST_BYTES) + { + SEMA_ERROR(default_expr, "Expected this default value to be a constant array of bytes."); + return false; + } + expr_replace(expr, default_expr); + expr->resolve_status = RESOLVE_RUNNING; + return true; + } + expr->const_expr = (ExprConst) { + .const_kind = CONST_BYTES, + .bytes.ptr = file->contents, + .bytes.len = file->content_len, + }; + expr->expr_kind = EXPR_CONST; + Type *type = type_get_array(type_char, file->content_len); + expr->type = type; + return true; +} + INLINE void register_includes(CompilationUnit *unit, Decl **decls) { FOREACH_BEGIN(Decl *include, decls) diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 6c6124c3a..9bcd98980 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -1126,7 +1126,7 @@ static inline bool sema_analyse_for_cond(SemaContext *context, ExprId *cond_ref, // If this is const true, then set this to infinite and remove the expression. Expr *cond_last = cond->expr_kind == EXPR_COND ? VECLAST(cond->cond_expr) : cond; assert(cond_last); - if (cond_last->expr_kind == EXPR_CONST && cond_last->const_expr.b) + if (expr_is_const(cond_last) && cond_last->const_expr.b) { if (cond->expr_kind != EXPR_COND || vec_size(cond->cond_expr) == 1) { @@ -1263,7 +1263,7 @@ static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statemen Type *inferred_type = NULL; // We may have an initializer list, in this case we rely on an inferred type. - if (expr_is_init_list(enumerator) || (enumerator->expr_kind == EXPR_CONST && enumerator->const_expr.const_kind == CONST_INITIALIZER)) + if (expr_is_init_list(enumerator) || expr_is_const_initializer(enumerator)) { bool may_be_array; bool is_const_size; @@ -1866,7 +1866,7 @@ static bool sema_analyse_nextcase_stmt(SemaContext *context, Ast *statement) break; } Expr *expr = case_stmt->case_stmt.expr; - if (expr->expr_kind == EXPR_CONST && expr->const_expr.typeid == type) + if (expr_is_const(expr) && expr->const_expr.typeid == type) { statement->nextcase_stmt.case_switch_stmt = astid(case_stmt); return true; @@ -1889,7 +1889,7 @@ static bool sema_analyse_nextcase_stmt(SemaContext *context, Ast *statement) statement->nextcase_stmt.defer_id = context_get_defers(context, context->active_scope.defer_last, parent->switch_stmt.defer, true); - if (target->expr_kind == EXPR_CONST) + if (expr_is_const(target)) { Ast *default_stmt = NULL; VECEACH(parent->switch_stmt.cases, i) @@ -2016,7 +2016,7 @@ static inline bool sema_check_type_case(SemaContext *context, Type *switch_type, Expr *expr = case_stmt->case_stmt.expr; if (!sema_analyse_expr_rhs(context, type_typeid, expr, false)) return false; - if (expr->expr_kind == EXPR_CONST) + if (expr_is_const(expr)) { Type *my_type = expr->const_expr.typeid; for (unsigned i = 0; i < index; i++) @@ -2024,7 +2024,7 @@ static inline bool sema_check_type_case(SemaContext *context, Type *switch_type, Ast *other = cases[i]; if (other->ast_kind != AST_CASE_STMT) continue; Expr *other_expr = other->case_stmt.expr; - if (other_expr->expr_kind == EXPR_CONST && other_expr->const_expr.typeid == my_type) + if (expr_is_const(other_expr) && other_expr->const_expr.typeid == my_type) { SEMA_ERROR(case_stmt, "The same type appears more than once."); SEMA_NOTE(other, "Here is the case with that type."); @@ -2172,7 +2172,7 @@ static bool sema_analyse_switch_body(SemaContext *context, Ast *statement, Sourc Ast *next = (i < case_count - 1) ? cases[i + 1] : NULL; PUSH_NEXT(next, statement); Ast *body = stmt->case_stmt.body; - if (stmt->ast_kind == AST_CASE_STMT && body && type_switch && var_holder && stmt->case_stmt.expr->expr_kind == EXPR_CONST) + if (stmt->ast_kind == AST_CASE_STMT && body && type_switch && var_holder && expr_is_const(stmt->case_stmt.expr)) { if (any_switch->is_assign) { diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index 46f3c1cf4..cbccf6483 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -44,7 +44,7 @@ bool sema_resolve_array_like_len(SemaContext *context, TypeInfo *type_info, Arra if (!sema_analyse_expr(context, len_expr)) return type_info_poison(type_info); // A constant expression is assumed. - if (len_expr->expr_kind != EXPR_CONST) + if (!expr_is_const(len_expr)) { SEMA_ERROR(len_expr, "Expected a constant value as length."); return type_info_poison(type_info); @@ -155,7 +155,7 @@ static inline bool sema_resolve_array_type(SemaContext *context, TypeInfo *type, default: UNREACHABLE } - assert(!type->array.len || type->array.len->expr_kind == EXPR_CONST); + assert(!type->array.len || expr_is_const(type->array.len)); type->resolve_status = RESOLVE_DONE; return true; } diff --git a/src/compiler/source_file.c b/src/compiler/source_file.c index ddd9482e0..cf644e3e5 100644 --- a/src/compiler/source_file.c +++ b/src/compiler/source_file.c @@ -55,6 +55,7 @@ File *source_file_load(const char *filename, bool *already_loaded, const char ** file->file_id = vec_size(global_context.loaded_sources); file->full_path = full_path; file->contents = source_text; + file->content_len = size; file_get_dir_and_filename_from_full(file->full_path, &file->name, &file->dir_path); vec_add(global_context.loaded_sources, file); return file; diff --git a/src/compiler/tilde_codegen_expr.c b/src/compiler/tilde_codegen_expr.c index e0f4d54fa..f00d187f3 100644 --- a/src/compiler/tilde_codegen_expr.c +++ b/src/compiler/tilde_codegen_expr.c @@ -81,8 +81,8 @@ static void tilde_emit_const_expr(TildeContext *c, TBEValue *value, Expr *expr) bool is_array = type_flat_is_char_array(str_type); if (llvm_is_local_eval(c) || !is_array) { - ArraySize strlen = expr->const_expr.string.len; - ArraySize size = expr->const_expr.string.len + 1; + ArraySize strlen = expr->const_expr.bytes.len; + ArraySize size = expr->const_expr.bytes.len + 1; if (type_flat_is_char_array(expr->type) && type->array.len > size) size = type->array.len; LLVMValueRef global_name = llvm_add_global_raw(c, ".str", @@ -91,7 +91,7 @@ static void tilde_emit_const_expr(TildeContext *c, TBEValue *value, Expr *expr) llvm_set_private_linkage(global_name); LLVMSetUnnamedAddress(global_name, LLVMGlobalUnnamedAddr); LLVMSetGlobalConstant(global_name, 1); - LLVMValueRef string = llvm_get_zstring(c, expr->const_expr.string.chars, expr->const_expr.string.len); + LLVMValueRef string = llvm_get_zstring(c, expr->const_expr.bytes.ptr, expr->const_expr.bytes.len); if (size > strlen + 1) { LLVMValueRef trailing_zeros = llvm_get_zero_raw(LLVMArrayType(c->byte_type, size - strlen - 1)); @@ -112,24 +112,24 @@ static void tilde_emit_const_expr(TildeContext *c, TBEValue *value, Expr *expr) return; } ArraySize array_len = type->array.len; - ArraySize size = expr->const_expr.string.len + 1; + ArraySize size = expr->const_expr.bytes.len + 1; bool zero_terminate = array_len == size; LLVMValueRef string; if (array_len <= size) { if (zero_terminate) { - string = llvm_get_zstring(c, expr->const_expr.string.chars, expr->const_expr.string.len); + string = llvm_get_zstring(c, expr->const_expr.bytes.ptr, expr->const_expr.bytes.len); } else { - string = llvm_get_bytes(c, expr->const_expr.string.chars, array_len); + string = llvm_get_bytes(c, expr->const_expr.bytes.ptr, array_len); } } else { char *buffer = ccalloc(1, array_len); - memcpy(buffer, expr->const_expr.string.chars, expr->const_expr.string.len); + memcpy(buffer, expr->const_expr.bytes.ptr, expr->const_expr.bytes.len); string = llvm_get_bytes(c, buffer, array_len); } llvm_value_set(be_value, string, type);*/ @@ -883,7 +883,7 @@ TBEValue tilde_emit_assign_expr(TildeContext *c, TBEValue *ref, Expr *expr, TB_R tilde_emit_expr(c, &value, expr); tilde_store(c, ref, &value); } - else if (expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_INITIALIZER) + else if (expr_is_const_initializer(expr)) { TODO //llvm_emit_const_initialize_reference(c, ref, expr); diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index c8176e791..d4e6cfc88 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -334,6 +334,8 @@ const char *token_type_to_string(TokenType type) return "$defined"; case TOKEN_CT_ELSE: return "$else"; + case TOKEN_CT_EMBED: + return "$embed"; case TOKEN_CT_EVAL: return "$eval"; case TOKEN_CT_EVALTYPE: diff --git a/src/version.h b/src/version.h index 07fe19d4e..8035b528b 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.557" \ No newline at end of file +#define COMPILER_VERSION "0.4.558" \ No newline at end of file diff --git a/test/test_suite/embed/embed_basic.c3t b/test/test_suite/embed/embed_basic.c3t new file mode 100644 index 000000000..8a592fbf7 --- /dev/null +++ b/test/test_suite/embed/embed_basic.c3t @@ -0,0 +1,26 @@ +// #target: macos-x64 +module testing; + +fn void main() +{ + char[*] data = $embed("embed_basic.c3"); + char* data2 = $embed("embed_basic.c3"); + char[] data3 = $embed("embed_basic.c3"); +} + +/* #expect: testing.ll + +@.bytes = private unnamed_addr constant [162 x i8] c"module testing; +@.bytes.1 = private unnamed_addr constant [162 x i8] c"module testing; +@.bytes.2 = private unnamed_addr constant [162 x i8] c"module testing; + +define void @testing.main() #0 { +entry: + %data = alloca [162 x i8], align 16 + %data2 = alloca ptr, align 8 + %data3 = alloca %"char[]", align 8 + call void @llvm.memcpy.p0.p0.i32(ptr align 16 %data, ptr align 1 @.bytes, i32 162, i1 false) + store ptr @.bytes.1, ptr %data2, align 8 + store %"char[]" { ptr @.bytes.2, i64 162 }, ptr %data3, align 8 + ret void +} \ No newline at end of file