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

Oleksandr T. via cfe-commits cfe-commits at lists.llvm.org
Wed Aug 7 09:32:29 PDT 2024


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

>From 56c8d3a8e5f860ec1f68adf997d502849de1cb50 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 1/6] [Clang] strengthen checks for 'main' function to meet
 [basic.start.main] p3 requirements

---
 clang/docs/ReleaseNotes.rst                   |  2 +
 .../clang/Basic/DiagnosticSemaKinds.td        |  3 +
 clang/lib/AST/Decl.cpp                        |  8 +--
 clang/lib/Sema/SemaDecl.cpp                   | 28 +++++++--
 .../basic/basic.start/basic.start.main/p3.cpp | 61 +++++++++++++++++++
 5 files changed, 91 insertions(+), 11 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 4c7bd099420ab..ad5fe7d96d6cb 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 an `extern` context as invalid according to [basic.start.main] p3. 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..37920ce6a2fed 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -990,6 +990,9 @@ def warn_main_redefined : Warning<"variable named 'main' with external linkage "
     "has undefined behavior">, InGroup<Main>;
 def ext_main_used : Extension<
     "referring to 'main' within an expression is a Clang extension">, InGroup<Main>;
+def ext_main_invalid_linkage_specification : ExtWarn<
+  "'main' should not be "
+  "'extern \"%select{C|C++}0\"'">, InGroup<Main>;
 
 /// parser diagnostics
 def ext_no_declarators : ExtWarn<"declaration does not declare anything">,
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 490c4a2fc525c..9bf2ee680073b 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -3292,11 +3292,9 @@ 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 DeclContext *DC = getDeclContext();
+  return isNamed(this, "main") && !getLangOpts().Freestanding &&
+         (DC->getRedeclContext()->isTranslationUnit() || isExternC());
 }
 
 bool FunctionDecl::isMSVCRTEntryPoint() const {
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 4fea38d1b02a9..613126e9423fd 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7353,6 +7353,15 @@ void emitReadOnlyPlacementAttrWarning(Sema &S, const VarDecl *VD) {
   }
 }
 
