Skip to content

Commit 37ec6e5

Browse files
authored
[Clang] Strengthen checks for main to meet [basic.start.main]p3’s requirements (llvm#101853)
Fixes llvm#101512.
1 parent ccdce04 commit 37ec6e5

File tree

5 files changed

+101
-16
lines changed

5 files changed

+101
-16
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ Improvements to Clang's diagnostics
160160
- Clang now always preserves the template arguments as written used
161161
to specialize template type aliases.
162162

163+
- Clang now diagnoses the use of ``main`` in an ``extern`` context as invalid according to [basic.start.main] p3. Fixes #GH101512.
164+
163165
Improvements to Clang's time-trace
164166
----------------------------------
165167

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -985,11 +985,14 @@ def err_main_arg_wrong : Error<"%select{first|second|third|fourth}0 "
985985
def warn_main_returns_bool_literal : Warning<"bool literal returned from "
986986
"'main'">, InGroup<Main>;
987987
def err_main_global_variable :
988-
Error<"main cannot be declared as global variable">;
988+
Error<"main cannot be declared as a variable %select{in the global scope|with C language linkage}0">;
989989
def warn_main_redefined : Warning<"variable named 'main' with external linkage "
990990
"has undefined behavior">, InGroup<Main>;
991991
def ext_main_used : Extension<
992992
"referring to 'main' within an expression is a Clang extension">, InGroup<Main>;
993+
def ext_main_invalid_linkage_specification : ExtWarn<
994+
"'main' should not be "
995+
"'extern \"%select{C|C++}0\"'">, InGroup<Main>;
993996

994997
/// parser diagnostics
995998
def ext_no_declarators : ExtWarn<"declaration does not declare anything">,

clang/lib/AST/Decl.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3292,11 +3292,9 @@ bool FunctionDecl::isImmediateFunction() const {
32923292
}
32933293

32943294
bool FunctionDecl::isMain() const {
3295-
const TranslationUnitDecl *tunit =
3296-
dyn_cast<TranslationUnitDecl>(getDeclContext()->getRedeclContext());
3297-
return tunit &&
3298-
!tunit->getASTContext().getLangOpts().Freestanding &&
3299-
isNamed(this, "main");
3295+
return isNamed(this, "main") && !getLangOpts().Freestanding &&
3296+
(getDeclContext()->getRedeclContext()->isTranslationUnit() ||
3297+
isExternC());
33003298
}
33013299

33023300
bool FunctionDecl::isMSVCRTEntryPoint() const {

clang/lib/Sema/SemaDecl.cpp

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7353,6 +7353,15 @@ void emitReadOnlyPlacementAttrWarning(Sema &S, const VarDecl *VD) {
73537353
}
73547354
}
73557355

7356+
// Checks if VD is declared at global scope or with C language linkage.
7357+
static bool isMainVar(DeclarationName Name, VarDecl *VD) {
7358+
return Name.getAsIdentifierInfo() &&
7359+
Name.getAsIdentifierInfo()->isStr("main") &&
7360+
!VD->getDescribedVarTemplate() &&
7361+
(VD->getDeclContext()->getRedeclContext()->isTranslationUnit() ||
7362+
VD->isExternC());
7363+
}
7364+
73567365
NamedDecl *Sema::ActOnVariableDeclarator(
73577366
Scope *S, Declarator &D, DeclContext *DC, TypeSourceInfo *TInfo,
73587367
LookupResult &Previous, MultiTemplateParamsArg TemplateParamLists,
@@ -8053,14 +8062,15 @@ NamedDecl *Sema::ActOnVariableDeclarator(
80538062
}
80548063

80558064
// Special handling of variable named 'main'.
8056-
if (Name.getAsIdentifierInfo() && Name.getAsIdentifierInfo()->isStr("main") &&
8057-
NewVD->getDeclContext()->getRedeclContext()->isTranslationUnit() &&
8058-
!getLangOpts().Freestanding && !NewVD->getDescribedVarTemplate()) {
8059-
8060-
// C++ [basic.start.main]p3
8061-
// A program that declares a variable main at global scope is ill-formed.
8065+
if (!getLangOpts().Freestanding && isMainVar(Name, NewVD)) {
8066+
// C++ [basic.start.main]p3:
8067+
// A program that declares
8068+
// - a variable main at global scope, or
8069+
// - an entity named main with C language linkage (in any namespace)
8070+
// is ill-formed
80628071
if (getLangOpts().CPlusPlus)
8063-
Diag(D.getBeginLoc(), diag::err_main_global_variable);
8072+
Diag(D.getBeginLoc(), diag::err_main_global_variable)
8073+
<< NewVD->isExternC();
80648074

80658075
// In C, and external-linkage variable named main results in undefined
80668076
// behavior.
@@ -12211,7 +12221,18 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
1221112221
return Redeclaration;
1221212222
}
1221312223

12214-
void Sema::CheckMain(FunctionDecl* FD, const DeclSpec& DS) {
12224+
void Sema::CheckMain(FunctionDecl *FD, const DeclSpec &DS) {
12225+
// [basic.start.main]p3
12226+
// The main function shall not be declared with a linkage-specification.
12227+
if (FD->isExternCContext() ||
12228+
(FD->isExternCXXContext() &&
12229+
FD->getDeclContext()->getRedeclContext()->isTranslationUnit())) {
12230+
Diag(FD->getLocation(), diag::ext_main_invalid_linkage_specification)
12231+
<< FD->getLanguageLinkage();
12232+
FD->setInvalidDecl();
12233+
return;
12234+
}
12235+
1221512236
// C++11 [basic.start.main]p3:
1221612237
// A program that [...] declares main to be inline, static or
1221712238
// constexpr is ill-formed.

clang/test/CXX/basic/basic.start/basic.start.main/p3.cpp

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88
// RUN: %clang_cc1 -fsyntax-only -verify %s -DTEST8
99
// RUN: %clang_cc1 -fsyntax-only -verify %s -DTEST9
1010
// RUN: %clang_cc1 -fsyntax-only -verify %s -DTEST10 -ffreestanding
11+
// RUN: %clang_cc1 -fsyntax-only -verify -pedantic %s -DTEST11
12+
// RUN: %clang_cc1 -fsyntax-only -verify -pedantic %s -DTEST12
13+
// RUN: %clang_cc1 -fsyntax-only -verify -pedantic %s -DTEST13
1114

1215
#if TEST1
13-
int main; // expected-error{{main cannot be declared as global variable}}
16+
int main; // expected-error{{main cannot be declared as a variable in the global scope}}
1417

1518
#elif TEST2
1619
// expected-no-diagnostics
@@ -46,7 +49,7 @@ namespace foo {
4649
#elif TEST8
4750
void z(void)
4851
{
49-
extern int main; // expected-error{{main cannot be declared as global variable}}
52+
extern int main; // expected-error{{main cannot be declared as a variable in the global scope}}}
5053
}
5154

5255
#elif TEST9
@@ -61,6 +64,64 @@ int q(void)
6164
// expected-no-diagnostics
6265
int main;
6366

67+
#elif TEST11
68+
extern "C" {
69+
namespace Y {
70+
int main; // expected-error {{main cannot be declared as a variable with C language linkage}}}
71+
}
72+
}
73+
namespace ns {
74+
extern "C" int main; // expected-error {{main cannot be declared as a variable with C language linkage}}
75+
}
76+
77+
#elif TEST12
78+
extern "C" struct A { int main(); }; // ok
79+
80+
namespace c {
81+
extern "C" void main(); // expected-warning {{'main' should not be 'extern "C"'}}
82+
}
83+
84+
extern "C" {
85+
namespace Z {
86+
void main(); // expected-warning {{'main' should not be 'extern "C"'}}
87+
}
88+
}
89+
90+
namespace ns {
91+
extern "C" struct A {
92+
int main; // ok
93+
};
94+
95+
extern "C" struct B {
96+
int main(); // ok
97+
};
98+
}
99+
100+
#elif TEST13
101+
extern "C++" {
102+
int main(); // expected-warning {{'main' should not be 'extern "C++"'}}
103+
}
104+
105+
extern "C" {
106+
int main(); // expected-warning {{'main' should not be 'extern "C"'}}
107+
}
108+
109+
extern "C" int main(); // expected-warning {{'main' should not be 'extern "C"'}}
110+
extern "C++" int main(); // expected-warning {{'main' should not be 'extern "C++"'}}
111+
112+
namespace ns1 {
113+
extern "C++" int main(); // ok
114+
extern "C" {
115+
extern "C++" {
116+
int main(void *); // ok
117+
}
118+
}
119+
}
120+
121+
namespace ns2 {
122+
extern "C++" void main() {} // ok
123+
}
124+
64125
#else
65126
#error Unknown Test
66127
#endif

0 commit comments

Comments
 (0)