Skip to content

Commit 62f19e7

Browse files
committed
Implement C++17 P0386R2, inline variables. (The 'inline' specifier gives a
variable weak discardable linkage and partially-ordered initialization, and is implied for constexpr static data members.) llvm-svn: 273754
1 parent b8da3a2 commit 62f19e7

File tree

27 files changed

+371
-70
lines changed

27 files changed

+371
-70
lines changed

clang/include/clang/AST/Decl.h

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,12 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
881881
/// variable; see isARCPseudoStrong() for details.
882882
unsigned ARCPseudoStrong : 1;
883883

884+
/// \brief Whether this variable is (C++1z) inline.
885+
unsigned IsInline : 1;
886+
887+
/// \brief Whether this variable has (C++1z) inline explicitly specified.
888+
unsigned IsInlineSpecified : 1;
889+
884890
/// \brief Whether this variable is (C++0x) constexpr.
885891
unsigned IsConstexpr : 1;
886892

@@ -1102,9 +1108,6 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
11021108
/// definition of a static data member.
11031109
bool isOutOfLine() const override;
11041110

1105-
/// \brief If this is a static data member, find its out-of-line definition.
1106-
VarDecl *getOutOfLineDefinition();
1107-
11081111
/// isFileVarDecl - Returns true for file scoped variable declaration.
11091112
bool isFileVarDecl() const {
11101113
Kind K = getKind();
@@ -1250,6 +1253,24 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
12501253
NonParmVarDeclBits.ARCPseudoStrong = ps;
12511254
}
12521255

