From 007ac778e1856fb819540b57d84595132b8d1c84 Mon Sep 17 00:00:00 2001 From: Francisco Gouveia Date: Wed, 19 Aug 2026 11:11:57 +0100 Subject: [PATCH 1/2] Add a new `IsConvertableGlobalVarDecl` function to avoid empty `thread_local`s --- cpp2rust/converter/converter.cpp | 17 +++++++++++++---- cpp2rust/converter/converter.h | 2 ++ .../converter/models/converter_refcount.cpp | 3 +++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 59410ced..fbab6edc 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -457,6 +457,17 @@ void Converter::ConvertVaListVarDecl(clang::VarDecl *decl) { StrCat(keyword_mut_, GetNamedDeclAsString(decl), token::kColon, "VaList"); } +bool Converter::IsConvertableGlobalVarDecl(clang::VarDecl *decl) { + if (!decl->isFileVarDecl()) { + return true; + } + if (decl->isThisDeclarationADefinition() == clang::VarDecl::DeclarationOnly && + !decl->hasInit()) { + return false; + } + return !globals_.contains(GetNamedDeclAsString(decl)); +} + bool Converter::ConvertVarDeclSkipInit(clang::VarDecl *decl) { auto qual_type = decl->getType(); auto name = GetNamedDeclAsString(decl); @@ -467,12 +478,10 @@ bool Converter::ConvertVarDeclSkipInit(clang::VarDecl *decl) { } if (decl->isFileVarDecl()) { - if ((decl->isThisDeclarationADefinition() == - clang::VarDecl::DeclarationOnly && - !decl->hasInit()) || - !globals_.insert(name).second) { + if (!IsConvertableGlobalVarDecl(decl)) { return false; } + globals_.insert(name); StrCat(AccessSpecifierAsString(decl->getAccess()), keyword::kStatic, keyword_mut_); ENSURE(decl_ids_.insert(GetID(decl)).second); diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 8a55ef2b..285bb600 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -100,6 +100,8 @@ class Converter : public clang::RecursiveASTVisitor { virtual void ConvertGlobalVarDecl(clang::VarDecl *decl); + bool IsConvertableGlobalVarDecl(clang::VarDecl *decl); + virtual void ConvertVaListVarDecl(clang::VarDecl *decl); virtual bool ConvertVarDeclSkipInit(clang::VarDecl *decl); diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index d0580d94..a4441a1e 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -740,6 +740,9 @@ void ConverterRefCount::EmitHoistedInArmAssignment(clang::VarDecl *decl) { } void ConverterRefCount::ConvertGlobalVarDecl(clang::VarDecl *decl) { + if (!IsConvertableGlobalVarDecl(decl)) { + return; + } StrCat("thread_local!"); { PushParen paren(*this); From 221ee449d09b28e7d2e43b002d79bffb56c20064 Mon Sep 17 00:00:00 2001 From: Francisco Gouveia Date: Wed, 19 Aug 2026 11:25:18 +0100 Subject: [PATCH 2/2] Add a test to ensure no empty `thread_local`s are being generated --- tests/unit/extern_global.c | 4 ++++ tests/unit/out/refcount/extern_global.rs | 17 +++++++++++++++++ tests/unit/out/unsafe/extern_global.rs | 17 +++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 tests/unit/extern_global.c create mode 100644 tests/unit/out/refcount/extern_global.rs create mode 100644 tests/unit/out/unsafe/extern_global.rs diff --git a/tests/unit/extern_global.c b/tests/unit/extern_global.c new file mode 100644 index 00000000..4d8fcc5a --- /dev/null +++ b/tests/unit/extern_global.c @@ -0,0 +1,4 @@ +extern int x; +int x; + +int main(void) { return x; } diff --git a/tests/unit/out/refcount/extern_global.rs b/tests/unit/out/refcount/extern_global.rs new file mode 100644 index 00000000..3b1cab0e --- /dev/null +++ b/tests/unit/out/refcount/extern_global.rs @@ -0,0 +1,17 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::{Read, Seek, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +thread_local!( + pub static x_0: Value = >::default(); +); +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + return (*x_0.with(Value::clone).borrow()); +} diff --git a/tests/unit/out/unsafe/extern_global.rs b/tests/unit/out/unsafe/extern_global.rs new file mode 100644 index 00000000..1b531f71 --- /dev/null +++ b/tests/unit/out/unsafe/extern_global.rs @@ -0,0 +1,17 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub static mut x_0: i32 = unsafe { 0_i32 }; +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + return x_0; +}