Skip to content

Conversation

@jhuber6
Copy link
Contributor

@jhuber6 jhuber6 commented Mar 5, 2025

Summary:
The -Wglobal-constructors option is useful for restricting the usage
of global constructors / destructors. However, it currently ignores the
attributes that introduce global constructors, meaning that the module
can still have ctors if -Werror is set. If this is intentional by the
user, I believe it would be more correct to push the diagnostic.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Mar 5, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 5, 2025

@llvm/pr-subscribers-clang

Author: Joseph Huber (jhuber6)

Changes

Summary:
The -Wglobal-constructors option is useful for restricting the usage
of global constructors / destructors. However, it currently ignores the
attributes that introduce global constructors, meaning that the module
can still have ctors if -Werror is set. If this is intentional by the
user, I believe it would be more correct to push the diagnostic.


Full diff: https://github.com/llvm/llvm-project/pull/129917.diff

2 Files Affected:

  • (modified) clang/lib/Sema/SemaDeclAttr.cpp (+12)
  • (modified) clang/test/SemaCXX/attr-require-constant-initialization.cpp (+5)
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index fdf3b9c636127..9daa47c533ad2 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -7596,6 +7596,18 @@ void Sema::ProcessDeclAttributeList(
     D->setInvalidDecl();
   }
 
+  // Warn on global constructors and destructors created by attributes.
+  if (D->hasAttr<ConstructorAttr>() &&
+      !getDiagnostics().isIgnored(diag::warn_global_constructor,
+                                  D->getLocation()))
+    Diag(D->getLocation(), diag::warn_global_constructor)
+        << D->getSourceRange();
+  if (D->hasAttr<DestructorAttr>() &&
+      !getDiagnostics().isIgnored(diag::warn_global_destructor,
+                                  D->getLocation()))
+    Diag(D->getLocation(), diag::warn_global_destructor)
+        << D->getSourceRange();
+
   // Do this check after processing D's attributes because the attribute
   // objc_method_family can change whether the given method is in the init
   // family, and it can be applied after objc_designated_initializer. This is a
diff --git a/clang/test/SemaCXX/attr-require-constant-initialization.cpp b/clang/test/SemaCXX/attr-require-constant-initialization.cpp
index 4c0a834551715..b1736ea7a3ebe 100644
--- a/clang/test/SemaCXX/attr-require-constant-initialization.cpp
+++ b/clang/test/SemaCXX/attr-require-constant-initialization.cpp
@@ -337,6 +337,11 @@ constexpr TestCtor<NotC> inval_constexpr(42); // expected-error {{must be initia
 ATTR constexpr TestCtor<NotC> inval_constexpr2(42); // expected-error {{must be initialized by a constant expression}}
 // expected-note@-1 {{in call to 'TestCtor(42)'}}
 
+[[gnu::constructor]] void ctor() {}
+// expected-warning@-1 {{declaration requires a global constructor}}
+[[gnu::destructor]] void dtor() {}
+// expected-warning@-1 {{declaration requires a global destructor}}
+
 #elif defined(TEST_THREE)
 #if defined(__cplusplus)
 #error This test requires C

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! You should also add a release note to clang/docs/ReleaseNotes.rst so users know about the change.

Comment on lines 7600 to 7609
if (D->hasAttr<ConstructorAttr>() &&
!getDiagnostics().isIgnored(diag::warn_global_constructor,
D->getLocation()))
Diag(D->getLocation(), diag::warn_global_constructor)
<< D->getSourceRange();
if (D->hasAttr<DestructorAttr>() &&
!getDiagnostics().isIgnored(diag::warn_global_destructor,
D->getLocation()))
Diag(D->getLocation(), diag::warn_global_destructor) << D->getSourceRange();

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this code should live in handleConstructorAttr() and handleDestructorAttr() instead. (The above code should live there as well, I suspect).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, why bother checking if it is ignored? We should just emit it, and count on the diagnostics engine to suppress it if it is ignored. We typically only check isIgnored if we are going to do 'a lot' of work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback. I think the above code had to be put there because it relies on having the CUDA device attribute parsed already.

@AaronBallman AaronBallman requested a review from erichkeane March 6, 2025 16:51
jhuber6 added 2 commits March 6, 2025 10:53
Summary:
The `-Wglobal-constructors` option is useful for restricting the usage
of global constructors / destructors. However, it currently ignores the
attributes that introduce global constructors, meaning that the module
can still have ctors if `-Werror` is set. If this is intentional by the
user, I believe it would be more correct to push the diagnostic.
if (AL.getNumArgs() &&
!S.checkUInt32Argument(AL, AL.getArgAsExpr(0), priority))
return;
S.Diag(D->getLocation(), diag::warn_global_constructor)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this firing always? I thought this is only supposed to fire on Global and ThreadLocal variables according to the GCC patch summary. But this will fire any time there is a ConstructorAttr or DestructorAttr somewhere.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How I see it is:

From GCC's docs: "The constructor attribute causes the function to be called automatically before execution enters main ()."

And -Wglobal-constructors is meant to tell you "this has a constructor which will fire before main" which matters for global and thread local variables, so the constructor attribute is sort of morally equivalent.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah! I missed that about the constructor attribute. Thanks! Makes sense to me.

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM once precommit CI comes back green. Thank you!

@jhuber6 jhuber6 merged commit d5cef39 into llvm:main Mar 7, 2025
12 checks passed
@jhuber6 jhuber6 deleted the ctordtor branch March 7, 2025 02:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants