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.taras...@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.taras...@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.taras...@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.taras...@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.taras...@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.taras...@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. _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits