[clang] [Clang] strengthen checks for 'main' function to meet [basic.start.main] p2 requirements (PR #101853)

Oleksandr T. via cfe-commits cfe-commits at lists.llvm.org
Sat Aug 3 14:58:15 PDT 2024


https://github.com/a-tarasyuk created https://github.com/llvm/llvm-project/pull/101853

Fixes #101512 

>From 16706eb1648653f41da5b6d10c1104b1cf1609bf Mon Sep 17 00:00:00 2001
From: Oleksandr T <oleksandr.tarasiuk at outlook.com>
Date: Sun, 4 Aug 2024 00:45:49 +0300
Subject: [PATCH] [Clang] strengthen checks for 'main' function to meet
 [basic.start.main] p2 requirements

---
 clang/docs/ReleaseNotes.rst                   |  2 ++
 .../clang/Basic/DiagnosticSemaKinds.td        |  3 ++
 clang/include/clang/Sema/Sema.h               |  2 +-
 clang/lib/AST/Decl.cpp                        | 14 +++++----
 clang/lib/Sema/SemaDecl.cpp                   | 15 +++++++---
 clang/test/CodeGenCXX/mangle.cpp              | 10 -------
 clang/test/SemaCXX/linkage1.cpp               | 29 +++++++++++++++++++
 7 files changed, 55 insertions(+), 20 deletions(-)
 create mode 100644 clang/test/SemaCXX/linkage1.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 4c7bd099420ab..3303db5a87ace 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -145,6 +145,8 @@ Improvements to Clang's diagnostics
 
 - -Wdangling-assignment-gsl is enabled by default.
 
+- Clang now diagnoses the use of `main` in `extern` context as invalid according to [basic.start.main] p2. Fixes #GH101512.
+
 Improvements to Clang's time-trace
 ----------------------------------
 
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 581434d33c5c9..dd95ea3cc6f0f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5986,6 +5986,9 @@ def err_new_abi_tag_on_redeclaration : Error<
   "'abi_tag' %0 missing in original declaration">;
 def note_use_ifdef_guards : Note<
   "unguarded header; consider using #ifdef guards or #pragma once">;
+def err_invalid_linkage_specification : Error<
+  "invalid linkage specification "
+  "'extern \"%select{C|C++}0\"'">;
 
 def warn_var_decl_not_read_only : Warning<
   "object of type %0 cannot be placed in read-only memory">,
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 2ec6367eccea0..0fc07d4a01ad3 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3585,7 +3585,7 @@ class Sema final : public SemaBase {
   /// \param OldT The portion of the type of the old declaration to check.
   bool canFullyTypeCheckRedeclaration(ValueDecl *NewD, ValueDecl *OldD,
                                       QualType NewT, QualType OldT);
-  void CheckMain(FunctionDecl *FD, const DeclSpec &D);
+  void CheckMain(FunctionDecl *FD, DeclContext *DC, const DeclSpec &D);
   void CheckMSVCRTEntryPoint(FunctionDecl *FD);
 
   /// Returns an implicit CodeSegAttr if a __declspec(code_seg) is found on a
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 490c4a2fc525c..447297103f396 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -3292,11 +3292,15 @@ bool FunctionDecl::isImmediateFunction() const {
 }
 
 bool FunctionDecl::isMain() const {
-  const TranslationUnitDecl *tunit =
-    dyn_cast<TranslationUnitDecl>(getDeclContext()->getRedeclContext());
-  return tunit &&
-         !tunit->getASTContext().getLangOpts().Freestanding &&
-         isNamed(this, "main");
+  const TranslationUnitDecl *TUnit =
+      dyn_cast<TranslationUnitDecl>(getDeclContext()->getRedeclContext());
+  const LinkageSpecDecl *LSD = dyn_cast<LinkageSpecDecl>(getDeclContext());
+  if (!TUnit && !LSD)
+    return false;
+  if ((TUnit && TUnit->getASTContext().getLangOpts().Freestanding) ||
+      (LSD && LSD->getASTContext().getLangOpts().Freestanding))
+    return false;
+  return isNamed(this, "main");
 }
 
 bool FunctionDecl::isMSVCRTEntryPoint() const {
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 4fea38d1b02a9..ae6d6681d6029 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -10308,7 +10308,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
   if (!getLangOpts().CPlusPlus) {
     // Perform semantic checking on the function declaration.
     if (!NewFD->isInvalidDecl() && NewFD->isMain())
-      CheckMain(NewFD, D.getDeclSpec());
+      CheckMain(NewFD, DC, D.getDeclSpec());
 
     if (!NewFD->isInvalidDecl() && NewFD->isMSVCRTEntryPoint())
       CheckMSVCRTEntryPoint(NewFD);
@@ -10473,7 +10473,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
 
     // Perform semantic checking on the function declaration.
     if (!NewFD->isInvalidDecl() && NewFD->isMain())
-      CheckMain(NewFD, D.getDeclSpec());
+      CheckMain(NewFD, DC, D.getDeclSpec());
 
     if (!NewFD->isInvalidDecl() && NewFD->isMSVCRTEntryPoint())
       CheckMSVCRTEntryPoint(NewFD);
@@ -12210,7 +12210,15 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
   return Redeclaration;
 }
 
-void Sema::CheckMain(FunctionDecl* FD, const DeclSpec& DS) {
+void Sema::CheckMain(FunctionDecl *FD, DeclContext *DC, const DeclSpec &DS) {
+  // [basic.start.main] p2
+  //   The main function shall not be declared with a linkage-specification.
+  if (DC->isExternCXXContext() || DC->isExternCContext()) {
+    Diag(FD->getLocation(), diag::err_invalid_linkage_specification)
+        << FD->getLanguageLinkage();
+    FD->setInvalidDecl();
+    return;
+  }
   // C++11 [basic.start.main]p3:
   //   A program that [...] declares main to be inline, static or
   //   constexpr is ill-formed.
@@ -12238,7 +12246,6 @@ void Sema::CheckMain(FunctionDecl* FD, const DeclSpec& DS) {
         << FixItHint::CreateRemoval(DS.getConstexprSpecLoc());
     FD->setConstexprKind(ConstexprSpecKind::Unspecified);
   }
-
   if (getLangOpts().OpenCL) {
     Diag(FD->getLocation(), diag::err_opencl_no_main)
         << FD->hasAttr<OpenCLKernelAttr>();
diff --git a/clang/test/CodeGenCXX/mangle.cpp b/clang/test/CodeGenCXX/mangle.cpp
index d0800af55c87e..c9d61def0a0d0 100644
--- a/clang/test/CodeGenCXX/mangle.cpp
+++ b/clang/test/CodeGenCXX/mangle.cpp
@@ -137,16 +137,6 @@ int f(struct a *x) {
     return x->b;
 }
 
-// PR5017
-extern "C" {
-struct Debug {
-  const Debug& operator<< (unsigned a) const { return *this; }
-};
-Debug dbg;
-// CHECK: @_ZNK5DebuglsEj
-int main(void) {  dbg << 32 ;}
-}
-
 template<typename T> struct S6 {
   typedef int B;
 };
diff --git a/clang/test/SemaCXX/linkage1.cpp b/clang/test/SemaCXX/linkage1.cpp
new file mode 100644
index 0000000000000..8be82209afc08
--- /dev/null
+++ b/clang/test/SemaCXX/linkage1.cpp
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -pedantic-errors %s
+
+namespace c {
+  extern "C" void main(); // expected-error {{invalid linkage specification 'extern "C"'}}
+}
+extern "C" {
+  int main(); // expected-error {{invalid linkage specification 'extern "C"'}}
+}
+extern "C" int main(); // expected-error {{invalid linkage specification 'extern "C"'}}
+extern "C" struct A { int main(); }; // ok
+
+namespace cpp {
+  extern "C++" int main(); // expected-error {{invalid linkage specification 'extern "C++"'}}
+}
+extern "C++" {
+  int main(); // expected-error {{invalid linkage specification 'extern "C++"'}}
+}
+extern "C++" int main(); // expected-error {{invalid linkage specification 'extern "C++"'}}
+extern "C++" struct B { int main(); }; // ok
+
+namespace ns {
+  extern "C" struct A {
+    int main; // ok
+  };
+
+  extern "C" struct B {
+    int main(); // ok
+  };
+}



More information about the cfe-commits mailing list