Implement and test aggregate initialization in C++. Major changes:
- Support initialization of reference members; complain if any
reference members are left uninitialized.
- Use C++ copy-initialization for initializing each element (falls
back to constraint checking in C)
- Make sure we diagnose when one tries to provide an initializer
list for a non-aggregate.
- Don't complain about empty initializers in C++ (they are permitted)
- Unrelated but necessary: don't bother trying to convert the
decl-specifier-seq to a type when we're dealing with a C++
constructor, destructor, or conversion operator; it results in
spurious warnings.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@63431 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 3395313..5dd45fb 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -1074,23 +1074,7 @@
<< Init->getSourceRange();
return CheckSingleInitializer(Init, DeclType, DirectInit);
- } else if (getLangOptions().CPlusPlus) {
- // C++ [dcl.init]p14:
- // [...] If the class is an aggregate (8.5.1), and the initializer
- // is a brace-enclosed list, see 8.5.1.
- //
- // Note: 8.5.1 is handled below; here, we diagnose the case where
- // we have an initializer list and a destination type that is not
- // an aggregate.
- // FIXME: In C++0x, this is yet another form of initialization.
- // FIXME: Move this checking into CheckInitList!
- if (const RecordType *ClassRec = DeclType->getAsRecordType()) {
- const CXXRecordDecl *ClassDecl = cast<CXXRecordDecl>(ClassRec->getDecl());
- if (!ClassDecl->isAggregate())
- return Diag(InitLoc, diag::err_init_non_aggr_init_list)
- << DeclType << Init->getSourceRange();
- }
- }
+ }
bool hadError = CheckInitList(InitList, DeclType);
Init = InitList;
diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp
index ed44acf..5d40e43 100644
--- a/lib/Sema/SemaInit.cpp
+++ b/lib/Sema/SemaInit.cpp
@@ -69,11 +69,14 @@
unsigned &Index,
InitListExpr *StructuredList,
unsigned &StructuredIndex);
- // FIXME: Does DeclType need to be a reference type?
- void CheckScalarType(InitListExpr *IList, QualType &DeclType,
+ void CheckScalarType(InitListExpr *IList, QualType DeclType,
unsigned &Index,
InitListExpr *StructuredList,
unsigned &StructuredIndex);
+ void CheckReferenceType(InitListExpr *IList, QualType DeclType,
+ unsigned &Index,
+ InitListExpr *StructuredList,
+ unsigned &StructuredIndex);
void CheckVectorType(InitListExpr *IList, QualType DeclType, unsigned &Index,
InitListExpr *StructuredList,
unsigned &StructuredIndex);
@@ -106,6 +109,8 @@
Expr *expr);
int numArrayElements(QualType DeclType);
int numStructUnionElements(QualType DeclType);
+
+ void FillInValueInitializations(InitListExpr *ILE);
public:
InitListChecker(Sema *S, InitListExpr *IL, QualType &T);
bool HadError() { return hadError; }
@@ -116,12 +121,12 @@
};
}
-
/// Recursively replaces NULL values within the given initializer list
/// with expressions that perform value-initialization of the
/// appropriate type.
-static void fillInValueInitializations(ASTContext &Context, InitListExpr *ILE) {
- assert((ILE->getType() != Context.VoidTy) && "Should not have void type");
+void InitListChecker::FillInValueInitializations(InitListExpr *ILE) {
+ assert((ILE->getType() != SemaRef->Context.VoidTy) &&
+ "Should not have void type");
if (const RecordType *RType = ILE->getType()->getAsRecordType()) {
unsigned Init = 0, NumInits = ILE->getNumInits();
for (RecordDecl::field_iterator Field = RType->getDecl()->field_begin(),
@@ -130,17 +135,31 @@
if (Field->isUnnamedBitfield())
continue;
- if (Init >= NumInits)
- break;
-
- // FIXME: Check for fields with reference type in C++?
- if (!ILE->getInit(Init))
+ if (Init >= NumInits) {
+ if (Field->getType()->isReferenceType()) {
+ // C++ [dcl.init.aggr]p9:
+ // If an incomplete or empty initializer-list leaves a
+ // member of reference type uninitialized, the program is
+ // ill-formed.
+ SemaRef->Diag(ILE->getSyntacticForm()->getLocStart(),
+ diag::err_init_reference_member_uninitialized)
+ << Field->getType()
+ << ILE->getSyntacticForm()->getSourceRange();
+ SemaRef->Diag(Field->getLocation(),
+ diag::note_uninit_reference_member);
+ hadError = true;
+ }
+ } else if (!ILE->getInit(Init))
ILE->setInit(Init,
- new (Context) ImplicitValueInitExpr(Field->getType()));
+ new (SemaRef->Context) ImplicitValueInitExpr(Field->getType()));
else if (InitListExpr *InnerILE
= dyn_cast<InitListExpr>(ILE->getInit(Init)))
- fillInValueInitializations(Context, InnerILE);
+ FillInValueInitializations(InnerILE);
++Init;
+
+ // Only look at the first initialization of a union.
+ if (RType->getDecl()->isUnion())
+ break;
}
return;
@@ -148,7 +167,7 @@
QualType ElementType;
- if (const ArrayType *AType = Context.getAsArrayType(ILE->getType()))
+ if (const ArrayType *AType = SemaRef->Context.getAsArrayType(ILE->getType()))
ElementType = AType->getElementType();
else if (const VectorType *VType = ILE->getType()->getAsVectorType())
ElementType = VType->getElementType();
@@ -158,9 +177,10 @@
for (unsigned Init = 0, NumInits = ILE->getNumInits(); Init != NumInits;
++Init) {
if (!ILE->getInit(Init))
- ILE->setInit(Init, new (Context) ImplicitValueInitExpr(ElementType));
+ ILE->setInit(Init,
+ new (SemaRef->Context) ImplicitValueInitExpr(ElementType));
else if (InitListExpr *InnerILE =dyn_cast<InitListExpr>(ILE->getInit(Init)))
- fillInValueInitializations(Context, InnerILE);
+ FillInValueInitializations(InnerILE);
}
}
@@ -175,9 +195,8 @@
= getStructuredSubobjectInit(IL, newIndex, T, 0, 0, SourceRange());
CheckExplicitInitList(IL, T, newIndex, FullyStructuredList, newStructuredIndex);
- if (!hadError) {
- fillInValueInitializations(SemaRef->Context, FullyStructuredList);
- }
+ if (!hadError)
+ FillInValueInitializations(FullyStructuredList);
}
int InitListChecker::numArrayElements(QualType DeclType) {
@@ -219,7 +238,6 @@
else
assert(0 && "CheckImplicitInitList(): Illegal type");
- // FIXME: Perhaps we should move this warning elsewhere?
if (maxElements == 0) {
SemaRef->Diag(ParentIList->getInit(Index)->getLocStart(),
diag::err_implicit_empty_initializer);
@@ -308,6 +326,20 @@
SemaRef->Diag(IList->getLocStart(), diag::err_illegal_initializer_type)
<< DeclType;
hadError = true;
+ } else if (DeclType->isRecordType()) {
+ // C++ [dcl.init]p14:
+ // [...] If the class is an aggregate (8.5.1), and the initializer
+ // is a brace-enclosed list, see 8.5.1.
+ //
+ // Note: 8.5.1 is handled below; here, we diagnose the case where
+ // we have an initializer list and a destination type that is not
+ // an aggregate.
+ // FIXME: In C++0x, this is yet another form of initialization.
+ SemaRef->Diag(IList->getLocStart(), diag::err_init_non_aggr_init_list)
+ << DeclType << IList->getSourceRange();
+ hadError = true;
+ } else if (DeclType->isReferenceType()) {
+ CheckReferenceType(IList, DeclType, Index, StructuredList, StructuredIndex);
} else {
// In C, all types are either scalars or aggregates, but
// additional handling is needed here for C++ (and possibly others?).
@@ -339,21 +371,70 @@
++Index;
} else if (ElemType->isScalarType()) {
CheckScalarType(IList, ElemType, Index, StructuredList, StructuredIndex);
- } else if (expr->getType()->getAsRecordType() &&
- SemaRef->Context.typesAreCompatible(
- expr->getType().getUnqualifiedType(),
- ElemType.getUnqualifiedType())) {
- UpdateStructuredListElement(StructuredList, StructuredIndex, expr);
- ++Index;
- // FIXME: Add checking
+ } else if (ElemType->isReferenceType()) {
+ CheckReferenceType(IList, ElemType, Index, StructuredList, StructuredIndex);
} else {
- CheckImplicitInitList(IList, ElemType, Index, StructuredList,
- StructuredIndex);
- ++StructuredIndex;
- }
+ if (SemaRef->getLangOptions().CPlusPlus) {
+ // C++ [dcl.init.aggr]p12:
+ // All implicit type conversions (clause 4) are considered when
+ // initializing the aggregate member with an ini- tializer from
+ // an initializer-list. If the initializer can initialize a
+ // member, the member is initialized. [...]
+ ImplicitConversionSequence ICS
+ = SemaRef->TryCopyInitialization(expr, ElemType);
+ if (ICS.ConversionKind != ImplicitConversionSequence::BadConversion) {
+ if (SemaRef->PerformImplicitConversion(expr, ElemType, ICS,
+ "initializing"))
+ hadError = true;
+ UpdateStructuredListElement(StructuredList, StructuredIndex, expr);
+ ++Index;
+ return;
+ }
+
+ // Fall through for subaggregate initialization
+ } else {
+ // C99 6.7.8p13:
+ //
+ // The initializer for a structure or union object that has
+ // automatic storage duration shall be either an initializer
+ // list as described below, or a single expression that has
+ // compatible structure or union type. In the latter case, the
+ // initial value of the object, including unnamed members, is
+ // that of the expression.
+ QualType ExprType = SemaRef->Context.getCanonicalType(expr->getType());
+ QualType ElemTypeCanon = SemaRef->Context.getCanonicalType(ElemType);
+ if (SemaRef->Context.typesAreCompatible(ExprType.getUnqualifiedType(),
+ ElemTypeCanon.getUnqualifiedType())) {
+ UpdateStructuredListElement(StructuredList, StructuredIndex, expr);
+ ++Index;
+ return;
+ }
+
+ // Fall through for subaggregate initialization
+ }
+
+ // C++ [dcl.init.aggr]p12:
+ //
+ // [...] Otherwise, if the member is itself a non-empty
+ // subaggregate, brace elision is assumed and the initializer is
+ // considered for the initialization of the first member of
+ // the subaggregate.
+ if (ElemType->isAggregateType() || ElemType->isVectorType()) {
+ CheckImplicitInitList(IList, ElemType, Index, StructuredList,
+ StructuredIndex);
+ ++StructuredIndex;
+ } else {
+ // We cannot initialize this element, so let
+ // PerformCopyInitialization produce the appropriate diagnostic.
+ SemaRef->PerformCopyInitialization(expr, ElemType, "initializing");
+ hadError = true;
+ ++Index;
+ ++StructuredIndex;
+ }
+ }
}
-void InitListChecker::CheckScalarType(InitListExpr *IList, QualType &DeclType,
+void InitListChecker::CheckScalarType(InitListExpr *IList, QualType DeclType,
unsigned &Index,
InitListExpr *StructuredList,
unsigned &StructuredIndex) {
@@ -399,6 +480,49 @@
}
}
+void InitListChecker::CheckReferenceType(InitListExpr *IList, QualType DeclType,
+ unsigned &Index,
+ InitListExpr *StructuredList,
+ unsigned &StructuredIndex) {
+ if (Index < IList->getNumInits()) {
+ Expr *expr = IList->getInit(Index);
+ if (isa<InitListExpr>(expr)) {
+ SemaRef->Diag(IList->getLocStart(), diag::err_init_non_aggr_init_list)
+ << DeclType << IList->getSourceRange();
+ hadError = true;
+ ++Index;
+ ++StructuredIndex;
+ return;
+ }
+
+ Expr *savExpr = expr; // Might be promoted by CheckSingleInitializer.
+ if (SemaRef->CheckReferenceInit(expr, DeclType))
+ hadError = true;
+ else if (savExpr != expr) {
+ // The type was promoted, update initializer list.
+ IList->setInit(Index, expr);
+ }
+ if (hadError)
+ ++StructuredIndex;
+ else
+ UpdateStructuredListElement(StructuredList, StructuredIndex, expr);
+ ++Index;
+ } else {
+ // FIXME: It would be wonderful if we could point at the actual
+ // member. In general, it would be useful to pass location
+ // information down the stack, so that we know the location (or
+ // decl) of the "current object" being initialized.
+ SemaRef->Diag(IList->getLocStart(),
+ diag::err_init_reference_member_uninitialized)
+ << DeclType
+ << IList->getSourceRange();
+ hadError = true;
+ ++Index;
+ ++StructuredIndex;
+ return;
+ }
+}
+
void InitListChecker::CheckVectorType(InitListExpr *IList, QualType DeclType,
unsigned &Index,
InitListExpr *StructuredList,
diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp
index 43f8baa..9d78d0a 100644
--- a/lib/Sema/SemaType.cpp
+++ b/lib/Sema/SemaType.cpp
@@ -18,8 +18,11 @@
#include "clang/Parse/DeclSpec.h"
using namespace clang;
-/// ConvertDeclSpecToType - Convert the specified declspec to the appropriate
-/// type object. This returns null on error.
+/// \brief Convert the specified declspec to the appropriate type
+/// object.
+/// \param DS the declaration specifiers
+/// \returns The type described by the declaration specifiers, or NULL
+/// if there was an error.
QualType Sema::ConvertDeclSpecToType(const DeclSpec &DS) {
// FIXME: Should move the logic from DeclSpec::Finish to here for validity
// checking.
@@ -81,6 +84,7 @@
// "At least one type specifier shall be given in the declaration
// specifiers in each declaration, and in the specifier-qualifier list in
// each struct declaration and type name."
+ // FIXME: this should be a hard error in C++
if (!DS.hasTypeSpecifier())
Diag(DS.getSourceRange().getBegin(), diag::ext_missing_type_specifier);
}
@@ -254,8 +258,26 @@
if (!getLangOptions().C99 && !getLangOptions().CPlusPlus0x &&
D.getDeclSpec().getTypeSpecWidth() == DeclSpec::TSW_longlong)
Diag(D.getDeclSpec().getTypeSpecWidthLoc(), diag::ext_longlong);
-
- QualType T = ConvertDeclSpecToType(D.getDeclSpec());
+
+ // Determine the type of the declarator. Not all forms of declarator
+ // have a type.
+ QualType T;
+ switch (D.getKind()) {
+ case Declarator::DK_Abstract:
+ case Declarator::DK_Normal:
+ case Declarator::DK_Operator:
+ T = ConvertDeclSpecToType(D.getDeclSpec());
+ break;
+
+ case Declarator::DK_Constructor:
+ case Declarator::DK_Destructor:
+ case Declarator::DK_Conversion:
+ // Constructors and destructors don't have return types. Use
+ // "void" instead. Conversion operators will check their return
+ // types separately.
+ T = Context.VoidTy;
+ break;
+ }
// Walk the DeclTypeInfo, building the recursive type as we go. DeclTypeInfos
// are ordered from the identifier out, which is opposite of what we want :).