Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions c2rust-transpile/src/translator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2022,6 +2022,27 @@ impl<'c> Translation<'c> {
// If this function is just a regular inline
if is_inline && !attrs.contains(&c_ast::Attribute::AlwaysInline) {
mk_ = mk_.single_attr("inline");

// * In C99, a function defined inline will never, and a function defined extern
// inline will always, emit an externally visible function.
// * If a non-static function is declared inline, then it must be defined in the
// same translation unit. The inline definition that does not use extern is
// not externally visible and does not prevent other translation units from
// defining the same function. This makes the inline keyword an alternative to
// static for defining functions inside header files, which may be included in
// multiple translation units of the same program.
// * always_inline implies inline -
// https://gcc.gnu.org/ml/gcc-help/2007-01/msg00051.html
// even if the `inline` keyword isn't present
// * gnu_inline instead applies gnu89 rules. extern inline will not emit an
// externally visible function.
self.use_feature("linkage");
if is_global && is_extern && !attrs.contains(&c_ast::Attribute::GnuInline) {
// ensures that public inlined rust function can be used in other modules
mk_ = mk_.single_attr("linkage = \"external\"");
}
// NOTE: it does not seem necessary to have an else branch here that
// specifies internal linkage in all other cases due to name mangling by rustc.
}

Ok(ConvertedDecl::Item(
Expand Down
10 changes: 9 additions & 1 deletion scripts/test_translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,15 @@ def run(self) -> List[TestOutcome]:
self.generated_files["c_obj"].extend(static_library.obj_files)

rust_file_builder = RustFileBuilder()
rust_file_builder.add_features(["libc", "extern_types", "simd_ffi", "stdsimd", "const_transmute", "nll", "custom_attribute"])
rust_file_builder.add_features([
"libc",
"extern_types",
"simd_ffi",
"stdsimd",
"const_transmute",
"nll",
"custom_attribute",
"linkage"])

# .c -> .rs
for c_file in self.c_files:
Expand Down
2 changes: 1 addition & 1 deletion tests/items/src/test_fn_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub fn test_fn_attrs() {
// extern void inline __attribute__((always_inline)) always_inline_extern(void) {}
// extern void inline __attribute__((__gnu_inline__)) gnu_inline_extern(void) {}
// extern void inline __attribute__((gnu_inline, always_inline)) always_inline_gnu_inline_extern(void) {}
assert!(src.contains("#[inline]\npub unsafe extern \"C\" fn rust_inline_extern"));
assert!(src.contains("#[inline]\n#[linkage = \"external\"]\npub unsafe extern \"C\" fn rust_inline_extern"));
assert!(src.contains("#[inline(always)]\npub unsafe extern \"C\" fn rust_always_inline_extern"));
assert!(src.contains("#[inline]\nunsafe extern \"C\" fn rust_gnu_inline_extern"));
assert!(src.contains("#[inline(always)]\nunsafe extern \"C\" fn rust_always_inline_gnu_inline_extern"));
Expand Down