diff --git a/CMakeLists.txt b/CMakeLists.txt index 9bdc72f25..43397cc67 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -205,17 +205,15 @@ add_executable(c3c src/build/project_creation.c src/compiler/ast.c src/compiler/bigint.c - src/compiler/c_abi_internal.h src/compiler/codegen_general.c src/compiler/compiler.c src/compiler/compiler.h src/compiler/context.c src/compiler/copying.c src/compiler/diagnostics.c - src/compiler/dwarf.h - src/compiler/enums.h src/compiler/float.c src/compiler/headers.c + src/compiler/json_output.c src/compiler/lexer.c src/compiler/libraries.c src/compiler/linker.c diff --git a/lib/std/collections/object.c3 b/lib/std/collections/object.c3 index 2ad164c11..ad01a534c 100644 --- a/lib/std/collections/object.c3 +++ b/lib/std/collections/object.c3 @@ -141,7 +141,7 @@ fn void Object.free(&self) { foreach (key : self.map.key_tlist()) { - self.map.get(key).free(); + (void)self.map.get(key).free(); free(key, .using = self.allocator); } self.map.free(); @@ -197,7 +197,7 @@ fn void Object.set_object(&self, String key, Object* new_object) @private defer { (void)free(entry.key, .using = self.allocator); - entry.value.free(); + (void)entry.value.free(); } self.map.set(key.copy(self.map.allocator), new_object); } diff --git a/lib/std/io/os/ls.c3 b/lib/std/io/os/ls.c3 index 8b38f0d56..c8a8ec716 100644 --- a/lib/std/io/os/ls.c3 +++ b/lib/std/io/os/ls.c3 @@ -41,7 +41,7 @@ fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Al { String filename = string::temp_from_wstring((WString)&find_data.cFileName)!; if (filename == ".." || filename == ".") continue; - list.append(path::new(filename, using)); + list.append(path::new(filename, using)!); }; } while (win32::findNextFileW(find, &find_data)); return list; diff --git a/lib/std/os/subprocess.c3 b/lib/std/os/subprocess.c3 index 3f00ed116..3e8c206a0 100644 --- a/lib/std/os/subprocess.c3 +++ b/lib/std/os/subprocess.c3 @@ -206,7 +206,7 @@ fn SubProcess! create(String[] command_line, SubProcessOptions options = {}, Str } start_info.hStdError = wr; - |}; + |}!; void *event_output; void *event_error; if (options.read_async) diff --git a/src/build/build_options.c b/src/build/build_options.c index 1e3c73b0c..44291a274 100644 --- a/src/build/build_options.c +++ b/src/build/build_options.c @@ -92,7 +92,7 @@ static void usage(void) OUTPUT(" --symtab - Sets the preferred symtab size."); OUTPUT(" -V --version - Print version information."); OUTPUT(" -E - Lex only."); - OUTPUT(" -P - Only parse and output the AST as S-expressions."); + OUTPUT(" -P - Only parse and output the AST as JSON."); OUTPUT(" -C - Only lex, parse and check."); OUTPUT(" - - Read code from standard in."); OUTPUT(" -o - Write output to ."); diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index af693addb..3716ef561 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -93,26 +93,6 @@ static void compiler_lex(void) exit_compiler(COMPILER_SUCCESS_EXIT); } -void compiler_parse(void) -{ - VECEACH(global_context.sources, i) - { - bool loaded = false; - const char *error; - File *file = source_file_load(global_context.sources[i], &loaded, &error); - if (!file) error_exit(error); - if (loaded) continue; - - global_context_clear_errors(); - parse_file(file); - } - if (active_target.read_stdin) - { - global_context_clear_errors(); - parse_stdin(); - } - exit_compiler(COMPILER_SUCCESS_EXIT); -} typedef struct CompileData_ @@ -291,6 +271,37 @@ void delete_object_files(const char **files, size_t count) } } +void compiler_parse(void) +{ + // Cleanup any errors (could there really be one here?!) + global_context_clear_errors(); + + // Add the standard library + if (global_context.lib_dir && !active_target.no_stdlib) + { + file_add_wildcard_files(&global_context.sources, global_context.lib_dir, true, c3_suffix_list, 3); + } + + // Load and parse all files. + bool has_error = false; + VECEACH(global_context.sources, i) + { + bool loaded = false; + const char *error; + File *file = source_file_load(global_context.sources[i], &loaded, &error); + if (!file) error_exit(error); + if (loaded) continue; + if (!parse_file(file)) has_error = true; + } + if (active_target.read_stdin) + { + if (!parse_stdin()) has_error = true; + } + + if (has_error) exit_compiler(EXIT_FAILURE); + compiler_parsing_time = bench_mark(); +} + void compiler_compile(void) { sema_analysis_run(); @@ -864,6 +875,9 @@ void compile() { compiler_parse(); compiler_parsing_time = bench_mark(); + emit_json(); + exit_compiler(COMPILER_SUCCESS_EXIT); + return; } compiler_compile(); diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index fa2d0d125..b762f2034 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -771,9 +771,10 @@ typedef struct bool is_builtin : 1; bool is_func_ref : 1; bool attr_pure : 1; - bool result_unused : 1; bool no_return : 1; bool is_dynamic_dispatch : 1; + bool has_optional_arg : 1; + bool must_use : 1; AstId body; union { @@ -1014,7 +1015,9 @@ typedef struct typedef struct { AstId first_stmt; - bool is_noreturn; + bool is_noreturn : 1; + bool is_must_use : 1; + bool had_optional_arg : 1; Decl **params; BlockExit **block_exit; } ExprMacroBlock; @@ -1917,7 +1920,7 @@ ARENA_DEF(type_info, TypeInfo) INLINE Type *typeinfotype(TypeInfoId id_) { - return type_infoptr(id_)->type; + return id_ ? type_infoptr(id_)->type : NULL; } bool ast_is_not_empty(Ast *ast); @@ -2245,6 +2248,7 @@ bool sema_analyse_cond_expr(SemaContext *context, Expr *expr); bool sema_analyse_expr_rhs(SemaContext *context, Type *to, Expr *expr, bool allow_optional); MemberIndex sema_get_initializer_const_array_size(SemaContext *context, Expr *initializer, bool *may_be_array, bool *is_const_size); bool sema_analyse_expr(SemaContext *context, Expr *expr); +bool sema_expr_check_discard(Expr *expr); bool sema_analyse_inferred_expr(SemaContext *context, Type *to, Expr *expr); bool sema_analyse_decl(SemaContext *context, Decl *decl); bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl); @@ -2289,6 +2293,8 @@ bool sema_type_error_on_binop(Expr *expr); File *source_file_by_id(FileId file); File *source_file_load(const char *filename, bool *already_loaded, const char **error); +void compiler_parse(void); +void emit_json(void); #define RANGE_EXTEND_PREV(x) do { (x)->span = extend_span_with_token((x)->span, c->prev_span); } while (0) @@ -2548,6 +2554,7 @@ INLINE bool type_is_wildcard(Type *type) INLINE bool type_is_optional(Type *type) { + if (!type) return false; DECL_TYPE_KIND_REAL(kind, type); return kind == TYPE_OPTIONAL; } diff --git a/src/compiler/json_output.c b/src/compiler/json_output.c new file mode 100644 index 000000000..5aebd1210 --- /dev/null +++ b/src/compiler/json_output.c @@ -0,0 +1,275 @@ +#include "compiler_internal.h" + +#define FOREACH_DECL(a__, modules__) \ + unsigned module_count_ = vec_size(modules__); \ + for (unsigned i_ = 0; i_ < module_count_; i_++) { \ + Module *module = modules__[i_]; \ + unsigned unit_count_ = vec_size(module->units); \ + for (unsigned j_ = 0; j_ < unit_count_; j_++) { \ + CompilationUnit *unit = module->units[j_]; \ + unsigned decl_count_ = vec_size(unit->global_decls); \ + for (unsigned k_ = 0; k_ < decl_count_; k_++) { \ + a__ = unit->global_decls[k_]; + +#define FOREACH_DECL_END } } } +#define INERT_COMMA do { if (first) { first = false; } else { fputs(",\n", file); } } while(0) + +static inline void emit_modules(FILE *file) +{ + + fputs("\t\"modules\": {\n", file); + FOREACH_BEGIN_IDX(i, Module *module, global_context.module_list) + if (i != 0) fputs(",\n", file); + fprintf(file, "\t\t\"%s\"", module->name->module); + FOREACH_END(); + fputs("\n\t},\n", file); + fputs("\t\"generic_modules\": {\n", file); + FOREACH_BEGIN_IDX(i, Module *module, global_context.generic_module_list) + if (i != 0) fputs(",\n", file); + fprintf(file, "\t\t\"%s\"", module->name->module); + FOREACH_END(); + fputs("\n\t}\n", file); +} + +static inline const char *decl_type_to_string(Decl *type) +{ + switch (type->decl_kind) + { + case DECL_ATTRIBUTE: return "attribute"; + case DECL_BITSTRUCT: return "bitstruct"; + case DECL_CT_ASSERT: return "$assert"; + case DECL_CT_ECHO: return "$echo"; + case DECL_DEFINE: return "def"; + case DECL_DISTINCT: return "distinct"; + case DECL_ENUM: return "enum"; + case DECL_ENUM_CONSTANT: return "enum_const"; + case DECL_FAULT: return "fault"; + case DECL_FAULTVALUE: return "fault_val"; + case DECL_FUNC: return "function"; + case DECL_FNTYPE: return "fntype"; + case DECL_GLOBALS: return "global"; + case DECL_INITIALIZE: return "initializer"; + case DECL_FINALIZE: return "finalizer"; + case DECL_IMPORT: return "import"; + case DECL_CT_INCLUDE: return "$include"; + case DECL_MACRO: return "macro"; + case DECL_STRUCT: return "struct"; + case DECL_TYPEDEF: return "typedef"; + case DECL_UNION: return "union"; + case DECL_VAR: + case DECL_LABEL: + case DECL_DECLARRAY: + case DECL_BODYPARAM: + case DECL_POISONED: + case DECL_ERASED: + UNREACHABLE + } + UNREACHABLE +} +static inline void emit_type_data(FILE *file, Module *module, Decl *type) +{ + fprintf(file, "\t\t\"%s::%s\": {\n", module->name->module, type->name); + fprintf(file, "\t\t\t\"kind\": \"%s\"", decl_type_to_string(type)); + if (type->decl_kind == DECL_STRUCT || type->decl_kind == DECL_UNION) + { + fputs(",\n\t\t\t\"members\": {\n", file); + FOREACH_BEGIN_IDX(i, Decl *member, type->strukt.members) + if (i != 0) fputs(",\n", file); + fprintf(file, "\t\t\t\t\"%s\"", member->name); + FOREACH_END(); + fputs("\n\t\t\t}", file); + } + fputs("\n\t\t}", file); +} + +void print_type(FILE *file, TypeInfo *type) +{ + if (type->resolve_status == RESOLVE_DONE) + { + fputs(type->type->name, file); + return; + } + switch (type->kind) + { + case TYPE_INFO_POISON: + UNREACHABLE; + case TYPE_INFO_IDENTIFIER: + case TYPE_INFO_CT_IDENTIFIER: + if (type->unresolved.path) + { + fprintf(file, "%s::", type->unresolved.path->module); + } + fputs(type->unresolved.name, file); + break; + case TYPE_INFO_TYPEOF: + scratch_buffer_clear(); + span_to_scratch(type->unresolved_type_expr->span); + fprintf(file, "$typeof(%s)", scratch_buffer_to_string()); + break; + case TYPE_INFO_VATYPE: + fprintf(file, "$vatype(...)"); + break; + case TYPE_INFO_EVALTYPE: + fprintf(file, "$evaltype(...)"); + break; + case TYPE_INFO_TYPEFROM: + fprintf(file, "$typefrom(...)"); + break; + case TYPE_INFO_ARRAY: + print_type(file, type->array.base); + scratch_buffer_clear(); + span_to_scratch(type->array.len->span); + fprintf(file, "[%s]", scratch_buffer_to_string()); + break; + case TYPE_INFO_VECTOR: + print_type(file, type->array.base); + scratch_buffer_clear(); + span_to_scratch(type->array.len->span); + fprintf(file, "[<%s>]", scratch_buffer_to_string()); + break; + case TYPE_INFO_INFERRED_ARRAY: + print_type(file, type->array.base); + fputs("[*]", file); + break; + case TYPE_INFO_INFERRED_VECTOR: + print_type(file, type->array.base); + fputs("[<>]", file); + break; + case TYPE_INFO_SUBARRAY: + print_type(file, type->array.base); + fputs("[]", file); + break; + case TYPE_INFO_POINTER: + print_type(file, type->array.base); + fputs("*", file); + break; + case TYPE_INFO_GENERIC: + print_type(file, type->array.base); + fputs("(<...>)", file); + break; + } + switch (type->subtype) + { + case TYPE_COMPRESSED_NONE: + break; + case TYPE_COMPRESSED_PTR: + fputs("*", file); + break; + case TYPE_COMPRESSED_SUB: + fputs("[]", file); + break; + case TYPE_COMPRESSED_SUBPTR: + fputs("[]*", file); + break; + case TYPE_COMPRESSED_PTRPTR: + fputs("**", file); + break; + case TYPE_COMPRESSED_PTRSUB: + fputs("*[]", file); + break; + case TYPE_COMPRESSED_SUBSUB: + fputs("[][]", file); + break; + } +} +static inline void emit_func_data(FILE *file, Module *module, Decl *func) +{ + fprintf(file, "\t\t\"%s::%s\": {\n", module->name->module, func->name); + fprintf(file, "\t\t\t\"rtype\": \""); + print_type(file, type_infoptr(func->func_decl.signature.rtype)); + fprintf(file, "\",\n"); + fputs("\t\t\t\"params\": [\n", file); + FOREACH_BEGIN_IDX(i, Decl *decl, func->func_decl.signature.params) + if (i != 0) fputs(",\n", file); + if (!decl) continue; + fputs("\t\t\t\t{\n", file); + fprintf(file, "\t\t\t\t\t\"name\": \"%s\",\n", decl->name ? decl->name : ""); + fprintf(file, "\t\t\t\t\t\"type\": \""); + if (decl->var.type_info) + { + print_type(file, decl->var.type_info); + } + else + { + fputs("", file); + } + fputs("\"\n", file); + fputs("\t\t\t\t}", file); + FOREACH_END(); + fputs("\n\t\t\t]\n", file); + + fputs("\n\t\t}", file); +} + +static inline bool decl_is_hidden(Decl *decl) +{ + return decl->visibility > VISIBLE_PUBLIC; +} + +static inline void emit_types(FILE *file) +{ + fputs("\t\"types\": {\n", file); + { + bool first = true; + FOREACH_DECL(Decl *type, global_context.module_list) + if (!decl_is_user_defined_type(type) && type->decl_kind != DECL_TYPEDEF) continue; + if (decl_is_hidden(type)) continue; + INERT_COMMA; + emit_type_data(file, module, type); + FOREACH_DECL_END; + } + + fputs("\n\t},\n", file); + fputs("\t\"generic_types\": {\n", file); + { + bool first = true; + FOREACH_DECL(Decl *type, global_context.generic_module_list) + if (!decl_is_user_defined_type(type) && type->decl_kind != DECL_TYPEDEF) continue; + if (decl_is_hidden(type)) continue; + INERT_COMMA; + emit_type_data(file, module, type); + FOREACH_DECL_END; + } + fputs("\n\t}\n", file); +} + +static inline void emit_functions(FILE *file) +{ + fputs("\t\"functions\": {\n", file); + { + bool first = true; + FOREACH_DECL(Decl *func, global_context.module_list) + if (func->decl_kind != DECL_FUNC) continue; + if (decl_is_hidden(func)) continue; + INERT_COMMA; + emit_func_data(file, module, func); + FOREACH_DECL_END; + } + fputs("\n\t},\n", file); + + fputs("\t\"generic_functions\": {\n", file); + { + bool first = true; + FOREACH_DECL(Decl *func, global_context.generic_module_list) + if (func->decl_kind != DECL_FUNC) continue; + if (decl_is_hidden(func)) continue; + INERT_COMMA; + emit_func_data(file, module, func); + FOREACH_DECL_END; + } + fputs("\n\t},\n", file); +} +static inline void emit_json_to_file(FILE *file) +{ + fputs("{\n", file); + emit_modules(file); + emit_types(file); + emit_functions(file); + fputs("\n}", file); +} + + +void emit_json(void) +{ + emit_json_to_file(stdout); +} \ No newline at end of file diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 2263709c3..1ab2df3fe 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -5855,6 +5855,9 @@ static inline void llvm_emit_return_block(GenContext *c, BEValue *be_value, Type goto DONE; } + // return foo() where foo() is a void! + if (type_no_optional(type_flatten(ret_expr->type)) == type_void) break; + LLVMInstructionEraseFromParent(exit.block_return_out); // Restore diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 19e960dc1..5cbc298e4 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1641,23 +1641,17 @@ static inline bool sema_call_analyse_func_invocation(SemaContext *context, Type return false; } - bool is_unused = expr->call_expr.result_unused; - if (sig->attrs.noreturn) expr->call_expr.no_return = true; if (!sema_call_analyse_invocation(context, expr, callee, &optional)) return false; Type *rtype = type->function.prototype->rtype; - if (is_unused && rtype != type_void) + expr->call_expr.has_optional_arg = optional; + if (rtype != type_void) { - if (sig->attrs.nodiscard) RETURN_SEMA_ERROR(expr, "The result of the function must be used."); - if (type_is_optional(rtype) && !sig->attrs.maydiscard) - { - RETURN_SEMA_ERROR(expr, "The optional result of the function must be used."); - } + expr->call_expr.must_use = sig->attrs.nodiscard || (type_is_optional(rtype) && !sig->attrs.maydiscard); } - expr->type = type_add_optional(rtype, optional); return true; @@ -1784,7 +1778,7 @@ static inline bool sema_expr_analyse_func_call(SemaContext *context, Expr *expr, -bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *struct_var, Decl *decl, bool optional) +bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *struct_var, Decl *decl, bool call_var_optional) { assert(decl->decl_kind == DECL_MACRO); @@ -1802,19 +1796,21 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s Ast *body = copy_ast_macro(astptr(decl->func_decl.body)); AstId docs = decl->func_decl.docs; if (docs) docs = astid(copy_ast_macro(astptr(docs))); + Signature *sig = &decl->func_decl.signature; copy_end(); CalledDecl callee = { .macro = true, .name = decl->name, .params = params, .block_parameter = decl->func_decl.body_param ? declptr(decl->func_decl.body_param)->name : NULL, - .signature = &decl->func_decl.signature, + .signature = sig, .struct_var = struct_var }; - if (!sema_call_analyse_invocation(context, call_expr, callee, &optional)) return false; + bool has_optional_arg = call_var_optional; + if (!sema_call_analyse_invocation(context, call_expr, callee, &has_optional_arg)) return false; - unsigned vararg_index = decl->func_decl.signature.vararg_index; + unsigned vararg_index = sig->vararg_index; Expr **args = call_expr->call_expr.arguments; VECEACH(params, i) { @@ -1825,7 +1821,6 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s // Splat? That's the simple case. if (call_expr->call_expr.splat_vararg) { - if (!sema_analyse_expr(context, args[i] = call_expr->call_expr.splat)) return false; } else @@ -1840,6 +1835,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s } } param->var.init_expr = args[i]; + has_optional_arg = has_optional_arg || IS_OPTIONAL(args[i]); } Decl **body_params = call_expr->call_expr.body_arguments; @@ -1896,21 +1892,11 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s macro_context.compilation_unit = context->unit; macro_context.macro_call_depth = context->macro_call_depth + 1; macro_context.call_env = context->call_env; - rtype = decl->func_decl.signature.rtype ? type_infoptr(decl->func_decl.signature.rtype)->type : NULL; + rtype = typeinfotype(sig->rtype); macro_context.expected_block_type = rtype; - bool may_be_optional = true; - if (rtype) - { - if (type_is_optional(rtype)) - { - optional = true; - rtype = type_no_optional(rtype); - } - else - { - may_be_optional = false; - } - } + bool optional_return = rtype && type_is_optional(rtype); + bool may_be_optional = !rtype || optional_return; + if (rtype) rtype = type_no_optional(rtype); context_change_scope_with_flags(¯o_context, SCOPE_MACRO); @@ -1945,7 +1931,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s if (!sema_analyse_statement(¯o_context, body)) goto EXIT_FAIL; params = macro_context.macro_params; - bool is_no_return = decl->func_decl.signature.attrs.noreturn; + bool is_no_return = sig->attrs.noreturn; if (!vec_size(macro_context.returns)) { @@ -2003,34 +1989,23 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s type_quoted_error_string(type)); goto EXIT_FAIL; } - if (may_be_optional) ret_expr->type = type_add_optional(ret_expr->type, may_be_optional); + ret_expr->type = type_add_optional(ret_expr->type, optional_return); } - call_expr->type = type_add_optional(rtype, optional); + call_expr->type = type_add_optional(rtype, optional_return || has_optional_arg); } else { Type *sum_returns = context_unify_returns(¯o_context); if (!sum_returns) goto EXIT_FAIL; - call_expr->type = type_add_optional(sum_returns, optional); + optional_return = type_is_optional(sum_returns); + call_expr->type = type_add_optional(sum_returns, optional_return || has_optional_arg); } assert(call_expr->type); - if (call_expr->call_expr.result_unused) + bool must_use = false; + if (rtype != type_void) { - Type *type = call_expr->type; - if (type != type_void) - { - if (decl->func_decl.signature.attrs.nodiscard) - { - SEMA_ERROR(call_expr, "The result of the macro must be used."); - goto EXIT_FAIL; - } - if (type_is_optional(type) && !decl->func_decl.signature.attrs.maydiscard) - { - SEMA_ERROR(call_expr, "The optional result of the macro must be used."); - goto EXIT_FAIL; - } - } + must_use = sig->attrs.nodiscard || (optional_return && !sig->attrs.maydiscard); } unsigned returns_found = vec_size(macro_context.returns); // We may have zero normal macro returns but the active scope still has a "jump end". @@ -2039,12 +2014,28 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s { is_no_return = true; } - if (returns_found) + if (returns_found == 1) { Ast *ret = macro_context.returns[0]; Expr *result = ret ? ret->return_stmt.expr : NULL; if (!result) goto NOT_CT; if (!expr_is_constant_eval(result, CONSTANT_EVAL_CONSTANT_VALUE)) goto NOT_CT; + bool only_ct_params = true; + VECEACH(params, i) + { + Decl *param = params[i]; + // Skip raw vararg + if (!param) continue; + switch (param->var.kind) + { + case VARDECL_PARAM_CT: + case VARDECL_PARAM_CT_TYPE: + case VARDECL_PARAM_EXPR: + break; + default: + goto NOT_CT; + } + } if (ast_is_compile_time(body)) { expr_replace(call_expr, result); @@ -2053,6 +2044,8 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s } NOT_CT: call_expr->expr_kind = EXPR_MACRO_BLOCK; + call_expr->macro_block.had_optional_arg = has_optional_arg; + call_expr->macro_block.is_must_use = must_use; call_expr->macro_block.first_stmt = body->compound_stmt.first_stmt; call_expr->macro_block.params = params; call_expr->macro_block.block_exit = block_exit_ref; @@ -4193,7 +4186,8 @@ static inline bool sema_expr_analyse_expr_list(SemaContext *context, Expr *expr) VECEACH(expr->expression_list, i) { Expr *checked_expr = expr->expression_list[i]; - success &= sema_analyse_expr(context, checked_expr); + if (!sema_analyse_expr(context, checked_expr)) return false; + if (i != last && !sema_expr_check_discard(checked_expr)) return false; } expr->type = expr->expression_list[last]->type; return success; @@ -7907,6 +7901,27 @@ bool sema_analyse_expr_lvalue(SemaContext *context, Expr *expr) } } +bool sema_expr_check_discard(Expr *expr) +{ + if (!IS_OPTIONAL(expr)) return true; + if (expr->expr_kind == EXPR_SUBSCRIPT_ASSIGN || expr->expr_kind == EXPR_SLICE_ASSIGN) return true; + if (expr->expr_kind == EXPR_BINARY && expr->binary_expr.operator >= BINARYOP_ASSIGN) return true; + if (expr->expr_kind == EXPR_MACRO_BLOCK) + { + if (expr->macro_block.is_must_use) RETURN_SEMA_ERROR(expr, "The result of the macro must be used."); + if (expr->macro_block.had_optional_arg) goto ERROR_ARGS; + return true; + } + if (expr->expr_kind == EXPR_CALL) + { + if (expr->call_expr.must_use) RETURN_SEMA_ERROR(expr, "The result of the function must be used."); + if (expr->call_expr.has_optional_arg) goto ERROR_ARGS; + return true; + } + RETURN_SEMA_ERROR(expr, "An optional value may not be discarded, you can ignore it with a void cast '(void)', rethrow on optional with '!' or panic '!!' to avoid this error."); +ERROR_ARGS: + RETURN_SEMA_ERROR(expr, "The result of this call is optional due to its argument(s). The optional result may not be implicitly discarded. Consider using '(void)', '!' or '!!' to handle this."); +} bool sema_analyse_expr(SemaContext *context, Expr *expr) { diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 264fbd2d9..271a09558 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -1040,11 +1040,8 @@ static inline bool sema_analyse_declare_stmt(SemaContext *context, Ast *statemen static inline bool sema_analyse_expr_stmt(SemaContext *context, Ast *statement) { Expr *expr = statement->expr_stmt; - if (expr->expr_kind == EXPR_CALL) - { - expr->call_expr.result_unused = true; - } if (!sema_analyse_expr(context, expr)) return false; + if (!sema_expr_check_discard(expr)) return false; switch (expr->expr_kind) { case EXPR_CALL: diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index 67387fda4..719b63e5e 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -301,33 +301,8 @@ static void assign_panicfn(void) */ void sema_analysis_run(void) { - // Cleanup any errors (could there really be one here?!) - global_context_clear_errors(); - // Add the standard library - if (global_context.lib_dir && !active_target.no_stdlib) - { - file_add_wildcard_files(&global_context.sources, global_context.lib_dir, true, c3_suffix_list, 3); - } - - // Load and parse all files. - bool has_error = false; - VECEACH(global_context.sources, i) - { - bool loaded = false; - const char *error; - File *file = source_file_load(global_context.sources[i], &loaded, &error); - if (!file) error_exit(error); - if (loaded) continue; - if (!parse_file(file)) has_error = true; - } - if (active_target.read_stdin) - { - if (!parse_stdin()) has_error = true; - } - - if (has_error) exit_compiler(EXIT_FAILURE); - compiler_parsing_time = bench_mark(); + compiler_parse(); // All global defines are added to the std module global_context.std_module_path = (Path) { .module = kw_std, .span = INVALID_SPAN, .len = (uint32_t) strlen(kw_std) }; diff --git a/src/version.h b/src/version.h index 5b8748452..68fe63be1 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.583" \ No newline at end of file +#define COMPILER_VERSION "0.4.584" \ No newline at end of file diff --git a/test/test_suite/defer/defer_with_rethrow.c3 b/test/test_suite/defer/defer_with_rethrow.c3 index 97440e7b5..beb9d1a47 100644 --- a/test/test_suite/defer/defer_with_rethrow.c3 +++ b/test/test_suite/defer/defer_with_rethrow.c3 @@ -7,7 +7,7 @@ fn int! bar() defer { {| foo()!; - |}; + |}!!; } defer foo()!; // #error: Rethrows are not allowed inside of defers. return 1; diff --git a/test/test_suite/errors/failable_catch.c3t b/test/test_suite/errors/failable_catch.c3t index 60c2f431b..443132a5d 100644 --- a/test/test_suite/errors/failable_catch.c3t +++ b/test/test_suite/errors/failable_catch.c3t @@ -19,12 +19,12 @@ fn int main() int! b = foo((a + 3) ?? 2); int! c = foo(0); - printf("a = %d\n", a); - printf("b = %d\n", b); - printf("c = %d\n", c); + (void)printf("a = %d\n", a); + (void)printf("b = %d\n", b); + (void)printf("c = %d\n", c); if (@catchof(c)) printf("c had error\n"); c = 3; - printf("c = %d\n", c); + (void)printf("c = %d\n", c); return 0; } diff --git a/test/test_suite/errors/optional_chained_init.c3t b/test/test_suite/errors/optional_chained_init.c3t index d280f6208..a627092b1 100644 --- a/test/test_suite/errors/optional_chained_init.c3t +++ b/test/test_suite/errors/optional_chained_init.c3t @@ -10,8 +10,8 @@ fn void test1() int! b = a = Test.FOO?; if (catch err = a) io::printfn("A err was: %s", err); if (catch err = b) io::printfn("B err was: %s", err); - io::printfn("A was: %s", a); - io::printfn("B was: %s", b); + (void)io::printfn("A was: %s", a); + (void)io::printfn("B was: %s", b); } fn void test2() @@ -21,8 +21,8 @@ fn void test2() int! b = a = x; if (catch err = a) io::printfn("A err was: %s", err); if (catch err = b) io::printfn("B err was: %s", err); - io::printfn("A was: %s", a); - io::printfn("B was: %s", b); + (void)io::printfn("A was: %s", a); + (void)io::printfn("B was: %s", b); } @@ -33,8 +33,8 @@ fn void test3() int! b = a = x; if (catch err = a) io::printfn("A err was: %s", err); if (catch err = b) io::printfn("B err was: %s", err); - io::printfn("A was: %s", a); - io::printfn("B was: %s", b); + (void)io::printfn("A was: %s", a); + (void)io::printfn("B was: %s", b); } fn void main() diff --git a/test/test_suite/errors/optional_discarded_func.c3 b/test/test_suite/errors/optional_discarded_func.c3 new file mode 100644 index 000000000..a578e1365 --- /dev/null +++ b/test/test_suite/errors/optional_discarded_func.c3 @@ -0,0 +1,54 @@ + +fn int abc(int x) { return 1; } +fn int! def2(int y) @maydiscard { return 1; } +fn int! def3(int z) { return 1; } + +fn void test1() +{ + int! x; + abc(x); // #error: The result of this call is optional due to its argumen +} + +fn void test2() +{ + int! x; + int y; + abc(y); + abc(x) + 4; // #error: An optional value may not be discarded +} + +fn void test3() +{ + int! x; + int y; + def2(1); + def2(x); // #error: The result of this call is optional due to its argumen +} + +fn void test4() +{ + int! x; + int y; + def2(1); + def2(x) + 4; // #error: An optional value may not be discarded +} + +fn void test5() +{ + int! x; + int y; + def3(y); // #error: The result of the function must be used +} + +fn void test6() +{ + int! x; + int y; + def3(x); // #error: The result of the function must be used +} + +fn void test7() +{ + int y; + def3(y) + 4; // #error: An optional value may not be discarded +} diff --git a/test/test_suite/errors/optional_discarded_macro.c3 b/test/test_suite/errors/optional_discarded_macro.c3 new file mode 100644 index 000000000..24c2bcf24 --- /dev/null +++ b/test/test_suite/errors/optional_discarded_macro.c3 @@ -0,0 +1,54 @@ + +macro int abc(int x) { return 1; } +macro int! def2(int y) @maydiscard { return 1; } +macro int! def3(int z) { return 1; } + +fn void test1() +{ + int! x; + abc(x); // #error: The result of this call is optional due to its argumen +} + +fn void test2() +{ + int! x; + int y; + abc(y); + abc(x) + 4; // #error: An optional value may not be discarded +} + +fn void test3() +{ + int! x; + int y; + def2(1); + def2(x); // #error: The result of this call is optional due to its argumen +} + +fn void test4() +{ + int! x; + int y; + def2(1); + def2(x) + 4; // #error: An optional value may not be discarded +} + +fn void test5() +{ + int! x; + int y; + def3(y); // #error: The result of the macro must +} + +fn void test6() +{ + int! x; + int y; + def3(x); // #error: The result of the macro must +} + +fn void test7() +{ + int y; + def3(y) + 4; // #error: An optional value may not be discarded +} diff --git a/test/test_suite/errors/optional_taddr_and_access.c3t b/test/test_suite/errors/optional_taddr_and_access.c3t index 64f4f552d..f23796c7c 100644 --- a/test/test_suite/errors/optional_taddr_and_access.c3t +++ b/test/test_suite/errors/optional_taddr_and_access.c3t @@ -17,10 +17,10 @@ fn void main() { int! z = 2; Foo*! w = &&Foo{ z, 0 }; - printf("%d\n", w.x); + (void)printf("%d\n", w.x); z = MyErr.FOO?; w = &&Foo{ z, 0 }; - printf("Not visible: %d\n", w.x); + (void)printf("Not visible: %d\n", w.x); } /* #expect: test.ll diff --git a/test/test_suite/functions/c_vararg_expansion.c3t b/test/test_suite/functions/c_vararg_expansion.c3t index db9304df8..1c36240ef 100644 --- a/test/test_suite/functions/c_vararg_expansion.c3t +++ b/test/test_suite/functions/c_vararg_expansion.c3t @@ -6,7 +6,7 @@ fn void main() { Foo a = 12.3; Foo! b = 33.5; - printf("%f %f %f", (float)a, a, b); + (void)printf("%f %f %f", (float)a, a, b); } /* #expect: test.ll diff --git a/test/test_suite/macros/macro_chained_return_void_optional.c3t b/test/test_suite/macros/macro_chained_return_void_optional.c3t new file mode 100644 index 000000000..b8724062d --- /dev/null +++ b/test/test_suite/macros/macro_chained_return_void_optional.c3t @@ -0,0 +1,9 @@ +module abi; + +fn void! abc() {} +macro void! abc_macro() => abc(); + +fn void main() +{ + (void)abc_macro(); +} diff --git a/test/test_suite/statements/custom_foreach_with_ref.c3t b/test/test_suite/statements/custom_foreach_with_ref.c3t index 56d85f3f2..5ce980701 100644 --- a/test/test_suite/statements/custom_foreach_with_ref.c3t +++ b/test/test_suite/statements/custom_foreach_with_ref.c3t @@ -84,7 +84,6 @@ fn void main() /* #expect: foo.ll %Foo = type { [3 x i32] } - @"$ct.foo.Foo" = linkonce global %.introspect { i8 10, ptr null, i64 12, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 @.str = private unnamed_addr constant [11 x i8] c"getFields\0A\00", align 1 @.__const = private unnamed_addr constant [5 x i32] [i32 3, i32 5, i32 2, i32 10, i32 111], align 16 @@ -102,10 +101,8 @@ fn void main() @.str.12 = private unnamed_addr constant [27 x i8] c"Pull value tempptr %d: %d\0A\00", align 1 @.str.13 = private unnamed_addr constant [7 x i8] c"%d %d\0A\00", align 1 @.str.14 = private unnamed_addr constant [7 x i8] c"%d %d\0A\00", align 1 - ; Function Attrs: nounwind declare void @printf(ptr, ...) #0 - ; Function Attrs: nounwind define void @foo.getFields(ptr noalias sret([5 x i32]) align 4 %0) #0 { entry: @@ -115,14 +112,12 @@ entry: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %0, ptr align 4 %literal, i32 20, i1 false) ret void } - ; Function Attrs: nounwind define ptr @foo.call(ptr %0) #0 { entry: call void (ptr, ...) @printf(ptr @.str.1) ret ptr %0 } - ; Function Attrs: nounwind define void @foo.main() #0 { entry: @@ -180,264 +175,241 @@ entry: call void (ptr, ...) @printf(ptr @.str.3, i32 %2, i32 %5, i32 %8) %9 = call ptr @foo.call(ptr %x) store ptr %9, ptr %.anon, align 8 + %10 = load ptr, ptr %.anon, align 8 store i32 3, ptr %.anon1, align 4 store i32 0, ptr %.anon2, align 4 br label %loop.cond - loop.cond: ; preds = %loop.body, %entry - %10 = load i32, ptr %.anon2, align 4 - %11 = load i32, ptr %.anon1, align 4 - %lt = icmp slt i32 %10, %11 + %11 = load i32, ptr %.anon2, align 4 + %12 = load i32, ptr %.anon1, align 4 + %lt = icmp slt i32 %11, %12 br i1 %lt, label %loop.body, label %loop.exit - loop.body: ; preds = %loop.cond - %12 = load i32, ptr %.anon2, align 4 - store i32 %12, ptr %i, align 4 - %13 = load ptr, ptr %.anon, align 8 - %14 = load i32, ptr %.anon2, align 4 - store i32 %14, ptr %a, align 4 - %15 = getelementptr inbounds %Foo, ptr %13, i32 0, i32 0 - %16 = load i32, ptr %a, align 4 - %sext = sext i32 %16 to i64 - %17 = getelementptr inbounds [3 x i32], ptr %15, i64 0, i64 %sext - %18 = load i32, ptr %17, align 4 - store i32 %18, ptr %y, align 4 - %19 = load i32, ptr %i, align 4 - %20 = load i32, ptr %y, align 4 - call void (ptr, ...) @printf(ptr @.str.4, i32 %19, i32 %20) - %21 = load i32, ptr %.anon2, align 4 - %add = add i32 %21, 1 + %13 = load i32, ptr %.anon2, align 4 + store i32 %13, ptr %i, align 4 + %14 = load ptr, ptr %.anon, align 8 + %15 = load i32, ptr %.anon2, align 4 + store i32 %15, ptr %a, align 4 + %16 = getelementptr inbounds %Foo, ptr %14, i32 0, i32 0 + %17 = load i32, ptr %a, align 4 + %sext = sext i32 %17 to i64 + %18 = getelementptr inbounds [3 x i32], ptr %16, i64 0, i64 %sext + %19 = load i32, ptr %18, align 4 + store i32 %19, ptr %y, align 4 + %20 = load i32, ptr %i, align 4 + %21 = load i32, ptr %y, align 4 + call void (ptr, ...) @printf(ptr @.str.4, i32 %20, i32 %21) + %22 = load i32, ptr %.anon2, align 4 + %add = add i32 %22, 1 store i32 %add, ptr %.anon2, align 4 br label %loop.cond - loop.exit: ; preds = %loop.cond store i32 3, ptr %.anon3, align 4 store i32 0, ptr %.anon4, align 4 br label %loop.cond5 - loop.cond5: ; preds = %loop.body7, %loop.exit - %22 = load i32, ptr %.anon4, align 4 - %23 = load i32, ptr %.anon3, align 4 - %lt6 = icmp slt i32 %22, %23 + %23 = load i32, ptr %.anon4, align 4 + %24 = load i32, ptr %.anon3, align 4 + %lt6 = icmp slt i32 %23, %24 br i1 %lt6, label %loop.body7, label %loop.exit14 - loop.body7: ; preds = %loop.cond5 - %24 = load i32, ptr %.anon4, align 4 - store i32 %24, ptr %i8, align 4 %25 = load i32, ptr %.anon4, align 4 - store i32 %25, ptr %a10, align 4 - %26 = getelementptr inbounds %Foo, ptr %x, i32 0, i32 0 - %27 = load i32, ptr %a10, align 4 - %sext11 = sext i32 %27 to i64 - %28 = getelementptr inbounds [3 x i32], ptr %26, i64 0, i64 %sext11 - store ptr %28, ptr %y9, align 8 - %29 = load ptr, ptr %y9, align 8 - %30 = load i32, ptr %29, align 4 - %add12 = add i32 %30, 1 - store i32 %add12, ptr %29, align 4 - %31 = load i32, ptr %i8, align 4 - %32 = load ptr, ptr %y9, align 8 - %33 = load i32, ptr %32, align 4 - call void (ptr, ...) @printf(ptr @.str.5, i32 %31, i32 %33) - %34 = load i32, ptr %.anon4, align 4 - %add13 = add i32 %34, 1 + store i32 %25, ptr %i8, align 4 + %26 = load i32, ptr %.anon4, align 4 + store i32 %26, ptr %a10, align 4 + %27 = getelementptr inbounds %Foo, ptr %x, i32 0, i32 0 + %28 = load i32, ptr %a10, align 4 + %sext11 = sext i32 %28 to i64 + %29 = getelementptr inbounds [3 x i32], ptr %27, i64 0, i64 %sext11 + store ptr %29, ptr %y9, align 8 + %30 = load ptr, ptr %y9, align 8 + %31 = load i32, ptr %30, align 4 + %add12 = add i32 %31, 1 + store i32 %add12, ptr %30, align 4 + %32 = load i32, ptr %i8, align 4 + %33 = load ptr, ptr %y9, align 8 + %34 = load i32, ptr %33, align 4 + call void (ptr, ...) @printf(ptr @.str.5, i32 %32, i32 %34) + %35 = load i32, ptr %.anon4, align 4 + %add13 = add i32 %35, 1 store i32 %add13, ptr %.anon4, align 4 br label %loop.cond5 - loop.exit14: ; preds = %loop.cond5 store i32 3, ptr %.anon15, align 4 store i32 0, ptr %.anon16, align 4 br label %loop.cond17 - loop.cond17: ; preds = %loop.body19, %loop.exit14 - %35 = load i32, ptr %.anon16, align 4 - %36 = load i32, ptr %.anon15, align 4 - %lt18 = icmp slt i32 %35, %36 + %36 = load i32, ptr %.anon16, align 4 + %37 = load i32, ptr %.anon15, align 4 + %lt18 = icmp slt i32 %36, %37 br i1 %lt18, label %loop.body19, label %loop.exit25 - loop.body19: ; preds = %loop.cond17 - %37 = load i32, ptr %.anon16, align 4 - store i32 %37, ptr %i20, align 4 %38 = load i32, ptr %.anon16, align 4 - store i32 %38, ptr %a22, align 4 - %39 = getelementptr inbounds %Foo, ptr %x, i32 0, i32 0 - %40 = load i32, ptr %a22, align 4 - %sext23 = sext i32 %40 to i64 - %41 = getelementptr inbounds [3 x i32], ptr %39, i64 0, i64 %sext23 - %42 = load i32, ptr %41, align 4 - store i32 %42, ptr %y21, align 4 - %43 = load i32, ptr %i20, align 4 - %44 = load i32, ptr %y21, align 4 - call void (ptr, ...) @printf(ptr @.str.6, i32 %43, i32 %44) - %45 = load i32, ptr %.anon16, align 4 - %add24 = add i32 %45, 1 + store i32 %38, ptr %i20, align 4 + %39 = load i32, ptr %.anon16, align 4 + store i32 %39, ptr %a22, align 4 + %40 = getelementptr inbounds %Foo, ptr %x, i32 0, i32 0 + %41 = load i32, ptr %a22, align 4 + %sext23 = sext i32 %41 to i64 + %42 = getelementptr inbounds [3 x i32], ptr %40, i64 0, i64 %sext23 + %43 = load i32, ptr %42, align 4 + store i32 %43, ptr %y21, align 4 + %44 = load i32, ptr %i20, align 4 + %45 = load i32, ptr %y21, align 4 + call void (ptr, ...) @printf(ptr @.str.6, i32 %44, i32 %45) + %46 = load i32, ptr %.anon16, align 4 + %add24 = add i32 %46, 1 store i32 %add24, ptr %.anon16, align 4 br label %loop.cond17 - loop.exit25: ; preds = %loop.cond17 store i32 3, ptr %.anon26, align 4 store i32 0, ptr %.anon27, align 4 br label %loop.cond28 - loop.cond28: ; preds = %loop.body30, %loop.exit25 - %46 = load i32, ptr %.anon27, align 4 - %47 = load i32, ptr %.anon26, align 4 - %lt29 = icmp slt i32 %46, %47 + %47 = load i32, ptr %.anon27, align 4 + %48 = load i32, ptr %.anon26, align 4 + %lt29 = icmp slt i32 %47, %48 br i1 %lt29, label %loop.body30, label %loop.exit36 - loop.body30: ; preds = %loop.cond28 - %48 = load i32, ptr %.anon27, align 4 - store i32 %48, ptr %i31, align 4 %49 = load i32, ptr %.anon27, align 4 - store i32 %49, ptr %a33, align 4 - %50 = getelementptr inbounds %Foo, ptr %x, i32 0, i32 0 - %51 = load i32, ptr %a33, align 4 - %sext34 = sext i32 %51 to i64 - %52 = getelementptr inbounds [3 x i32], ptr %50, i64 0, i64 %sext34 - %53 = load i32, ptr %52, align 4 - store i32 %53, ptr %y32, align 4 - %54 = load i32, ptr %i31, align 4 - %55 = load i32, ptr %y32, align 4 - call void (ptr, ...) @printf(ptr @.str.7, i32 %54, i32 %55) - %56 = load i32, ptr %.anon27, align 4 - %add35 = add i32 %56, 1 + store i32 %49, ptr %i31, align 4 + %50 = load i32, ptr %.anon27, align 4 + store i32 %50, ptr %a33, align 4 + %51 = getelementptr inbounds %Foo, ptr %x, i32 0, i32 0 + %52 = load i32, ptr %a33, align 4 + %sext34 = sext i32 %52 to i64 + %53 = getelementptr inbounds [3 x i32], ptr %51, i64 0, i64 %sext34 + %54 = load i32, ptr %53, align 4 + store i32 %54, ptr %y32, align 4 + %55 = load i32, ptr %i31, align 4 + %56 = load i32, ptr %y32, align 4 + call void (ptr, ...) @printf(ptr @.str.7, i32 %55, i32 %56) + %57 = load i32, ptr %.anon27, align 4 + %add35 = add i32 %57, 1 store i32 %add35, ptr %.anon27, align 4 br label %loop.cond28 - loop.exit36: ; preds = %loop.cond28 store i32 3, ptr %.anon37, align 4 store i32 0, ptr %.anon38, align 4 br label %loop.cond39 - loop.cond39: ; preds = %loop.body41, %loop.exit36 - %57 = load i32, ptr %.anon38, align 4 - %58 = load i32, ptr %.anon37, align 4 - %lt40 = icmp slt i32 %57, %58 + %58 = load i32, ptr %.anon38, align 4 + %59 = load i32, ptr %.anon37, align 4 + %lt40 = icmp slt i32 %58, %59 br i1 %lt40, label %loop.body41, label %loop.exit48 - loop.body41: ; preds = %loop.cond39 - %59 = load i32, ptr %.anon38, align 4 - store i32 %59, ptr %i42, align 4 %60 = load i32, ptr %.anon38, align 4 - store i32 %60, ptr %a44, align 4 - %61 = getelementptr inbounds %Foo, ptr %x, i32 0, i32 0 - %62 = load i32, ptr %a44, align 4 - %sext45 = sext i32 %62 to i64 - %63 = getelementptr inbounds [3 x i32], ptr %61, i64 0, i64 %sext45 - %64 = load i32, ptr %63, align 4 - store i32 %64, ptr %y43, align 4 - %65 = load i32, ptr %i42, align 4 - %66 = load i32, ptr %y43, align 4 - call void (ptr, ...) @printf(ptr @.str.8, i32 %65, i32 %66) - %67 = load i32, ptr %i42, align 4 - %add46 = add i32 %67, 1 + store i32 %60, ptr %i42, align 4 + %61 = load i32, ptr %.anon38, align 4 + store i32 %61, ptr %a44, align 4 + %62 = getelementptr inbounds %Foo, ptr %x, i32 0, i32 0 + %63 = load i32, ptr %a44, align 4 + %sext45 = sext i32 %63 to i64 + %64 = getelementptr inbounds [3 x i32], ptr %62, i64 0, i64 %sext45 + %65 = load i32, ptr %64, align 4 + store i32 %65, ptr %y43, align 4 + %66 = load i32, ptr %i42, align 4 + %67 = load i32, ptr %y43, align 4 + call void (ptr, ...) @printf(ptr @.str.8, i32 %66, i32 %67) + %68 = load i32, ptr %i42, align 4 + %add46 = add i32 %68, 1 store i32 %add46, ptr %i42, align 4 - %68 = load i32, ptr %.anon38, align 4 - %add47 = add i32 %68, 1 + %69 = load i32, ptr %.anon38, align 4 + %add47 = add i32 %69, 1 store i32 %add47, ptr %.anon38, align 4 br label %loop.cond39 - loop.exit48: ; preds = %loop.cond39 call void @llvm.memcpy.p0.p0.i32(ptr align 16 %.anon49, ptr align 16 @.__const.9, i32 20, i1 false) store i64 0, ptr %.anon50, align 8 br label %loop.cond51 - loop.cond51: ; preds = %loop.body52, %loop.exit48 - %69 = load i64, ptr %.anon50, align 8 - %gt = icmp ugt i64 5, %69 - br i1 %gt, label %loop.body52, label %loop.exit57 - -loop.body52: ; preds = %loop.cond51 %70 = load i64, ptr %.anon50, align 8 - store i64 %70, ptr %i53, align 8 + %gt = icmp ugt i64 5, %70 + br i1 %gt, label %loop.body52, label %loop.exit57 +loop.body52: ; preds = %loop.cond51 %71 = load i64, ptr %.anon50, align 8 - %72 = getelementptr inbounds [5 x i32], ptr %.anon49, i64 0, i64 %71 - %73 = load i32, ptr %72, align 4 - store i32 %73, ptr %y54, align 4 - %74 = load i64, ptr %i53, align 8 - %75 = load i32, ptr %y54, align 4 - call void (ptr, ...) @printf(ptr @.str.10, i64 %74, i32 %75) - %76 = load i64, ptr %i53, align 8 - %add55 = add i64 %76, 1 + store i64 %71, ptr %i53, align 8 + %72 = load i64, ptr %.anon50, align 8 + %73 = getelementptr inbounds [5 x i32], ptr %.anon49, i64 0, i64 %72 + %74 = load i32, ptr %73, align 4 + store i32 %74, ptr %y54, align 4 + %75 = load i64, ptr %i53, align 8 + %76 = load i32, ptr %y54, align 4 + call void (ptr, ...) @printf(ptr @.str.10, i64 %75, i32 %76) + %77 = load i64, ptr %i53, align 8 + %add55 = add i64 %77, 1 store i64 %add55, ptr %i53, align 8 - %77 = load i64, ptr %.anon50, align 8 - %add56 = add i64 %77, 1 + %78 = load i64, ptr %.anon50, align 8 + %add56 = add i64 %78, 1 store i64 %add56, ptr %.anon50, align 8 br label %loop.cond51 - loop.exit57: ; preds = %loop.cond51 call void @foo.getFields(ptr sret([5 x i32]) align 4 %.anon58) store i64 0, ptr %.anon59, align 8 br label %loop.cond60 - loop.cond60: ; preds = %loop.body62, %loop.exit57 - %78 = load i64, ptr %.anon59, align 8 - %gt61 = icmp ugt i64 5, %78 - br i1 %gt61, label %loop.body62, label %loop.exit66 - -loop.body62: ; preds = %loop.cond60 %79 = load i64, ptr %.anon59, align 8 - store i64 %79, ptr %i63, align 8 + %gt61 = icmp ugt i64 5, %79 + br i1 %gt61, label %loop.body62, label %loop.exit66 +loop.body62: ; preds = %loop.cond60 %80 = load i64, ptr %.anon59, align 8 - %81 = getelementptr inbounds [5 x i32], ptr %.anon58, i64 0, i64 %80 - %82 = load i32, ptr %81, align 4 - store i32 %82, ptr %y64, align 4 - %83 = load i64, ptr %i63, align 8 - %84 = load i32, ptr %y64, align 4 - call void (ptr, ...) @printf(ptr @.str.11, i64 %83, i32 %84) - %85 = load i64, ptr %.anon59, align 8 - %add65 = add i64 %85, 1 + store i64 %80, ptr %i63, align 8 + %81 = load i64, ptr %.anon59, align 8 + %82 = getelementptr inbounds [5 x i32], ptr %.anon58, i64 0, i64 %81 + %83 = load i32, ptr %82, align 4 + store i32 %83, ptr %y64, align 4 + %84 = load i64, ptr %i63, align 8 + %85 = load i32, ptr %y64, align 4 + call void (ptr, ...) @printf(ptr @.str.11, i64 %84, i32 %85) + %86 = load i64, ptr %.anon59, align 8 + %add65 = add i64 %86, 1 store i64 %add65, ptr %.anon59, align 8 br label %loop.cond60 - loop.exit66: ; preds = %loop.cond60 call void @foo.getFields(ptr sret([5 x i32]) align 4 %sretparam) store ptr %sretparam, ptr %.anon67, align 8 store i64 0, ptr %.anon68, align 8 br label %loop.cond69 - loop.cond69: ; preds = %loop.body71, %loop.exit66 - %86 = load i64, ptr %.anon68, align 8 - %gt70 = icmp ugt i64 5, %86 - br i1 %gt70, label %loop.body71, label %loop.exit75 - -loop.body71: ; preds = %loop.cond69 %87 = load i64, ptr %.anon68, align 8 - store i64 %87, ptr %i72, align 8 - %88 = load ptr, ptr %.anon67, align 8 - %89 = load i64, ptr %.anon68, align 8 - %90 = getelementptr inbounds [5 x i32], ptr %88, i64 0, i64 %89 - %91 = load i32, ptr %90, align 4 - store i32 %91, ptr %y73, align 4 - %92 = load i64, ptr %i72, align 8 - %93 = load i32, ptr %y73, align 4 - call void (ptr, ...) @printf(ptr @.str.12, i64 %92, i32 %93) - %94 = load i64, ptr %.anon68, align 8 - %add74 = add i64 %94, 1 + %gt70 = icmp ugt i64 5, %87 + br i1 %gt70, label %loop.body71, label %loop.exit75 +loop.body71: ; preds = %loop.cond69 + %88 = load i64, ptr %.anon68, align 8 + store i64 %88, ptr %i72, align 8 + %89 = load ptr, ptr %.anon67, align 8 + %90 = load i64, ptr %.anon68, align 8 + %91 = getelementptr inbounds [5 x i32], ptr %89, i64 0, i64 %90 + %92 = load i32, ptr %91, align 4 + store i32 %92, ptr %y73, align 4 + %93 = load i64, ptr %i72, align 8 + %94 = load i32, ptr %y73, align 4 + call void (ptr, ...) @printf(ptr @.str.12, i64 %93, i32 %94) + %95 = load i64, ptr %.anon68, align 8 + %add74 = add i64 %95, 1 store i64 %add74, ptr %.anon68, align 8 br label %loop.cond69 - loop.exit75: ; preds = %loop.cond69 - %95 = getelementptr inbounds %Foo, ptr %x, i32 0, i32 0 - %96 = getelementptr inbounds [3 x i32], ptr %95, i64 0, i64 0 - %97 = load i32, ptr %96, align 4 - %98 = getelementptr inbounds %Foo, ptr %x, i32 0, i32 0 - %99 = getelementptr inbounds [3 x i32], ptr %98, i64 0, i64 1 - %100 = load i32, ptr %99, align 4 - call void (ptr, ...) @printf(ptr @.str.13, i32 %97, i32 %100) - %101 = getelementptr inbounds %Foo, ptr %x, i32 0, i32 0 - %102 = getelementptr inbounds [3 x i32], ptr %101, i64 0, i64 1 - store ptr %102, ptr %y76, align 8 - %103 = load ptr, ptr %y76, align 8 - %104 = load i32, ptr %103, align 4 - %add77 = add i32 %104, 1 - store i32 %add77, ptr %103, align 4 - %105 = getelementptr inbounds %Foo, ptr %x, i32 0, i32 0 - %106 = getelementptr inbounds [3 x i32], ptr %105, i64 0, i64 0 - %107 = load i32, ptr %106, align 4 - %108 = getelementptr inbounds %Foo, ptr %x, i32 0, i32 0 - %109 = getelementptr inbounds [3 x i32], ptr %108, i64 0, i64 1 - %110 = load i32, ptr %109, align 4 - call void (ptr, ...) @printf(ptr @.str.14, i32 %107, i32 %110) + %96 = getelementptr inbounds %Foo, ptr %x, i32 0, i32 0 + %97 = getelementptr inbounds [3 x i32], ptr %96, i64 0, i64 0 + %98 = load i32, ptr %97, align 4 + %99 = getelementptr inbounds %Foo, ptr %x, i32 0, i32 0 + %100 = getelementptr inbounds [3 x i32], ptr %99, i64 0, i64 1 + %101 = load i32, ptr %100, align 4 + call void (ptr, ...) @printf(ptr @.str.13, i32 %98, i32 %101) + %102 = getelementptr inbounds %Foo, ptr %x, i32 0, i32 0 + %103 = getelementptr inbounds [3 x i32], ptr %102, i64 0, i64 1 + store ptr %103, ptr %y76, align 8 + %104 = load ptr, ptr %y76, align 8 + %105 = load i32, ptr %104, align 4 + %add77 = add i32 %105, 1 + store i32 %add77, ptr %104, align 4 + %106 = getelementptr inbounds %Foo, ptr %x, i32 0, i32 0 + %107 = getelementptr inbounds [3 x i32], ptr %106, i64 0, i64 0 + %108 = load i32, ptr %107, align 4 + %109 = getelementptr inbounds %Foo, ptr %x, i32 0, i32 0 + %110 = getelementptr inbounds [3 x i32], ptr %109, i64 0, i64 1 + %111 = load i32, ptr %110, align 4 + call void (ptr, ...) @printf(ptr @.str.14, i32 %108, i32 %111) ret void } diff --git a/test/test_suite/stdlib/map.c3t b/test/test_suite/stdlib/map.c3t index e0fce457a..8deb94bb6 100644 --- a/test/test_suite/stdlib/map.c3t +++ b/test/test_suite/stdlib/map.c3t @@ -27,7 +27,7 @@ fn void main() io::printfn("Map size: %d", map.count); map.set(1, Foo { 2, null }); io::printfn("Map size: %d", map.count); - io::printfn("Val: %d", map.get(1).x); + (void)io::printfn("Val: %d", map.get(1).x); io::printfn("Has 1: %s", map.has_key(1)); io::printfn("Has 2: %s", map.has_key(2)); map.set(7, Foo { 4, null });