Implement support for C++11 in-class initialization of non-static data members.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@132878 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index c2fee32..9446c0e 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -2610,7 +2610,8 @@
/*IdentifierInfo=*/0,
Context.getTypeDeclType(Record),
TInfo,
- /*BitWidth=*/0, /*Mutable=*/false);
+ /*BitWidth=*/0, /*Mutable=*/false,
+ /*HasInit=*/false);
Anon->setAccess(AS);
if (getLangOptions().CPlusPlus)
FieldCollector->Add(cast<FieldDecl>(Anon));
@@ -2700,7 +2701,8 @@
/*IdentifierInfo=*/0,
Context.getTypeDeclType(Record),
TInfo,
- /*BitWidth=*/0, /*Mutable=*/false);
+ /*BitWidth=*/0, /*Mutable=*/false,
+ /*HasInit=*/false);
Anon->setImplicit();
// Add the anonymous struct object to the current context.
@@ -7512,14 +7514,13 @@
return false;
}
-/// ActOnField - Each field of a struct/union/class is passed into this in order
+/// ActOnField - Each field of a C struct/union is passed into this in order
/// to create a FieldDecl object for it.
-Decl *Sema::ActOnField(Scope *S, Decl *TagD,
- SourceLocation DeclStart,
- Declarator &D, ExprTy *BitfieldWidth) {
+Decl *Sema::ActOnField(Scope *S, Decl *TagD, SourceLocation DeclStart,
+ Declarator &D, ExprTy *BitfieldWidth) {
FieldDecl *Res = HandleField(S, cast_or_null<RecordDecl>(TagD),
DeclStart, D, static_cast<Expr*>(BitfieldWidth),
- AS_public);
+ /*HasInit=*/false, AS_public);
return Res;
}
@@ -7527,7 +7528,7 @@
///
FieldDecl *Sema::HandleField(Scope *S, RecordDecl *Record,
SourceLocation DeclStart,
- Declarator &D, Expr *BitWidth,
+ Declarator &D, Expr *BitWidth, bool HasInit,
AccessSpecifier AS) {
IdentifierInfo *II = D.getIdentifier();
SourceLocation Loc = DeclStart;
@@ -7576,8 +7577,8 @@
= (D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_mutable);
SourceLocation TSSL = D.getSourceRange().getBegin();
FieldDecl *NewFD
- = CheckFieldDecl(II, T, TInfo, Record, Loc, Mutable, BitWidth, TSSL,
- AS, PrevDecl, &D);
+ = CheckFieldDecl(II, T, TInfo, Record, Loc, Mutable, BitWidth, HasInit,
+ TSSL, AS, PrevDecl, &D);
if (NewFD->isInvalidDecl())
Record->setInvalidDecl();
@@ -7606,7 +7607,7 @@
FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T,
TypeSourceInfo *TInfo,
RecordDecl *Record, SourceLocation Loc,
- bool Mutable, Expr *BitWidth,
+ bool Mutable, Expr *BitWidth, bool HasInit,
SourceLocation TSSL,
AccessSpecifier AS, NamedDecl *PrevDecl,
Declarator *D) {
@@ -7686,7 +7687,7 @@
}
FieldDecl *NewFD = FieldDecl::Create(Context, Record, TSSL, Loc, II, T, TInfo,
- BitWidth, Mutable);
+ BitWidth, Mutable, HasInit);
if (InvalidDecl)
NewFD->setInvalidDecl();
@@ -8289,16 +8290,17 @@
// Now that the record is complete, do any delayed exception spec checks
// we were missing.
- if (!DelayedDestructorExceptionSpecChecks.empty()) {
+ while (!DelayedDestructorExceptionSpecChecks.empty()) {
const CXXDestructorDecl *Dtor =
DelayedDestructorExceptionSpecChecks.back().first;
- if (Dtor->getParent() == Record) {
- assert(!Dtor->getParent()->isDependentType() &&
- "Should not ever add destructors of templates into the list.");
- CheckOverridingFunctionExceptionSpec(Dtor,
- DelayedDestructorExceptionSpecChecks.back().second);
- DelayedDestructorExceptionSpecChecks.pop_back();
- }
+ if (Dtor->getParent() != Record)
+ break;
+
+ assert(!Dtor->getParent()->isDependentType() &&
+ "Should not ever add destructors of templates into the list.");
+ CheckOverridingFunctionExceptionSpec(Dtor,
+ DelayedDestructorExceptionSpecChecks.back().second);
+ DelayedDestructorExceptionSpecChecks.pop_back();
}
} else {
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index a4ba5fa..ce99efb 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -113,8 +113,8 @@
void Sema::ImplicitExceptionSpecification::CalledDecl(CXXMethodDecl *Method) {
assert(Context && "ImplicitExceptionSpecification without an ASTContext");
- // If we have an MSAny spec already, don't bother.
- if (!Method || ComputedEST == EST_MSAny)
+ // If we have an MSAny or unknown spec already, don't bother.
+ if (!Method || ComputedEST == EST_MSAny || ComputedEST == EST_Delayed)
return;
const FunctionProtoType *Proto
@@ -123,12 +123,15 @@
ExceptionSpecificationType EST = Proto->getExceptionSpecType();
// If this function can throw any exceptions, make a note of that.
- if (EST == EST_MSAny || EST == EST_None) {
+ if (EST == EST_Delayed || EST == EST_MSAny || EST == EST_None) {
ClearExceptions();
ComputedEST = EST;
return;
}
+ // FIXME: If the call to this decl is using any of its default arguments, we
+ // need to search them for potentially-throwing calls.
+
// If this function has a basic noexcept, it doesn't affect the outcome.
if (EST == EST_BasicNoexcept)
return;
@@ -175,6 +178,35 @@
Exceptions.push_back(*E);
}
+void Sema::ImplicitExceptionSpecification::CalledExpr(Expr *E) {
+ if (!E || ComputedEST == EST_MSAny || ComputedEST == EST_Delayed)
+ return;
+
+ // FIXME:
+ //
+ // C++0x [except.spec]p14:
+ // [An] implicit exception-specification specifies the type-id T if and
+ // only if T is allowed by the exception-specification of a function directly
+ // invoked by f’s implicit definition; f shall allow all exceptions if any
+ // function it directly invokes allows all exceptions, and f shall allow no
+ // exceptions if every function it directly invokes allows no exceptions.
+ //
+ // Note in particular that if an implicit exception-specification is generated
+ // for a function containing a throw-expression, that specification can still
+ // be noexcept(true).
+ //
+ // Note also that 'directly invoked' is not defined in the standard, and there
+ // is no indication that we should only consider potentially-evaluated calls.
+ //
+ // Ultimately we should implement the intent of the standard: the exception
+ // specification should be the set of exceptions which can be thrown by the
+ // implicit definition. For now, we assume that any non-nothrow expression can
+ // throw any exception.
+
+ if (E->CanThrow(*Context))
+ ComputedEST = EST_None;
+}
+
bool
Sema::SetParamDefaultArgument(ParmVarDecl *Param, Expr *Arg,
SourceLocation EqualLoc) {
@@ -1029,13 +1061,15 @@
/// ActOnCXXMemberDeclarator - This is invoked when a C++ class member
/// declarator is parsed. 'AS' is the access specifier, 'BW' specifies the
-/// bitfield width if there is one and 'InitExpr' specifies the initializer if
-/// any.
+/// bitfield width if there is one, 'InitExpr' specifies the initializer if
+/// one has been parsed, and 'HasDeferredInit' is true if an initializer is
+/// present but parsing it has been deferred.
Decl *
Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
MultiTemplateParamsArg TemplateParameterLists,
ExprTy *BW, const VirtSpecifiers &VS,
- ExprTy *InitExpr, bool IsDefinition) {
+ ExprTy *InitExpr, bool HasDeferredInit,
+ bool IsDefinition) {
const DeclSpec &DS = D.getDeclSpec();
DeclarationNameInfo NameInfo = GetNameForDeclarator(D);
DeclarationName Name = NameInfo.getName();
@@ -1050,6 +1084,7 @@
assert(isa<CXXRecordDecl>(CurContext));
assert(!DS.isFriendSpecified());
+ assert(!Init || !HasDeferredInit);
bool isFunc = false;
if (D.isFunctionDeclarator())
@@ -1121,9 +1156,11 @@
// FIXME: Check for template parameters!
// FIXME: Check that the name is an identifier!
Member = HandleField(S, cast<CXXRecordDecl>(CurContext), Loc, D, BitWidth,
- AS);
+ HasDeferredInit, AS);
assert(Member && "HandleField never returns null");
} else {
+ assert(!HasDeferredInit);
+
Member = HandleDeclarator(S, D, move(TemplateParameterLists), IsDefinition);
if (!Member) {
return 0;
@@ -1194,6 +1231,14 @@
if (Init)
AddInitializerToDecl(Member, Init, false,
DS.getTypeSpecType() == DeclSpec::TST_auto);
+ else if (DS.getTypeSpecType() == DeclSpec::TST_auto &&
+ DS.getStorageClassSpec() == DeclSpec::SCS_static) {
+ // C++0x [dcl.spec.auto]p4: 'auto' can only be used in the type of a static
+ // data member if a brace-or-equal-initializer is provided.
+ Diag(Loc, diag::err_auto_var_requires_init)
+ << Name << cast<ValueDecl>(Member)->getType();
+ Member->setInvalidDecl();
+ }
FinalizeDeclaration(Member);
@@ -1202,6 +1247,47 @@
return Member;
}
+/// ActOnCXXInClassMemberInitializer - This is invoked after parsing an
+/// in-class initializer for a non-static C++ class member. Such parsing
+/// is deferred until the class is complete.
+void
+Sema::ActOnCXXInClassMemberInitializer(Decl *D, SourceLocation EqualLoc,
+ Expr *InitExpr) {
+ FieldDecl *FD = cast<FieldDecl>(D);
+
+ if (!InitExpr) {
+ FD->setInvalidDecl();
+ FD->removeInClassInitializer();
+ return;
+ }
+
+ ExprResult Init = InitExpr;
+ if (!FD->getType()->isDependentType() && !InitExpr->isTypeDependent()) {
+ // FIXME: if there is no EqualLoc, this is list-initialization.
+ Init = PerformCopyInitialization(
+ InitializedEntity::InitializeMember(FD), EqualLoc, InitExpr);
+ if (Init.isInvalid()) {
+ FD->setInvalidDecl();
+ return;
+ }
+
+ CheckImplicitConversions(Init.get(), EqualLoc);
+ }
+
+ // C++0x [class.base.init]p7:
+ // The initialization of each base and member constitutes a
+ // full-expression.
+ Init = MaybeCreateExprWithCleanups(Init);
+ if (Init.isInvalid()) {
+ FD->setInvalidDecl();
+ return;
+ }
+
+ InitExpr = Init.release();
+
+ FD->setInClassInitializer(InitExpr);
+}
+
/// \brief Find the direct and/or virtual base specifiers that
/// correspond to the given base type, for use in base initialization
/// within a constructor.
@@ -2073,7 +2159,7 @@
};
}
-static bool CollectFieldInitializer(BaseAndFieldInfo &Info,
+static bool CollectFieldInitializer(Sema &SemaRef, BaseAndFieldInfo &Info,
FieldDecl *Top, FieldDecl *Field) {
// Overwhelmingly common case: we have a direct initializer for this field.
@@ -2082,6 +2168,18 @@
return false;
}
+ // C++0x [class.base.init]p8: if the entity is a non-static data member that
+ // has a brace-or-equal-initializer, the entity is initialized as specified
+ // in [dcl.init].
+ if (Field->hasInClassInitializer()) {
+ Info.AllToInit.push_back(
+ new (SemaRef.Context) CXXCtorInitializer(SemaRef.Context, Field,
+ SourceLocation(),
+ SourceLocation(), 0,
+ SourceLocation()));
+ return false;
+ }
+
if (Info.IIK == IIK_Default && Field->isAnonymousStructOrUnion()) {
const RecordType *FieldClassType = Field->getType()->getAs<RecordType>();
assert(FieldClassType && "anonymous struct/union without record type");
@@ -2104,7 +2202,7 @@
// field in the class is also initialized, so exit immediately.
return false;
} else if ((*FA)->isAnonymousStructOrUnion()) {
- if (CollectFieldInitializer(Info, Top, *FA))
+ if (CollectFieldInitializer(SemaRef, Info, Top, *FA))
return true;
}
}
@@ -2119,7 +2217,7 @@
// necessary.
for (RecordDecl::field_iterator FA = FieldClassDecl->field_begin(),
EA = FieldClassDecl->field_end(); FA != EA; FA++) {
- if (CollectFieldInitializer(Info, Top, *FA))
+ if (CollectFieldInitializer(SemaRef, Info, Top, *FA))
return true;
}
}
@@ -2128,7 +2226,7 @@
// Don't try to build an implicit initializer if there were semantic
// errors in any of the initializers (and therefore we might be
// missing some that the user actually wrote).
- if (Info.AnyErrorsInInits)
+ if (Info.AnyErrorsInInits || Field->isInvalidDecl())
return false;
CXXCtorInitializer *Init = 0;
@@ -2260,7 +2358,7 @@
"Incomplete array type is not valid");
continue;
}
- if (CollectFieldInitializer(Info, *Field, *Field))
+ if (CollectFieldInitializer(*this, Info, *Field, *Field))
HadError = true;
}
@@ -2939,6 +3037,9 @@
for (RecordDecl::field_iterator F = Record->field_begin(),
FEnd = Record->field_end();
F != FEnd; ++F) {
+ if (F->hasInClassInitializer())
+ continue;
+
if (F->getType()->isReferenceType() ||
(F->getType().isConstQualified() && F->getType()->isScalarType())) {
if (!Complained) {
@@ -3066,6 +3167,11 @@
ImplicitExceptionSpecification Spec
= ComputeDefaultedDefaultCtorExceptionSpec(CD->getParent());
FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI();
+ if (EPI.ExceptionSpecType == EST_Delayed) {
+ // Exception specification depends on some deferred part of the class. We'll
+ // try again when the class's definition has been fully processed.
+ return;
+ }
const FunctionProtoType *CtorType = CD->getType()->getAs<FunctionProtoType>(),
*ExceptionType = Context.getFunctionType(
Context.VoidTy, 0, 0, EPI)->getAs<FunctionProtoType>();
@@ -3383,12 +3489,15 @@
for (CXXRecordDecl::field_iterator FI = RD->field_begin(),
FE = RD->field_end();
FI != FE; ++FI) {
+ if (FI->isInvalidDecl())
+ continue;
+
QualType FieldType = Context.getBaseElementType(FI->getType());
CXXRecordDecl *FieldRecord = FieldType->getAsCXXRecordDecl();
-
+
// -- any non-static data member with no brace-or-equal-initializer is of
// reference type
- if (FieldType->isReferenceType())
+ if (FieldType->isReferenceType() && !FI->hasInClassInitializer())
return true;
// -- X is a union and all its variant members are of const-qualified type
@@ -3413,6 +3522,7 @@
// array thereof) with no brace-or-equal-initializer does not have a
// user-provided default constructor
if (FieldType.isConstQualified() &&
+ !FI->hasInClassInitializer() &&
!FieldRecord->hasUserProvidedDefaultConstructor())
return true;
@@ -3445,18 +3555,21 @@
continue;
}
- // -- any non-static data member ... has class type M (or array thereof)
- // and either M has no default constructor or overload resolution as
- // applied to M's default constructor results in an ambiguity or in a
- // function that is deleted or inaccessible from the defaulted default
- // constructor.
- CXXConstructorDecl *FieldDefault = LookupDefaultConstructor(FieldRecord);
- if (!FieldDefault || FieldDefault->isDeleted())
- return true;
- if (CheckConstructorAccess(Loc, FieldDefault, FieldDefault->getAccess(),
- PDiag()) != AR_accessible)
- return true;
- } else if (!Union && FieldType.isConstQualified()) {
+ // -- any non-static data member with no brace-or-equal-initializer has
+ // class type M (or array thereof) and either M has no default
+ // constructor or overload resolution as applied to M's default
+ // constructor results in an ambiguity or in a function that is deleted
+ // or inaccessible from the defaulted default constructor.
+ if (!FI->hasInClassInitializer()) {
+ CXXConstructorDecl *FieldDefault = LookupDefaultConstructor(FieldRecord);
+ if (!FieldDefault || FieldDefault->isDeleted())
+ return true;
+ if (CheckConstructorAccess(Loc, FieldDefault, FieldDefault->getAccess(),
+ PDiag()) != AR_accessible)
+ return true;
+ }
+ } else if (!Union && FieldType.isConstQualified() &&
+ !FI->hasInClassInitializer()) {
// -- any non-variant non-static data member of const-qualified type (or
// array thereof) with no brace-or-equal-initializer does not have a
// user-provided default constructor
@@ -5905,7 +6018,12 @@
for (RecordDecl::field_iterator F = ClassDecl->field_begin(),
FEnd = ClassDecl->field_end();
F != FEnd; ++F) {
- if (const RecordType *RecordTy
+ if (F->hasInClassInitializer()) {
+ if (Expr *E = F->getInClassInitializer())
+ ExceptSpec.CalledExpr(E);
+ else if (!F->isInvalidDecl())
+ ExceptSpec.SetDelayed();
+ } else if (const RecordType *RecordTy
= Context.getBaseElementType(F->getType())->getAs<RecordType>()) {
CXXRecordDecl *FieldRecDecl = cast<CXXRecordDecl>(RecordTy->getDecl());
CXXConstructorDecl *Constructor = LookupDefaultConstructor(FieldRecDecl);
@@ -6001,6 +6119,59 @@
}
}
+/// Get any existing defaulted default constructor for the given class. Do not
+/// implicitly define one if it does not exist.
+static CXXConstructorDecl *getDefaultedDefaultConstructorUnsafe(Sema &Self,
+ CXXRecordDecl *D) {
+ ASTContext &Context = Self.Context;
+ QualType ClassType = Context.getTypeDeclType(D);
+ DeclarationName ConstructorName
+ = Context.DeclarationNames.getCXXConstructorName(
+ Context.getCanonicalType(ClassType.getUnqualifiedType()));
+
+ DeclContext::lookup_const_iterator Con, ConEnd;
+ for (llvm::tie(Con, ConEnd) = D->lookup(ConstructorName);
+ Con != ConEnd; ++Con) {
+ // A function template cannot be defaulted.
+ if (isa<FunctionTemplateDecl>(*Con))
+ continue;
+
+ CXXConstructorDecl *Constructor = cast<CXXConstructorDecl>(*Con);
+ if (Constructor->isDefaultConstructor())
+ return Constructor->isDefaulted() ? Constructor : 0;
+ }
+ return 0;
+}
+
+void Sema::ActOnFinishDelayedMemberInitializers(Decl *D) {
+ if (!D) return;
+ AdjustDeclIfTemplate(D);
+
+ CXXRecordDecl *ClassDecl = cast<CXXRecordDecl>(D);
+ CXXConstructorDecl *CtorDecl
+ = getDefaultedDefaultConstructorUnsafe(*this, ClassDecl);
+
+ if (!CtorDecl) return;
+
+ // Compute the exception specification for the default constructor.
+ const FunctionProtoType *CtorTy =
+ CtorDecl->getType()->castAs<FunctionProtoType>();
+ if (CtorTy->getExceptionSpecType() == EST_Delayed) {
+ ImplicitExceptionSpecification Spec =
+ ComputeDefaultedDefaultCtorExceptionSpec(ClassDecl);
+ FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI();
+ assert(EPI.ExceptionSpecType != EST_Delayed);
+
+ CtorDecl->setType(Context.getFunctionType(Context.VoidTy, 0, 0, EPI));
+ }
+
+ // If the default constructor is explicitly defaulted, checking the exception
+ // specification is deferred until now.
+ if (!CtorDecl->isInvalidDecl() && CtorDecl->isExplicitlyDefaulted() &&
+ !ClassDecl->isDependentType())
+ CheckExplicitlyDefaultedDefaultConstructor(CtorDecl);
+}
+
void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) {
// We start with an initial pass over the base classes to collect those that
// inherit constructors from. If there are none, we can forgo all further
@@ -6094,7 +6265,9 @@
// Build up a function type for this particular constructor.
// FIXME: The working paper does not consider that the exception spec
// for the inheriting constructor might be larger than that of the
- // source. This code doesn't yet, either.
+ // source. This code doesn't yet, either. When it does, this code will
+ // need to be delayed until after exception specifications and in-class
+ // member initializers are attached.
const Type *NewCtorType;
if (params == maxParams)
NewCtorType = BaseCtorType;
diff --git a/lib/Sema/SemaExceptionSpec.cpp b/lib/Sema/SemaExceptionSpec.cpp
index 0f6108c..7bcec31 100644
--- a/lib/Sema/SemaExceptionSpec.cpp
+++ b/lib/Sema/SemaExceptionSpec.cpp
@@ -298,8 +298,6 @@
// - both are non-throwing, regardless of their form,
// - both have the form noexcept(constant-expression) and the constant-
// expressions are equivalent,
- // - one exception-specification is a noexcept-specification allowing all
- // exceptions and the other is of the form throw(type-id-list), or
// - both are dynamic-exception-specifications that have the same set of
// adjusted types.
//
@@ -307,8 +305,6 @@
// of the form throw(), noexcept, or noexcept(constant-expression) where the
// constant-expression yields true.
//
- // CWG 1073 Proposed resolution: Strike the third bullet above.
- //
// C++0x [except.spec]p4: If any declaration of a function has an exception-
// specifier that is not a noexcept-specification allowing all exceptions,
// all declarations [...] of that function shall have a compatible
@@ -320,6 +316,9 @@
ExceptionSpecificationType OldEST = Old->getExceptionSpecType();
ExceptionSpecificationType NewEST = New->getExceptionSpecType();
+ assert(OldEST != EST_Delayed && NewEST != EST_Delayed &&
+ "Shouldn't see unknown exception specifications here");
+
// Shortcut the case where both have no spec.
if (OldEST == EST_None && NewEST == EST_None)
return false;
@@ -506,6 +505,9 @@
ExceptionSpecificationType SubEST = Subset->getExceptionSpecType();
+ assert(SuperEST != EST_Delayed && SubEST != EST_Delayed &&
+ "Shouldn't see unknown exception specifications here");
+
// It does not. If the subset contains everything, we've failed.
if (SubEST == EST_None || SubEST == EST_MSAny) {
Diag(SubLoc, DiagID);
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index dafd56b..0549e94 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -1333,8 +1333,8 @@
// We've found a member of an anonymous struct/union that is
// inside a non-anonymous struct/union, so in a well-formed
// program our base object expression is "this".
- CXXMethodDecl *method = tryCaptureCXXThis();
- if (!method) {
+ QualType ThisTy = getAndCaptureCurrentThisType();
+ if (ThisTy.isNull()) {
Diag(loc, diag::err_invalid_member_use_in_static_method)
<< indirectField->getDeclName();
return ExprError();
@@ -1342,10 +1342,9 @@
// Our base object expression is "this".
baseObjectExpr =
- new (Context) CXXThisExpr(loc, method->getThisType(Context),
- /*isImplicit=*/ true);
+ new (Context) CXXThisExpr(loc, ThisTy, /*isImplicit=*/ true);
baseObjectIsPointer = true;
- baseQuals = Qualifiers::fromCVRMask(method->getTypeQualifiers());
+ baseQuals = ThisTy->castAs<PointerType>()->getPointeeType().getQualifiers();
}
// Build the implicit member references to the field of the
@@ -1492,14 +1491,23 @@
/// conservatively answer "yes", in which case some errors will simply
/// not be caught until template-instantiation.
static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef,
+ Scope *CurScope,
const LookupResult &R) {
assert(!R.empty() && (*R.begin())->isCXXClassMember());
DeclContext *DC = SemaRef.getFunctionLevelDeclContext();
+
bool isStaticContext =
(!isa<CXXMethodDecl>(DC) ||
cast<CXXMethodDecl>(DC)->isStatic());
+ // C++0x [expr.prim]p4:
+ // Otherwise, if a member-declarator declares a non-static data member
+ // of a class X, the expression this is a prvalue of type "pointer to X"
+ // within the optional brace-or-equal-initializer.
+ if (CurScope->getFlags() & Scope::ThisScope)
+ isStaticContext = false;
+
if (R.isUnresolvableResult())
return isStaticContext ? IMA_Unresolved_StaticContext : IMA_Unresolved;
@@ -1547,8 +1555,11 @@
return IMA_Error_StaticContext;
}
- CXXRecordDecl *
- contextClass = cast<CXXMethodDecl>(DC)->getParent()->getCanonicalDecl();
+ CXXRecordDecl *contextClass;
+ if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(DC))
+ contextClass = MD->getParent()->getCanonicalDecl();
+ else
+ contextClass = cast<CXXRecordDecl>(DC);
// [class.mfct.non-static]p3:
// ...is used in the body of a non-static member function of class X,
@@ -2026,7 +2037,7 @@
Sema::BuildPossibleImplicitMemberExpr(const CXXScopeSpec &SS,
LookupResult &R,
const TemplateArgumentListInfo *TemplateArgs) {
- switch (ClassifyImplicitMemberAccess(*this, R)) {
+ switch (ClassifyImplicitMemberAccess(*this, CurScope, R)) {
case IMA_Instance:
return BuildImplicitMemberExpr(SS, R, TemplateArgs, true);
@@ -2469,19 +2480,18 @@
// If this is known to be an instance access, go ahead and build an
// implicit 'this' expression now.
// 'this' expression now.
- CXXMethodDecl *method = tryCaptureCXXThis();
- assert(method && "didn't correctly pre-flight capture of 'this'");
+ QualType ThisTy = getAndCaptureCurrentThisType();
+ assert(!ThisTy.isNull() && "didn't correctly pre-flight capture of 'this'");
- QualType thisType = method->getThisType(Context);
Expr *baseExpr = 0; // null signifies implicit access
if (IsKnownInstance) {
SourceLocation Loc = R.getNameLoc();
if (SS.getRange().isValid())
Loc = SS.getRange().getBegin();
- baseExpr = new (Context) CXXThisExpr(loc, thisType, /*isImplicit=*/true);
+ baseExpr = new (Context) CXXThisExpr(loc, ThisTy, /*isImplicit=*/true);
}
- return BuildMemberReferenceExpr(baseExpr, thisType,
+ return BuildMemberReferenceExpr(baseExpr, ThisTy,
/*OpLoc*/ SourceLocation(),
/*IsArrow*/ true,
SS,
diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp
index 50462ab..2f5a890 100644
--- a/lib/Sema/SemaExprCXX.cpp
+++ b/lib/Sema/SemaExprCXX.cpp
@@ -17,6 +17,7 @@
#include "clang/Sema/Lookup.h"
#include "clang/Sema/ParsedTemplate.h"
#include "clang/Sema/ScopeInfo.h"
+#include "clang/Sema/Scope.h"
#include "clang/Sema/TemplateDeduction.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/CXXInheritance.h"
@@ -575,42 +576,54 @@
return Owned(E);
}
-CXXMethodDecl *Sema::tryCaptureCXXThis() {
+QualType Sema::getAndCaptureCurrentThisType() {
// Ignore block scopes: we can capture through them.
// Ignore nested enum scopes: we'll diagnose non-constant expressions
// where they're invalid, and other uses are legitimate.
// Don't ignore nested class scopes: you can't use 'this' in a local class.
DeclContext *DC = CurContext;
+ unsigned NumBlocks = 0;
while (true) {
- if (isa<BlockDecl>(DC)) DC = cast<BlockDecl>(DC)->getDeclContext();
- else if (isa<EnumDecl>(DC)) DC = cast<EnumDecl>(DC)->getDeclContext();
+ if (isa<BlockDecl>(DC)) {
+ DC = cast<BlockDecl>(DC)->getDeclContext();
+ ++NumBlocks;
+ } else if (isa<EnumDecl>(DC))
+ DC = cast<EnumDecl>(DC)->getDeclContext();
else break;
}
- // If we're not in an instance method, error out.
- CXXMethodDecl *method = dyn_cast<CXXMethodDecl>(DC);
- if (!method || !method->isInstance())
- return 0;
+ QualType ThisTy;
+ if (CXXMethodDecl *method = dyn_cast<CXXMethodDecl>(DC)) {
+ if (method && method->isInstance())
+ ThisTy = method->getThisType(Context);
+ } else if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(DC)) {
+ // C++0x [expr.prim]p4:
+ // Otherwise, if a member-declarator declares a non-static data member
+ // of a class X, the expression this is a prvalue of type "pointer to X"
+ // within the optional brace-or-equal-initializer.
+ Scope *S = getScopeForContext(DC);
+ if (!S || S->getFlags() & Scope::ThisScope)
+ ThisTy = Context.getPointerType(Context.getRecordType(RD));
+ }
- // Mark that we're closing on 'this' in all the block scopes, if applicable.
- for (unsigned idx = FunctionScopes.size() - 1;
- isa<BlockScopeInfo>(FunctionScopes[idx]);
- --idx)
- cast<BlockScopeInfo>(FunctionScopes[idx])->CapturesCXXThis = true;
+ // Mark that we're closing on 'this' in all the block scopes we ignored.
+ if (!ThisTy.isNull())
+ for (unsigned idx = FunctionScopes.size() - 1;
+ NumBlocks; --idx, --NumBlocks)
+ cast<BlockScopeInfo>(FunctionScopes[idx])->CapturesCXXThis = true;
- return method;
+ return ThisTy;
}
-ExprResult Sema::ActOnCXXThis(SourceLocation loc) {
+ExprResult Sema::ActOnCXXThis(SourceLocation Loc) {
/// C++ 9.3.2: In the body of a non-static member function, the keyword this
/// is a non-lvalue expression whose value is the address of the object for
/// which the function is called.
- CXXMethodDecl *method = tryCaptureCXXThis();
- if (!method) return Diag(loc, diag::err_invalid_this_use);
+ QualType ThisTy = getAndCaptureCurrentThisType();
+ if (ThisTy.isNull()) return Diag(Loc, diag::err_invalid_this_use);
- return Owned(new (Context) CXXThisExpr(loc, method->getThisType(Context),
- /*isImplicit=*/false));
+ return Owned(new (Context) CXXThisExpr(Loc, ThisTy, /*isImplicit=*/false));
}
ExprResult
@@ -2663,7 +2676,6 @@
return true;
bool FoundAssign = false;
- bool AllNoThrow = true;
DeclarationName Name = C.DeclarationNames.getCXXOperatorName(OO_Equal);
LookupResult Res(Self, DeclarationNameInfo(Name, KeyLoc),
Sema::LookupOrdinaryName);
@@ -2675,15 +2687,15 @@
FoundAssign = true;
const FunctionProtoType *CPT
= Operator->getType()->getAs<FunctionProtoType>();
- if (!CPT->isNothrow(Self.Context)) {
- AllNoThrow = false;
- break;
- }
+ if (CPT->getExceptionSpecType() == EST_Delayed)
+ return false;
+ if (!CPT->isNothrow(Self.Context))
+ return false;
}
}
}
- return FoundAssign && AllNoThrow;
+ return FoundAssign;
}
return false;
case UTT_HasNothrowCopy:
@@ -2700,7 +2712,6 @@
return true;
bool FoundConstructor = false;
- bool AllNoThrow = true;
unsigned FoundTQs;
DeclContext::lookup_const_iterator Con, ConEnd;
for (llvm::tie(Con, ConEnd) = Self.LookupConstructors(RD);
@@ -2715,16 +2726,16 @@
FoundConstructor = true;
const FunctionProtoType *CPT
= Constructor->getType()->getAs<FunctionProtoType>();
+ if (CPT->getExceptionSpecType() == EST_Delayed)
+ return false;
// FIXME: check whether evaluating default arguments can throw.
// For now, we'll be conservative and assume that they can throw.
- if (!CPT->isNothrow(Self.Context) || CPT->getNumArgs() > 1) {
- AllNoThrow = false;
- break;
- }
+ if (!CPT->isNothrow(Self.Context) || CPT->getNumArgs() > 1)
+ return false;
}
}
- return FoundConstructor && AllNoThrow;
+ return FoundConstructor;
}
return false;
case UTT_HasNothrowConstructor:
@@ -2750,6 +2761,8 @@
if (Constructor->isDefaultConstructor()) {
const FunctionProtoType *CPT
= Constructor->getType()->getAs<FunctionProtoType>();
+ if (CPT->getExceptionSpecType() == EST_Delayed)
+ return false;
// TODO: check whether evaluating default arguments can throw.
// For now, we'll be conservative and assume that they can throw.
return CPT->isNothrow(Self.Context) && CPT->getNumArgs() == 0;
diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp
index 444fb9a..3c19641 100644
--- a/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/lib/Sema/SemaTemplateInstantiate.cpp
@@ -13,6 +13,7 @@
#include "clang/Sema/SemaInternal.h"
#include "TreeTransform.h"
#include "clang/Sema/DeclSpec.h"
+#include "clang/Sema/Initialization.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Template.h"
#include "clang/Sema/TemplateDeduction.h"
@@ -1744,6 +1745,8 @@
TemplateDeclInstantiator Instantiator(*this, Instantiation, TemplateArgs);
llvm::SmallVector<Decl*, 4> Fields;
+ llvm::SmallVector<std::pair<FieldDecl*, FieldDecl*>, 4>
+ FieldsWithMemberInitializers;
for (RecordDecl::decl_iterator Member = Pattern->decls_begin(),
MemberEnd = Pattern->decls_end();
Member != MemberEnd; ++Member) {
@@ -1766,9 +1769,13 @@
Decl *NewMember = Instantiator.Visit(*Member);
if (NewMember) {
- if (FieldDecl *Field = dyn_cast<FieldDecl>(NewMember))
+ if (FieldDecl *Field = dyn_cast<FieldDecl>(NewMember)) {
Fields.push_back(Field);
- else if (NewMember->isInvalidDecl())
+ FieldDecl *OldField = cast<FieldDecl>(*Member);
+ if (OldField->getInClassInitializer())
+ FieldsWithMemberInitializers.push_back(std::make_pair(OldField,
+ Field));
+ } else if (NewMember->isInvalidDecl())
Invalid = true;
} else {
// FIXME: Eventually, a NULL return will mean that one of the
@@ -1782,6 +1789,43 @@
Fields.data(), Fields.size(), SourceLocation(), SourceLocation(),
0);
CheckCompletedCXXClass(Instantiation);
+
+ // Attach any in-class member initializers now the class is complete.
+ for (unsigned I = 0, N = FieldsWithMemberInitializers.size(); I != N; ++I) {
+ FieldDecl *OldField = FieldsWithMemberInitializers[I].first;
+ FieldDecl *NewField = FieldsWithMemberInitializers[I].second;
+ Expr *OldInit = OldField->getInClassInitializer();
+ ExprResult NewInit = SubstExpr(OldInit, TemplateArgs);
+
+ // If the initialization is no longer dependent, check it now.
+ if ((OldField->getType()->isDependentType() || OldInit->isTypeDependent())
+ && !NewField->getType()->isDependentType()
+ && !NewInit.get()->isTypeDependent()) {
+ // FIXME: handle list-initialization
+ SourceLocation EqualLoc = NewField->getLocation();
+ NewInit = PerformCopyInitialization(
+ InitializedEntity::InitializeMember(NewField), EqualLoc,
+ NewInit.release());
+
+ if (!NewInit.isInvalid()) {
+ CheckImplicitConversions(NewInit.get(), EqualLoc);
+
+ // C++0x [class.base.init]p7:
+ // The initialization of each base and member constitutes a
+ // full-expression.
+ NewInit = MaybeCreateExprWithCleanups(NewInit);
+ }
+ }
+
+ if (NewInit.isInvalid())
+ NewField->setInvalidDecl();
+ else
+ NewField->setInClassInitializer(NewInit.release());
+ }
+
+ if (!FieldsWithMemberInitializers.empty())
+ ActOnFinishDelayedMemberInitializers(Instantiation);
+
if (Instantiation->isInvalidDecl())
Invalid = true;
else {
diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 22db4c8..e78aa29 100644
--- a/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -477,6 +477,7 @@
D->getLocation(),
D->isMutable(),
BitWidth,
+ D->hasInClassInitializer(),
D->getTypeSpecStartLoc(),
D->getAccess(),
0);
diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp
index e7f9885..5fd8afa 100644
--- a/lib/Sema/SemaType.cpp
+++ b/lib/Sema/SemaType.cpp
@@ -1593,6 +1593,8 @@
Error = 0; // Function prototype
break;
case Declarator::MemberContext:
+ if (D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static)
+ break;
switch (cast<TagDecl>(CurContext)->getTagKind()) {
case TTK_Enum: assert(0 && "unhandled tag kind"); break;
case TTK_Struct: Error = 1; /* Struct member */ break;
diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h
index 245499e..ff2e46a 100644
--- a/lib/Sema/TreeTransform.h
+++ b/lib/Sema/TreeTransform.h
@@ -6697,8 +6697,12 @@
ExprResult
TreeTransform<Derived>::TransformCXXThisExpr(CXXThisExpr *E) {
DeclContext *DC = getSema().getFunctionLevelDeclContext();
- CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(DC);
- QualType T = MD->getThisType(getSema().Context);
+ QualType T;
+ if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(DC))
+ T = MD->getThisType(getSema().Context);
+ else
+ T = getSema().Context.getPointerType(
+ getSema().Context.getRecordType(cast<CXXRecordDecl>(DC)));
if (!getDerived().AlwaysRebuild() && T == E->getType())
return SemaRef.Owned(E);