diff --git a/lib/std/io/file.c3 b/lib/std/io/file.c3 index 95da59d23..0e27ca96a 100644 --- a/lib/std/io/file.c3 +++ b/lib/std/io/file.c3 @@ -175,6 +175,8 @@ fn char[]! load_buffer(String filename, char[] buffer) } +fn char[]! load(Allocator allocator, String filename) => load_new(filename, allocator); + fn char[]! load_new(String filename, Allocator allocator = allocator::heap()) { File file = open(filename, "rb")!; diff --git a/releasenotes.md b/releasenotes.md index 6a1ee0529..6a16195a7 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -7,6 +7,8 @@ - Add `--enable-new-generics` to enable `Foo{int}` generic syntax. - `{| |}` expression blocks deprecated. - c3c `--test-leak-report` flag for displaying full memory lead report if any +- Output into /.build/obj/ by default. +- Output llvm/asm into llvm/ and asm/ by default. ### Fixes - Bug appearing when `??` was combined with boolean in some cases. @@ -18,6 +20,8 @@ - Crash when trying to define a method macro that isn't `@construct` but has no arguments. - Regression, `.gitkeep` files were generated incorrectly. - Aliases are now correctly handled as if they were variables/functions in regards to namespacing and accept `@builtin`. +- Correctly handle in/out when interacting with inout. +- Don't delete .o files not produced by the compiler. ### Stdlib changes diff --git a/src/build/builder.c b/src/build/builder.c index 0f0d7d32a..91e2e1dd0 100644 --- a/src/build/builder.c +++ b/src/build/builder.c @@ -461,23 +461,54 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions * default: UNREACHABLE; } + if (target->arch_os_target == ARCH_OS_TARGET_DEFAULT) target->arch_os_target = default_target; + + if (target->arch_os_target == ARCH_OS_TARGET_DEFAULT) + { + error_exit("Unable to detect the default target, please set an explicit --target value."); + } + + const char *target_name = arch_os_target[target->arch_os_target]; if (command_accepts_files(options->command)) { - target->build_dir = options->build_dir ? options->build_dir : NULL; - target->object_file_dir = options->obj_out ? options->obj_out : target->build_dir; - target->ir_file_dir = options->llvm_out ? options->llvm_out : target->build_dir; - target->asm_file_dir = options->asm_out ? options->asm_out : target->build_dir; - target->script_dir = options->script_dir ? options->script_dir : target->script_dir; + target->build_dir = options->build_dir ? options->build_dir : ".build"; + if (!target->script_dir) target->script_dir = target->build_dir; } else { - target->build_dir = options->build_dir ? options->build_dir : "build"; - target->object_file_dir = options->obj_out ? options->obj_out : file_append_path(target->build_dir, "tmp"); - target->ir_file_dir = options->llvm_out ? options->llvm_out : file_append_path(target->build_dir, "llvm_ir"); - target->asm_file_dir = options->asm_out ? options->asm_out : file_append_path(target->build_dir, "asm"); - target->script_dir = options->script_dir ? options->script_dir : target->script_dir; + if (!target->build_dir) target->build_dir = "build"; + if (options->build_dir) + { + target->build_dir = options->build_dir; + } + else + { + options->build_dir = target->build_dir; + } if (!target->script_dir) target->script_dir = "scripts"; } + target->ir_file_dir = options->llvm_out; + target->asm_file_dir = options->asm_out; + target->object_file_dir = options->obj_out; + if (!target->ir_file_dir) + { + target->ir_file_dir = options->build_dir + ? file_append_path(file_append_path(options->build_dir, "llvm"), target_name) + : file_append_path("llvm", target_name); + } + if (!target->asm_file_dir) + { + target->asm_file_dir = options->build_dir + ? file_append_path(file_append_path(options->build_dir, "asm"), target_name) + : file_append_path("asm", target_name); + } + if (!target->object_file_dir) + { + target->object_file_dir = options->build_dir + ? file_append_path(file_append_path(options->build_dir, "obj"), target_name) + : file_append_path("obj", target_name); + } + switch (options->compile_option) { case COMPILE_NORMAL: diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 30cdcdcff..46d5266aa 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -391,13 +391,21 @@ void compiler_parse(void) static void create_output_dir(const char *dir) { - if (!file_exists(dir)) + if (!dir) return; + if (file_exists(dir)) { - if (!dir_make(dir)) error_exit("Failed to create output directory %s.", dir); + if (!file_is_dir(dir)) error_exit("Output directory is not a directory %s.", dir); + return; + } + scratch_buffer_clear(); + scratch_buffer_append(dir); + if (!dir_make_recursive(scratch_buffer_to_string())) + { + error_exit("Failed to create directory '%s'.", dir); } - if (!file_is_dir(dir)) error_exit("Output directory is not a directory %s.", dir); } + void compiler_compile(void) { if (compiler.build.lsp_output) @@ -439,31 +447,19 @@ void compiler_compile(void) if (compiler.build.asm_file_dir || compiler.build.ir_file_dir || compiler.build.emit_object_files) { - if (compiler.build.build_dir && !file_exists(compiler.build.build_dir) && !dir_make(compiler.build.build_dir)) - { - error_exit("Failed to create build directory '%s'.", compiler.build.build_dir); - } + create_output_dir(compiler.build.build_dir); } if (compiler.build.ir_file_dir && (compiler.build.emit_llvm || compiler.build.test_output || compiler.build.lsp_output)) { - if (!file_exists(compiler.build.ir_file_dir) && !dir_make(compiler.build.ir_file_dir)) - { - error_exit("Failed to create output directory '%s'.", compiler.build.ir_file_dir); - } + create_output_dir(compiler.build.ir_file_dir); } if (compiler.build.asm_file_dir && compiler.build.emit_asm) { - if (!file_exists(compiler.build.asm_file_dir) && !dir_make(compiler.build.asm_file_dir)) - { - error_exit("Failed to create output directory '%s'.", compiler.build.asm_file_dir); - } + create_output_dir(compiler.build.asm_file_dir); } if (compiler.build.object_file_dir && compiler.build.emit_object_files) { - if (!file_exists(compiler.build.object_file_dir) && !dir_make(compiler.build.object_file_dir)) - { - error_exit("Failed to create output directory '%s'.", compiler.build.object_file_dir); - } + create_output_dir(compiler.build.object_file_dir); } if (compiler.build.type == TARGET_TYPE_EXECUTABLE && !compiler.context.main && !compiler.build.no_entry) { @@ -533,14 +529,14 @@ void compiler_compile(void) free_arenas(); uint32_t output_file_count = vec_size(gen_contexts); - unsigned objfiles = vec_size(compiler.build.object_files); + unsigned external_objfile_count = vec_size(compiler.build.object_files); unsigned cfiles = vec_size(compiler.build.csources); unsigned cfiles_library = 0; FOREACH(LibraryTarget *, lib, compiler.build.ccompiling_libraries) { cfiles_library += vec_size(lib->csources); } - unsigned total_output = output_file_count + cfiles + cfiles_library + objfiles; + unsigned total_output = output_file_count + cfiles + cfiles_library + external_objfile_count; if (total_output > MAX_OUTPUT_FILES) { error_exit("Too many output files."); @@ -575,7 +571,7 @@ void compiler_compile(void) obj_file_next += compile_cfiles(lib->cc ? lib->cc : compiler.build.cc, lib->csources, lib->cflags, lib->cinclude_dirs, obj_file_next, lib->parent->provides); } - for (unsigned i = 0; i < objfiles; i++) + for (unsigned i = 0; i < external_objfile_count; i++) { obj_file_next[0] = compiler.build.object_files[i]; obj_file_next++; @@ -624,7 +620,8 @@ void compiler_compile(void) puts("# output-files-end"); } - output_file_count += cfiles + cfiles_library + objfiles; + output_file_count += cfiles + cfiles_library + external_objfile_count; + unsigned objfile_delete_count = output_file_count - external_objfile_count; free(compile_data); compiler_codegen_time = bench_mark(); @@ -671,7 +668,7 @@ void compiler_compile(void) platform_linker(output_exe, obj_files, output_file_count); compiler_link_time = bench_mark(); compiler_print_bench(); - delete_object_files(obj_files, output_file_count); + delete_object_files(obj_files, objfile_delete_count); } else { @@ -684,7 +681,7 @@ void compiler_compile(void) } else { - delete_object_files(obj_files, output_file_count); + delete_object_files(obj_files, objfile_delete_count); } } @@ -750,7 +747,7 @@ void compiler_compile(void) { error_exit("Failed to produce static library '%s'.", output_static); } - delete_object_files(obj_files, output_file_count); + delete_object_files(obj_files, objfile_delete_count); compiler_link_time = bench_mark(); compiler_print_bench(); OUTF("Static library '%s' created.\n", output_static); @@ -770,7 +767,7 @@ void compiler_compile(void) { error_exit("Failed to produce dynamic library '%s'.", output_dynamic); } - delete_object_files(obj_files, output_file_count); + delete_object_files(obj_files, objfile_delete_count); OUTF("Dynamic library '%s' created.\n", output_dynamic); compiler_link_time = bench_mark(); compiler_print_bench(); diff --git a/src/compiler/sema_asm.c b/src/compiler/sema_asm.c index 0c63283cd..0b0fd24c4 100644 --- a/src/compiler/sema_asm.c +++ b/src/compiler/sema_asm.c @@ -459,7 +459,7 @@ static inline bool sema_check_asm_memvar(SemaContext *context, AsmInlineBlock *b if (is_read) { decl->var.is_read = true; - if (decl->var.out_param) + if (decl->var.out_param && !decl->var.in_param) { RETURN_SEMA_ERROR(expr, "An 'out' variable may not be read from."); } @@ -468,7 +468,7 @@ static inline bool sema_check_asm_memvar(SemaContext *context, AsmInlineBlock *b if (is_write) { decl->var.is_written = true; - if (decl->var.in_param) + if (decl->var.in_param && !decl->var.out_param) { RETURN_SEMA_ERROR(expr, "An 'in' variable may not be written to."); } diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index a897fc288..dfe2c3478 100755 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -3205,6 +3205,8 @@ static inline bool sema_analyse_doc_header(SemaContext *context, AstId doc, param->var.out_param = true; break; case INOUT_INOUT: + param->var.out_param = true; + param->var.in_param = true; break; } if (!may_be_pointer && type->type_kind != TYPE_SLICE) diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 9cd70e0a0..b5eed607d 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -792,7 +792,7 @@ CHECK_INNER: if (inner->expr_kind != EXPR_IDENTIFIER) return true; Decl *decl = inner->ident_expr; if (decl->decl_kind != DECL_VAR) return true; - if (!decl->var.in_param) return true; + if (!decl->var.in_param || decl->var.out_param) return true; RETURN_SEMA_ERROR(inner, "'in' parameters may not be assigned to."); } @@ -1902,21 +1902,21 @@ static inline bool sema_call_check_contract_param_match(SemaContext *context, De { if (expr->unary_expr.expr->ident_expr->var.kind == VARDECL_CONST && param->var.out_param) { - SEMA_ERROR(expr, "A const parameter may not be passed into a function or macro as an 'out' argument."); + SEMA_ERROR(expr, "A const parameter may not be passed into a function or macro as an 'out' or 'inout' argument."); return false; } } if (expr->expr_kind != EXPR_IDENTIFIER) return true; Decl *ident = expr->ident_expr; if (ident->decl_kind != DECL_VAR) return true; - if (ident->var.out_param && param->var.in_param) + if (ident->var.out_param && !ident->var.in_param && param->var.in_param) { - SEMA_ERROR(expr, "An 'out' parameter may not be passed into a function or macro as an 'in' argument."); + SEMA_ERROR(expr, "An 'out' parameter may not be passed into a function or macro as an 'in' or 'inout' argument."); return false; } - if (ident->var.in_param && param->var.out_param) + if (ident->var.in_param && !ident->var.out_param && param->var.out_param) { - SEMA_ERROR(expr, "An 'in' parameter may not be passed into a function or macro as an 'out' argument."); + SEMA_ERROR(expr, "An 'in' parameter may not be passed into a function or macro as an 'out' or 'inout' argument."); return false; } return true; @@ -10170,7 +10170,7 @@ static inline bool sema_cast_rvalue(SemaContext *context, Expr *expr, bool mutat if (inner->expr_kind != EXPR_IDENTIFIER) break; Decl *decl = inner->ident_expr; if (decl->decl_kind != DECL_VAR) break; - if (!decl->var.out_param) break; + if (!decl->var.out_param || decl->var.in_param) break; RETURN_SEMA_ERROR(expr, "'out' parameters may not be read."); } case EXPR_UNARY: @@ -10180,7 +10180,7 @@ static inline bool sema_cast_rvalue(SemaContext *context, Expr *expr, bool mutat if (inner->expr_kind != EXPR_IDENTIFIER) break; Decl *decl = inner->ident_expr; if (decl->decl_kind != DECL_VAR) break; - if (!decl->var.out_param) break; + if (!decl->var.out_param || decl->var.in_param) break; RETURN_SEMA_ERROR(expr, "'out' parameters may not be read."); } default: diff --git a/src/compiler/target.c b/src/compiler/target.c index 0546c0128..7d241b55a 100644 --- a/src/compiler/target.c +++ b/src/compiler/target.c @@ -1887,13 +1887,6 @@ void target_setup(BuildTarget *target) error_exit("Failed to find Windows def file: '%s' in path.", target->win.def); } - if (target->arch_os_target == ARCH_OS_TARGET_DEFAULT) target->arch_os_target = default_target; - - if (target->arch_os_target == ARCH_OS_TARGET_DEFAULT) - { - error_exit("Unable to detect the default target, please set an explicit --target value."); - } - if (target->arch_os_target == ELF_XTENSA && !XTENSA_AVAILABLE) { error_exit("Xtensa support is not available with this LLVM version."); diff --git a/test/src/test_suite_runner.c3 b/test/src/test_suite_runner.c3 index 80db7501f..00454be8d 100644 --- a/test/src/test_suite_runner.c3 +++ b/test/src/test_suite_runner.c3 @@ -393,6 +393,8 @@ fn void test_file(Path file_path) cmdline.push(compiler_path.str_view()); cmdline.push("compile-only"); cmdline.push("--test"); + cmdline.push("--llvm-out"); + cmdline.push("."); foreach (file : settings.input_files) { cmdline.push(file.name); diff --git a/test/test_suite/contracts/in_out.c3 b/test/test_suite/contracts/in_out.c3 index 52c12af4c..03f81503d 100644 --- a/test/test_suite/contracts/in_out.c3 +++ b/test/test_suite/contracts/in_out.c3 @@ -8,7 +8,7 @@ fn void bar(Foo* f) <* @param [in] f *> fn void foo(Foo* f) { - bar(f); // #error: macro as an 'out' argument + bar(f); // #error: An 'in' parameter may not be passed i } <* @param [in] f *> diff --git a/test/test_suite/contracts/inout_in.c3 b/test/test_suite/contracts/inout_in.c3 new file mode 100644 index 000000000..36ba05aa2 --- /dev/null +++ b/test/test_suite/contracts/inout_in.c3 @@ -0,0 +1,21 @@ +<* + @param [in] foo +*> +fn void test(int* foo) +{ + test2(foo); // #error: An 'in' parameter may not be passed into +} + +<* + @param [out] foo +*> +fn void test3(int* foo) +{ + test2(foo); // #error: An 'out' parameter may not be passed into +} +<* + @param [inout] foo +*> +fn void test2(int* foo) +{ +}