[MS] Pretend constexpr variable template specializations are inline

Fixes link errors with clang and the latest Visual C++ 14.21.27702
headers, which was reported as PR42027.

I chose to intentionally make these things linkonce_odr, i.e.
discardable, so that we don't emit definitions of these things in every
translation unit that includes STL headers.

Note that this is *not* what MSVC does: MSVC has not yet implemented C++
DR2387, so they emit fully specialized constexpr variable templates with
static / internal linkage.

Reviewers: rsmith

Differential Revision: https://reviews.llvm.org/D63175

llvm-svn: 363191
This commit is contained in:
Reid Kleckner 2019-06-12 18:53:49 +00:00
parent 000f9cc62a
commit 4e9538997e
2 changed files with 27 additions and 4 deletions

View File

@ -9799,10 +9799,22 @@ static GVALinkage basicGVALinkageForVariable(const ASTContext &Context,
return StrongLinkage;
case TSK_ExplicitSpecialization:
return Context.getTargetInfo().getCXXABI().isMicrosoft() &&
VD->isStaticDataMember()
? GVA_StrongODR
: StrongLinkage;
if (Context.getTargetInfo().getCXXABI().isMicrosoft()) {
// If this is a fully specialized constexpr variable template, pretend it
// was marked inline. MSVC 14.21.27702 headers define _Is_integral in a
// header this way, and we don't want to emit non-discardable definitions
// of these variables in every TU that includes <type_traits>. This
// behavior can be removed if the headers change to explicitly mark such
// variable template specializations inline.
if (isa<VarTemplateSpecializationDecl>(VD) && VD->isConstexpr())
return GVA_DiscardableODR;
// Use ODR linkage for static data members of fully specialized templates
// to prevent duplicate definition errors with MSVC.
if (VD->isStaticDataMember())
return GVA_StrongODR;
}
return StrongLinkage;
case TSK_ExplicitInstantiationDefinition:
return GVA_StrongODR;

View File

@ -0,0 +1,11 @@
// RUN: %clang_cc1 -emit-llvm -triple=x86_64-windows-msvc -fms-compatibility %s -o - | FileCheck %s
template <typename> constexpr bool _Is_integer = false;
template <> constexpr bool _Is_integer<int> = true;
template <> constexpr bool _Is_integer<char> = false;
extern "C" const bool *escape = &_Is_integer<int>;
// CHECK: @"??$_Is_integer@H@@3_NB" = linkonce_odr dso_local constant i8 1, comdat, align 1
// Should not emit _Is_integer<char>, since it's not referenced.
// CHECK-NOT: @"??$_Is_integer@D@@3_NB"
// CHECK: @escape = dso_local global i8* @"??$_Is_integer@H@@3_NB", align 8