1256+
/// Whether this variable is (C++1z) inline.
1257+
bool isInline() const {
1258+
return isa<ParmVarDecl>(this) ? false : NonParmVarDeclBits.IsInline;
1259+
}
1260+
bool isInlineSpecified() const {
1261+
return isa<ParmVarDecl>(this) ? false
1262+
: NonParmVarDeclBits.IsInlineSpecified;
1263+
}
1264+
void setInlineSpecified() {
1265+
assert(!isa<ParmVarDecl>(this));
1266+
NonParmVarDeclBits.IsInline = true;
1267+
NonParmVarDeclBits.IsInlineSpecified = true;
1268+
}
1269+
void setImplicitlyInline() {
1270+
assert(!isa<ParmVarDecl>(this));
1271+
NonParmVarDeclBits.IsInline = true;
1272+
}
1273+
12531274
/// Whether this variable is (C++11) constexpr.
12541275
bool isConstexpr() const {
12551276
return isa<ParmVarDecl>(this) ? false : NonParmVarDeclBits.IsConstexpr;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,12 +339,16 @@ def err_language_linkage_spec_not_ascii : Error<
339339
def warn_use_out_of_scope_declaration : Warning<
340340
"use of out-of-scope declaration of %0">;
341341
def err_inline_non_function : Error<
342-
"'inline' can only appear on functions">;
342+
"'inline' can only appear on functions%select{| and non-local variables}0">;
343343
def err_noreturn_non_function : Error<
344344
"'_Noreturn' can only appear on functions">;
345345
def warn_qual_return_type : Warning<
346346
"'%0' type qualifier%s1 on return type %plural{1:has|:have}1 no effect">,
347347
InGroup<IgnoredQualifiers>, DefaultIgnore;
348+
def warn_deprecated_redundant_constexpr_static_def : Warning<
349+
"out-of-line definition of constexpr static data member is redundant "
350+
"in C++17 and is deprecated">,
351+
InGroup<Deprecated>, DefaultIgnore;
348352

349353
def warn_decl_shadow :
350354
Warning<"declaration shadows a %select{"
@@ -1086,6 +1090,12 @@ def warn_cxx14_compat_static_assert_no_message : Warning<
10861090
"static_assert with no message is incompatible with C++ standards before C++1z">,
10871091
DefaultIgnore, InGroup<CXXPre1zCompat>;
10881092

1093+
def ext_inline_variable : ExtWarn<
1094+
"inline variables are a C++1z extension">, InGroup<CXX1z>;
1095+
def warn_cxx14_compat_inline_variable : Warning<
1096+
"inline variables are incompatible with C++ standards before C++1z">,
1097+
DefaultIgnore, InGroup<CXXPre1zCompat>;
1098+
10891099
def warn_inline_namespace_reopened_noninline : Warning<
10901100
"inline namespace cannot be reopened as a non-inline namespace">;
10911101
def err_inline_namespace_mismatch : Error<
@@ -4305,6 +4315,7 @@ def warn_undefined_internal : Warning<
43054315
InGroup<DiagGroup<"undefined-internal">>;
43064316
def warn_undefined_inline : Warning<"inline function %q0 is not defined">,
43074317
InGroup<DiagGroup<"undefined-inline">>;
4318+
def err_undefined_inline_var : Error<"inline variable %q0 is not defined">;
43084319
def note_used_here : Note<"used here">;
43094320

43104321
def err_internal_linkage_redeclaration : Error<

clang/include/clang/Sema/SemaInternal.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,11 @@ inline void MarkVarDeclODRUsed(VarDecl *Var,
7373
// Keep track of used but undefined variables.
7474
// FIXME: We shouldn't suppress this warning for static data members.
7575
if (Var->hasDefinition(SemaRef.Context) == VarDecl::DeclarationOnly &&
76-
!Var->isExternallyVisible() &&
77-
!(Var->isStaticDataMember() && Var->hasInit())) {
78-
SourceLocation &old = SemaRef.UndefinedButUsed[Var->getCanonicalDecl()];
79-
if (old.isInvalid()) old = Loc;
76+
(!Var->isExternallyVisible() || Var->isInline()) &&
77+
!(Var->isStaticDataMember() && Var->hasInit())) {
78+
SourceLocation &old = SemaRef.UndefinedButUsed[Var->getCanonicalDecl()];
79+
if (old.isInvalid())
80+
old = Loc;
8081
}
8182
QualType CaptureType, DeclRefType;
8283
SemaRef.tryCaptureVariable(Var, Loc, Sema::TryCapture_Implicit,

clang/lib/AST/ASTContext.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8486,15 +8486,19 @@ static GVALinkage basicGVALinkageForVariable(const ASTContext &Context,
84868486
if (Context.isMSStaticDataMemberInlineDefinition(VD))
84878487
return GVA_DiscardableODR;
84888488

8489+
GVALinkage StrongLinkage = GVA_StrongExternal;
8490+
if (VD->isInline())
8491+
StrongLinkage = GVA_DiscardableODR;
8492+
84898493
switch (VD->getTemplateSpecializationKind()) {
84908494
case TSK_Undeclared:
8491-
return GVA_StrongExternal;
8495+
return StrongLinkage;
84928496

84938497
case TSK_ExplicitSpecialization:
84948498
return Context.getTargetInfo().getCXXABI().isMicrosoft() &&
84958499
VD->isStaticDataMember()
84968500
? GVA_StrongODR
8497-
: GVA_StrongExternal;
8501+
: StrongLinkage;
84988502

84998503
case TSK_ExplicitInstantiationDefinition:
85008504
return GVA_StrongODR;

clang/lib/AST/ASTDumper.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1196,6 +1196,10 @@ void ASTDumper::VisitVarDecl(const VarDecl *D) {
11961196
OS << " __module_private__";
11971197
if (D->isNRVOVariable())
11981198
OS << " nrvo";
1199+
if (D->isInline())
1200+
OS << " inline";
1201+
if (D->isConstexpr())
1202+
OS << " constexpr";
11991203
if (D->hasInit()) {
12001204
switch (D->getInitStyle()) {
12011205
case VarDecl::CInit: OS << " cinit"; break;

clang/lib/AST/Decl.cpp

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -592,12 +592,14 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D,
592592
if (Var->getStorageClass() == SC_Static)
593593
return LinkageInfo::internal();
594594

595-
// - a non-volatile object or reference that is explicitly declared const
596-
// or constexpr and neither explicitly declared extern nor previously
597-
// declared to have external linkage; or (there is no equivalent in C99)
595+
// - a non-inline, non-volatile object or reference that is explicitly
596+
// declared const or constexpr and neither explicitly declared extern
597+
// nor previously declared to have external linkage; or (there is no
598+
// equivalent in C99)
598599
if (Context.getLangOpts().CPlusPlus &&
599600
Var->getType().isConstQualified() &&
600-
!Var->getType().isVolatileQualified()) {
601+
!Var->getType().isVolatileQualified() &&
602+
!Var->isInline()) {
601603
const VarDecl *PrevVar = Var->getPreviousDecl();
602604
if (PrevVar)
603605
return getLVForDecl(PrevVar, computation);
@@ -1912,7 +1914,9 @@ VarDecl::isThisDeclarationADefinition(ASTContext &C) const {
19121914
// C++ [basic.def]p2:
19131915
// A declaration is a definition unless [...] it contains the 'extern'
19141916
// specifier or a linkage-specification and neither an initializer [...],
1915-
// it declares a static data member in a class declaration [...].
1917+
// it declares a non-inline static data member in a class declaration [...],
1918+
// it declares a static data member outside a class definition and the variable
1919+
// was defined within the class with the constexpr specifier [...],
19161920
// C++1y [temp.expl.spec]p15:
19171921
// An explicit specialization of a static data member or an explicit
19181922
// specialization of a static data member template is a definition if the
@@ -1922,6 +1926,8 @@ VarDecl::isThisDeclarationADefinition(ASTContext &C) const {
19221926
// a static data member template outside the containing class?
19231927
if (isStaticDataMember()) {
19241928
if (isOutOfLine() &&
1929+
!(getCanonicalDecl()->isInline() &&
1930+
getCanonicalDecl()->isConstexpr()) &&
19251931
(hasInit() ||
19261932
// If the first declaration is out-of-line, this may be an
19271933
// instantiation of an out-of-line partial specialization of a variable
@@ -1932,6 +1938,8 @@ VarDecl::isThisDeclarationADefinition(ASTContext &C) const {
19321938
TSK_ExplicitSpecialization) ||
19331939
isa<VarTemplatePartialSpecializationDecl>(this)))
19341940
return Definition;
1941+
else if (!isOutOfLine() && isInline())
1942+
return Definition;
19351943
else
19361944
return DeclarationOnly;
19371945
}
@@ -2072,18 +2080,6 @@ bool VarDecl::isOutOfLine() const {
20722080
return false;
20732081
}
20742082

2075-
VarDecl *VarDecl::getOutOfLineDefinition() {
2076-
if (!isStaticDataMember())
2077-
return nullptr;
2078-
2079-
for (auto RD : redecls()) {
2080-
if (RD->getLexicalDeclContext()->isFileContext())
2081-
return RD;
2082-
}
2083-
2084-
return nullptr;
2085-
}
2086-
20872083
void VarDecl::setInit(Expr *I) {
20882084
if (auto *Eval = Init.dyn_cast<EvaluatedStmt *>()) {
20892085
Eval->~EvaluatedStmt();

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3746,6 +3746,12 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
37463746
case Decl::Namespace:
37473747
EmitNamespace(cast<NamespaceDecl>(D));
37483748
break;
3749+
case Decl::CXXRecord:
3750+
// Emit any static data members, they may be definitions.
3751+
for (auto *I : cast<CXXRecordDecl>(D)->decls())
3752+
if (isa<VarDecl>(I) || isa<CXXRecordDecl>(I))
3753+
EmitTopLevelDecl(I);
3754+
break;
37493755
// No code generation needed.
37503756
case Decl::UsingShadow:
37513757
case Decl::ClassTemplate:

clang/lib/CodeGen/ItaniumCXXABI.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1905,10 +1905,18 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
19051905
bool shouldPerformInit) {
19061906
CGBuilderTy &Builder = CGF.Builder;
19071907

1908-
// We only need to use thread-safe statics for local non-TLS variables;
1909-
// global initialization is always single-threaded.
1908+
// Inline variables that weren't instantiated from variable templates have
1909+
// partially-ordered initialization within their translation unit.
1910+
bool NonTemplateInline =
1911+
D.isInline() &&
1912+
!isTemplateInstantiation(D.getTemplateSpecializationKind());
1913+
1914+
// We only need to use thread-safe statics for local non-TLS variables and
1915+
// inline variables; other global initialization is always single-threaded
1916+
// or (through lazy dynamic loading in multiple threads) unsequenced.
19101917
bool threadsafe = getContext().getLangOpts().ThreadsafeStatics &&
1911-
D.isLocalVarDecl() && !D.getTLSKind();
1918+
(D.isLocalVarDecl() || NonTemplateInline) &&
1919+
!D.getTLSKind();
19121920

19131921
// If we have a global variable with internal linkage and thread-safe statics
19141922
// are disabled, we can just let the guard variable be of type i8.
@@ -1962,7 +1970,11 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
19621970
if (!D.isLocalVarDecl() && C &&
19631971
CGM.getTarget().getTriple().isOSBinFormatELF()) {
19641972
guard->setComdat(C);
1965-
CGF.CurFn->setComdat(C);
1973+
// An inline variable's guard function is run from the per-TU
1974+
// initialization function, not via a dedicated global ctor function, so
1975+
// we can't put it in a comdat.
1976+
if (!NonTemplateInline)
1977+
CGF.CurFn->setComdat(C);
19661978
} else if (CGM.supportsCOMDAT() && guard->isWeakForLinker()) {
19671979
guard->setComdat(CGM.getModule().getOrInsertComdat(guard->getName()));
19681980
}

clang/lib/Sema/Sema.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,8 @@ static bool ShouldRemoveFromUnused(Sema *SemaRef, const DeclaratorDecl *D) {
469469
return false;
470470
}
471471

472-
/// Obtains a sorted list of functions that are undefined but ODR-used.
472+
/// Obtains a sorted list of functions and variables that are undefined but
473+
/// ODR-used.
473474
void Sema::getUndefinedButUsed(
474475
SmallVectorImpl<std::pair<NamedDecl *, SourceLocation> > &Undefined) {
475476
for (const auto &UndefinedUse : UndefinedButUsed) {
@@ -488,9 +489,10 @@ void Sema::getUndefinedButUsed(
488489
!FD->getMostRecentDecl()->isInlined())
489490
continue;
490491
} else {
491-
if (cast<VarDecl>(ND)->hasDefinition() != VarDecl::DeclarationOnly)
492+
auto *VD = cast<VarDecl>(ND);
493+
if (VD->hasDefinition() != VarDecl::DeclarationOnly)
492494
continue;
493-
if (ND->isExternallyVisible())
495+
if (VD->isExternallyVisible() && !VD->getMostRecentDecl()->isInline())
494496
continue;
495497
}
496498

@@ -522,10 +524,15 @@ static void checkUndefinedButUsed(Sema &S) {
522524
if (!ND->isExternallyVisible()) {
523525
S.Diag(ND->getLocation(), diag::warn_undefined_internal)
524526
<< isa<VarDecl>(ND) << ND;
525-
} else {
526-
assert(cast<FunctionDecl>(ND)->getMostRecentDecl()->isInlined() &&
527+
} else if (auto *FD = dyn_cast<FunctionDecl>(ND)) {
528+
assert(FD->getMostRecentDecl()->isInlined() &&
527529
"used object requires definition but isn't inline or internal?");
530+
// FIXME: This is ill-formed; we should reject.
528531
S.Diag(ND->getLocation(), diag::warn_undefined_inline) << ND;
532+
} else {
533+
assert(cast<VarDecl>(ND)->getMostRecentDecl()->isInline() &&
534+
"used var requires definition but isn't inline or internal?");
535+
S.Diag(ND->getLocation(), diag::err_undefined_inline_var) << ND;
529536
}
530537
if (I->second.isValid())
531538
S.Diag(I->second, diag::note_used_here);

0 commit comments

Comments
 (0)