+// Checks if VD is declared at global scope or with C language linkage
+static bool isMainVar(DeclarationName Name, VarDecl *VD) {
+  return Name.getAsIdentifierInfo() &&
+         Name.getAsIdentifierInfo()->isStr("main") &&
+         !VD->getDescribedVarTemplate() &&
+         (VD->getDeclContext()->getRedeclContext()->isTranslationUnit() ||
+          VD->isExternC());
+}
+
 NamedDecl *Sema::ActOnVariableDeclarator(
     Scope *S, Declarator &D, DeclContext *DC, TypeSourceInfo *TInfo,
     LookupResult &Previous, MultiTemplateParamsArg TemplateParamLists,
@@ -8052,10 +8061,7 @@ NamedDecl *Sema::ActOnVariableDeclarator(
   }
 
   // Special handling of variable named 'main'.
-  if (Name.getAsIdentifierInfo() && Name.getAsIdentifierInfo()->isStr("main") &&
-      NewVD->getDeclContext()->getRedeclContext()->isTranslationUnit() &&
-      !getLangOpts().Freestanding && !NewVD->getDescribedVarTemplate()) {
-
+  if (!getLangOpts().Freestanding && isMainVar(Name, NewVD)) {
     // C++ [basic.start.main]p3
     // A program that declares a variable main at global scope is ill-formed.
     if (getLangOpts().CPlusPlus)
@@ -12210,7 +12216,18 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
   return Redeclaration;
 }
 
-void Sema::CheckMain(FunctionDecl* FD, const DeclSpec& DS) {
+void Sema::CheckMain(FunctionDecl *FD, const DeclSpec &DS) {
+  // [basic.start.main]p3
+  //    The main function shall not be declared with a linkage-specification.
+  if (FD->isExternCContext() ||
+      (FD->isExternCXXContext() &&
+       FD->getDeclContext()->getRedeclContext()->isTranslationUnit())) {
+    Diag(FD->getLocation(), diag::ext_main_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 +12255,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/CXX/basic/basic.start/basic.start.main/p3.cpp b/clang/test/CXX/basic/basic.start/basic.start.main/p3.cpp
index f7085ca31d6c9..6e98af81ea9a8 100644
--- a/clang/test/CXX/basic/basic.start/basic.start.main/p3.cpp
+++ b/clang/test/CXX/basic/basic.start/basic.start.main/p3.cpp
@@ -8,6 +8,9 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s -DTEST8
 // RUN: %clang_cc1 -fsyntax-only -verify %s -DTEST9
 // RUN: %clang_cc1 -fsyntax-only -verify %s -DTEST10 -ffreestanding
+// RUN: %clang_cc1 -fsyntax-only -verify -pedantic-errors %s -DTEST11
+// RUN: %clang_cc1 -fsyntax-only -verify -pedantic-errors %s -DTEST12
+// RUN: %clang_cc1 -fsyntax-only -verify -pedantic-errors %s -DTEST13
 
 #if TEST1
 int main; // expected-error{{main cannot be declared as global variable}}
@@ -61,6 +64,64 @@ int q(void)
 // expected-no-diagnostics
 int main;
 
+#elif TEST11
+extern "C" {
+  namespace Y {
+    int main; // expected-error {{main cannot be declared as global variable}}
+  }
+}
+namespace ns {
+  extern "C" int main; // expected-error {{main cannot be declared as global variable}}
+}
+
+#elif TEST12
+extern "C" struct A { int main(); }; // ok
+
+namespace c {
+  extern "C" void main(); // expected-error {{'main' should not be 'extern "C"'}}
+}
+
+extern "C" {
+  namespace Z {
+    void main(); // expected-error {{'main' should not be 'extern "C"'}}
+  }
+}
+
+namespace ns {
+  extern "C" struct A {
+    int main; // ok
+  };
+
+  extern "C" struct B {
+    int main(); // ok
+  };
+}
+
+#elif TEST13
+extern "C++" {
+  int main(); // expected-error {{'main' should not be 'extern "C++"'}}
+}
+
+extern "C" {
+  int main(); // expected-error {{'main' should not be 'extern "C"'}}
+}
+
+extern "C" int main(); // expected-error {{'main' should not be 'extern "C"'}}
+extern "C++" int main(); // expected-error {{'main' should not be 'extern "C++"'}}
+
+namespace ns1 {
+  extern "C++" int main(); // ok
+  extern "C" {
+    extern "C++" {
+      int main(void *); // ok
+    }
+  }
+}
+
+namespace ns2 {
+  extern "C++" void main() {} // ok
+}
+
 #else
 #error Unknown Test
 #endif

>From 87f5b9c319ce7c476afde7743b9c56c547f6b2dc Mon Sep 17 00:00:00 2001
From: Oleksandr T <oleksandr.tarasiuk at outlook.com>
Date: Tue, 6 Aug 2024 18:59:19 +0300
Subject: [PATCH 2/6] fix typo. use -pedantic instead of -pedantic-errors

---
 clang/lib/Sema/SemaDecl.cpp                    |  2 +-
 .../basic/basic.start/basic.start.main/p3.cpp  | 18 +++++++++---------
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 613126e9423fd..9103e65eb4bce 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7353,7 +7353,7 @@ void emitReadOnlyPlacementAttrWarning(Sema &S, const VarDecl *VD) {
   }
 }
 
-// Checks if VD is declared at global scope or with C language linkage
+// Checks if VD is declared at global scope or with C language linkage.
 static bool isMainVar(DeclarationName Name, VarDecl *VD) {
   return Name.getAsIdentifierInfo() &&
          Name.getAsIdentifierInfo()->isStr("main") &&
diff --git a/clang/test/CXX/basic/basic.start/basic.start.main/p3.cpp b/clang/test/CXX/basic/basic.start/basic.start.main/p3.cpp
index 6e98af81ea9a8..9fbbdc72651e4 100644
--- a/clang/test/CXX/basic/basic.start/basic.start.main/p3.cpp
+++ b/clang/test/CXX/basic/basic.start/basic.start.main/p3.cpp
@@ -8,9 +8,9 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s -DTEST8
 // RUN: %clang_cc1 -fsyntax-only -verify %s -DTEST9
 // RUN: %clang_cc1 -fsyntax-only -verify %s -DTEST10 -ffreestanding
-// RUN: %clang_cc1 -fsyntax-only -verify -pedantic-errors %s -DTEST11
-// RUN: %clang_cc1 -fsyntax-only -verify -pedantic-errors %s -DTEST12
-// RUN: %clang_cc1 -fsyntax-only -verify -pedantic-errors %s -DTEST13
+// RUN: %clang_cc1 -fsyntax-only -verify -pedantic %s -DTEST11
+// RUN: %clang_cc1 -fsyntax-only -verify -pedantic %s -DTEST12
+// RUN: %clang_cc1 -fsyntax-only -verify -pedantic %s -DTEST13
 
 #if TEST1
 int main; // expected-error{{main cannot be declared as global variable}}
@@ -78,12 +78,12 @@ namespace ns {
 extern "C" struct A { int main(); }; // ok
 
 namespace c {
-  extern "C" void main(); // expected-error {{'main' should not be 'extern "C"'}}
+  extern "C" void main(); // expected-warning {{'main' should not be 'extern "C"'}}
 }
 
 extern "C" {
   namespace Z {
-    void main(); // expected-error {{'main' should not be 'extern "C"'}}
+    void main(); // expected-warning {{'main' should not be 'extern "C"'}}
   }
 }
 
@@ -99,15 +99,15 @@ namespace ns {
 
 #elif TEST13
 extern "C++" {
-  int main(); // expected-error {{'main' should not be 'extern "C++"'}}
+  int main(); // expected-warning {{'main' should not be 'extern "C++"'}}
 }
 
 extern "C" {
-  int main(); // expected-error {{'main' should not be 'extern "C"'}}
+  int main(); // expected-warning {{'main' should not be 'extern "C"'}}
 }
 
-extern "C" int main(); // expected-error {{'main' should not be 'extern "C"'}}
-extern "C++" int main(); // expected-error {{'main' should not be 'extern "C++"'}}
+extern "C" int main(); // expected-warning {{'main' should not be 'extern "C"'}}
+extern "C++" int main(); // expected-warning {{'main' should not be 'extern "C++"'}}
 
 namespace ns1 {
   extern "C++" int main(); // ok

>From 41583815b688c7283414124f30d7c186a46dd89c Mon Sep 17 00:00:00 2001
From: Oleksandr T <oleksandr.tarasiuk at outlook.com>
Date: Tue, 6 Aug 2024 19:45:32 +0300
Subject: [PATCH 3/6] update diagnostics and add additional comments

---
 clang/include/clang/Basic/DiagnosticSemaKinds.td      |  2 +-
 clang/lib/Sema/SemaDecl.cpp                           | 11 ++++++++---
 .../CXX/basic/basic.start/basic.start.main/p3.cpp     |  8 ++++----
 3 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 37920ce6a2fed..ecbe7fa2cf76d 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -985,7 +985,7 @@ def err_main_arg_wrong : Error<"%select{first|second|third|fourth}0 "
 def warn_main_returns_bool_literal : Warning<"bool literal returned from "
     "'main'">, InGroup<Main>;
 def err_main_global_variable :
-    Error<"main cannot be declared as global variable">;
+    Error<"main cannot be declared as a variable %select{in the global scope|with C language linkage}0">;
 def warn_main_redefined : Warning<"variable named 'main' with external linkage "
     "has undefined behavior">, InGroup<Main>;
 def ext_main_used : Extension<
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 9103e65eb4bce..e6a6efe1d72d6 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8062,10 +8062,14 @@ NamedDecl *Sema::ActOnVariableDeclarator(
 
   // Special handling of variable named 'main'.
   if (!getLangOpts().Freestanding && isMainVar(Name, NewVD)) {
-    // C++ [basic.start.main]p3
-    // A program that declares a variable main at global scope is ill-formed.
+    // C++ [basic.start.main]p3:
+    //   A program that declares
+    //    - a variable main at global scope, or
+    //    - an entity named main with C language linkage (in any namespace)
+    //   is ill-formed
     if (getLangOpts().CPlusPlus)
-      Diag(D.getBeginLoc(), diag::err_main_global_variable);
+      Diag(D.getBeginLoc(), diag::err_main_global_variable)
+          << NewVD->isExternC();
 
     // In C, and external-linkage variable named main results in undefined
     // behavior.
@@ -12255,6 +12259,7 @@ 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/CXX/basic/basic.start/basic.start.main/p3.cpp b/clang/test/CXX/basic/basic.start/basic.start.main/p3.cpp
index 9fbbdc72651e4..d23d00ccdeebc 100644
--- a/clang/test/CXX/basic/basic.start/basic.start.main/p3.cpp
+++ b/clang/test/CXX/basic/basic.start/basic.start.main/p3.cpp
@@ -13,7 +13,7 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -pedantic %s -DTEST13
 
 #if TEST1
-int main; // expected-error{{main cannot be declared as global variable}}
+int main; // expected-error{{main cannot be declared as a variable in the global scope}}
 
 #elif TEST2
 // expected-no-diagnostics
@@ -49,7 +49,7 @@ namespace foo {
 #elif TEST8
 void z(void)
 {
-  extern int main;  // expected-error{{main cannot be declared as global variable}}
+  extern int main;  // expected-error{{main cannot be declared as a variable in the global scope}}}
 }
 
 #elif TEST9
@@ -67,11 +67,11 @@ int main;
 #elif TEST11
 extern "C" {
   namespace Y {
-    int main; // expected-error {{main cannot be declared as global variable}}
+    int main; // expected-error {{main cannot be declared as a variable with C language linkage}}}
   }
 }
 namespace ns {
-  extern "C" int main; // expected-error {{main cannot be declared as global variable}}
+  extern "C" int main; // expected-error {{main cannot be declared as a variable with C language linkage}}
 }
 
 #elif TEST12

>From bab9ce4586d282528aca9473886851152d9559a2 Mon Sep 17 00:00:00 2001
From: Oleksandr T <oleksandr.tarasiuk at outlook.com>
Date: Tue, 6 Aug 2024 21:55:44 +0300
Subject: [PATCH 4/6] refactor method for recognizing the 'main' function

---
 clang/docs/ReleaseNotes.rst |  2 +-
 clang/lib/Sema/SemaDecl.cpp | 33 ++++++++++++++++++++++++---------
 2 files changed, 25 insertions(+), 10 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index ad5fe7d96d6cb..7beae653f3de4 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -145,7 +145,7 @@ Improvements to Clang's diagnostics
 
 - -Wdangling-assignment-gsl is enabled by default.
 
-- Clang now diagnoses the use of `main` in an `extern` context as invalid according to [basic.start.main] p3. Fixes #GH101512.
+- Clang now diagnoses the use of ``main`` in an ``extern`` context as invalid according to [basic.start.main] p3. Fixes #GH101512.
 
 Improvements to Clang's time-trace
 ----------------------------------
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index e6a6efe1d72d6..79e3229677b5e 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7353,13 +7353,26 @@ void emitReadOnlyPlacementAttrWarning(Sema &S, const VarDecl *VD) {
   }
 }
 
-// Checks if VD is declared at global scope or with C language linkage.
-static bool isMainVar(DeclarationName Name, VarDecl *VD) {
-  return Name.getAsIdentifierInfo() &&
-         Name.getAsIdentifierInfo()->isStr("main") &&
-         !VD->getDescribedVarTemplate() &&
-         (VD->getDeclContext()->getRedeclContext()->isTranslationUnit() ||
-          VD->isExternC());
+enum MainVarClassificationKind {
+  IMV_NotMain,
+  IMV_GlobalMain,
+  IMV_CLinkageMain,
+};
+
+// Determines if the variable is a 'main' function defined in global scope or
+// with C linkage.
+static MainVarClassificationKind classifyMainVar(DeclarationName Name,
+                                                 VarDecl *VD) {
+  if (Name.getAsIdentifierInfo() && Name.getAsIdentifierInfo()->isStr("main") &&
+      !VD->getDescribedVarTemplate()) {
+    if (VD->getDeclContext()->getRedeclContext()->isTranslationUnit()) {
+      return IMV_GlobalMain;
+    }
+    if (VD->isExternC()) {
+      return IMV_CLinkageMain;
+    }
+  }
+  return IMV_NotMain;
 }
 
 NamedDecl *Sema::ActOnVariableDeclarator(
@@ -8061,7 +8074,9 @@ NamedDecl *Sema::ActOnVariableDeclarator(
   }
 
   // Special handling of variable named 'main'.
-  if (!getLangOpts().Freestanding && isMainVar(Name, NewVD)) {
+  MainVarClassificationKind MainKind = classifyMainVar(Name, NewVD);
+  if (!getLangOpts().Freestanding &&
+      MainKind != MainVarClassificationKind::IMV_NotMain) {
     // C++ [basic.start.main]p3:
     //   A program that declares
     //    - a variable main at global scope, or
@@ -8069,7 +8084,7 @@ NamedDecl *Sema::ActOnVariableDeclarator(
     //   is ill-formed
     if (getLangOpts().CPlusPlus)
       Diag(D.getBeginLoc(), diag::err_main_global_variable)
-          << NewVD->isExternC();
+          << (MainKind == MainVarClassificationKind::IMV_CLinkageMain);
 
     // In C, and external-linkage variable named main results in undefined
     // behavior.

>From 60387907c489e0065c987cce960120f805f0f117 Mon Sep 17 00:00:00 2001
From: Oleksandr T <oleksandr.tarasiuk at outlook.com>
Date: Wed, 7 Aug 2024 01:19:09 +0300
Subject: [PATCH 5/6] remove useless variable

---
 clang/lib/AST/Decl.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 9bf2ee680073b..d832ce4190ff1 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -3292,9 +3292,9 @@ bool FunctionDecl::isImmediateFunction() const {
 }
 
 bool FunctionDecl::isMain() const {
-  const DeclContext *DC = getDeclContext();
   return isNamed(this, "main") && !getLangOpts().Freestanding &&
-         (DC->getRedeclContext()->isTranslationUnit() || isExternC());
+         (getDeclContext()->getRedeclContext()->isTranslationUnit() ||
+          isExternC());
 }
 
 bool FunctionDecl::isMSVCRTEntryPoint() const {

>From 3a8b206373e846c351576880c0b40b5903fc8c17 Mon Sep 17 00:00:00 2001
From: Oleksandr T <oleksandr.tarasiuk at outlook.com>
Date: Wed, 7 Aug 2024 19:32:13 +0300
Subject: [PATCH 6/6] revert helper for recognizing the 'main' function

---
 clang/lib/Sema/SemaDecl.cpp | 33 +++++++++------------------------
 1 file changed, 9 insertions(+), 24 deletions(-)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 79e3229677b5e..e6a6efe1d72d6 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7353,26 +7353,13 @@ void emitReadOnlyPlacementAttrWarning(Sema &S, const VarDecl *VD) {
   }
 }
 
-enum MainVarClassificationKind {
-  IMV_NotMain,
-  IMV_GlobalMain,
-  IMV_CLinkageMain,
-};
-
-// Determines if the variable is a 'main' function defined in global scope or
-// with C linkage.
-static MainVarClassificationKind classifyMainVar(DeclarationName Name,
-                                                 VarDecl *VD) {
-  if (Name.getAsIdentifierInfo() && Name.getAsIdentifierInfo()->isStr("main") &&
-      !VD->getDescribedVarTemplate()) {
-    if (VD->getDeclContext()->getRedeclContext()->isTranslationUnit()) {
-      return IMV_GlobalMain;
-    }
-    if (VD->isExternC()) {
-      return IMV_CLinkageMain;
-    }
-  }
-  return IMV_NotMain;
+// Checks if VD is declared at global scope or with C language linkage.
+static bool isMainVar(DeclarationName Name, VarDecl *VD) {
+  return Name.getAsIdentifierInfo() &&
+         Name.getAsIdentifierInfo()->isStr("main") &&
+         !VD->getDescribedVarTemplate() &&
+         (VD->getDeclContext()->getRedeclContext()->isTranslationUnit() ||
+          VD->isExternC());
 }
 
 NamedDecl *Sema::ActOnVariableDeclarator(
@@ -8074,9 +8061,7 @@ NamedDecl *Sema::ActOnVariableDeclarator(
   }
 
   // Special handling of variable named 'main'.
-  MainVarClassificationKind MainKind = classifyMainVar(Name, NewVD);
-  if (!getLangOpts().Freestanding &&
-      MainKind != MainVarClassificationKind::IMV_NotMain) {
+  if (!getLangOpts().Freestanding && isMainVar(Name, NewVD)) {
     // C++ [basic.start.main]p3:
     //   A program that declares
     //    - a variable main at global scope, or
@@ -8084,7 +8069,7 @@ NamedDecl *Sema::ActOnVariableDeclarator(
     //   is ill-formed
     if (getLangOpts().CPlusPlus)
       Diag(D.getBeginLoc(), diag::err_main_global_variable)
-          << (MainKind == MainVarClassificationKind::IMV_CLinkageMain);
+          << NewVD->isExternC();
 
     // In C, and external-linkage variable named main results in undefined
     // behavior.



More information about the cfe-commits mailing list