From: David Majnemer Date: Mon, 16 Sep 2013 22:44:20 +0000 (+0000) Subject: [-cxx-abi microsoft] Correctly identify Win32 entry points X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e9f6f33f0cf98a3e39025a57a0079cd316ed98f8;p=clang [-cxx-abi microsoft] Correctly identify Win32 entry points Summary: This fixes several issues with the original implementation: - Win32 entry points cannot be in namespaces - A Win32 entry point cannot be a function template, diagnose if we it. - Win32 entry points cannot be overloaded. - Win32 entry points implicitly return, similar to main. Reviewers: rnk, rsmith, whunt, timurrrr Reviewed By: rnk CC: cfe-commits, nrieck Differential Revision: http://llvm-reviews.chandlerc.com/D1683 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@190818 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 1ca63489f6..7cd61fcc22 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -1735,6 +1735,10 @@ public: /// entry point into an executable program. bool isMain() const; + /// \brief Determines whether this function is a MSVCRT user defined entry + /// point. + bool isMSVCRTEntryPoint() const; + /// \brief Determines whether this operator new or delete is one /// of the reserved global placement operators: /// void *operator new(size_t, void *); diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 518190c2a4..9f620fefa1 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -419,7 +419,7 @@ def ext_noreturn_main : ExtWarn< def note_main_remove_noreturn : Note<"remove '_Noreturn'">; def err_constexpr_main : Error< "'main' is not allowed to be declared constexpr">; -def err_main_template_decl : Error<"'main' cannot be a template">; +def err_mainlike_template_decl : Error<"'%0' cannot be a template">; def err_main_returns_nonint : Error<"'main' must return 'int'">; def ext_main_returns_nonint : ExtWarn<"return type of 'main' is not 'int'">, InGroup; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index ad27b3fd3f..573f028019 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1495,6 +1495,7 @@ public: FunctionDecl *NewFD, LookupResult &Previous, bool IsExplicitSpecialization); void CheckMain(FunctionDecl *FD, const DeclSpec &D); + void CheckMSVCRTEntryPoint(FunctionDecl *FD); Decl *ActOnParamDeclarator(Scope *S, Declarator &D); ParmVarDecl *BuildParmVarDeclForTypedef(DeclContext *DC, SourceLocation Loc, diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 1c2ead0ee5..0fa1b980f3 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -2204,6 +2204,33 @@ bool FunctionDecl::isMain() const { isNamed(this, "main"); } +bool FunctionDecl::isMSVCRTEntryPoint() const { + const TranslationUnitDecl *TUnit = + dyn_cast(getDeclContext()->getRedeclContext()); + if (!TUnit) + return false; + + // Even though we aren't really targeting MSVCRT if we are freestanding, + // semantic analysis for these functions remains the same. + + // MSVCRT entry points only exist on MSVCRT targets. + if (!TUnit->getASTContext().getTargetInfo().getTriple().isOSMSVCRT()) + return false; + + // Nameless functions like constructors cannot be entry points. + if (!getIdentifier()) + return false; + + return llvm::StringSwitch(getName()) + .Cases("main", // an ANSI console app + "wmain", // a Unicode console App + "WinMain", // an ANSI GUI app + "wWinMain", // a Unicode GUI app + "DllMain", // a DLL + true) + .Default(false); +} + bool FunctionDecl::isReservedGlobalPlacementOperator() const { assert(getDeclName().getNameKind() == DeclarationName::CXXOperatorName); assert(getDeclName().getCXXOverloadedOperator() == OO_New || diff --git a/lib/AST/MicrosoftMangle.cpp b/lib/AST/MicrosoftMangle.cpp index 9cea06187e..8506c4c8dd 100644 --- a/lib/AST/MicrosoftMangle.cpp +++ b/lib/AST/MicrosoftMangle.cpp @@ -24,7 +24,6 @@ #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/StringMap.h" -#include "llvm/ADT/StringSwitch.h" using namespace clang; @@ -71,29 +70,6 @@ static const FunctionDecl *getStructor(const FunctionDecl *fn) { return fn; } -// The ABI expects that we would never mangle "typical" user-defined entry -// points regardless of visibility or freestanding-ness. -// -// N.B. This is distinct from asking about "main". "main" has a lot of special -// rules associated with it in the standard while these user-defined entry -// points are outside of the purview of the standard. For example, there can be -// only one definition for "main" in a standards compliant program; however -// nothing forbids the existence of wmain and WinMain in the same translation -// unit. -static bool isUserDefinedEntryPoint(const FunctionDecl *FD) { - if (!FD->getIdentifier()) - return false; - - return llvm::StringSwitch(FD->getName()) - .Cases("main", // An ANSI console app - "wmain", // A Unicode console App - "WinMain", // An ANSI GUI app - "wWinMain", // A Unicode GUI app - "DllMain", // A DLL - true) - .Default(false); -} - /// MicrosoftCXXNameMangler - Manage the mangling of a single name for the /// Microsoft Visual C++ ABI. class MicrosoftCXXNameMangler { @@ -254,7 +230,16 @@ bool MicrosoftMangleContext::shouldMangleDeclName(const NamedDecl *D) { if (FD->hasAttr()) return true; - if (isUserDefinedEntryPoint(FD)) + // The ABI expects that we would never mangle "typical" user-defined entry + // points regardless of visibility or freestanding-ness. + // + // N.B. This is distinct from asking about "main". "main" has a lot of + // special rules associated with it in the standard while these + // user-defined entry points are outside of the purview of the standard. + // For example, there can be only one definition for "main" in a standards + // compliant program; however nothing forbids the existence of wmain and + // WinMain in the same translation unit. + if (FD->isMSVCRTEntryPoint()) return false; // C++ functions and those whose names are not a simple identifier need diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 9c93e11312..a2e05e0094 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -6881,6 +6881,9 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, if (!NewFD->isInvalidDecl() && NewFD->isMain()) CheckMain(NewFD, D.getDeclSpec()); + if (!NewFD->isInvalidDecl() && NewFD->isMSVCRTEntryPoint()) + CheckMSVCRTEntryPoint(NewFD); + if (!NewFD->isInvalidDecl()) D.setRedeclaration(CheckFunctionDeclaration(S, NewFD, Previous, isExplicitSpecialization)); @@ -7000,6 +7003,9 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, if (!NewFD->isInvalidDecl() && NewFD->isMain()) CheckMain(NewFD, D.getDeclSpec()); + if (!NewFD->isInvalidDecl() && NewFD->isMSVCRTEntryPoint()) + CheckMSVCRTEntryPoint(NewFD); + if (NewFD->isInvalidDecl()) { // If this is a class member, mark the class invalid immediately. // This avoids some consistency errors later. @@ -7670,7 +7676,27 @@ void Sema::CheckMain(FunctionDecl* FD, const DeclSpec& DS) { } if (!FD->isInvalidDecl() && FD->getDescribedFunctionTemplate()) { - Diag(FD->getLocation(), diag::err_main_template_decl); + Diag(FD->getLocation(), diag::err_mainlike_template_decl) << FD->getName(); + FD->setInvalidDecl(); + } +} + +void Sema::CheckMSVCRTEntryPoint(FunctionDecl *FD) { + QualType T = FD->getType(); + assert(T->isFunctionType() && "function decl is not of function type"); + const FunctionType *FT = T->castAs(); + + // Set an implicit return of 'zero' if the function can return some integral, + // enumeration, pointer or nullptr type. + if (FT->getResultType()->isIntegralOrEnumerationType() || + FT->getResultType()->isAnyPointerType() || + FT->getResultType()->isNullPtrType()) + // DllMain is exempt because a return value of zero means it failed. + if (FD->getName() != "DllMain") + FD->setHasImplicitReturnZero(true); + + if (!FD->isInvalidDecl() && FD->getDescribedFunctionTemplate()) { + Diag(FD->getLocation(), diag::err_mainlike_template_decl) << FD->getName(); FD->setInvalidDecl(); } } diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index c7620e49f7..800f983249 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -21,6 +21,7 @@ #include "clang/AST/TypeOrdering.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/PartialDiagnostic.h" +#include "clang/Basic/TargetInfo.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/Initialization.h" #include "clang/Sema/Lookup.h" @@ -979,6 +980,10 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old, if (New->isMain()) return false; + // MSVCRT user defined entry points cannot be overloaded. + if (New->isMSVCRTEntryPoint()) + return false; + FunctionTemplateDecl *OldTemplate = Old->getDescribedFunctionTemplate(); FunctionTemplateDecl *NewTemplate = New->getDescribedFunctionTemplate(); diff --git a/test/SemaCXX/ms-overload-entry-point.cpp b/test/SemaCXX/ms-overload-entry-point.cpp new file mode 100644 index 0000000000..67fed01f63 --- /dev/null +++ b/test/SemaCXX/ms-overload-entry-point.cpp @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -fms-extensions -triple i386-pc-win32 %s + +template +int wmain() { // expected-error{{'wmain' cannot be a template}} + return 0; +} + +namespace { +int WinMain(void) { return 0; } +int WinMain(int) { return 0; } +} + +void wWinMain(void) {} // expected-note{{previous definition is here}} +void wWinMain(int) {} // expected-error{{conflicting types for 'wWinMain'}} + +int foo() { + wmain(); // expected-error{{no matching function for call to 'wmain'}} + wmain(); // expected-error{{no matching function for call to 'wmain'}} + WinMain(); + return 0; +}