From 6ac99ed83c9875bba9570b609a1a12c3fcb2282e Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sun, 5 Mar 2023 20:02:53 +0100 Subject: [PATCH] Improve debug info. --- src/compiler/llvm_codegen.c | 28 ++++++++++++- src/compiler/llvm_codegen_debug_info.c | 42 +++++++++++-------- src/compiler/llvm_codegen_expr.c | 8 ++++ src/compiler/llvm_codegen_function.c | 6 +-- src/compiler/llvm_codegen_internal.h | 12 +++++- src/compiler/llvm_codegen_module.c | 39 +++++++++-------- src/compiler/llvm_codegen_stmt.c | 2 +- src/compiler/parse_stmt.c | 3 +- src/compiler/sema_casts.c | 40 +++++++++++++++--- src/version.h | 2 +- test/test_suite/debug_symbols/constants.c3t | 30 +++++++------ .../failed_distinct_float_conversions.c3 | 9 ++++ 12 files changed, 155 insertions(+), 66 deletions(-) create mode 100644 test/test_suite/expressions/casts/failed_distinct_float_conversions.c3 diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 8de08933b..46c439252 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -726,6 +726,7 @@ static void llvm_codegen_setup() attribute_id.readonly = lookup_attribute("readonly"); attribute_id.sext = lookup_attribute("signext"); attribute_id.sret = lookup_attribute("sret"); + attribute_id.uwtable = lookup_attribute("uwtable"); attribute_id.writeonly = lookup_attribute("writeonly"); attribute_id.zext = lookup_attribute("zeroext"); intrinsics_setup = true; @@ -1239,6 +1240,24 @@ void **llvm_gen(Module** modules, unsigned module_count) return (void**)gen_contexts; } +LLVMMetadataRef llvm_get_debug_file(GenContext *c, FileId file_id) +{ + VECEACH(c->debug.debug_files, i) + { + DebugFile *ref = &c->debug.debug_files[i]; + if (ref->file_id == file_id) return ref->debug_file; + } + File *f = source_file_by_id(file_id); + LLVMMetadataRef file = LLVMDIBuilderCreateFile(c->debug.builder, + f->name, + strlen(f->name), + f->dir_path, + strlen(f->dir_path)); + DebugFile debug_file = { .file_id = file_id, .debug_file = file }; + vec_add(c->debug.debug_files, debug_file); + return file; +} + static GenContext *llvm_gen_module(Module *module, LLVMContextRef shared_context) { if (!vec_size(module->units)) return NULL; @@ -1250,11 +1269,14 @@ static GenContext *llvm_gen_module(Module *module, LLVMContextRef shared_context gencontext_begin_module(gen_context); bool only_used = active_target.strip_unused && !active_target.testing; + FOREACH_BEGIN(CompilationUnit *unit, module->units) gencontext_init_file_emit(gen_context, unit); gen_context->debug.compile_unit = unit->llvm.debug_compile_unit; - gen_context->debug.file = unit->llvm.debug_file; + gen_context->debug.file = (DebugFile){ + .debug_file = unit->llvm.debug_file, + .file_id = unit->file->file_id }; FOREACH_BEGIN(Decl *initializer, unit->xxlizers) has_elements = true; @@ -1301,7 +1323,9 @@ static GenContext *llvm_gen_module(Module *module, LLVMContextRef shared_context FOREACH_BEGIN(CompilationUnit *unit, module->units) gen_context->debug.compile_unit = unit->llvm.debug_compile_unit; - gen_context->debug.file = unit->llvm.debug_file; + gen_context->debug.file = (DebugFile){ + .debug_file = unit->llvm.debug_file, + .file_id = unit->file->file_id }; FOREACH_BEGIN(Decl *var, unit->vars) if (only_used && !var->is_live) continue; diff --git a/src/compiler/llvm_codegen_debug_info.c b/src/compiler/llvm_codegen_debug_info.c index 7b769c777..cc569578e 100644 --- a/src/compiler/llvm_codegen_debug_info.c +++ b/src/compiler/llvm_codegen_debug_info.c @@ -29,7 +29,7 @@ static inline LLVMMetadataRef llvm_get_debug_struct(GenContext *c, Type *type, c size_t external_name_len = strlen(external_name); if (loc) { - file = c->debug.file; + file = c->debug.file.debug_file; row = loc->row; if (!row) row = 1; } @@ -58,7 +58,7 @@ static inline LLVMMetadataRef llvm_get_debug_member(GenContext *c, Type *type, c c->debug.builder, scope, name, strlen(name), - loc ? c->debug.file : NULL, + loc ? c->debug.file.debug_file : NULL, loc ? loc->row : 0, type_size(type) * 8, (uint32_t)(type_abi_alignment(type) * 8), @@ -90,12 +90,12 @@ void llvm_emit_debug_global_var(GenContext *c, Decl *global) SourceSpan loc = global->span; global->var.backend_debug_ref = LLVMDIBuilderCreateGlobalVariableExpression( c->debug.builder, - c->debug.file, + c->debug.file.debug_file, global->name, strlen(global->name), global->extname, strlen(global->extname), - c->debug.file, + c->debug.file.debug_file, loc.row ? loc.row : 1, llvm_get_debug_type(c, global->type), decl_is_local(global), @@ -122,10 +122,10 @@ void llvm_emit_debug_function(GenContext *c, Decl *decl) uint32_t row = decl->span.row; if (!row) row = 1; c->debug.function = LLVMDIBuilderCreateFunction(c->debug.builder, - c->debug.file, + c->debug.file.debug_file, decl->name, strlen(decl->name), decl->extname, strlen(decl->extname), - c->debug.file, + c->debug.file.debug_file, row, llvm_get_debug_type(c, decl->type), decl_is_local(decl), @@ -146,12 +146,13 @@ void llvm_emit_debug_local_var(GenContext *c, Decl *decl) if (!col) col = 1; const char *name = decl->name; if (!name) name = ".temp"; + LLVMMetadataRef scope = llvm_debug_current_scope(c); LLVMMetadataRef var = LLVMDIBuilderCreateAutoVariable( c->debug.builder, - c->debug.function, + scope, name, strlen(name), - c->debug.file, + c->debug.file.debug_file, row, llvm_get_debug_type(c, decl->type), active_target.optimization_level != OPTIMIZATION_NONE, @@ -164,7 +165,7 @@ void llvm_emit_debug_local_var(GenContext *c, Decl *decl) decl->backend_ref, var, LLVMDIBuilderCreateExpression(c->debug.builder, NULL, 0), LLVMDIBuilderCreateDebugLocation(c->context, row, col, - c->debug.function, inline_at), + scope, inline_at), LLVMGetInsertBlock(c->builder)); } @@ -191,7 +192,7 @@ void llvm_emit_debug_parameter(GenContext *c, Decl *parameter, unsigned index) name, strlen(name), index + 1, - c->debug.file, + c->debug.file.debug_file, row, llvm_get_debug_type(c, parameter->type), always_preserve, @@ -238,7 +239,7 @@ static LLVMMetadataRef llvm_debug_forward_comp(GenContext *c, Type *type, const return LLVMDIBuilderCreateReplaceableCompositeType(c->debug.builder, id_counter++, type->name, strlen(type->name), scope, - c->debug.file, row, + c->debug.file.debug_file, row, 1 /* version TODO */, type_size(type) * 8, type_abi_alignment(type) * 8, @@ -261,8 +262,13 @@ void llvm_debug_push_lexical_scope(GenContext *context, SourceSpan location) unsigned row = location.row; unsigned col = location.col; + LLVMMetadataRef debug_file = context->debug.file.debug_file; + if (location.file_id != context->debug.file.file_id) + { + debug_file = llvm_get_debug_file(context, location.file_id); + } LLVMMetadataRef block = - LLVMDIBuilderCreateLexicalBlock(context->debug.builder, scope, context->debug.file, + LLVMDIBuilderCreateLexicalBlock(context->debug.builder, scope, debug_file, row ? row : 1, col ? col : 1); @@ -321,7 +327,7 @@ static LLVMMetadataRef llvm_debug_enum_type(GenContext *c, Type *type, LLVMMetad LLVMMetadataRef real = LLVMDIBuilderCreateEnumerationType(c->debug.builder, scope, type->decl->name, strlen(type->decl->name), - c->debug.file, row ? row : 1, type_size(type) * 8, + c->debug.file.debug_file, row ? row : 1, type_size(type) * 8, type_abi_alignment(type) * 8, elements, vec_size(elements), llvm_get_debug_type(c, enum_real_type)); @@ -363,7 +369,7 @@ static LLVMMetadataRef llvm_debug_structlike_type(GenContext *c, Type *type, LLV scope, type->decl->name ? type->decl->name : "", type->decl->name ? strlen(type->decl->name) : 0, - c->debug.file, row ? row : 1, type_size(type) * 8, + c->debug.file.debug_file, row ? row : 1, type_size(type) * 8, type_abi_alignment(type) * 8, LLVMDIFlagZero, elements, vec_size(members), @@ -460,8 +466,8 @@ static LLVMMetadataRef llvm_debug_typedef_type(GenContext *c, Type *type) LLVMMetadataRef real = LLVMDIBuilderCreateTypedef(c->debug.builder, llvm_get_debug_type(c, original_type), decl->name, strlen(decl->name), - c->debug.file, row ? row : 1, - c->debug.file, type_abi_alignment(type)); + c->debug.file.debug_file, row ? row : 1, + c->debug.file.debug_file, type_abi_alignment(type)); if (type->backend_debug_type) { LLVMMetadataReplaceAllUsesWith(type->backend_debug_type, real); @@ -507,7 +513,7 @@ static LLVMMetadataRef llvm_debug_func_type(GenContext *c, Type *type) vec_add(buffer, llvm_get_debug_type(c, prototype->param_types[i])); } return LLVMDIBuilderCreateSubroutineType(c->debug.builder, - c->debug.file, + c->debug.file.debug_file, buffer, vec_size(buffer), 0); } @@ -590,5 +596,5 @@ static inline LLVMMetadataRef llvm_get_debug_type_internal(GenContext *c, Type * LLVMMetadataRef llvm_get_debug_type(GenContext *c, Type *type) { // All types should be generated in the outer scope. - return llvm_get_debug_type_internal(c, type, c->debug.file); + return llvm_get_debug_type_internal(c, type, c->debug.file.debug_file); } diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 94a1e7a3f..ed7cf1abe 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -5611,7 +5611,15 @@ static inline void llvm_emit_macro_block(GenContext *context, BEValue *be_value, llvm_store_decl(context, val, &value); FOREACH_END(); + if (llvm_use_debug(context)) + { + llvm_debug_push_lexical_scope(context, astptr(expr->macro_block.first_stmt)->span); + } llvm_emit_return_block(context, be_value, expr->type, expr->macro_block.first_stmt, expr->macro_block.block_exit); + if (llvm_use_debug(context)) + { + llvm_debug_scope_pop(context); + } } LLVMValueRef llvm_emit_call_intrinsic(GenContext *context, unsigned intrinsic, LLVMTypeRef *types, unsigned type_count, diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index 761a032c7..a3ba1a771 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -574,13 +574,13 @@ void llvm_emit_xxlizer(GenContext *c, Decl *decl) { uint32_t row = decl->span.row; if (!row) row = 1; - LLVMMetadataRef type = LLVMDIBuilderCreateSubroutineType(c->debug.builder, c->debug.file, NULL, 0, 0); + LLVMMetadataRef type = LLVMDIBuilderCreateSubroutineType(c->debug.builder, c->debug.file.debug_file, NULL, 0, 0); c->debug.function = LLVMDIBuilderCreateFunction(c->debug.builder, - c->debug.file, + c->debug.file.debug_file, scratch_buffer.str, scratch_buffer.len, scratch_buffer.str, scratch_buffer.len, - c->debug.file, + c->debug.file.debug_file, row, type, true, diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index ec9b40259..5753f5d7b 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -41,12 +41,19 @@ typedef struct LLVMBasicBlockRef next_block; } BreakContinue; +typedef struct DebugFile_ +{ + FileId file_id; + LLVMMetadataRef debug_file; +} DebugFile; + typedef struct { unsigned runtime_version : 8; bool enable_stacktrace : 1; LLVMDIBuilderRef builder; - LLVMMetadataRef file; + DebugFile *debug_files; + DebugFile file; LLVMMetadataRef compile_unit; LLVMMetadataRef function; SourceSpan current_range; @@ -61,6 +68,7 @@ typedef struct } DebugContext; + typedef struct GenContext_ { bool shared_context; @@ -225,6 +233,7 @@ typedef struct unsigned readonly; // No reads on pointer unsigned sext; // sign extend unsigned sret; // struct return pointer + unsigned uwtable; unsigned writeonly; // No writes on pointer unsigned zext; // zero extend } LLVMAttributes; @@ -466,6 +475,7 @@ LLVMValueRef llvm_get_opt_ref(GenContext *c, Decl *decl); #define POP_OPT() c->catch_block = _old_catch; c->opt_var = _old_opt_var // -- Debug -- +LLVMMetadataRef llvm_get_debug_file(GenContext *c, FileId file_id); INLINE bool llvm_use_debug(GenContext *context); void llvm_debug_scope_push(GenContext *context, LLVMMetadataRef debug_scope); void llvm_debug_scope_pop(GenContext *context); diff --git a/src/compiler/llvm_codegen_module.c b/src/compiler/llvm_codegen_module.c index 425d0ff60..983f26551 100644 --- a/src/compiler/llvm_codegen_module.c +++ b/src/compiler/llvm_codegen_module.c @@ -28,6 +28,12 @@ static inline LLVMTypeRef create_fault_type(GenContext *c) return type; } +static void llvm_set_module_flag(GenContext *c, LLVMModuleFlagBehavior flag_behavior, const char *flag, uint64_t value, Type *type) +{ + LLVMMetadataRef val = LLVMValueAsMetadata(LLVMConstInt(LLVMIntTypeInContext(c->context, type_bit_size(type)), value, false)); + LLVMAddModuleFlag(c->module, flag_behavior, flag, strlen(flag), val); +} + void gencontext_begin_module(GenContext *c) { assert(!c->module && "Expected no module"); @@ -41,7 +47,6 @@ void gencontext_begin_module(GenContext *c) LLVMSetModuleDataLayout(c->module, c->target_data); LLVMSetSourceFileName(c->module, c->code_module->name->module, strlen(c->code_module->name->module)); - LLVMTypeRef options_type = LLVMInt8TypeInContext(c->context); static const char *pic_level = "PIC Level"; static const char *pie_level = "PIE Level"; @@ -49,20 +54,16 @@ void gencontext_begin_module(GenContext *c) switch (active_target.reloc_model) { case RELOC_BIG_PIE: - setting = LLVMValueAsMetadata(LLVMConstInt(options_type, (unsigned)2 /* PIE */, false)); - LLVMAddModuleFlag(c->module, LLVMModuleFlagBehaviorOverride, pie_level, strlen(pie_level), setting); + llvm_set_module_flag(c, LLVMModuleFlagBehaviorOverride, pie_level, (unsigned)2 /* PIE */, type_uint); FALLTHROUGH; case RELOC_BIG_PIC: - setting = LLVMValueAsMetadata(LLVMConstInt(options_type, (unsigned)2 /* PIC */, false)); - LLVMAddModuleFlag(c->module, LLVMModuleFlagBehaviorOverride, pic_level, strlen(pic_level), setting); + llvm_set_module_flag(c, LLVMModuleFlagBehaviorOverride, pic_level, (unsigned)2 /* PIC */, type_uint); break; case RELOC_SMALL_PIE: - setting = LLVMValueAsMetadata(LLVMConstInt(options_type, (unsigned)1 /* pie */, false)); - LLVMAddModuleFlag(c->module, LLVMModuleFlagBehaviorOverride, pie_level, strlen(pie_level), setting); + llvm_set_module_flag(c, LLVMModuleFlagBehaviorOverride, pie_level, (unsigned)1 /* pie */, type_uint); FALLTHROUGH; case RELOC_SMALL_PIC: - setting = LLVMValueAsMetadata(LLVMConstInt(options_type, (unsigned)1 /* pic */, false)); - LLVMAddModuleFlag(c->module, LLVMModuleFlagBehaviorOverride, pic_level, strlen(pic_level), setting); + llvm_set_module_flag(c, LLVMModuleFlagBehaviorOverride, pic_level, (unsigned)1 /* pic */, type_uint); break; default: break; @@ -120,8 +121,12 @@ void gencontext_begin_module(GenContext *c) { if (active_target.arch_os_target == WINDOWS_X64 || active_target.arch_os_target == WINDOWS_AARCH64) { - setting = LLVMValueAsMetadata(LLVMConstInt(options_type, (unsigned)1 /* pic */, false)); - LLVMAddModuleFlag(c->module, LLVMModuleFlagBehaviorError, "CodeView", strlen("CodeView"), setting); + llvm_set_module_flag(c, LLVMModuleFlagBehaviorError, "CodeView", 1, type_uint); + } + else + { + llvm_set_module_flag(c, LLVMModuleFlagBehaviorWarning, "Dwarf Version", 4, type_uint); + llvm_set_module_flag(c, LLVMModuleFlagBehaviorWarning, "Debug Info Version", 3, type_uint); } c->debug.runtime_version = 1; c->debug.builder = LLVMCreateDIBuilder(c->module); @@ -145,18 +150,12 @@ void gencontext_init_file_emit(GenContext *c, CompilationUnit *unit) { if (active_target.debug_info != DEBUG_INFO_NONE) { - const char *filename = unit->file->name; - const char *dir_path = unit->file->dir_path; // Set runtime version here. - unit->llvm.debug_file = LLVMDIBuilderCreateFile(c->debug.builder, - filename, - strlen(filename), - dir_path, - strlen(dir_path)); + unit->llvm.debug_file = llvm_get_debug_file(c, unit->file->file_id); bool is_optimized = active_target.optimization_level != OPTIMIZATION_NONE; const char *dwarf_flags = ""; - unsigned runtime_version = 1; + unsigned runtime_version = 0; LLVMDWARFEmissionKind emission_kind = active_target.debug_info == DEBUG_INFO_FULL ? LLVMDWARFEmissionFull : LLVMDWARFEmissionLineTablesOnly; const char *debug_output_file = ""; @@ -171,7 +170,7 @@ void gencontext_init_file_emit(GenContext *c, CompilationUnit *unit) return; } unit->llvm.debug_compile_unit = LLVMDIBuilderCreateCompileUnit(c->debug.builder, - LLVMDWARFSourceLanguageC, + LLVMDWARFSourceLanguageC11, unit->llvm.debug_file, DWARF_PRODUCER_NAME, strlen(DWARF_PRODUCER_NAME), diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index a84bebe77..39d318354 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -1425,7 +1425,7 @@ void llvm_emit_panic_on_true(GenContext *c, LLVMValueRef value, const char *pani void llvm_emit_stmt(GenContext *c, Ast *ast) { - EMIT_LOC(c, ast); + if (ast->ast_kind != AST_COMPOUND_STMT) EMIT_LOC(c, ast); assert(!c->catch_block && "Did not expect a catch block here."); switch (ast->ast_kind) { diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index f1a53740f..04d9c67e1 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -1460,14 +1460,15 @@ Ast *parse_jump_stmt_no_eos(ParseContext *c) */ Ast* parse_compound_stmt(ParseContext *c) { - CONSUME_OR_RET(TOKEN_LBRACE, poisoned_ast); Ast *ast = ast_new_curr(c, AST_COMPOUND_STMT); + CONSUME_OR_RET(TOKEN_LBRACE, poisoned_ast); AstId *next = &ast->compound_stmt.first_stmt; while (!try_consume(c, TOKEN_RBRACE)) { ASSIGN_AST_OR_RET(Ast *stmt, parse_stmt(c), poisoned_ast); ast_append(&next, stmt); } + RANGE_EXTEND_PREV(ast); return ast; } diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 1de54677e..4d8269fe7 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -1072,6 +1072,7 @@ bool cast_implicit(SemaContext *context, Expr *expr, Type *to_type) { return cast_expr_inner(context, expr, to_type, false, false, false); } + /** * Try to make an explicit cast, Optional types are allowed. */ @@ -1157,6 +1158,9 @@ static bool cast_from_array(SemaContext *context, Expr *expr, Type *from, Type * return cast_with_optional(expr, to_type, add_optional); } +/** + * Check a vector element and see if it may implicitly convert to the other type. + */ static bool cast_vector_element_may_implicitly_convert(Expr *expr, Type *from, Type *to) { from = from->canonical; @@ -1165,7 +1169,7 @@ static bool cast_vector_element_may_implicitly_convert(Expr *expr, Type *from, T // Same type - we're fine. if (from == to) return true; - // If any of the elements are distinct we know it doesn't work. + // If any of the elements are distinct we know it doesn't work, if (from->type_kind == TYPE_DISTINCT || to->type_kind == TYPE_DISTINCT) return false; // Casting from bool always works (for int, float) @@ -1210,6 +1214,11 @@ static bool cast_vector_element_may_implicitly_convert(Expr *expr, Type *from, T return to_size > from_size && expr_is_simple(expr); } +/** + * Check all casts from vectors: + * 1. To arrays and inferred arrays -> if the array type matches, eg int[<2>] to int[2] + * 2. To vectors and inferred vectors -> if the vector type can be promoted, e.g. int[<2>] to long[<2>] + */ static bool cast_from_vector(SemaContext *context, Expr *expr, Type *from, Type *to, Type *to_type, bool add_optional, bool is_explicit, bool silent) { bool not_to_vector = !type_kind_is_any_vector(to->type_kind); @@ -1257,7 +1266,9 @@ static bool cast_from_vector(SemaContext *context, Expr *expr, Type *from, Type /** - * Cast an enum. + * Cast from an enum to other types. + * 1. Only explicit casts are allowed. + * 2. Casting to any integer is valid for the explicit cast. */ static bool cast_from_enum(SemaContext *context, Expr *expr, Type *from, Type *to, Type *to_type, bool add_optional, bool is_explicit, bool silent) { @@ -1378,24 +1389,38 @@ CAST: return cast_with_optional(expr, to_type, add_optional); } -static inline bool cast_float(SemaContext *context, Expr *expr, Type *from, Type *to, Type *to_type, bool add_optional, bool is_explicit, bool silent) +/** + * Casting from a float + * 1. To ints and bools -> valid with explicit casts. + * 2. To vectors -> valid if the it can cast to vector element type. + * 3. To floats -> narrowing if sub expression can be narrowed. Widening if the expr is simple. + * 4. To distinct -> try as if it was the base type if const, otherwise fail. + */ +static inline bool cast_from_float(SemaContext *context, Expr *expr, Type *from, Type *to, Type *to_type, bool add_optional, bool is_explicit, bool silent) { RETRY: switch (to->type_kind) { case ALL_INTS: case TYPE_BOOL: + // Bool and ints? Explicit casts only. if (is_explicit) goto CAST; goto REQUIRE_CAST; case TYPE_VECTOR: + // Check if the underlying element may be cast. to = to->array.base->canonical; goto RETRY; case ALL_FLOATS: { + // All explicit casts just work. if (is_explicit) goto CAST; + // If widening, only allow simple expressions. ByteSize to_size = type_size(to); ByteSize from_size = type_size(from); if (to_size > from_size) goto ONLY_SIMPLE; + // If same size, just cast. + if (to_size == from_size) goto CAST; + // If const, check it fits. if (expr_is_const(expr) && expr_const_will_overflow(&expr->const_expr, type_flatten(to)->type_kind)) { if (silent) return false; @@ -1403,7 +1428,7 @@ RETRY: type_quoted_error_string(to_type)); return false; } - if (to_size == from_size) goto CAST; + // Otherwise, check if the underlying code may narrow. Expr *problem = recursive_may_narrow(expr, to); if (problem) { @@ -1411,9 +1436,11 @@ RETRY: expr = problem; goto REQUIRE_CAST; } + // If no problem -> cast. goto CAST; } case TYPE_DISTINCT: + // Ignore distinct if casting from a const. if (expr_is_const(expr)) { to = type_flatten(to); @@ -1422,7 +1449,8 @@ RETRY: else { if (silent) return false; - bool may_explicit = cast_expr_inner(context, expr_copy(expr), to_type, true, true, false); + // Give a good error message, suggesting a cast if the cast had worked + bool may_explicit = cast_from_float(context, expr_copy(expr), from, type_flatten(to), to_type, add_optional, true, true); if (may_explicit) goto REQUIRE_CAST; break; } @@ -1584,7 +1612,7 @@ static bool cast_expr_inner(SemaContext *context, Expr *expr, Type *to_type, boo case ALL_INTS: return cast_from_integer(context, expr, from, to, to_type, add_optional, is_explicit, silent); case ALL_FLOATS: - return cast_float(context, expr, from, to, to_type, add_optional, is_explicit, silent); + return cast_from_float(context, expr, from, to, to_type, add_optional, is_explicit, silent); case TYPE_POISONED: return false; case TYPE_VOID: diff --git a/src/version.h b/src/version.h index a9a63f1fe..5b12e8168 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.97" \ No newline at end of file +#define COMPILER_VERSION "0.4.98" \ No newline at end of file diff --git a/test/test_suite/debug_symbols/constants.c3t b/test/test_suite/debug_symbols/constants.c3t index 23f86fb72..afd80f610 100644 --- a/test/test_suite/debug_symbols/constants.c3t +++ b/test/test_suite/debug_symbols/constants.c3t @@ -12,17 +12,21 @@ const FOO @private = ~(uint)(0); @constants.CC = protected unnamed_addr constant i32 -1, align 4 @constants.FOO = protected unnamed_addr constant i32 -1, align 4 -!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!0, !1} +!llvm.dbg.cu = !{!2} -!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "c3c", isOptimized: false, runtimeVersion: 1, emissionKind: FullDebug, globals: !2, splitDebugInlining: false) -!1 = !DIFile(filename: "constants.c3", -!DIGlobalVariableExpression -distinct !DIGlobalVariable(name: "AA", linkageName: "constants.AA", scope: !1, -!DIBasicType(name: "char", size: 8, encoding: DW_ATE_unsigned_char) -!DIGlobalVariableExpression -distinct !DIGlobalVariable(name: "BB", linkageName: "constants.BB", scope: !1, file: !1, line: 2 -!DIGlobalVariableExpression -distinct !DIGlobalVariable(name: "CC", linkageName: "constants.CC", scope: !1, file: !1, line: 3 - !DIBasicType(name: "uint", size: 32, encoding: DW_ATE_unsigned) - !DIGlobalVariableExpression - distinct !DIGlobalVariable(name: "FOO", linkageName: "constants.FOO", scope: !1, file: !1, line: +!0 = !{i32 2, !"Dwarf Version", i32 4} +!1 = !{i32 2, !"Debug Info Version", i32 3} +!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "c3c", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false) +!3 = !DIFile(filename: "constants.c3", +!4 = !{!5, !8, !10, !13} +!5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression()) +!6 = distinct !DIGlobalVariable(name: "AA", linkageName: "constants.AA", scope: !3 +!7 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_unsigned_char) +!8 = !DIGlobalVariableExpression(var: !9, expr: !DIExpression()) +!9 = distinct !DIGlobalVariable(name: "BB", linkageName: "constants.BB", scope: !3, file: !3, line: 2 +!10 = !DIGlobalVariableExpression(var: !11, expr: !DIExpression()) +!11 = distinct !DIGlobalVariable(name: "CC", linkageName: "constants.CC", scope: !3, file: !3, line: 3 +!12 = !DIBasicType(name: "uint", size: 32, encoding: DW_ATE_unsigned) +!13 = !DIGlobalVariableExpression(var: !14, expr: !DIExpression()) +!14 = distinct !DIGlobalVariable(name: "FOO", linkageName: "constants.FOO", scope: !3, file: !3, line: diff --git a/test/test_suite/expressions/casts/failed_distinct_float_conversions.c3 b/test/test_suite/expressions/casts/failed_distinct_float_conversions.c3 new file mode 100644 index 000000000..fea9b6ea4 --- /dev/null +++ b/test/test_suite/expressions/casts/failed_distinct_float_conversions.c3 @@ -0,0 +1,9 @@ +define Foo = distinct double; +define Bar = distinct void*; +fn int main() +{ + float f = 1; + Foo foo = f; // #error: you may use a cast + Bar bar = f; // #error: It is not possible to convert + return 1; +} \ No newline at end of file