From 69a1e1b5ffa04140c1ef9dc25aef9961a48e310b Mon Sep 17 00:00:00 2001 From: perl Date: Thu, 20 Jun 2019 15:52:14 -0700 Subject: [PATCH 1/3] Add linkage attribute to (non-gnu89) inline functions to avoid bzip2 linker error. --- c2rust-transpile/src/translator/mod.rs | 22 ++++++++++++++++++++++ scripts/test_translator.py | 10 +++++++++- tests/items/src/test_fn_attrs.rs | 14 +++++++------- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 3f096b51a0..dbe5e5ea9f 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -2022,6 +2022,28 @@ 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\""); + } else { + // ensures that non-public inlined rust functions do not clash btw. modules + mk_ = mk_.single_attr("linkage = \"internal\""); + } } Ok(ConvertedDecl::Item( diff --git a/scripts/test_translator.py b/scripts/test_translator.py index 3b361c429e..b46d53034f 100755 --- a/scripts/test_translator.py +++ b/scripts/test_translator.py @@ -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: diff --git a/tests/items/src/test_fn_attrs.rs b/tests/items/src/test_fn_attrs.rs index 412c8f1849..cff938e6e0 100644 --- a/tests/items/src/test_fn_attrs.rs +++ b/tests/items/src/test_fn_attrs.rs @@ -25,8 +25,8 @@ pub fn test_fn_attrs() { // static void inline inline_static(void) {} assert!(src.contains("#[inline(always)]\nunsafe extern \"C\" fn rust_always_inline_static")); assert!(src.contains("#[inline(never)]\nunsafe extern \"C\" fn rust_noinline_static")); - assert!(src.contains("#[inline]\nunsafe extern \"C\" fn rust_inline_static")); - assert!(src.contains("#[inline]\nunsafe extern \"C\" fn rust_gnu_inline_static")); + assert!(src.contains("#[inline]\n#[linkage = \"internal\"]\nunsafe extern \"C\" fn rust_inline_static")); + assert!(src.contains("#[inline]\n#[linkage = \"internal\"]\nunsafe extern \"C\" fn rust_gnu_inline_static")); assert!(src.contains("#[cold]\nunsafe extern \"C\" fn rust_cold_used_attrs")); // __attribute__((__always_inline__)) void always_inline_nonstatic(void) {} @@ -34,18 +34,18 @@ pub fn test_fn_attrs() { // void inline inline_nonstatic(void) {} assert!(src.contains("#[inline(always)]\nunsafe extern \"C\" fn rust_always_inline_nonstatic")); assert!(src.contains("#[inline(never)]\npub unsafe extern \"C\" fn rust_noinline_nonstatic")); - assert!(src.contains("#[inline]\nunsafe extern \"C\" fn rust_inline_nonstatic")); - assert!(src.contains("#[inline]\nunsafe extern \"C\" fn rust_gnu_inline_nonstatic")); + assert!(src.contains("#[inline]\n#[linkage = \"internal\"]\nunsafe extern \"C\" fn rust_inline_nonstatic")); + assert!(src.contains("#[inline]\n#[linkage = \"internal\"]\nunsafe extern \"C\" fn rust_gnu_inline_nonstatic")); // extern void inline inline_extern(void) {} // 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]\n#[linkage = \"internal\"]\nunsafe extern \"C\" fn rust_gnu_inline_extern")); assert!(src.contains("#[inline(always)]\nunsafe extern \"C\" fn rust_always_inline_gnu_inline_extern")); - assert!(src.contains("#[inline]\nunsafe extern \"C\" fn rust_gnu_inline_non_canonical_definition_extern")); + assert!(src.contains("#[inline]\n#[linkage = \"internal\"]\nunsafe extern \"C\" fn rust_gnu_inline_non_canonical_definition_extern")); if cfg!(not(target_os = "macos")) { // aliased_fn is aliased to the inline_extern function From 14d8add2724a21f018f288730f328b2d419c6f13 Mon Sep 17 00:00:00 2001 From: perl Date: Thu, 20 Jun 2019 16:36:37 -0700 Subject: [PATCH 2/3] Don't use attribute to request internal linkage --- c2rust-transpile/src/translator/mod.rs | 5 ++--- tests/items/src/test_fn_attrs.rs | 12 ++++++------ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index dbe5e5ea9f..8d4d1520e0 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -2040,10 +2040,9 @@ impl<'c> Translation<'c> { 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\""); - } else { - // ensures that non-public inlined rust functions do not clash btw. modules - mk_ = mk_.single_attr("linkage = \"internal\""); } + // 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( diff --git a/tests/items/src/test_fn_attrs.rs b/tests/items/src/test_fn_attrs.rs index cff938e6e0..52cdc72550 100644 --- a/tests/items/src/test_fn_attrs.rs +++ b/tests/items/src/test_fn_attrs.rs @@ -25,8 +25,8 @@ pub fn test_fn_attrs() { // static void inline inline_static(void) {} assert!(src.contains("#[inline(always)]\nunsafe extern \"C\" fn rust_always_inline_static")); assert!(src.contains("#[inline(never)]\nunsafe extern \"C\" fn rust_noinline_static")); - assert!(src.contains("#[inline]\n#[linkage = \"internal\"]\nunsafe extern \"C\" fn rust_inline_static")); - assert!(src.contains("#[inline]\n#[linkage = \"internal\"]\nunsafe extern \"C\" fn rust_gnu_inline_static")); + assert!(src.contains("#[inline]\nunsafe extern \"C\" fn rust_inline_static")); + assert!(src.contains("#[inline]\nunsafe extern \"C\" fn rust_gnu_inline_static")); assert!(src.contains("#[cold]\nunsafe extern \"C\" fn rust_cold_used_attrs")); // __attribute__((__always_inline__)) void always_inline_nonstatic(void) {} @@ -34,8 +34,8 @@ pub fn test_fn_attrs() { // void inline inline_nonstatic(void) {} assert!(src.contains("#[inline(always)]\nunsafe extern \"C\" fn rust_always_inline_nonstatic")); assert!(src.contains("#[inline(never)]\npub unsafe extern \"C\" fn rust_noinline_nonstatic")); - assert!(src.contains("#[inline]\n#[linkage = \"internal\"]\nunsafe extern \"C\" fn rust_inline_nonstatic")); - assert!(src.contains("#[inline]\n#[linkage = \"internal\"]\nunsafe extern \"C\" fn rust_gnu_inline_nonstatic")); + assert!(src.contains("#[inline]\nunsafe extern \"C\" fn rust_inline_nonstatic")); + assert!(src.contains("#[inline]\nunsafe extern \"C\" fn rust_gnu_inline_nonstatic")); // extern void inline inline_extern(void) {} // extern void inline __attribute__((always_inline)) always_inline_extern(void) {} @@ -43,9 +43,9 @@ pub fn test_fn_attrs() { // extern void inline __attribute__((gnu_inline, always_inline)) always_inline_gnu_inline_extern(void) {} 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]\n#[linkage = \"internal\"]\nunsafe extern \"C\" fn rust_gnu_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")); - assert!(src.contains("#[inline]\n#[linkage = \"internal\"]\nunsafe extern \"C\" fn rust_gnu_inline_non_canonical_definition_extern")); + assert!(src.contains("#[inline]\nunsafe extern \"C\" fn rust_gnu_inline_non_canonical_definition_extern")); if cfg!(not(target_os = "macos")) { // aliased_fn is aliased to the inline_extern function From 4c6a482ca42b9b4e10e5e75b5a29c54335c9c35b Mon Sep 17 00:00:00 2001 From: perl Date: Thu, 20 Jun 2019 16:51:42 -0700 Subject: [PATCH 3/3] Address DJK's comments --- c2rust-transpile/src/translator/mod.rs | 2 +- scripts/test_translator.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 8d4d1520e0..aefbace829 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -2036,8 +2036,8 @@ impl<'c> Translation<'c> { // 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) { + self.use_feature("linkage"); // ensures that public inlined rust function can be used in other modules mk_ = mk_.single_attr("linkage = \"external\""); } diff --git a/scripts/test_translator.py b/scripts/test_translator.py index b46d53034f..b9b3e0a003 100755 --- a/scripts/test_translator.py +++ b/scripts/test_translator.py @@ -351,7 +351,8 @@ def run(self) -> List[TestOutcome]: "const_transmute", "nll", "custom_attribute", - "linkage"]) + "linkage", + ]) # .c -> .rs for c_file in self.c_files: