https://github.com/a-tarasyuk updated https://github.com/llvm/llvm-project/pull/101853
>From 3f8050482f54138c8a836e67e45131d5e9ccf5cc 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] [Clang] strengthen checks for 'main' function to meet [basic.start.main] p2 requirements --- clang/docs/ReleaseNotes.rst | 2 + clang/include/clang/AST/DeclBase.h | 2 + .../clang/Basic/DiagnosticSemaKinds.td | 2 + clang/include/clang/Sema/Sema.h | 3 +- clang/lib/AST/Decl.cpp | 8 ++-- clang/lib/Sema/SemaDecl.cpp | 42 ++++++++++++++----- clang/test/SemaCXX/linkage1.cpp | 22 ++++++++++ clang/test/SemaCXX/linkage3.cpp | 5 +++ 8 files changed, 69 insertions(+), 17 deletions(-) create mode 100644 clang/test/SemaCXX/linkage1.cpp create mode 100644 clang/test/SemaCXX/linkage3.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/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 40f01abf384e9..e28626eabe8e8 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -2156,6 +2156,8 @@ class DeclContext { return getDeclKind() == Decl::TranslationUnit; } + bool isLinkageSpec() const { return getDeclKind() == Decl::LinkageSpec; } + bool isRecord() const { return getDeclKind() >= Decl::firstRecord && getDeclKind() <= Decl::lastRecord; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 581434d33c5c9..e86b391264d2a 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -990,6 +990,8 @@ 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 err_main_invalid_linkage_specification : ExtWarn< + "'main' cannot have linkage specification 'extern \"C\"'">, InGroup<Main>; /// parser diagnostics def ext_no_declarators : ExtWarn<"declaration does not declare anything">, diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 2ec6367eccea0..d791991fcfd8b 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3585,8 +3585,9 @@ 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); + bool CheckLinkageSpecification(DeclContext *DC, Decl *D); /// Returns an implicit CodeSegAttr if a __declspec(code_seg) is found on a /// containing class. Otherwise it will return implicit SectionAttr if the diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 490c4a2fc525c..aa2ad1752cc5c 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() || DC->isLinkageSpec()); } bool FunctionDecl::isMSVCRTEntryPoint() const { diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 4fea38d1b02a9..832082d547bf4 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -7353,6 +7353,15 @@ void emitReadOnlyPlacementAttrWarning(Sema &S, const VarDecl *VD) { } } +static bool isMainVar(DeclarationName Name, VarDecl *VD) { + if (Name.getAsIdentifierInfo() && Name.getAsIdentifierInfo()->isStr("main") && + !VD->getDescribedVarTemplate()) { + const DeclContext *DC = VD->getDeclContext(); + return DC->getRedeclContext()->isTranslationUnit() || DC->isLinkageSpec(); + } + return false; +} + NamedDecl *Sema::ActOnVariableDeclarator( Scope *S, Declarator &D, DeclContext *DC, TypeSourceInfo *TInfo, LookupResult &Previous, MultiTemplateParamsArg TemplateParamLists, @@ -8052,15 +8061,13 @@ 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 (isMainVar(Name, NewVD) && !getLangOpts().Freestanding) { // C++ [basic.start.main]p3 // A program that declares a variable main at global scope is ill-formed. - if (getLangOpts().CPlusPlus) - Diag(D.getBeginLoc(), diag::err_main_global_variable); - + if (getLangOpts().CPlusPlus) { + if (!CheckLinkageSpecification(DC, NewVD)) + Diag(D.getBeginLoc(), diag::err_main_global_variable); + } // In C, and external-linkage variable named main results in undefined // behavior. else if (NewVD->hasExternalFormalLinkage()) @@ -10308,7 +10315,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 +10480,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 +12217,10 @@ 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) { + if (CheckLinkageSpecification(DC, FD)) + return; + // C++11 [basic.start.main]p3: // A program that [...] declares main to be inline, static or // constexpr is ill-formed. @@ -12238,7 +12248,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>(); @@ -12370,6 +12379,17 @@ void Sema::CheckMain(FunctionDecl* FD, const DeclSpec& DS) { } } +bool Sema::CheckLinkageSpecification(DeclContext *DC, Decl *D) { + // [basic.start.main] p2 + // The main function shall not be declared with a linkage-specification. + if (DC->isExternCContext()) { + Diag(D->getLocation(), diag::err_main_invalid_linkage_specification); + D->setInvalidDecl(); + return true; + } + return false; +} + static bool isDefaultStdCall(FunctionDecl *FD, Sema &S) { // Default calling convention for main and wmain is __cdecl diff --git a/clang/test/SemaCXX/linkage1.cpp b/clang/test/SemaCXX/linkage1.cpp new file mode 100644 index 0000000000000..3446e85af44be --- /dev/null +++ b/clang/test/SemaCXX/linkage1.cpp @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -pedantic-errors %s + +namespace c { + extern "C" void main(); // expected-error {{'main' cannot have linkage specification 'extern "C"'}} +} +extern "C" { + int main(); // expected-error {{'main' cannot have linkage specification 'extern "C"'}} +} + +extern "C" int main(); // expected-error {{'main' cannot have linkage specification 'extern "C"'}} +extern "C" struct A { int main(); }; // ok + +namespace ns { + extern "C" int main; // expected-error {{'main' cannot have linkage specification 'extern "C"'}} + extern "C" struct A { + int main; // ok + }; + + extern "C" struct B { + int main(); // ok + }; +} diff --git a/clang/test/SemaCXX/linkage3.cpp b/clang/test/SemaCXX/linkage3.cpp new file mode 100644 index 0000000000000..f80d76121d4af --- /dev/null +++ b/clang/test/SemaCXX/linkage3.cpp @@ -0,0 +1,5 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -pedantic-errors %s + +extern "C" { + void* main; // expected-error {{'main' cannot have linkage specification 'extern "C"'}} +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits