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
This commit is contained in:
Richard Smith 2016-06-25 00:15:56 +00:00
parent b8da3a2bb2
commit 62f19e700d
27 changed files with 371 additions and 70 deletions

View File

@ -881,6 +881,12 @@ protected:
/// variable; see isARCPseudoStrong() for details.
unsigned ARCPseudoStrong : 1;
/// \brief Whether this variable is (C++1z) inline.
unsigned IsInline : 1;
/// \brief Whether this variable has (C++1z) inline explicitly specified.
unsigned IsInlineSpecified : 1;
/// \brief Whether this variable is (C++0x) constexpr.
unsigned IsConstexpr : 1;
@ -1102,9 +1108,6 @@ public:
/// definition of a static data member.
bool isOutOfLine() const override;
/// \brief If this is a static data member, find its out-of-line definition.
VarDecl *getOutOfLineDefinition();
/// isFileVarDecl - Returns true for file scoped variable declaration.
bool isFileVarDecl() const {
Kind K = getKind();
@ -1250,6 +1253,24 @@ public:
NonParmVarDeclBits.ARCPseudoStrong = ps;
}
/// Whether this variable is (C++1z) inline.
bool isInline() const {
return isa<ParmVarDecl>(this) ? false : NonParmVarDeclBits.IsInline;
}
bool isInlineSpecified() const {
return isa<ParmVarDecl>(this) ? false
: NonParmVarDeclBits.IsInlineSpecified;
}
void setInlineSpecified() {
assert(!isa<ParmVarDecl>(this));
NonParmVarDeclBits.IsInline = true;
NonParmVarDeclBits.IsInlineSpecified = true;
}
void setImplicitlyInline() {
assert(!isa<ParmVarDecl>(this));
NonParmVarDeclBits.IsInline = true;
}
/// Whether this variable is (C++11) constexpr.
bool isConstexpr() const {
return isa<ParmVarDecl>(this) ? false : NonParmVarDeclBits.IsConstexpr;

View File

@ -339,12 +339,16 @@ def err_language_linkage_spec_not_ascii : Error<
def warn_use_out_of_scope_declaration : Warning<
"use of out-of-scope declaration of %0">;
def err_inline_non_function : Error<
"'inline' can only appear on functions">;
"'inline' can only appear on functions%select{| and non-local variables}0">;
def err_noreturn_non_function : Error<
"'_Noreturn' can only appear on functions">;
def warn_qual_return_type : Warning<
"'%0' type qualifier%s1 on return type %plural{1:has|:have}1 no effect">,
InGroup<IgnoredQualifiers>, DefaultIgnore;
def warn_deprecated_redundant_constexpr_static_def : Warning<
"out-of-line definition of constexpr static data member is redundant "
"in C++17 and is deprecated">,
InGroup<Deprecated>, DefaultIgnore;
def warn_decl_shadow :
Warning<"declaration shadows a %select{"
@ -1086,6 +1090,12 @@ def warn_cxx14_compat_static_assert_no_message : Warning<
"static_assert with no message is incompatible with C++ standards before C++1z">,
DefaultIgnore, InGroup<CXXPre1zCompat>;
def ext_inline_variable : ExtWarn<
"inline variables are a C++1z extension">, InGroup<CXX1z>;
def warn_cxx14_compat_inline_variable : Warning<
"inline variables are incompatible with C++ standards before C++1z">,
DefaultIgnore, InGroup<CXXPre1zCompat>;
def warn_inline_namespace_reopened_noninline : Warning<
"inline namespace cannot be reopened as a non-inline namespace">;
def err_inline_namespace_mismatch : Error<
@ -4305,6 +4315,7 @@ def warn_undefined_internal : Warning<
InGroup<DiagGroup<"undefined-internal">>;
def warn_undefined_inline : Warning<"inline function %q0 is not defined">,
InGroup<DiagGroup<"undefined-inline">>;
def err_undefined_inline_var : Error<"inline variable %q0 is not defined">;
def note_used_here : Note<"used here">;
def err_internal_linkage_redeclaration : Error<

View File

@ -73,10 +73,11 @@ inline void MarkVarDeclODRUsed(VarDecl *Var,
// Keep track of used but undefined variables.
// FIXME: We shouldn't suppress this warning for static data members.
if (Var->hasDefinition(SemaRef.Context) == VarDecl::DeclarationOnly &&
!Var->isExternallyVisible() &&
!(Var->isStaticDataMember() && Var->hasInit())) {
SourceLocation &old = SemaRef.UndefinedButUsed[Var->getCanonicalDecl()];
if (old.isInvalid()) old = Loc;
(!Var->isExternallyVisible() || Var->isInline()) &&
!(Var->isStaticDataMember() && Var->hasInit())) {
SourceLocation &old = SemaRef.UndefinedButUsed[Var->getCanonicalDecl()];
if (old.isInvalid())
old = Loc;
}
QualType CaptureType, DeclRefType;
SemaRef.tryCaptureVariable(Var, Loc, Sema::TryCapture_Implicit,

View File

@ -8486,15 +8486,19 @@ static GVALinkage basicGVALinkageForVariable(const ASTContext &Context,
if (Context.isMSStaticDataMemberInlineDefinition(VD))
return GVA_DiscardableODR;
GVALinkage StrongLinkage = GVA_StrongExternal;
if (VD->isInline())
StrongLinkage = GVA_DiscardableODR;
switch (VD->getTemplateSpecializationKind()) {
case TSK_Undeclared:
return GVA_StrongExternal;
return StrongLinkage;
case TSK_ExplicitSpecialization:
return Context.getTargetInfo().getCXXABI().isMicrosoft() &&
VD->isStaticDataMember()
? GVA_StrongODR
: GVA_StrongExternal;
: StrongLinkage;
case TSK_ExplicitInstantiationDefinition:
return GVA_StrongODR;

View File

@ -1196,6 +1196,10 @@ void ASTDumper::VisitVarDecl(const VarDecl *D) {
OS << " __module_private__";
if (D->isNRVOVariable())
OS << " nrvo";
if (D->isInline())
OS << " inline";
if (D->isConstexpr())
OS << " constexpr";
if (D->hasInit()) {
switch (D->getInitStyle()) {
case VarDecl::CInit: OS << " cinit"; break;

View File

@ -592,12 +592,14 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D,
if (Var->getStorageClass() == SC_Static)
return LinkageInfo::internal();
// - a non-volatile object or reference that is explicitly declared const
// or constexpr and neither explicitly declared extern nor previously
// declared to have external linkage; or (there is no equivalent in C99)
// - a non-inline, non-volatile object or reference that is explicitly
// declared const or constexpr and neither explicitly declared extern
// nor previously declared to have external linkage; or (there is no
// equivalent in C99)
if (Context.getLangOpts().CPlusPlus &&
Var->getType().isConstQualified() &&
!Var->getType().isVolatileQualified()) {
!Var->getType().isVolatileQualified() &&
!Var->isInline()) {
const VarDecl *PrevVar = Var->getPreviousDecl();
if (PrevVar)
return getLVForDecl(PrevVar, computation);
@ -1912,7 +1914,9 @@ VarDecl::isThisDeclarationADefinition(ASTContext &C) const {
// C++ [basic.def]p2:
// A declaration is a definition unless [...] it contains the 'extern'
// specifier or a linkage-specification and neither an initializer [...],
// it declares a static data member in a class declaration [...].
// it declares a non-inline static data member in a class declaration [...],
// it declares a static data member outside a class definition and the variable
// was defined within the class with the constexpr specifier [...],
// C++1y [temp.expl.spec]p15:
// An explicit specialization of a static data member or an explicit
// specialization of a static data member template is a definition if the
@ -1922,6 +1926,8 @@ VarDecl::isThisDeclarationADefinition(ASTContext &C) const {
// a static data member template outside the containing class?
if (isStaticDataMember()) {
if (isOutOfLine() &&
!(getCanonicalDecl()->isInline() &&
getCanonicalDecl()->isConstexpr()) &&
(hasInit() ||
// If the first declaration is out-of-line, this may be an
// instantiation of an out-of-line partial specialization of a variable
@ -1932,6 +1938,8 @@ VarDecl::isThisDeclarationADefinition(ASTContext &C) const {
TSK_ExplicitSpecialization) ||
isa<VarTemplatePartialSpecializationDecl>(this)))
return Definition;
else if (!isOutOfLine() && isInline())
return Definition;
else
return DeclarationOnly;
}
@ -2072,18 +2080,6 @@ bool VarDecl::isOutOfLine() const {
return false;
}
VarDecl *VarDecl::getOutOfLineDefinition() {
if (!isStaticDataMember())
return nullptr;
for (auto RD : redecls()) {
if (RD->getLexicalDeclContext()->isFileContext())
return RD;
}
return nullptr;
}
void VarDecl::setInit(Expr *I) {
if (auto *Eval = Init.dyn_cast<EvaluatedStmt *>()) {
Eval->~EvaluatedStmt();

View File

@ -3746,6 +3746,12 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
case Decl::Namespace:
EmitNamespace(cast<NamespaceDecl>(D));
break;
case Decl::CXXRecord:
// Emit any static data members, they may be definitions.
for (auto *I : cast<CXXRecordDecl>(D)->decls())
if (isa<VarDecl>(I) || isa<CXXRecordDecl>(I))
EmitTopLevelDecl(I);
break;
// No code generation needed.
case Decl::UsingShadow:
case Decl::ClassTemplate:

View File

@ -1905,10 +1905,18 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
bool shouldPerformInit) {
CGBuilderTy &Builder = CGF.Builder;
// We only need to use thread-safe statics for local non-TLS variables;
// global initialization is always single-threaded.
// Inline variables that weren't instantiated from variable templates have
// partially-ordered initialization within their translation unit.
bool NonTemplateInline =
D.isInline() &&
!isTemplateInstantiation(D.getTemplateSpecializationKind());
// We only need to use thread-safe statics for local non-TLS variables and
// inline variables; other global initialization is always single-threaded
// or (through lazy dynamic loading in multiple threads) unsequenced.
bool threadsafe = getContext().getLangOpts().ThreadsafeStatics &&
D.isLocalVarDecl() && !D.getTLSKind();
(D.isLocalVarDecl() || NonTemplateInline) &&
!D.getTLSKind();
// If we have a global variable with internal linkage and thread-safe statics
// are disabled, we can just let the guard variable be of type i8.
@ -1962,7 +1970,11 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
if (!D.isLocalVarDecl() && C &&
CGM.getTarget().getTriple().isOSBinFormatELF()) {
guard->setComdat(C);
CGF.CurFn->setComdat(C);
// An inline variable's guard function is run from the per-TU
// initialization function, not via a dedicated global ctor function, so
// we can't put it in a comdat.
if (!NonTemplateInline)
CGF.CurFn->setComdat(C);
} else if (CGM.supportsCOMDAT() && guard->isWeakForLinker()) {
guard->setComdat(CGM.getModule().getOrInsertComdat(guard->getName()));
}

View File

@ -469,7 +469,8 @@ static bool ShouldRemoveFromUnused(Sema *SemaRef, const DeclaratorDecl *D) {
return false;
}
/// Obtains a sorted list of functions that are undefined but ODR-used.
/// Obtains a sorted list of functions and variables that are undefined but
/// ODR-used.
void Sema::getUndefinedButUsed(
SmallVectorImpl<std::pair<NamedDecl *, SourceLocation> > &Undefined) {
for (const auto &UndefinedUse : UndefinedButUsed) {
@ -488,9 +489,10 @@ void Sema::getUndefinedButUsed(
!FD->getMostRecentDecl()->isInlined())
continue;
} else {
if (cast<VarDecl>(ND)->hasDefinition() != VarDecl::DeclarationOnly)
auto *VD = cast<VarDecl>(ND);
if (VD->hasDefinition() != VarDecl::DeclarationOnly)
continue;
if (ND->isExternallyVisible())
if (VD->isExternallyVisible() && !VD->getMostRecentDecl()->isInline())
continue;
}
@ -522,10 +524,15 @@ static void checkUndefinedButUsed(Sema &S) {
if (!ND->isExternallyVisible()) {
S.Diag(ND->getLocation(), diag::warn_undefined_internal)
<< isa<VarDecl>(ND) << ND;
} else {
assert(cast<FunctionDecl>(ND)->getMostRecentDecl()->isInlined() &&
} else if (auto *FD = dyn_cast<FunctionDecl>(ND)) {
assert(FD->getMostRecentDecl()->isInlined() &&
"used object requires definition but isn't inline or internal?");
// FIXME: This is ill-formed; we should reject.
S.Diag(ND->getLocation(), diag::warn_undefined_inline) << ND;
} else {
assert(cast<VarDecl>(ND)->getMostRecentDecl()->isInline() &&
"used var requires definition but isn't inline or internal?");
S.Diag(ND->getLocation(), diag::err_undefined_inline_var) << ND;
}
if (I->second.isValid())
S.Diag(I->second, diag::note_used_here);

View File

@ -1457,6 +1457,9 @@ bool Sema::ShouldWarnIfUnusedFileScopedDecl(const DeclaratorDecl *D) const {
if (VD->isStaticDataMember() &&
VD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
return false;
if (VD->isInline() && !isMainFileLoc(*this, VD->getLocation()))
return false;
} else {
return false;
}
@ -3621,6 +3624,23 @@ void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous) {
return New->setInvalidDecl();
}
if (New->isInline() && !Old->getMostRecentDecl()->isInline()) {
if (VarDecl *Def = Old->getDefinition()) {
// C++1z [dcl.fcn.spec]p4:
// If the definition of a variable appears in a translation unit before
// its first declaration as inline, the program is ill-formed.
Diag(New->getLocation(), diag::err_inline_decl_follows_def) << New;
Diag(Def->getLocation(), diag::note_previous_definition);
}
}
// If this redeclaration makes the function inline, we may need to add it to
// UndefinedButUsed.
if (!Old->isInline() && New->isInline() && Old->isUsed(false) &&
!Old->getDefinition() && !New->isThisDeclarationADefinition())
UndefinedButUsed.insert(std::make_pair(Old->getCanonicalDecl(),
SourceLocation()));
if (New->getTLSKind() != Old->getTLSKind()) {
if (!Old->getTLSKind()) {
Diag(New->getLocation(), diag::err_thread_non_thread) << New->getDeclName();
@ -3652,6 +3672,12 @@ void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous) {
New->getDeclContext()->isDependentContext())) {
// The previous definition is hidden, and multiple definitions are
// permitted (in separate TUs). Form another definition of it.
} else if (Old->isStaticDataMember() &&
Old->getCanonicalDecl()->isInline() &&
Old->getCanonicalDecl()->isConstexpr()) {
// This definition won't be a definition any more once it's been merged.
Diag(New->getLocation(),
diag::warn_deprecated_redundant_constexpr_static_def);
} else {
Diag(New->getLocation(), diag::err_redefinition) << New;
Diag(Def->getLocation(), diag::note_previous_definition);
@ -3680,6 +3706,9 @@ void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous) {
New->setAccess(Old->getAccess());
if (NewTemplate)
NewTemplate->setAccess(New->getAccess());
if (Old->isInline())
New->setImplicitlyInline();
}
/// ParsedFreeStandingDeclSpec - This method is invoked when a declspec with
@ -3836,6 +3865,10 @@ Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS, DeclSpec &DS,
<< DS.getSourceRange();
}
if (DS.isInlineSpecified())
Diag(DS.getInlineSpecLoc(), diag::err_inline_non_function)
<< getLangOpts().CPlusPlus1z;
if (DS.isConstexprSpecified()) {
// C++0x [dcl.constexpr]p1: constexpr can only be applied to declarations
// and definitions of functions and variables.
@ -5261,11 +5294,7 @@ NamedDecl *Sema::findLocallyScopedExternCDecl(DeclarationName Name) {
/// does not identify a function.
void Sema::DiagnoseFunctionSpecifiers(const DeclSpec &DS) {
// FIXME: We should probably indicate the identifier in question to avoid
// confusion for constructs like "inline int a(), b;"
if (DS.isInlineSpecified())
Diag(DS.getInlineSpecLoc(),
diag::err_inline_non_function);
// confusion for constructs like "virtual int a(), b;"
if (DS.isVirtualSpecified())
Diag(DS.getVirtualSpecLoc(),
diag::err_virtual_non_function);
@ -5294,6 +5323,9 @@ Sema::ActOnTypedefDeclarator(Scope* S, Declarator& D, DeclContext* DC,
DiagnoseFunctionSpecifiers(D.getDeclSpec());
if (D.getDeclSpec().isInlineSpecified())
Diag(D.getDeclSpec().getInlineSpecLoc(), diag::err_inline_non_function)
<< getLangOpts().CPlusPlus1z;
if (D.getDeclSpec().isConstexprSpecified())
Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_invalid_constexpr)
<< 1;
@ -6098,8 +6130,14 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC,
NewVD->setTemplateParameterListsInfo(
Context, TemplateParamLists.drop_back(VDTemplateParamLists));
if (D.getDeclSpec().isConstexprSpecified())
if (D.getDeclSpec().isConstexprSpecified()) {
NewVD->setConstexpr(true);
// C++1z [dcl.spec.constexpr]p1:
// A static data member declared with the constexpr specifier is
// implicitly an inline variable.
if (NewVD->isStaticDataMember() && getLangOpts().CPlusPlus1z)
NewVD->setImplicitlyInline();
}
if (D.getDeclSpec().isConceptSpecified()) {
if (VarTemplateDecl *VTD = NewVD->getDescribedVarTemplate())
@ -6142,6 +6180,20 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC,
}
}
if (D.getDeclSpec().isInlineSpecified()) {
if (CurContext->isFunctionOrMethod()) {
// 'inline' is not allowed on block scope variable declaration.
Diag(D.getDeclSpec().getInlineSpecLoc(),
diag::err_inline_declaration_block_scope) << Name
<< FixItHint::CreateRemoval(D.getDeclSpec().getInlineSpecLoc());
} else {
Diag(D.getDeclSpec().getInlineSpecLoc(),
getLangOpts().CPlusPlus1z ? diag::warn_cxx14_compat_inline_variable
: diag::ext_inline_variable);
NewVD->setInlineSpecified();
}
}
// Set the lexical context. If the declarator has a C++ scope specifier, the
// lexical context will be different from the semantic context.
NewVD->setLexicalDeclContext(CurContext);
@ -9759,7 +9811,7 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init,
diag::ext_aggregate_init_not_constant)
<< Culprit->getSourceRange();
}
} else if (VDecl->isStaticDataMember() &&
} else if (VDecl->isStaticDataMember() && !VDecl->isInline() &&
VDecl->getLexicalDeclContext()->isRecord()) {
// This is an in-class initialization for a static data member, e.g.,
//
@ -9773,8 +9825,8 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init,
// const enumeration type, see 9.4.2.
//
// C++11 [class.static.data]p3:
// If a non-volatile const static data member is of integral or
// enumeration type, its declaration in the class definition can
// If a non-volatile non-inline const static data member is of integral
// or enumeration type, its declaration in the class definition can
// specify a brace-or-equal-initializer in which every initalizer-clause
// that is an assignment-expression is a constant expression. A static
// data member of literal type can be declared in the class definition
@ -9957,14 +10009,21 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl,
// the definition of a variable [...] or the declaration of a static data
// member.
if (Var->isConstexpr() && !Var->isThisDeclarationADefinition()) {
if (Var->isStaticDataMember())
Diag(Var->getLocation(),
diag::err_constexpr_static_mem_var_requires_init)
<< Var->getDeclName();
else
if (Var->isStaticDataMember()) {
// C++1z removes the relevant rule; the in-class declaration is always
// a definition there.
if (!getLangOpts().CPlusPlus1z) {
Diag(Var->getLocation(),
diag::err_constexpr_static_mem_var_requires_init)
<< Var->getDeclName();
Var->setInvalidDecl();
return;
}
} else {
Diag(Var->getLocation(), diag::err_invalid_constexpr_var_decl);
Var->setInvalidDecl();
return;
Var->setInvalidDecl();
return;
}
}
// C++ Concepts TS [dcl.spec.concept]p1: [...] A variable template
@ -10754,6 +10813,9 @@ Decl *Sema::ActOnParamDeclarator(Scope *S, Declarator &D) {
if (DeclSpec::TSCS TSCS = DS.getThreadStorageClassSpec())
Diag(DS.getThreadStorageClassSpecLoc(), diag::err_invalid_thread)
<< DeclSpec::getSpecifierName(TSCS);
if (DS.isInlineSpecified())
Diag(DS.getInlineSpecLoc(), diag::err_inline_non_function)
<< getLangOpts().CPlusPlus1z;
if (DS.isConstexprSpecified())
Diag(DS.getConstexprSpecLoc(), diag::err_invalid_constexpr)
<< 0;
@ -13327,6 +13389,9 @@ FieldDecl *Sema::HandleField(Scope *S, RecordDecl *Record,
DiagnoseFunctionSpecifiers(D.getDeclSpec());
if (D.getDeclSpec().isInlineSpecified())
Diag(D.getDeclSpec().getInlineSpecLoc(), diag::err_inline_non_function)
<< getLangOpts().CPlusPlus1z;
if (DeclSpec::TSCS TSCS = D.getDeclSpec().getThreadStorageClassSpec())
Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(),
diag::err_invalid_thread)

View File

@ -13995,6 +13995,9 @@ MSPropertyDecl *Sema::HandleMSProperty(Scope *S, RecordDecl *Record,
DiagnoseFunctionSpecifiers(D.getDeclSpec());
if (D.getDeclSpec().isInlineSpecified())
Diag(D.getDeclSpec().getInlineSpecLoc(), diag::err_inline_non_function)
<< getLangOpts().CPlusPlus1z;
if (DeclSpec::TSCS TSCS = D.getDeclSpec().getThreadStorageClassSpec())
Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(),
diag::err_invalid_thread)

View File

@ -4612,6 +4612,9 @@ Decl *Sema::ActOnObjCExceptionDecl(Scope *S, Declarator &D) {
Diag(DS.getStorageClassSpecLoc(), diag::err_storage_spec_on_catch_parm)
<< DeclSpec::getSpecifierName(SCS);
}
if (DS.isInlineSpecified())
Diag(DS.getInlineSpecLoc(), diag::err_inline_non_function)
<< getLangOpts().CPlusPlus1z;
if (DeclSpec::TSCS TSCS = D.getDeclSpec().getThreadStorageClassSpec())
Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(),
diag::err_invalid_thread)

View File

@ -2520,8 +2520,7 @@ Sema::InstantiateClassMembers(SourceLocation PointOfInstantiation,
// specialization and is only an explicit instantiation definition
// of members whose definition is visible at the point of
// instantiation.
if (!Var->getInstantiatedFromStaticDataMember()
->getOutOfLineDefinition())
if (!Var->getInstantiatedFromStaticDataMember()->getDefinition())
continue;
Var->setTemplateSpecializationKind(TSK, PointOfInstantiation);

View File

@ -3881,11 +3881,12 @@ void Sema::BuildVariableInstantiation(
Context.setManglingNumber(NewVar, Context.getManglingNumber(OldVar));
Context.setStaticLocalNumber(NewVar, Context.getStaticLocalNumber(OldVar));
// Delay instantiation of the initializer for variable templates until a
// definition of the variable is needed. We need it right away if the type
// contains 'auto'.
// Delay instantiation of the initializer for variable templates or inline
// static data members until a definition of the variable is needed. We need
// it right away if the type contains 'auto'.
if ((!isa<VarTemplateSpecializationDecl>(NewVar) &&
!InstantiatingVarTemplate) ||
!InstantiatingVarTemplate &&
!(OldVar->isInline() && OldVar->isThisDeclarationADefinition())) ||
NewVar->getType()->isUndeducedType())
InstantiateVariableInitializer(NewVar, OldVar, TemplateArgs);
@ -3901,6 +3902,13 @@ void Sema::BuildVariableInstantiation(
void Sema::InstantiateVariableInitializer(
VarDecl *Var, VarDecl *OldVar,
const MultiLevelTemplateArgumentList &TemplateArgs) {
// We propagate the 'inline' flag with the initializer, because it
// would otherwise imply that the variable is a definition for a
// non-static data member.
if (OldVar->isInlineSpecified())
Var->setInlineSpecified();
else if (OldVar->isInline())
Var->setImplicitlyInline();
if (Var->getAnyInitializer())
// We already have an initializer in the class.
@ -4083,7 +4091,7 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation,
assert(PatternDecl && "data member was not instantiated from a template?");
assert(PatternDecl->isStaticDataMember() && "not a static data member?");
Def = PatternDecl->getOutOfLineDefinition();
Def = PatternDecl->getDefinition();
}
// FIXME: Check that the definition is visible before trying to instantiate
@ -4181,11 +4189,16 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation,
LocalInstantiationScope Local(*this);
VarDecl *OldVar = Var;
if (!VarSpec)
if (Def->isStaticDataMember() && !Def->isOutOfLine()) {
// We're instantiating an inline static data member whose definition was
// provided inside the class.
// FIXME: Update record?
InstantiateVariableInitializer(Var, Def, TemplateArgs);
} else if (!VarSpec) {
Var = cast_or_null<VarDecl>(SubstDecl(Def, Var->getDeclContext(),
TemplateArgs));
else if (Var->isStaticDataMember() &&
Var->getLexicalDeclContext()->isRecord()) {
} else if (Var->isStaticDataMember() &&
Var->getLexicalDeclContext()->isRecord()) {
// We need to instantiate the definition of a static data member template,
// and all we have is the in-class declaration of it. Instantiate a separate
// declaration of the definition.

View File

@ -1217,6 +1217,8 @@ ASTDeclReader::RedeclarableResult ASTDeclReader::VisitVarDeclImpl(VarDecl *VD) {
VD->NonParmVarDeclBits.NRVOVariable = Record[Idx++];
VD->NonParmVarDeclBits.CXXForRangeDecl = Record[Idx++];
VD->NonParmVarDeclBits.ARCPseudoStrong = Record[Idx++];
VD->NonParmVarDeclBits.IsInline = Record[Idx++];
VD->NonParmVarDeclBits.IsInlineSpecified = Record[Idx++];
VD->NonParmVarDeclBits.IsConstexpr = Record[Idx++];
VD->NonParmVarDeclBits.IsInitCapture = Record[Idx++];
VD->NonParmVarDeclBits.PreviousDeclInSameBlockScope = Record[Idx++];

View File

@ -895,6 +895,8 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) {
Record.push_back(D->isNRVOVariable());
Record.push_back(D->isCXXForRangeDecl());
Record.push_back(D->isARCPseudoStrong());
Record.push_back(D->isInline());
Record.push_back(D->isInlineSpecified());
Record.push_back(D->isConstexpr());
Record.push_back(D->isInitCapture());
Record.push_back(D->isPreviousDeclInSameBlockScope());
@ -941,6 +943,7 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) {
D->getInit() == nullptr &&
!isa<ParmVarDecl>(D) &&
!isa<VarTemplateSpecializationDecl>(D) &&
!D->isInline() &&
!D->isConstexpr() &&
!D->isInitCapture() &&
!D->isPreviousDeclInSameBlockScope() &&
@ -1916,6 +1919,8 @@ void ASTWriter::WriteDeclAbbrevs() {
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isNRVOVariable
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isCXXForRangeDecl
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isARCPseudoStrong
Abv->Add(BitCodeAbbrevOp(0)); // isInline
Abv->Add(BitCodeAbbrevOp(0)); // isInlineSpecified
Abv->Add(BitCodeAbbrevOp(0)); // isConstexpr
Abv->Add(BitCodeAbbrevOp(0)); // isInitCapture
Abv->Add(BitCodeAbbrevOp(0)); // isPrevDeclInSameScope

View File

@ -0,0 +1,8 @@
// RUN: %clang_cc1 -std=c++1z -verify %s -Wdeprecated
namespace {
struct A {
static constexpr int n = 0;
};
const int A::n; // expected-warning {{deprecated}}
}

View File

@ -0,0 +1,6 @@
// RUN: %clang_cc1 -std=c++1z -verify %s
inline int f(); // expected-warning {{inline function 'f' is not defined}}
extern inline int n; // expected-error {{inline variable 'n' is not defined}}
int use = f() + n; // expected-note 2{{used here}}

View File

@ -0,0 +1,7 @@
// RUN: %clang_cc1 -std=c++1z -verify %s
struct X {
static struct A a;
static inline struct B b; // expected-error {{incomplete type}} expected-note {{forward decl}}
static inline struct C c = {}; // expected-error {{incomplete type}} expected-note {{forward decl}}
};

View File

@ -1,4 +1,5 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z %s
struct NonLit { // expected-note 3{{no constexpr constructors}}
NonLit();
@ -6,7 +7,7 @@ struct NonLit { // expected-note 3{{no constexpr constructors}}
struct S {
static constexpr int a = 0;
static constexpr int b; // expected-error {{declaration of constexpr static data member 'b' requires an initializer}}
static constexpr int b; // expected-error {{initializ}} expected-note 0-1{{previous}}
static constexpr int c = 0;
static const int d;
@ -16,19 +17,27 @@ struct S {
static const double f = 0.0; // expected-error {{requires 'constexpr' specifier}} expected-note {{add 'constexpr'}}
static char *const g = 0; // expected-error {{requires 'constexpr' specifier}}
static const NonLit h = NonLit(); // expected-error {{must be initialized out of line}}
static inline int i; // expected-note {{previous}} expected-warning 0-1{{extension}}
static inline int j; // expected-note {{previous}} expected-warning 0-1{{extension}}
static constexpr int k = 0;
};
constexpr int S::a;
constexpr int S::b = 0;
constexpr int S::b = 0; // expected-error 0-1{{redefinition}}
const int S::c;
constexpr int S::d = 0;
constexpr int S::d2;
int S::i; // expected-error {{redefinition}}
int S::j; // expected-error {{redefinition}}
const int S::k; // ok (deprecated)
template<typename T>
struct U {
static constexpr int a = 0;
static constexpr int b; // expected-error {{declaration of constexpr static data member 'b' requires an initializer}}
static constexpr int b; // expected-error {{initializ}}
static constexpr NonLit h = NonLit(); // expected-error {{cannot have non-literal type 'const NonLit'}}
static constexpr T c = T(); // expected-error {{cannot have non-literal type}}
static const T d;

View File

@ -1,4 +1,6 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++14 %s
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z %s
struct notlit { // expected-note {{not literal because}}
notlit() {}
@ -26,7 +28,12 @@ void f2(constexpr int i) {} // expected-error {{function parameter cannot be con
// non-static member
struct s2 {
constexpr int mi1; // expected-error {{non-static data member cannot be constexpr; did you intend to make it const?}}
static constexpr int mi2; // expected-error {{requires an initializer}}
static constexpr int mi2;
#if __cplusplus <= 201402L
// expected-error@-2 {{requires an initializer}}
#else
// expected-error@-4 {{default initialization of an object of const}}
#endif
mutable constexpr int mi3 = 3; // expected-error-re {{non-static data member cannot be constexpr{{$}}}} expected-error {{'mutable' and 'const' cannot be mixed}}
};
// typedef
@ -71,7 +78,7 @@ struct ConstexprDtor {
template <typename T> constexpr T ft(T t) { return t; }
template <typename T> T gt(T t) { return t; }
struct S {
template<typename T> constexpr T f(); // expected-warning {{C++14}}
template<typename T> constexpr T f(); // expected-warning 0-1{{C++14}} expected-note 0-1{{candidate}}
template <typename T>
T g() const; // expected-note-re {{candidate template ignored: could not match 'T (){{( __attribute__\(\(thiscall\)\))?}} const' against 'char (){{( __attribute__\(\(thiscall\)\))?}}'}}
};
@ -82,7 +89,15 @@ template <> char ft(char c) { return c; } // expected-note {{previous}}
template <> constexpr char ft(char nl); // expected-error {{constexpr declaration of 'ft<char>' follows non-constexpr declaration}}
template <> constexpr int gt(int nl) { return nl; }
template <> notlit S::f() const { return notlit(); }
template <> constexpr int S::g() { return 0; } // expected-note {{previous}} expected-warning {{C++14}}
#if __cplusplus >= 201402L
// expected-error@-2 {{no function template matches}}
#endif
template <> constexpr int S::g() { return 0; } // expected-note {{previous}}
#if __cplusplus < 201402L
// expected-warning@-2 {{C++14}}
#else
// expected-error@-4 {{does not match any declaration in 'S'}}
#endif
template <> int S::g() const; // expected-error {{non-constexpr declaration of 'g<int>' follows constexpr declaration}}
// specializations can drop the 'constexpr' but not the implied 'const'.
template <> char S::g() { return 0; } // expected-error {{no function template matches}}
@ -123,3 +138,11 @@ int next(constexpr int x) { // expected-error {{function parameter cannot be con
}
extern constexpr int memsz; // expected-error {{constexpr variable declaration must be a definition}}
namespace {
struct A {
static constexpr int n = 0;
};
// FIXME: We should diagnose this prior to C++17.
const int &r = A::n;
}

View File

@ -0,0 +1,8 @@
// RUN: %clang_cc1 -std=c++1z -verify %s
inline int f(); // ok
inline int n; // ok
inline typedef int t; // expected-error {{'inline' can only appear on functions and non-local variables}}
inline struct S {}; // expected-error {{'inline' can only appear on functions and non-local variables}}
inline struct T {} s; // ok

View File

@ -0,0 +1,15 @@
// RUN: %clang_cc1 -std=c++1z -verify %s
void x() {
inline int f(int); // expected-error {{inline declaration of 'f' not allowed in block scope}}
inline int n; // expected-error {{inline declaration of 'n' not allowed in block scope}}
static inline int m; // expected-error {{inline declaration of 'm' not allowed in block scope}}
}
inline void g();
struct X {
inline void f();
// FIXME: This is ill-formed per [dcl.inline]p5.
inline void g();
inline void h() {}
};

View File

@ -54,6 +54,16 @@ namespace ScopedEnum {
int test2 = g<int>(); // expected-note {{here}}
}
// - static data members
namespace StaticDataMembers {
template<typename T>
struct A {
static const int n = T::error; // expected-error {{has no members}}
static inline int m = T::error; // expected-warning {{extension}}
};
A<int> ai; // expected-note {{here}}
}
// And it cases the implicit instantiations of the definitions of:
// - unscoped member enumerations

View File

@ -0,0 +1,63 @@
// RUN: %clang_cc1 -std=c++1z %s -emit-llvm -o - -triple x86_64-linux-gnu | FileCheck %s
struct Q {
// CHECK: @_ZN1Q1kE = linkonce_odr constant i32 5, comdat
static constexpr int k = 5;
};
const int &r = Q::k;
int f();
// const does not imply internal linkage.
// CHECK: @external_inline = linkonce_odr constant i32 5, comdat
inline const int external_inline = 5;
const int &use1 = external_inline;
// static still does, though.
// CHECK: @_ZL15internal_inline = internal constant i32 5
static inline const int internal_inline = 5;
const int &use2 = internal_inline;
int a = f();
// CHECK: @b = linkonce_odr global i32 0, comdat
// CHECK: @_ZGV1b = linkonce_odr global i64 0, comdat($b)
inline int b = f();
int c = f();
template<typename T> struct X {
static int a;
static inline int b;
static int c;
};
// CHECK: @_ZN1XIiE1aE = linkonce_odr global i32 10
// CHECK: @_ZN1XIiE1bE = global i32 20
// CHECK-NOT: @_ZN1XIiE1cE
template<> inline int X<int>::a = 10;
int &use3 = X<int>::a;
template<> int X<int>::b = 20;
template<> inline int X<int>::c = 30;
// CHECK-LABEL: define {{.*}}global_var_init
// CHECK: call i32 @_Z1fv
// CHECK-LABEL: define {{.*}}global_var_init
// CHECK-NOT: comdat
// CHECK-SAME: {{$}}
// CHECK: load atomic {{.*}} acquire
// CHECK: br
// CHECK: __cxa_guard_acquire(i64* @_ZGV1b)
// CHECK: br
// CHECK: call i32 @_Z1fv
// CHECK: __cxa_guard_release(i64* @_ZGV1b)
// CHECK-LABEL: define {{.*}}global_var_init
// CHECK: call i32 @_Z1fv
template<typename T> inline int d = f();
int e = d<int>;
// CHECK-LABEL: define {{.*}}global_var_init{{.*}}comdat
// CHECK: _ZGV1dIiE
// CHECK-NOT: __cxa_guard_acquire(i64* @_ZGV1b)
// CHECK: call i32 @_Z1fv
// CHECK-NOT: __cxa_guard_release(i64* @_ZGV1b)

View File

@ -49,7 +49,7 @@ inline int useConst () {
#include "inline.c"
// Check that we don't allow illegal uses of inline
inline int a; // expected-error{{'inline' can only appear on functions}}
inline int a; // expected-warning{{inline variables are a C++1z extension}}
typedef inline int b; // expected-error{{'inline' can only appear on functions}}
int d(inline int a); // expected-error{{'inline' can only appear on functions}}

View File

@ -715,7 +715,7 @@ as the draft C++1z standard evolves.</p>
<tr>
<td>Inline variables</td>
<td><a href="http://wg21.link/p0386r2">P0386R2</a></td>
<td class="none" align="center">No</td>
<td class="svn" align="center">SVN</td>
</tr>
<tr>
<td>Structured bindings</td>