[clang] 37ec6e5 - [Clang] Strengthen checks for `main` to meet `[basic.start.main]p3`’s requirements (#101853)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 7 16:15:39 PDT 2024
Author: Oleksandr T.
Date: 2024-08-08T01:15:35+02:00
New Revision: 37ec6e5f12afe4a37872bf28f280423696f39019
URL: https://github.com/llvm/llvm-project/commit/37ec6e5f12afe4a37872bf28f280423696f39019
DIFF: https://github.com/llvm/llvm-project/commit/37ec6e5f12afe4a37872bf28f280423696f39019.diff
LOG: [Clang] Strengthen checks for `main` to meet `[basic.start.main]p3`’s requirements (#101853)
Fixes #101512.
Added:
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/AST/Decl.cpp
clang/lib/Sema/SemaDecl.cpp
clang/test/CXX/basic/basic.start/basic.start.main/p3.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index a4a862ccba212..ffdb5f8f5af3e 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -160,6 +160,8 @@ Improvements to Clang's diagnostics
- Clang now always preserves the template arguments as written used
to specialize template type aliases.
+- 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 02dc0ffe41594..5cdf36660b2a6 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -985,11 +985,14 @@ 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<
"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..d832ce4190ff1 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");
+ return isNamed(this, "main") && !getLangOpts().Freestanding &&
+ (getDeclContext()->getRedeclContext()->isTranslationUnit() ||
+ isExternC());
}
bool FunctionDecl::isMSVCRTEntryPoint() const {
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 1da16d6e93b3f..2bf494bec0d28 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,
@@ -8053,14 +8062,15 @@ NamedDecl *Sema::ActOnVariableDeclarator(
}
// Special handling of variable named 'main'.
- if (Name.getAsIdentifierInfo() && Name.getAsIdentifierInfo()->isStr("main") &&
- NewVD->getDeclContext()->getRedeclContext()->isTranslationUnit() &&
- !getLangOpts().Freestanding && !NewVD->getDescribedVarTemplate()) {
-
- // C++ [basic.start.main]p3
- // A program that declares a variable main at global scope is ill-formed.
+ if (!getLangOpts().Freestanding && isMainVar(Name, NewVD)) {
+ // 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.
@@ -12211,7 +12221,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.
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..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
@@ -8,9 +8,12 @@
// 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 %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}}
+int main; // expected-error{{main cannot be declared as a variable in the global scope}}
#elif TEST2
// expected-no-diagnostics
@@ -46,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
@@ -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 a variable with C language linkage}}}
+ }
+}
+namespace ns {
+ extern "C" int main; // expected-error {{main cannot be declared as a variable with C language linkage}}
+}
+
+#elif TEST12
+extern "C" struct A { int main(); }; // ok
+
+namespace c {
+ extern "C" void main(); // expected-warning {{'main' should not be 'extern "C"'}}
+}
+
+extern "C" {
+ namespace Z {
+ void main(); // expected-warning {{'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-warning {{'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"'}}
+extern "C++" int main(); // expected-warning {{'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
More information about the cfe-commits
mailing list