-
Notifications
You must be signed in to change notification settings - Fork 14.1k
[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
Conversation
@llvm/pr-subscribers-clang Author: Oleksandr T. (a-tarasyuk) ChangesFixes #101512 Full diff: https://github.com/llvm/llvm-project/pull/101853.diff 8 Files Affected:
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"'}}
+}
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can put some of the tests in clang/test/CXX/basic/basic.start/basic.start.main/p3.cpp
You lost the check for extern "C++" int main() {}
at global scope. To recap:
// These are disallowed by [basic.start.main]p3
// The main function shall not be declared with a linkage-specification.
// We only want a pedantic warning for these
extern "C++" {
int main();
}
extern "C" {
int main();
}
// These are disallowed by [basic.start.main]p(3.4)
// We want a hard error
namespace X { extern "C" int main; }
extern "C" {
namespace Y {
int main;
}
namespace Z {
void main();
}
}
// These are allowed
namespace W {
extern "C++" int main();
extern "C" {
extern "C++" {
int main(void*);
}
}
}
(You should add a test for extern "C" { namespace namespace_name { int main; } }
too)
Did you try doing it like #101512 (comment) , checking Decl->getLanguageLinkage()
rather than checking the DeclContext it's declared in?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs some cleanup I think, but it’s going in the right direction.
befd46e
to
a8d0a65
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few minor things, but other than that this is pretty much done I’d say.
…in] p3 requirements
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two more minor changes but LGTM otherwise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Diagnostic changes are a minor nit, LGTM otherwise
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
I’ll wait a bit to see if anyone else wants to comment on this since we’ve gone back and forth over a few things w/ this pr, but if not, I’ll merge this sometime later.
@Sirraide thanks |
llvm/llvm-project#101853 started to enforce the C++ standard langauge that disallows main from being declared in a linkage specification.
…#22336) llvm/llvm-project#101853 started to enforce the C++ standard langauge that disallows main from being declared in a linkage specification.
This is invalid in C++, and clang recently started warning on it as of #101853
According to the standard, the main function should have no linkage-specification [1]. New version of clang will enforce this via a warning [2] which causes build failure if building with `-Werror` which treats warnings as error. Remove the superfluous linkage-specification to fix the error. [1]: https://eel.is/c++draft/basic.start#main-3 [2]: llvm/llvm-project#101853 Fixed: chromium:358427300 Change-Id: I541166cbd122c7e296ecdd2b5a9f9a107fe82872 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/5782349 Auto-Submit: Sylvain Defresne <sdefresne@chromium.org> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
This reverts commit b60dc9d. Reason for revert: Fails to roll into Chromium https://chromium-review.googlesource.com/c/chromium/src/+/5782182 https://cr-buildbucket.appspot.com/build/8739800008401884241 Original change's description: > Remove the linkage-specification on `main` > > According to the standard, the main function should have no > linkage-specification [1]. New version of clang will enforce > this via a warning [2] which causes build failure if building > with `-Werror` which treats warnings as error. > > Remove the superfluous linkage-specification to fix the error. > > [1]: https://eel.is/c++draft/basic.start#main-3 > [2]: llvm/llvm-project#101853 > > Fixed: chromium:358427300 > Change-Id: I541166cbd122c7e296ecdd2b5a9f9a107fe82872 > Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/5782349 > Auto-Submit: Sylvain Defresne <sdefresne@chromium.org> > Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> > Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org> Change-Id: Id9f99c21480e7f72617b2907c35356e5033a3ce4 No-Presubmit: true No-Tree-Checks: true No-Try: true Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/5782859 Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com> Auto-Submit: Yuly Novikov <ynovikov@chromium.org> Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Summary: According to the C++ standard, The main function shall not be declared with a linkage-specification. after some changes in llvm#101853 this started emitting warnings when building / testing the C library. This source file is shared with the overlay tests as well as the full build tests. The full build tests are compiled with `-ffreestanding`, as are all the startup / integration files. The standard says freestanding environment are all implementation defined, so this is valid in those cases. This patch simply prevents adding the linkage when we are compiling unit tests, which are hosted. This is a continuation on llvm#102825.
Summary: According to the C++ standard, The main function shall not be declared with a linkage-specification. after some changes in llvm#101853 this started emitting warnings when building / testing the C library. This source file is shared with the overlay tests as well as the full build tests. The full build tests are compiled with `-ffreestanding`, as are all the startup / integration files. The standard says freestanding environment are all implementation defined, so this is valid in those cases. This patch simply prevents adding the linkage when we are compiling unit tests, which are hosted. This is a continuation on llvm#102825.
Summary: According to the C++ standard, The main function shall not be declared with a linkage-specification. after some changes in #101853 this started emitting warnings when building / testing the C library. This source file is shared with the overlay tests as well as the full build tests. The full build tests are compiled with `-ffreestanding`, as are all the startup / integration files. The standard says freestanding environment are all implementation defined, so this is valid in those cases. This patch simply prevents adding the linkage when we are compiling unit tests, which are hosted. This is a continuation on #102825.
This is a reland of commit b60dc9d Original change's description: > Remove the linkage-specification on `main` > > According to the standard, the main function should have no > linkage-specification [1]. New version of clang will enforce > this via a warning [2] which causes build failure if building > with `-Werror` which treats warnings as error. > > Remove the superfluous linkage-specification to fix the error. > > [1]: https://eel.is/c++draft/basic.start#main-3 > [2]: llvm/llvm-project#101853 > > Fixed: chromium:358427300 > Change-Id: I541166cbd122c7e296ecdd2b5a9f9a107fe82872 > Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/5782349 > Auto-Submit: Sylvain Defresne <sdefresne@chromium.org> > Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> > Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org> Fixed: chromium:358427300 Change-Id: I380fdf74dc2a056f94af35c5c6c566a88d750c7d Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/5785953 Auto-Submit: Sylvain Defresne <sdefresne@chromium.org> Commit-Queue: Yuly Novikov <ynovikov@chromium.org> Reviewed-by: Yuly Novikov <ynovikov@chromium.org>
Hi @a-tarasyuk, it looks like this patch introduces a couple of problems. I guess, this boils down to marking the declaration of The first problem is a clang crash: https://gcc.godbolt.org/z/dWETErhzM The second one is a change of behavior of code that declares The problem with both cases is that in some codebases the non-trivial amount of cleanup is necessary for this, but Clang doesn't allow to postpone the cleanup and retain the old behavior by just ignoring the diagnostic ( Please fix this ASAP or revert the patch, if it requires non-trivial / controversial changes or further discussion. Thanks! |
Fixes #101512