Skip to content

Commit 3f80504

Browse files
committed
[Clang] strengthen checks for 'main' function to meet [basic.start.main] p2 requirements
1 parent 1a9acd7 commit 3f80504

File tree

8 files changed

+69
-17
lines changed

8 files changed

+69
-17
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ Improvements to Clang's diagnostics
145145

146146
- -Wdangling-assignment-gsl is enabled by default.
147147

148+
- Clang now diagnoses the use of `main` in `extern` context as invalid according to [basic.start.main] p2. Fixes #GH101512.
149+
148150
Improvements to Clang's time-trace
149151
----------------------------------
150152

clang/include/clang/AST/DeclBase.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2156,6 +2156,8 @@ class DeclContext {
21562156
return getDeclKind() == Decl::TranslationUnit;
21572157
}
21582158

2159+
bool isLinkageSpec() const { return getDeclKind() == Decl::LinkageSpec; }
2160+
21592161
bool isRecord() const {
21602162
return getDeclKind() >= Decl::firstRecord &&
21612163
getDeclKind() <= Decl::lastRecord;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -990,6 +990,8 @@ 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 err_main_invalid_linkage_specification : ExtWarn<
994+
"'main' cannot have linkage specification 'extern \"C\"'">, InGroup<Main>;
993995

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

clang/include/clang/Sema/Sema.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3585,8 +3585,9 @@ class Sema final : public SemaBase {
35853585
/// \param OldT The portion of the type of the old declaration to check.
35863586
bool canFullyTypeCheckRedeclaration(ValueDecl *NewD, ValueDecl *OldD,
35873587
QualType NewT, QualType OldT);
3588-
void CheckMain(FunctionDecl *FD, const DeclSpec &D);
3588+
void CheckMain(FunctionDecl *FD, DeclContext *DC, const DeclSpec &D);
35893589
void CheckMSVCRTEntryPoint(FunctionDecl *FD);
3590+
bool CheckLinkageSpecification(DeclContext *DC, Decl *D);
35903591

35913592
/// Returns an implicit CodeSegAttr if a __declspec(code_seg) is found on a
35923593
/// containing class. Otherwise it will return implicit SectionAttr if the

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+
const DeclContext *DC = getDeclContext();
3296+
return isNamed(this, "main") && !getLangOpts().Freestanding &&
3297+
(DC->getRedeclContext()->isTranslationUnit() || DC->isLinkageSpec());
33003298
}
33013299

33023300
bool FunctionDecl::isMSVCRTEntryPoint() const {

clang/lib/Sema/SemaDecl.cpp

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

7356+
static bool isMainVar(DeclarationName Name, VarDecl *VD) {
7357+
if (Name.getAsIdentifierInfo() && Name.getAsIdentifierInfo()->isStr("main") &&
7358+
!VD->getDescribedVarTemplate()) {
7359+
const DeclContext *DC = VD->getDeclContext();
7360+
return DC->getRedeclContext()->isTranslationUnit() || DC->isLinkageSpec();
7361+
}
7362+
return false;
7363+
}
7364+
73567365
NamedDecl *Sema::ActOnVariableDeclarator(
73577366
Scope *S, Declarator &D, DeclContext *DC, TypeSourceInfo *TInfo,
73587367
LookupResult &Previous, MultiTemplateParamsArg TemplateParamLists,
@@ -8052,15 +8061,13 @@ NamedDecl *Sema::ActOnVariableDeclarator(
80528061
}
80538062

80548063
// Special handling of variable named 'main'.
8055-
if (Name.getAsIdentifierInfo() && Name.getAsIdentifierInfo()->isStr("main") &&
8056-
NewVD->getDeclContext()->getRedeclContext()->isTranslationUnit() &&
8057-
!getLangOpts().Freestanding && !NewVD->getDescribedVarTemplate()) {
8058-
8064+
if (isMainVar(Name, NewVD) && !getLangOpts().Freestanding) {
80598065
// C++ [basic.start.main]p3
80608066
// A program that declares a variable main at global scope is ill-formed.
8061-
if (getLangOpts().CPlusPlus)
8062-
Diag(D.getBeginLoc(), diag::err_main_global_variable);
8063-
8067+
if (getLangOpts().CPlusPlus) {
8068+
if (!CheckLinkageSpecification(DC, NewVD))
8069+
Diag(D.getBeginLoc(), diag::err_main_global_variable);
8070+
}
80648071
// In C, and external-linkage variable named main results in undefined
80658072
// behavior.
80668073
else if (NewVD->hasExternalFormalLinkage())
@@ -10308,7 +10315,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
1030810315
if (!getLangOpts().CPlusPlus) {
1030910316
// Perform semantic checking on the function declaration.
1031010317
if (!NewFD->isInvalidDecl() && NewFD->isMain())
10311-
CheckMain(NewFD, D.getDeclSpec());
10318+
CheckMain(NewFD, DC, D.getDeclSpec());
1031210319

1031310320
if (!NewFD->isInvalidDecl() && NewFD->isMSVCRTEntryPoint())
1031410321
CheckMSVCRTEntryPoint(NewFD);
@@ -10473,7 +10480,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
1047310480

1047410481
// Perform semantic checking on the function declaration.
1047510482
if (!NewFD->isInvalidDecl() && NewFD->isMain())
10476-
CheckMain(NewFD, D.getDeclSpec());
10483+
CheckMain(NewFD, DC, D.getDeclSpec());
1047710484

1047810485
if (!NewFD->isInvalidDecl() && NewFD->isMSVCRTEntryPoint())
1047910486
CheckMSVCRTEntryPoint(NewFD);
@@ -12210,7 +12217,10 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
1221012217
return Redeclaration;
1221112218
}
1221212219

12213-
void Sema::CheckMain(FunctionDecl* FD, const DeclSpec& DS) {
12220+
void Sema::CheckMain(FunctionDecl *FD, DeclContext *DC, const DeclSpec &DS) {
12221+
if (CheckLinkageSpecification(DC, FD))
12222+
return;
12223+
1221412224
// C++11 [basic.start.main]p3:
1221512225
// A program that [...] declares main to be inline, static or
1221612226
// constexpr is ill-formed.
@@ -12238,7 +12248,6 @@ void Sema::CheckMain(FunctionDecl* FD, const DeclSpec& DS) {
1223812248
<< FixItHint::CreateRemoval(DS.getConstexprSpecLoc());
1223912249
FD->setConstexprKind(ConstexprSpecKind::Unspecified);
1224012250
}
12241-
1224212251
if (getLangOpts().OpenCL) {
1224312252
Diag(FD->getLocation(), diag::err_opencl_no_main)
1224412253
<< FD->hasAttr<OpenCLKernelAttr>();
@@ -12370,6 +12379,17 @@ void Sema::CheckMain(FunctionDecl* FD, const DeclSpec& DS) {
1237012379
}
1237112380
}
1237212381

12382+
bool Sema::CheckLinkageSpecification(DeclContext *DC, Decl *D) {
12383+
// [basic.start.main] p2
12384+
// The main function shall not be declared with a linkage-specification.
12385+
if (DC->isExternCContext()) {
12386+
Diag(D->getLocation(), diag::err_main_invalid_linkage_specification);
12387+
D->setInvalidDecl();
12388+
return true;
12389+
}
12390+
return false;
12391+
}
12392+
1237312393
static bool isDefaultStdCall(FunctionDecl *FD, Sema &S) {
1237412394

1237512395
// Default calling convention for main and wmain is __cdecl

clang/test/SemaCXX/linkage1.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify -pedantic-errors %s
2+
3+
namespace c {
4+
extern "C" void main(); // expected-error {{'main' cannot have linkage specification 'extern "C"'}}
5+
}
6+
extern "C" {
7+
int main(); // expected-error {{'main' cannot have linkage specification 'extern "C"'}}
8+
}
9+
10+
extern "C" int main(); // expected-error {{'main' cannot have linkage specification 'extern "C"'}}
11+
extern "C" struct A { int main(); }; // ok
12+
13+
namespace ns {
14+
extern "C" int main; // expected-error {{'main' cannot have linkage specification 'extern "C"'}}
15+
extern "C" struct A {
16+
int main; // ok
17+
};
18+
19+
extern "C" struct B {
20+
int main(); // ok
21+
};
22+
}

clang/test/SemaCXX/linkage3.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify -pedantic-errors %s
2+
3+
extern "C" {
4+
void* main; // expected-error {{'main' cannot have linkage specification 'extern "C"'}}
5+
}

0 commit comments

Comments
 (0)