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