Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
----------------------------------

Expand Down
5 changes: 4 additions & 1 deletion clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">,
Expand Down
8 changes: 3 additions & 5 deletions clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
37 changes: 29 additions & 8 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -8052,14 +8061,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.
Expand Down Expand Up @@ -12210,7 +12220,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.
Expand Down
65 changes: 63 additions & 2 deletions clang/test/CXX/basic/basic.start/basic.start.main/p3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Loading