diff --git a/releasenotes.md b/releasenotes.md index 2b5e620bc..f1386f2d3 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -22,6 +22,7 @@ - Ability of `vendor-fetch` to register the fetched dependencies in the project file. - Allow the "self" parameter to be $/# for macro methods. - Support compile time slicing of untyped lists. +- Allow specifying an import module using `@wasm` #1305. ### Fixes - Issue where a lambda wasn't correctly registered as external. #1408 diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index ff85ee225..e2f507b11 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -345,6 +345,7 @@ typedef struct const char *deprecated; const char **links; const char *section; + const char *wasm_module; SourceSpan overload; } ResolvedAttrData; diff --git a/src/compiler/copying.c b/src/compiler/copying.c index e424933ed..d8170b08d 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -806,6 +806,7 @@ static ResolvedAttrData *copy_attrs_resolved(CopyStruct *c, ResolvedAttrData *da .deprecated = data->deprecated, .links = data->links, .section = data->section, + .wasm_module = data->wasm_module }; return copy; } diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index de4f97b7a..14a3c45a5 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -1183,6 +1183,10 @@ void llvm_append_function_attributes(GenContext *c, Decl *decl) { scratch_buffer_set_extern_decl_name(decl, true); llvm_attribute_add_string(c, function, "wasm-import-name", scratch_buffer_to_string(), -1); + if (decl->attrs_resolved && decl->attrs_resolved->wasm_module) + { + llvm_attribute_add_string(c, function, "wasm-import-module", decl->attrs_resolved->wasm_module, -1); + } } if (decl->alignment != type_abi_alignment(decl->type)) { diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 2ba3434da..b83f13890 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -2419,10 +2419,9 @@ static bool sema_analyse_attribute(SemaContext *context, ResolvedAttrData *attr_ // No attribute has more than one argument right now. unsigned args = vec_size(attr->exprs); - if (args > 1 && type != ATTRIBUTE_LINK && type != ATTRIBUTE_TAG) + if (args > 1 && type != ATTRIBUTE_LINK && type != ATTRIBUTE_TAG && type != ATTRIBUTE_WASM) { - SEMA_ERROR(attr->exprs[1], "Too many arguments for the attribute."); - return false; + RETURN_SEMA_ERROR(attr->exprs[1], "Too many arguments for the attribute."); } Expr *expr = args ? attr->exprs[0] : NULL; @@ -2554,6 +2553,41 @@ static bool sema_analyse_attribute(SemaContext *context, ResolvedAttrData *attr_ decl->alignment = (AlignSize)align; return true; } + case ATTRIBUTE_WASM: + if (args > 2) RETURN_SEMA_ERROR(attr->exprs[2], "Too many arguments to '@wasm', expected 0, 1 or 2 arguments"); + decl->is_export = true; + if (context->unit->module->is_generic) + { + RETURN_SEMA_ERROR(attr, "'@wasm' is not allowed in generic modules."); + } + if (args == 0) return true; + if (decl->has_extname) + { + RETURN_SEMA_ERROR(expr, "An external name is already defined, please use '@wasm` without an argument."); + } + if (args == 2) + { + if (!decl->is_extern) + { + RETURN_SEMA_ERROR(expr, "Specifying a wasm import module name is only valid for extern declarations."); + } + Expr *module = expr; + expr = attr->exprs[1]; + if (!sema_analyse_expr(context, module)) return false; + if (!expr_is_const_string(module)) + { + RETURN_SEMA_ERROR(module, "Expected a constant string value as argument."); + } + attr_data->wasm_module = module->const_expr.bytes.ptr; + if (!sema_analyse_expr(context, expr)) return false; + if (!expr_is_const_string(expr)) + { + RETURN_SEMA_ERROR(expr, "Expected a constant string value as argument."); + } + decl->extname = expr->const_expr.bytes.ptr; + decl->has_extname = true; + } + return true; case ATTRIBUTE_EXPORT: if (context->unit->module->is_generic) { @@ -2729,9 +2763,6 @@ static bool sema_analyse_attribute(SemaContext *context, ResolvedAttrData *attr_ } decl->is_weak = true; break; - case ATTRIBUTE_WASM: - decl->is_export = true; - break; case ATTRIBUTE_NAKED: assert(domain == ATTR_FUNC); decl->func_decl.attr_naked = true; @@ -2915,7 +2946,7 @@ static bool sema_analyse_attributes(SemaContext *context, Decl *decl, Attr **att if (!sema_analyse_attributes_inner(context, &data, decl, attrs, domain, NULL, erase_decl)) return false; if (*erase_decl) return true; decl->resolved_attributes = true; - if (data.tags || data.deprecated || data.links || data.section || data.overload.row) + if (data.tags || data.deprecated || data.links || data.section || data.overload.row || data.wasm_module ) { ResolvedAttrData *copy = MALLOCS(ResolvedAttrData); *copy = data; diff --git a/test/test_suite/attributes/attributes_repeat_param.c3t b/test/test_suite/attributes/attributes_repeat_param.c3t new file mode 100644 index 000000000..9c3ea7915 --- /dev/null +++ b/test/test_suite/attributes/attributes_repeat_param.c3t @@ -0,0 +1,26 @@ +// #target: macos-aarch64 +module test; + +def @Test(x) = { @extern("Foo" +++ x) }; + +fn void hello_world() @Test("Megaman") +{} + +fn void ello_world() @Test("Pegasus") +{} + +fn int main() +{ + hello_world(); + ello_world(); + return 0; +} + +/* #expect: test.ll + +define i32 @main() #0 { +entry: + call void @FooMegaman() + call void @FooPegasus() + ret i32 0 +} diff --git a/test/test_suite/attributes/wasm_import.c3t b/test/test_suite/attributes/wasm_import.c3t new file mode 100644 index 000000000..7c96caa95 --- /dev/null +++ b/test/test_suite/attributes/wasm_import.c3t @@ -0,0 +1,8 @@ +// #target: wasm32 +module test; +extern fn void test() @wasm("hello", "world"); + +/* #expect: test.ll + +declare void @world() #0 +attributes #0 = { nounwind uwtable "no-trapping-math"="true" "stack-protector-buffer-size"="8" "wasm-export-name"="world" "wasm-import-module"="hello" "wasm-import-name"="world" } \ No newline at end of file diff --git a/test/test_suite/attributes/wasm_module.c3 b/test/test_suite/attributes/wasm_module.c3 new file mode 100644 index 000000000..d7307f047 --- /dev/null +++ b/test/test_suite/attributes/wasm_module.c3 @@ -0,0 +1,5 @@ + +fn void test() @wasm("hello", "world") { } // #error: Specifying a wasm import module +extern fn void test2() @wasm("a", "b", "c"); // #error: Too many arguments to +extern fn void test3() @extern("hello") @wasm("a"); // #error: An external name +extern fn void test4() @extern("hello") @wasm("a", "b"); // #error: An external name \ No newline at end of file