diff --git a/spec/compiler/codegen/fun_spec.cr b/spec/compiler/codegen/fun_spec.cr new file mode 100644 index 000000000000..a512d8b2a687 --- /dev/null +++ b/spec/compiler/codegen/fun_spec.cr @@ -0,0 +1,22 @@ +require "../../spec_helper" + +describe "Codegen: fun" do + it "sets linkage" do + mod = codegen(<<-CRYSTAL, inject_primitives: false) + fun foo + end + + @[Linkage("external")] + fun ext_foo + end + + @[Linkage("internal")] + fun int_foo + end + CRYSTAL + + mod.functions["foo"].linkage.should eq(LLVM::Linkage::External) + mod.functions["ext_foo"].linkage.should eq(LLVM::Linkage::External) + mod.functions["int_foo"].linkage.should eq(LLVM::Linkage::Internal) + end +end diff --git a/spec/compiler/semantic/annotation_spec.cr b/spec/compiler/semantic/annotation_spec.cr index 249a23c149a0..4d09b6103d13 100644 --- a/spec/compiler/semantic/annotation_spec.cr +++ b/spec/compiler/semantic/annotation_spec.cr @@ -1126,7 +1126,7 @@ describe "Semantic: annotation" do fun foo : Void end ), - "funs can only be annotated with: NoInline, AlwaysInline, Naked, ReturnsTwice, Raises, CallConvention" + "funs can only be annotated with: NoInline, AlwaysInline, Naked, ReturnsTwice, Raises, CallConvention or Linkage" end it "doesn't carry link annotation from lib to fun" do diff --git a/src/compiler/crystal/codegen/ast.cr b/src/compiler/crystal/codegen/ast.cr index ab4fa76d4e16..ed3465be1284 100644 --- a/src/compiler/crystal/codegen/ast.cr +++ b/src/compiler/crystal/codegen/ast.cr @@ -72,6 +72,10 @@ module Crystal nil end + def linkage + nil + end + @c_calling_convention : Bool? = nil property c_calling_convention diff --git a/src/compiler/crystal/codegen/fun.cr b/src/compiler/crystal/codegen/fun.cr index c56bde6e5c2a..ef0609d1cac3 100644 --- a/src/compiler/crystal/codegen/fun.cr +++ b/src/compiler/crystal/codegen/fun.cr @@ -395,6 +395,10 @@ class Crystal::CodeGenVisitor context.fun.call_convention = call_convention end + if linkage = target_def.linkage + context.fun.linkage = linkage + end + i = 0 args.each do |arg| param = context.fun.params[i + offset] diff --git a/src/compiler/crystal/program.cr b/src/compiler/crystal/program.cr index 840afd2b6552..699da7fe413e 100644 --- a/src/compiler/crystal/program.cr +++ b/src/compiler/crystal/program.cr @@ -239,6 +239,7 @@ module Crystal types["Extern"] = @extern_annotation = AnnotationType.new self, self, "Extern" types["Flags"] = @flags_annotation = AnnotationType.new self, self, "Flags" types["Link"] = @link_annotation = AnnotationType.new self, self, "Link" + types["Linkage"] = @linkage_annotation = AnnotationType.new self, self, "Linkage" types["Naked"] = @naked_annotation = AnnotationType.new self, self, "Naked" types["NoInline"] = @no_inline_annotation = AnnotationType.new self, self, "NoInline" types["Packed"] = @packed_annotation = AnnotationType.new self, self, "Packed" @@ -534,7 +535,8 @@ module Crystal packed_annotation thread_local_annotation no_inline_annotation always_inline_annotation naked_annotation returns_twice_annotation raises_annotation primitive_annotation call_convention_annotation - flags_annotation link_annotation extern_annotation deprecated_annotation experimental_annotation) %} + flags_annotation link_annotation linkage_annotation extern_annotation + deprecated_annotation experimental_annotation) %} def {{name.id}} @{{name.id}}.not_nil! end diff --git a/src/compiler/crystal/semantic/ast.cr b/src/compiler/crystal/semantic/ast.cr index f4fa683efe82..b2849fbeff95 100644 --- a/src/compiler/crystal/semantic/ast.cr +++ b/src/compiler/crystal/semantic/ast.cr @@ -689,6 +689,7 @@ module Crystal property real_name : String property! fun_def : FunDef property call_convention : LLVM::CallConvention? + property linkage : LLVM::Linkage? property wasm_import_module : String? property? dead = false diff --git a/src/compiler/crystal/semantic/top_level_visitor.cr b/src/compiler/crystal/semantic/top_level_visitor.cr index cfc8dddc81f1..62ad1c1e26a9 100644 --- a/src/compiler/crystal/semantic/top_level_visitor.cr +++ b/src/compiler/crystal/semantic/top_level_visitor.cr @@ -528,6 +528,26 @@ class Crystal::TopLevelVisitor < Crystal::SemanticVisitor node.body = primitive end + private def process_fun_linkage_annotation(node, ann) + if ann.args.size != 1 + ann.wrong_number_of_arguments "annotation Linkage", ann.args.size, 1 + end + + arg = ann.args.first + unless arg.is_a?(StringLiteral) + arg.raise "argument to Linkage must be a string" + end + + case arg.value + when "internal" + node.linkage = LLVM::Linkage::Internal + when "external" + node.linkage = LLVM::Linkage::External + else + arg.raise "invalid linkage. Valid values are 'external' and 'internal'" + end + end + def visit(node : Include) check_outside_exp node, "include" include_in current_type, node, :included @@ -984,8 +1004,10 @@ class Crystal::TopLevelVisitor < Crystal::SemanticVisitor call_convention = parse_call_convention(ann, call_convention) elsif annotation_type == @program.primitive_annotation process_def_primitive_annotation(external, ann) + elsif annotation_type == @program.linkage_annotation + process_fun_linkage_annotation(external, ann) else - ann.raise "funs can only be annotated with: NoInline, AlwaysInline, Naked, ReturnsTwice, Raises, CallConvention" + ann.raise "funs can only be annotated with: NoInline, AlwaysInline, Naked, ReturnsTwice, Raises, CallConvention or Linkage" end end