diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2013-04-20 22:23:05 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2013-04-20 22:23:05 +0000 |
commit | c3bf52ced9652f555aa0767bb822ec4c64546212 (patch) | |
tree | e1bc9a6ecec46f15038eb98b525de05179d8479b | |
parent | 7db58e0fe92a7a64daf14f78ceea103ba7fbf03d (diff) |
C++1y: Allow aggregates to have default initializers.
Add a CXXDefaultInitExpr, analogous to CXXDefaultArgExpr, and use it both in
CXXCtorInitializers and in InitListExprs to represent a default initializer.
There's an additional complication here: because the default initializer can
refer to the initialized object via its 'this' pointer, we need to make sure
that 'this' points to the right thing within the evaluation.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@179958 91177308-0d34-0410-b5e6-96231b3b80d8
37 files changed, 495 insertions, 41 deletions
diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 5506d211cc..eb6035680b 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -1754,8 +1754,6 @@ class CXXCtorInitializer { /// \brief The argument used to initialize the base or member, which may /// end up constructing an object (when multiple arguments are involved). - /// If 0, this is a field initializer, and the in-class member initializer - /// will be used. Stmt *Init; /// LParenLoc - Location of the left paren of the ctor-initializer. @@ -1840,7 +1838,7 @@ public: /// implicit ctor initializer generated for a field with an initializer /// defined on the member declaration. bool isInClassMemberInitializer() const { - return !Init; + return isa<CXXDefaultInitExpr>(Init); } /// isDelegatingInitializer - Returns true when this initializer is creating @@ -1967,14 +1965,8 @@ public: getNumArrayIndices()); } - /// \brief Get the initializer. This is 0 if this is an in-class initializer - /// for a non-static data member which has not yet been parsed. - Expr *getInit() const { - if (!Init) - return getAnyMember()->getInClassInitializer(); - - return static_cast<Expr*>(Init); - } + /// \brief Get the initializer. + Expr *getInit() const { return static_cast<Expr*>(Init); } }; /// CXXConstructorDecl - Represents a C++ constructor within a diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index 3189426f9d..91e5b21eac 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -884,6 +884,53 @@ public: friend class ASTStmtWriter; }; +/// \brief This wraps a use of a C++ default initializer (technically, +/// a brace-or-equal-initializer for a non-static data member) when it +/// is implicitly used in a mem-initializer-list in a constructor +/// (C++11 [class.base.init]p8) or in aggregate initialization +/// (C++1y [dcl.init.aggr]p7). +class CXXDefaultInitExpr : public Expr { + /// \brief The field whose default is being used. + FieldDecl *Field; + + /// \brief The location where the default initializer expression was used. + SourceLocation Loc; + + CXXDefaultInitExpr(ASTContext &C, SourceLocation Loc, FieldDecl *Field, + QualType T); + + CXXDefaultInitExpr(EmptyShell Empty) : Expr(CXXDefaultInitExprClass, Empty) {} + +public: + // Field is the non-static data member whose default initializer is used + // by this expression. + static CXXDefaultInitExpr *Create(ASTContext &C, SourceLocation Loc, + FieldDecl *Field) { + return new (C) CXXDefaultInitExpr(C, Loc, Field, Field->getType()); + } + + // Get the field whose initializer will be used. + FieldDecl *getField() { return Field; } + const FieldDecl *getField() const { return Field; } + + // Get the initialization expression that will be used. + const Expr *getExpr() const { return Field->getInClassInitializer(); } + Expr *getExpr() { return Field->getInClassInitializer(); } + + SourceLocation getLocStart() const LLVM_READONLY { return Loc; } + SourceLocation getLocEnd() const LLVM_READONLY { return Loc; } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXDefaultInitExprClass; + } + + // Iterators + child_range children() { return child_range(); } + + friend class ASTReader; + friend class ASTStmtReader; +}; + /// CXXTemporary - Represents a C++ temporary. class CXXTemporary { /// Destructor - The destructor that needs to be called. diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index df41b6fa5a..eb5a8123c2 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -2170,6 +2170,7 @@ DEF_TRAVERSE_STMT(CompoundLiteralExpr, { DEF_TRAVERSE_STMT(CXXBindTemporaryExpr, { }) DEF_TRAVERSE_STMT(CXXBoolLiteralExpr, { }) DEF_TRAVERSE_STMT(CXXDefaultArgExpr, { }) +DEF_TRAVERSE_STMT(CXXDefaultInitExpr, { }) DEF_TRAVERSE_STMT(CXXDeleteExpr, { }) DEF_TRAVERSE_STMT(ExprWithCleanups, { }) DEF_TRAVERSE_STMT(CXXNullPtrLiteralExpr, { }) diff --git a/include/clang/Basic/StmtNodes.td b/include/clang/Basic/StmtNodes.td index ad25e57c89..cbfce83c4b 100644 --- a/include/clang/Basic/StmtNodes.td +++ b/include/clang/Basic/StmtNodes.td @@ -108,6 +108,7 @@ def CXXNullPtrLiteralExpr : DStmt<Expr>; def CXXThisExpr : DStmt<Expr>; def CXXThrowExpr : DStmt<Expr>; def CXXDefaultArgExpr : DStmt<Expr>; +def CXXDefaultInitExpr : DStmt<Expr>; def CXXScalarValueInitExpr : DStmt<Expr>; def CXXNewExpr : DStmt<Expr>; def CXXDeleteExpr : DStmt<Expr>; diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index 9f5e8b1224..81f8980425 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -1267,6 +1267,7 @@ namespace clang { EXPR_CXX_THIS, // CXXThisExpr EXPR_CXX_THROW, // CXXThrowExpr EXPR_CXX_DEFAULT_ARG, // CXXDefaultArgExpr + EXPR_CXX_DEFAULT_INIT, // CXXDefaultInitExpr EXPR_CXX_BIND_TEMPORARY, // CXXBindTemporaryExpr EXPR_CXX_SCALAR_VALUE_INIT, // CXXScalarValueInitExpr diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 2c7c4d335d..ece831571c 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -505,7 +505,7 @@ void CXXRecordDecl::addedMember(Decl *D) { // C++ [dcl.init.aggr]p1: // An aggregate is an array or a class with no user-declared // constructors [...]. - // C++0x [dcl.init.aggr]p1: + // C++11 [dcl.init.aggr]p1: // An aggregate is an array or a class with no user-provided // constructors [...]. if (getASTContext().getLangOpts().CPlusPlus11 @@ -690,7 +690,10 @@ void CXXRecordDecl::addedMember(Decl *D) { // C++11 [dcl.init.aggr]p1: // An aggregate is a [...] class with [...] no // brace-or-equal-initializers for non-static data members. - data().Aggregate = false; + // + // This rule was removed in C++1y. + if (!getASTContext().getLangOpts().CPlusPlus1y) + data().Aggregate = false; // C++11 [class]p10: // A POD struct is [...] a trivial class. diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp index 995925350c..bcc4e3865a 100644 --- a/lib/AST/DeclPrinter.cpp +++ b/lib/AST/DeclPrinter.cpp @@ -487,7 +487,7 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { for (CXXConstructorDecl::init_const_iterator B = CDecl->init_begin(), E = CDecl->init_end(); B != E; ++B) { - CXXCtorInitializer * BMInitializer = (*B); + CXXCtorInitializer *BMInitializer = (*B); if (BMInitializer->isInClassMemberInitializer()) continue; diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 748d3084fe..1303fb0cee 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -100,11 +100,20 @@ Expr::skipRValueSubobjectAdjustments( const Expr * Expr::findMaterializedTemporary(const MaterializeTemporaryExpr *&MTE) const { const Expr *E = this; + + // This might be a default initializer for a reference member. Walk over the + // wrapper node for that. + if (const CXXDefaultInitExpr *DAE = dyn_cast<CXXDefaultInitExpr>(E)) + E = DAE->getExpr(); + // Look through single-element init lists that claim to be lvalues. They're // just syntactic wrappers in this case. if (const InitListExpr *ILE = dyn_cast<InitListExpr>(E)) { - if (ILE->getNumInits() == 1 && ILE->isGLValue()) + if (ILE->getNumInits() == 1 && ILE->isGLValue()) { E = ILE->getInit(0); + if (const CXXDefaultInitExpr *DAE = dyn_cast<CXXDefaultInitExpr>(E)) + E = DAE->getExpr(); + } } // Look through expressions for materialized temporaries (for now). @@ -2174,6 +2183,9 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, case CXXDefaultArgExprClass: return (cast<CXXDefaultArgExpr>(this) ->getExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx)); + case CXXDefaultInitExprClass: + return (cast<CXXDefaultInitExpr>(this) + ->getExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx)); case CXXNewExprClass: // FIXME: In theory, there might be new expressions that don't have side @@ -2851,6 +2863,12 @@ bool Expr::HasSideEffects(const ASTContext &Ctx) const { case CXXDefaultArgExprClass: return cast<CXXDefaultArgExpr>(this)->getExpr()->HasSideEffects(Ctx); + case CXXDefaultInitExprClass: + if (const Expr *E = cast<CXXDefaultInitExpr>(this)->getExpr()) + return E->HasSideEffects(Ctx); + // If we've not yet parsed the initializer, assume it has side-effects. + return true; + case CXXDynamicCastExprClass: { // A dynamic_cast expression has side-effects if it can throw. const CXXDynamicCastExpr *DCE = cast<CXXDynamicCastExpr>(this); @@ -3038,8 +3056,12 @@ Expr::isNullPointerConstant(ASTContext &Ctx, return GE->getResultExpr()->isNullPointerConstant(Ctx, NPC); } else if (const CXXDefaultArgExpr *DefaultArg = dyn_cast<CXXDefaultArgExpr>(this)) { - // See through default argument expressions + // See through default argument expressions. return DefaultArg->getExpr()->isNullPointerConstant(Ctx, NPC); + } else if (const CXXDefaultInitExpr *DefaultInit + = dyn_cast<CXXDefaultInitExpr>(this)) { + // See through default initializer expressions. + return DefaultInit->getExpr()->isNullPointerConstant(Ctx, NPC); } else if (isa<GNUNullExpr>(this)) { // The GNU __null extension is always a null pointer constant. return NPCK_GNUNull; diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp index 12a47fcd78..277c8c09a5 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -704,6 +704,17 @@ CXXDefaultArgExpr::Create(ASTContext &C, SourceLocation Loc, SubExpr); } +CXXDefaultInitExpr::CXXDefaultInitExpr(ASTContext &C, SourceLocation Loc, + FieldDecl *Field, QualType T) + : Expr(CXXDefaultInitExprClass, T.getNonLValueExprType(C), + T->isLValueReferenceType() ? VK_LValue : T->isRValueReferenceType() + ? VK_XValue + : VK_RValue, + /*FIXME*/ OK_Ordinary, false, false, false, false), + Field(Field), Loc(Loc) { + assert(Field->hasInClassInitializer()); +} + CXXTemporary *CXXTemporary::Create(ASTContext &C, const CXXDestructorDecl *Destructor) { return new (C) CXXTemporary(Destructor); diff --git a/lib/AST/ExprClassification.cpp b/lib/AST/ExprClassification.cpp index c2a35f42c1..bcb6d4e809 100644 --- a/lib/AST/ExprClassification.cpp +++ b/lib/AST/ExprClassification.cpp @@ -298,6 +298,10 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::CXXDefaultArgExprClass: return ClassifyInternal(Ctx, cast<CXXDefaultArgExpr>(E)->getExpr()); + // Same idea for default initializers. + case Expr::CXXDefaultInitExprClass: + return ClassifyInternal(Ctx, cast<CXXDefaultInitExpr>(E)->getExpr()); + // Same idea for temporary binding. case Expr::CXXBindTemporaryExprClass: return ClassifyInternal(Ctx, cast<CXXBindTemporaryExpr>(E)->getSubExpr()); diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 3675b2ff44..4e468806dc 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -301,6 +301,22 @@ namespace { ~CallStackFrame(); }; + /// Temporarily override 'this'. + class ThisOverrideRAII { + public: + ThisOverrideRAII(CallStackFrame &Frame, const LValue *NewThis, bool Enable) + : Frame(Frame), OldThis(Frame.This) { + if (Enable) + Frame.This = NewThis; + } + ~ThisOverrideRAII() { + Frame.This = OldThis; + } + private: + CallStackFrame &Frame; + const LValue *OldThis; + }; + /// A partial diagnostic which we might know in advance that we are not going /// to emit. class OptionalDiagnostic { @@ -2397,6 +2413,8 @@ public: { return StmtVisitorTy::Visit(E->getReplacement()); } RetTy VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) { return StmtVisitorTy::Visit(E->getExpr()); } + RetTy VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *E) + { return StmtVisitorTy::Visit(E->getExpr()); } // We cannot create any objects for which cleanups are required, so there is // nothing to do here; all cleanups must come from unevaluated subexpressions. RetTy VisitExprWithCleanups(const ExprWithCleanups *E) @@ -3398,12 +3416,20 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { // If the initializer list for a union does not contain any elements, the // first element of the union is value-initialized. + // FIXME: The element should be initialized from an initializer list. + // Is this difference ever observable for initializer lists which + // we don't build? ImplicitValueInitExpr VIE(Field->getType()); const Expr *InitExpr = E->getNumInits() ? E->getInit(0) : &VIE; LValue Subobject = This; if (!HandleLValueMember(Info, InitExpr, Subobject, Field, &Layout)) return false; + + // Temporarily override This, in case there's a CXXDefaultInitExpr in here. + ThisOverrideRAII ThisOverride(*Info.CurrentCall, &This, + isa<CXXDefaultInitExpr>(InitExpr)); + return EvaluateInPlace(Result.getUnionValue(), Info, Subobject, InitExpr); } @@ -3433,10 +3459,14 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { // Perform an implicit value-initialization for members beyond the end of // the initializer list. ImplicitValueInitExpr VIE(HaveInit ? Info.Ctx.IntTy : Field->getType()); + const Expr *Init = HaveInit ? E->getInit(ElementNo++) : &VIE; - if (!EvaluateInPlace( - Result.getStructField(Field->getFieldIndex()), - Info, Subobject, HaveInit ? E->getInit(ElementNo++) : &VIE)) { + // Temporarily override This, in case there's a CXXDefaultInitExpr in here. + ThisOverrideRAII ThisOverride(*Info.CurrentCall, &This, + isa<CXXDefaultInitExpr>(Init)); + + if (!EvaluateInPlace(Result.getStructField(Field->getFieldIndex()), Info, + Subobject, Init)) { if (!Info.keepEvaluatingAfterFailure()) return false; Success = false; @@ -6807,6 +6837,8 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) { } case Expr::CXXDefaultArgExprClass: return CheckICE(cast<CXXDefaultArgExpr>(E)->getExpr(), Ctx); + case Expr::CXXDefaultInitExprClass: + return CheckICE(cast<CXXDefaultInitExpr>(E)->getExpr(), Ctx); case Expr::ChooseExprClass: { return CheckICE(cast<ChooseExpr>(E)->getChosenSubExpr(Ctx), Ctx); } diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index e82b017e4a..0b77933f0b 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -2473,6 +2473,10 @@ recurse: mangleExpression(cast<CXXDefaultArgExpr>(E)->getExpr(), Arity); break; + case Expr::CXXDefaultInitExprClass: + mangleExpression(cast<CXXDefaultInitExpr>(E)->getExpr(), Arity); + break; + case Expr::SubstNonTypeTemplateParmExprClass: mangleExpression(cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement(), Arity); diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index 1b2285c794..95bacb64b3 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -1314,7 +1314,11 @@ void StmtPrinter::VisitCXXThrowExpr(CXXThrowExpr *Node) { } void StmtPrinter::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *Node) { - // Nothing to print: we picked up the default argument + // Nothing to print: we picked up the default argument. +} + +void StmtPrinter::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *Node) { + // Nothing to print: we picked up the default initializer. } void StmtPrinter::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *Node) { diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp index d99400c603..8ade242d56 100644 --- a/lib/AST/StmtProfile.cpp +++ b/lib/AST/StmtProfile.cpp @@ -789,6 +789,11 @@ void StmtProfiler::VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *S) { VisitDecl(S->getParam()); } +void StmtProfiler::VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *S) { + VisitExpr(S); + VisitDecl(S->getField()); +} + void StmtProfiler::VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *S) { VisitExpr(S); VisitDecl( diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp index 1adb8b84e4..096c7a080b 100644 --- a/lib/Analysis/CFG.cpp +++ b/lib/Analysis/CFG.cpp @@ -1085,11 +1085,16 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) { return VisitExprWithCleanups(cast<ExprWithCleanups>(S), asc); case Stmt::CXXDefaultArgExprClass: + case Stmt::CXXDefaultInitExprClass: // FIXME: The expression inside a CXXDefaultArgExpr is owned by the // called function's declaration, not by the caller. If we simply add // this expression to the CFG, we could end up with the same Expr // appearing multiple times. // PR13385 / <rdar://problem/12156507> + // + // It's likewise possible for multiple CXXDefaultInitExprs for the same + // expression to be used in the same function (through aggregate + // initialization). return VisitStmt(S, asc); case Stmt::CXXBindTemporaryExprClass: diff --git a/lib/CodeGen/CGClass.cpp b/lib/CodeGen/CGClass.cpp index 05925567b0..2b9a55b2a6 100644 --- a/lib/CodeGen/CGClass.cpp +++ b/lib/CodeGen/CGClass.cpp @@ -1139,6 +1139,7 @@ void CodeGenFunction::EmitCtorPrologue(const CXXConstructorDecl *CD, InitializeVTablePointers(ClassDecl); // And finally, initialize class members. + FieldConstructionScope FCS(*this, CXXThisValue); ConstructorMemcpyizer CM(*this, CD, Args); for (; B != E; B++) { CXXCtorInitializer *Member = (*B); diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index a0725b5f9c..ea13de17ab 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -903,6 +903,10 @@ LValue CodeGenFunction::EmitLValue(const Expr *E) { return EmitNullInitializationLValue(cast<CXXScalarValueInitExpr>(E)); case Expr::CXXDefaultArgExprClass: return EmitLValue(cast<CXXDefaultArgExpr>(E)->getExpr()); + case Expr::CXXDefaultInitExprClass: { + CXXDefaultInitExprScope Scope(*this); + return EmitLValue(cast<CXXDefaultInitExpr>(E)->getExpr()); + } case Expr::CXXTypeidExprClass: return EmitCXXTypeidLValue(cast<CXXTypeidExpr>(E)); diff --git a/lib/CodeGen/CGExprAgg.cpp b/lib/CodeGen/CGExprAgg.cpp index 6b29b3155a..b974e1dcc6 100644 --- a/lib/CodeGen/CGExprAgg.cpp +++ b/lib/CodeGen/CGExprAgg.cpp @@ -170,6 +170,10 @@ public: void VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE) { Visit(DAE->getExpr()); } + void VisitCXXDefaultInitExpr(CXXDefaultInitExpr *DIE) { + CodeGenFunction::CXXDefaultInitExprScope Scope(CGF); + Visit(DIE->getExpr()); + } void VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E); void VisitCXXConstructExpr(const CXXConstructExpr *E); void VisitLambdaExpr(LambdaExpr *E); @@ -1189,7 +1193,10 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) { // the optimizer, especially with bitfields. unsigned NumInitElements = E->getNumInits(); RecordDecl *record = E->getType()->castAs<RecordType>()->getDecl(); - + + // Prepare a 'this' for CXXDefaultInitExprs. + CodeGenFunction::FieldConstructionScope FCS(CGF, Dest.getAddr()); + if (record->isUnion()) { // Only initialize one field of a union. The field itself is // specified by the initializer list. diff --git a/lib/CodeGen/CGExprComplex.cpp b/lib/CodeGen/CGExprComplex.cpp index 5fc73aa790..36f974a313 100644 --- a/lib/CodeGen/CGExprComplex.cpp +++ b/lib/CodeGen/CGExprComplex.cpp @@ -182,6 +182,10 @@ public: ComplexPairTy VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE) { return Visit(DAE->getExpr()); } + ComplexPairTy VisitCXXDefaultInitExpr(CXXDefaultInitExpr *DIE) { + CodeGenFunction::CXXDefaultInitExprScope Scope(CGF); + return Visit(DIE->getExpr()); + } ComplexPairTy VisitExprWithCleanups(ExprWithCleanups *E) { CGF.enterFullExpression(E); CodeGenFunction::RunCleanupsScope Scope(CGF); diff --git a/lib/CodeGen/CGExprConstant.cpp b/lib/CodeGen/CGExprConstant.cpp index faaf6468f1..f5c8187c26 100644 --- a/lib/CodeGen/CGExprConstant.cpp +++ b/lib/CodeGen/CGExprConstant.cpp @@ -757,6 +757,12 @@ public: return Visit(DAE->getExpr()); } + llvm::Constant *VisitCXXDefaultInitExpr(CXXDefaultInitExpr *DIE) { + // No need for a DefaultInitExprScope: we don't handle 'this' in a + // constant expression. + return Visit(DIE->getExpr()); + } + llvm::Constant *VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E) { return Visit(E->GetTemporaryExpr()); } diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp index ea1eae8088..c1c252d12b 100644 --- a/lib/CodeGen/CGExprScalar.cpp +++ b/lib/CodeGen/CGExprScalar.cpp @@ -344,6 +344,10 @@ public: Value *VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE) { return Visit(DAE->getExpr()); } + Value *VisitCXXDefaultInitExpr(CXXDefaultInitExpr *DIE) { + CodeGenFunction::CXXDefaultInitExprScope Scope(CGF); + return Visit(DIE->getExpr()); + } Value *VisitCXXThisExpr(CXXThisExpr *TE) { return CGF.LoadCXXThis(); } diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp index cda3ce0286..a12e7dc445 100644 --- a/lib/CodeGen/CodeGenFunction.cpp +++ b/lib/CodeGen/CodeGenFunction.cpp @@ -45,6 +45,7 @@ CodeGenFunction::CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext) DidCallStackSave(false), IndirectBranch(0), SwitchInsn(0), CaseRangeBlock(0), UnreachableBlock(0), CXXABIThisDecl(0), CXXABIThisValue(0), CXXThisValue(0), + CXXDefaultInitExprThis(0), CXXStructorImplicitParamDecl(0), CXXStructorImplicitParamValue(0), OutermostConditional(0), CurLexicalScope(0), TerminateLandingPad(0), TerminateHandler(0), TrapBB(0) { diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 6e2d62760f..f0c2c1e672 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -1206,12 +1206,53 @@ private: /// lazily by getUnreachableBlock(). llvm::BasicBlock *UnreachableBlock; +public: + /// A scope within which we are constructing the fields of an object which + /// might use a CXXDefaultInitExpr. This stashes away a 'this' value to use + /// if we need to evaluate a CXXDefaultInitExpr within the evaluation. + class FieldConstructionScope { + public: + FieldConstructionScope(CodeGenFunction &CGF, llvm::Value *This) + : CGF(CGF), OldCXXDefaultInitExprThis(CGF.CXXDefaultInitExprThis) { + CGF.CXXDefaultInitExprThis = This; + } + ~FieldConstructionScope() { + CGF.CXXDefaultInitExprThis = OldCXXDefaultInitExprThis; + } + + private: + CodeGenFunction &CGF; + llvm::Value *OldCXXDefaultInitExprThis; + }; + + /// The scope of a CXXDefaultInitExpr. Within this scope, the value of 'this' + /// is overridden to be the object under construction. + class CXXDefaultInitExprScope { + public: + CXXDefaultInitExprScope(CodeGenFunction &CGF) + : CGF(CGF), OldCXXThisValue(CGF.CXXThisValue) { + CGF.CXXThisValue = CGF.CXXDefaultInitExprThis; + } + ~CXXDefaultInitExprScope() { + CGF.CXXThisValue = OldCXXThisValue; + } + + public: + CodeGenFunction &CGF; + llvm::Value *OldCXXThisValue; + }; + +private: /// CXXThisDecl - When generating code for a C++ member function, /// this will hold the implicit 'this' declaration. ImplicitParamDecl *CXXABIThisDecl; llvm::Value *CXXABIThisValue; llvm::Value *CXXThisValue; + /// The value of 'this' to use when evaluating CXXDefaultInitExprs within + /// this expression. + llvm::Value *CXXDefaultInitExprThis; + /// CXXStructorImplicitParamDecl - When generating code for a constructor or /// destructor, this will hold the implicit argument (e.g. VTT). ImplicitParamDecl *CXXStructorImplicitParamDecl; diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 94a507449b..b29ce23fe9 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -3054,7 +3054,7 @@ struct BaseAndFieldInfo { AllToInit.push_back(Init); // Check whether this initializer makes the field "used". - if (Init->getInit() && Init->getInit()->HasSideEffects(S.Context)) + if (Init->getInit()->HasSideEffects(S.Context)) S.UnusedPrivateFields.remove(Init->getAnyMember()); return false; @@ -3103,16 +3103,18 @@ static bool CollectFieldInitializer(Sema &SemaRef, BaseAndFieldInfo &Info, // has a brace-or-equal-initializer, the entity is initialized as specified // in [dcl.init]. if (Field->hasInClassInitializer() && !Info.isImplicitCopyOrMove()) { + Expr *DIE = CXXDefaultInitExpr::Create(SemaRef.Context, + Info.Ctor->getLocation(), Field); CXXCtorInitializer *Init; if (Indirect) Init = new (SemaRef.Context) CXXCtorInitializer(SemaRef.Context, Indirect, SourceLocation(), - SourceLocation(), 0, + SourceLocation(), DIE, SourceLocation()); else Init = new (SemaRef.Context) CXXCtorInitializer(SemaRef.Context, Field, SourceLocation(), - SourceLocation(), 0, + SourceLocation(), DIE, SourceLocation()); return Info.addFieldInitializer(Init); } diff --git a/lib/Sema/SemaExceptionSpec.cpp b/lib/Sema/SemaExceptionSpec.cpp index 4758718006..1a5f4824d0 100644 --- a/lib/Sema/SemaExceptionSpec.cpp +++ b/lib/Sema/SemaExceptionSpec.cpp @@ -1011,7 +1011,6 @@ CanThrowResult Sema::canThrow(const Expr *E) { case Expr::ConditionalOperatorClass: case Expr::CompoundLiteralExprClass: case Expr::CXXConstCastExprClass: - case Expr::CXXDefaultArgExprClass: case Expr::CXXReinterpretCastExprClass: case Expr::DesignatedInitExprClass: case Expr::ExprWithCleanupsClass: @@ -1044,6 +1043,12 @@ CanThrowResult Sema::canThrow(const Expr *E) { case Expr::StmtExprClass: return CT_Can; + case Expr::CXXDefaultArgExprClass: + return canThrow(cast<CXXDefaultArgExpr>(E)->getExpr()); + + case Expr::CXXDefaultInitExprClass: + return canThrow(cast<CXXDefaultInitExpr>(E)->getExpr()); + case Expr::ChooseExprClass: if (E->isTypeDependent() || E->isValueDependent()) return CT_Dependent; diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 5a5fd26835..0e513992ba 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -293,6 +293,21 @@ void InitListChecker::FillInValueInitForField(unsigned Init, FieldDecl *Field, InitializedEntity MemberEntity = InitializedEntity::InitializeMember(Field, &ParentEntity); if (Init >= NumInits || !ILE->getInit(Init)) { + // If there's no explicit initializer but we have a default initializer, use + // that. This only happens in C++1y, since classes with default + // initializers are not aggregates in C++11. + if (Field->hasInClassInitializer()) { + Expr *DIE = CXXDefaultInitExpr::Create(SemaRef.Context, + ILE->getRBraceLoc(), Field); + if (Init < NumInits) + ILE->setInit(Init, DIE); + else { + ILE->updateInit(SemaRef.Context, Init, DIE); + RequiresSecondPass = true; + } + return; + } + // FIXME: We probably don't need to handle references // specially here, since value-initialization of references is // handled in InitializationSequence. @@ -358,15 +373,24 @@ InitListChecker::FillInValueInitializations(const InitializedEntity &Entity, Loc = ILE->getSyntacticForm()->getLocStart(); if (const RecordType *RType = ILE->getType()->getAs<RecordType>()) { - if (RType->getDecl()->isUnion() && - ILE->getInitializedFieldInUnion()) + const RecordDecl *RDecl = RType->getDecl(); + if (RDecl->isUnion() && ILE->getInitializedFieldInUnion()) FillInValueInitForField(0, ILE->getInitializedFieldInUnion(), Entity, ILE, RequiresSecondPass); - else { + else if (RDecl->isUnion() && isa<CXXRecordDecl>(RDecl) && + cast<CXXRecordDecl>(RDecl)->hasInClassInitializer()) { + for (RecordDecl::field_iterator Field = RDecl->field_begin(), + FieldEnd = RDecl->field_end(); + Field != FieldEnd; ++Field) { + if (Field->hasInClassInitializer()) { + FillInValueInitForField(0, *Field, Entity, ILE, RequiresSecondPass); + break; + } + } + } else { unsigned Init = 0; - for (RecordDecl::field_iterator - Field = RType->getDecl()->field_begin(), - FieldEnd = RType->getDecl()->field_end(); + for (RecordDecl::field_iterator Field = RDecl->field_begin(), + FieldEnd = RDecl->field_end(); Field != FieldEnd; ++Field) { if (Field->isUnnamedBitfield()) continue; @@ -381,7 +405,7 @@ InitListChecker::FillInValueInitializations(const InitializedEntity &Entity, ++Init; // Only look at the first initialization of a union. - if (RType->getDecl()->isUnion()) + if (RDecl->isUnion()) break; } } @@ -1323,8 +1347,23 @@ void InitListChecker::CheckStructUnionTypes(const InitializedEntity &Entity, } if (DeclType->isUnionType() && IList->getNumInits() == 0) { - // Value-initialize the first named member of the union. RecordDecl *RD = DeclType->getAs<RecordType>()->getDecl(); + + // If there's a default initializer, use it. + if (isa<CXXRecordDecl>(RD) && cast<CXXRecordDecl>(RD)->hasInClassInitializer()) { + if (VerifyOnly) + return; + for (RecordDecl::field_iterator FieldEnd = RD->field_end(); + Field != FieldEnd; ++Field) { + if (Field->hasInClassInitializer()) { + StructuredList->setInitializedFieldInUnion(*Field); + // FIXME: Actually build a CXXDefaultInitExpr? + return; + } + } + } + + // Value-initialize the first named member of the union. for (RecordDecl::field_iterator FieldEnd = RD->field_end(); Field != FieldEnd; ++Field) { if (Field->getDeclName()) { @@ -1428,7 +1467,7 @@ void InitListChecker::CheckStructUnionTypes(const InitializedEntity &Entity, // Find first (if any) named field and emit warning. for (RecordDecl::field_iterator it = Field, end = RD->field_end(); it != end; ++it) { - if (!it->isUnnamedBitfield()) { + if (!it->isUnnamedBitfield() && !it->hasInClassInitializer()) { SemaRef.Diag(IList->getSourceRange().getEnd(), diag::warn_missing_field_initializers) << it->getName(); break; @@ -1441,7 +1480,7 @@ void InitListChecker::CheckStructUnionTypes(const InitializedEntity &Entity, !Field->getType()->isIncompleteArrayType()) { // FIXME: Should check for holes left by designated initializers too. for (; Field != FieldEnd && !hadError; ++Field) { - if (!Field->isUnnamedBitfield()) + if (!Field->isUnnamedBitfield() && !Field->hasInClassInitializer()) CheckValueInitializable( InitializedEntity::InitializeMember(*Field, &Entity)); } diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 55f1587a85..97316f89b3 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -1980,6 +1980,17 @@ public: Param)); } + /// \brief Build a new C++11 default-initialization expression. + /// + /// By default, builds a new default field initialization expression, which + /// does not require any semantic analysis. Subclasses may override this + /// routine to provide different behavior. + ExprResult RebuildCXXDefaultInitExpr(SourceLocation Loc, + FieldDecl *Field) { + return getSema().Owned(CXXDefaultInitExpr::Create(getSema().Context, Loc, + Field)); + } + /// \brief Build a new C++ zero-initialization expression. /// /// By default, performs semantic analysis to build the new expression. @@ -7262,6 +7273,21 @@ TreeTransform<Derived>::TransformCXXDefaultArgExpr(CXXDefaultArgExpr *E) { template<typename Derived> ExprResult +TreeTransform<Derived>::TransformCXXDefaultInitExpr(CXXDefaultInitExpr *E) { + FieldDecl *Field + = cast_or_null<FieldDecl>(getDerived().TransformDecl(E->getLocStart(), + E->getField())); + if (!Field) + return ExprError(); + + if (!getDerived().AlwaysRebuild() && Field == E->getField()) + return SemaRef.Owned(E); + + return getDerived().RebuildCXXDefaultInitExpr(E->getExprLoc(), Field); +} + +template<typename Derived> +ExprResult TreeTransform<Derived>::TransformCXXScalarValueInitExpr( CXXScalarValueInitExpr *E) { TypeSourceInfo *T = getDerived().TransformType(E->getTypeSourceInfo()); diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp index b18114fdcb..c7748b7f6b 100644 --- a/lib/Serialization/ASTReaderStmt.cpp +++ b/lib/Serialization/ASTReaderStmt.cpp @@ -1230,6 +1230,12 @@ void ASTStmtReader::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) { E->Loc = ReadSourceLocation(Record, Idx); } +void ASTStmtReader::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E) { + VisitExpr(E); + E->Field = ReadDeclAs<FieldDecl>(Record, Idx); + E->Loc = ReadSourceLocation(Record, Idx); +} + void ASTStmtReader::VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E) { VisitExpr(E); E->setTemporary(Reader.ReadCXXTemporary(F, Record, Idx)); @@ -2111,6 +2117,9 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { S = new (Context) CXXDefaultArgExpr(Empty); break; } + case EXPR_CXX_DEFAULT_INIT: + S = new (Context) CXXDefaultInitExpr(Empty); + break; case EXPR_CXX_BIND_TEMPORARY: S = new (Context) CXXBindTemporaryExpr(Empty); break; diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp index 61ddec0fb0..5c8e213384 100644 --- a/lib/Serialization/ASTWriterStmt.cpp +++ b/lib/Serialization/ASTWriterStmt.cpp @@ -1223,6 +1223,13 @@ void ASTStmtWriter::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) { Code = serialization::EXPR_CXX_DEFAULT_ARG; } +void ASTStmtWriter::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E) { + VisitExpr(E); + Writer.AddDeclRef(E->getField(), Record); + Writer.AddSourceLocation(E->getExprLoc(), Record); + Code = serialization::EXPR_CXX_DEFAULT_INIT; +} + void ASTStmtWriter::VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E) { VisitExpr(E); Writer.AddCXXTemporary(E->getTemporary(), Record); diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 92a3a952df..5b6e97d3fb 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -604,6 +604,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, switch (S->getStmtClass()) { // C++ and ARC stuff we don't support yet. case Expr::ObjCIndirectCopyRestoreExprClass: + case Stmt::CXXDefaultInitExprClass: case Stmt::CXXDependentScopeMemberExprClass: case Stmt::CXXPseudoDestructorExprClass: case Stmt::CXXTryStmtClass: diff --git a/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp b/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp index fef3692609..8767678362 100644 --- a/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp +++ b/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1y %s -DCXX1Y // An aggregate is an array or a class... struct Aggr { @@ -18,9 +19,6 @@ struct NonAggr1a { // expected-note 2 {{candidate constructor}} NonAggr1a(int, int); // expected-note {{candidate constructor}} int k; }; -// In C++0x, 'user-provided' is only defined for special member functions, so -// this type is considered to be an aggregate. This is considered to be -// a language defect. NonAggr1a na1a = { 42 }; // expected-error {{no matching constructor for initialization of 'NonAggr1a'}} struct NonAggr1b { @@ -30,10 +28,15 @@ struct NonAggr1b { NonAggr1b na1b = { 42 }; // expected-error {{no matching constructor for initialization of 'NonAggr1b'}} // no brace-or-equal-initializers for non-static data members, ... -struct NonAggr2 { // expected-note 3 {{candidate constructor}} +// Note, this bullet was removed in C++1y. +struct NonAggr2 { int m = { 123 }; }; -NonAggr2 na2 = { 42 }; // expected-error {{no matching constructor for initialization of 'NonAggr2'}} +NonAggr2 na2 = { 42 }; +#ifndef CXX1Y +// expected-error@-2 {{no matching constructor for initialization of 'NonAggr2'}} +// expected-note@-6 3 {{candidate constructor}} +#endif // no private... struct NonAggr3 { // expected-note 3 {{candidate constructor}} diff --git a/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p7.cpp b/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p7.cpp new file mode 100644 index 0000000000..d1fbe766d5 --- /dev/null +++ b/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p7.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -std=c++1y %s -verify + +// expected-no-diagnostics + +struct S { int a; const char *b; int c; int d = b[a]; }; +constexpr S ss = { 1, "asdf" }; + +static_assert(ss.a == 1, ""); +static_assert(ss.b[2] == 'd', ""); +static_assert(ss.c == 0, ""); +static_assert(ss.d == 's', ""); + +struct X { int i, j, k = 42; }; +constexpr X a[] = { 1, 2, 3, 4, 5, 6 }; +constexpr X b[2] = { { 1, 2, 3 }, { 4, 5, 6 } }; + +constexpr bool operator==(X a, X b) { + return a.i == b.i && a.j == b.j && a.k == b.k; +} + +static_assert(sizeof(a) == sizeof(b), ""); +static_assert(a[0] == b[0], ""); +static_assert(a[1] == b[1], ""); diff --git a/test/CodeGenCXX/cxx1y-initializer-aggregate.cpp b/test/CodeGenCXX/cxx1y-initializer-aggregate.cpp new file mode 100644 index 0000000000..ef78c434e3 --- /dev/null +++ b/test/CodeGenCXX/cxx1y-initializer-aggregate.cpp @@ -0,0 +1,74 @@ +// RUN: %clang_cc1 -std=c++1y %s -triple x86_64-linux-gnu -emit-llvm -o - | FileCheck %s + +struct A { + int n = 0; + const char *p; + char k = p[n]; + int f(); + int x = f(); + union { + char c; + double d = 1.0; + }; +}; + +int f(); + +union B { + int a; + int f(); + int b = f(); +}; + +A a { .p = "foobar" }; +A b { 4, "bazquux", .x = 42, .c = 9 }; +A c { 1, 0, 'A', f(), { 3 } }; + +// CHECK: @[[STR_A:.*]] = {{.*}} [7 x i8] c"foobar\00" +// CHECK: @[[STR_B:.*]] = {{.*}} [8 x i8] c"bazquux\00" + +B x; +B y {}; +B z { 1 }; +// CHECK: @z = global {{.*}} { i32 1 } + +// Initialization of 'a': + +// CHECK: store i32 0, i32* getelementptr inbounds ({{.*}} @a, i32 0, i32 0) +// CHECK: store i8* {{.*}} @[[STR_A]]{{.*}}, i8** getelementptr inbounds ({{.*}} @a, i32 0, i32 1) +// CHECK: load i32* getelementptr inbounds ({{.*}} @a, i32 0, i32 0) +// CHECK: load i8** getelementptr inbounds ({{.*}} @a, i32 0, i32 1) +// CHECK: getelementptr inbounds i8* %{{.*}}, {{.*}} %{{.*}} +// CHECK: store i8 %{{.*}}, i8* getelementptr inbounds ({{.*}} @a, i32 0, i32 2) +// CHECK: call i32 @_ZN1A1fEv({{.*}} @a) +// CHECK: store i32 %{{.*}}, i32* getelementptr inbounds ({{.*}}* @a, i32 0, i32 3) +// CHECK: call void @{{.*}}C1Ev({{.*}} getelementptr inbounds (%struct.A* @a, i32 0, i32 4)) + +// Initialization of 'b': + +// CHECK: store i32 4, i32* getelementptr inbounds ({{.*}} @b, i32 0, i32 0) +// CHECK: store i8* {{.*}} @[[STR_B]]{{.*}}, i8** getelementptr inbounds ({{.*}} @b, i32 0, i32 1) +// CHECK: load i32* getelementptr inbounds ({{.*}} @b, i32 0, i32 0) +// CHECK: load i8** getelementptr inbounds ({{.*}} @b, i32 0, i32 1) +// CHECK: getelementptr inbounds i8* %{{.*}}, {{.*}} %{{.*}} +// CHECK: store i8 %{{.*}}, i8* getelementptr inbounds ({{.*}} @b, i32 0, i32 2) +// CHECK-NOT: @_ZN1A1fEv +// CHECK: store i32 42, i32* getelementptr inbounds ({{.*}}* @b, i32 0, i32 3) +// CHECK-NOT: C1Ev +// CHECK: store i8 9, i8* {{.*}} @b, i32 0, i32 4) + +// Initialization of 'c': + +// CHECK: store i32 1, i32* getelementptr inbounds ({{.*}} @c, i32 0, i32 0) +// CHECK: store i8* null, i8** getelementptr inbounds ({{.*}} @c, i32 0, i32 1) +// CHECK-NOT: load +// CHECK: store i8 65, i8* getelementptr inbounds ({{.*}} @c, i32 0, i32 2) +// CHECK: call i32 @_Z1fv() +// CHECK: store i32 %{{.*}}, i32* getelementptr inbounds ({{.*}}* @c, i32 0, i32 3) +// CHECK-NOT: C1Ev +// CHECK: store i8 3, i8* {{.*}} @c, i32 0, i32 4) + +// CHECK: call void @_ZN1BC1Ev({{.*}} @x) + +// CHECK: call i32 @_ZN1B1fEv({{.*}} @y) +// CHECK: store i32 %{{.*}}, i32* getelementptr inbounds ({{.*}} @y, i32 0, i32 0) diff --git a/test/SemaCXX/cxx1y-initializer-aggregates.cpp b/test/SemaCXX/cxx1y-initializer-aggregates.cpp new file mode 100644 index 0000000000..9b542403de --- /dev/null +++ b/test/SemaCXX/cxx1y-initializer-aggregates.cpp @@ -0,0 +1,63 @@ +// RUN: %clang_cc1 -std=c++1y %s -verify + +namespace in_class_init { + union U { char c; double d = 4.0; }; + constexpr U u1 = U(); + constexpr U u2 {}; + constexpr U u3 { 'x' }; + static_assert(u1.d == 4.0, ""); + static_assert(u2.d == 4.0, ""); + static_assert(u3.c == 'x', ""); + + struct A { + int n = 5; + int m = n * 3; + union { + char c; + double d = 4.0; + }; + }; + constexpr A a1 {}; + constexpr A a2 { 8 }; + constexpr A a3 { 1, 2, { 3 } }; + constexpr A a4 { 1, 2, { .d = 3.0 } }; + static_assert(a1.d == 4.0, ""); + static_assert(a2.m == 24, ""); + static_assert(a2.d == 4.0, ""); + static_assert(a3.c == 3, ""); + static_assert(a3.d == 4.0, ""); // expected-error {{constant expression}} expected-note {{active member 'c'}} + static_assert(a4.d == 3.0, ""); + + struct B { + int n; + constexpr int f() { return n * 5; } + int m = f(); + }; + B b1 {}; + constexpr B b2 { 2 }; + B b3 { 1, 2 }; + static_assert(b2.m == 10, ""); + + struct C { + int k; + union { + int l = k; // expected-error {{invalid use of non-static}} + }; + }; +} + +namespace nested_aggregate_init { + struct A { + int n = 5; + int b = n * 3; + }; + struct B { + constexpr B(int k) : d(1.23), k(k) {} + // Within this aggregate, both this object's 'this' and the temporary's + // 'this' are used. + constexpr int f() const { return A{k}.b; } + double d; + int k; + }; + static_assert(B(6).f() == 18, ""); +} diff --git a/tools/libclang/CXCursor.cpp b/tools/libclang/CXCursor.cpp index edcc85b45e..e490b5da3d 100644 --- a/tools/libclang/CXCursor.cpp +++ b/tools/libclang/CXCursor.cpp @@ -215,6 +215,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::TypeTraitExprClass: case Stmt::CXXBindTemporaryExprClass: case Stmt::CXXDefaultArgExprClass: + case Stmt::CXXDefaultInitExprClass: case Stmt::CXXScalarValueInitExprClass: case Stmt::CXXUuidofExprClass: case Stmt::ChooseExprClass: diff --git a/tools/libclang/RecursiveASTVisitor.h b/tools/libclang/RecursiveASTVisitor.h index 6c9da93b73..dd2c836d9c 100644 --- a/tools/libclang/RecursiveASTVisitor.h +++ b/tools/libclang/RecursiveASTVisitor.h @@ -2091,6 +2091,7 @@ DEF_TRAVERSE_STMT(CompoundLiteralExpr, { }) DEF_TRAVERSE_STMT(CXXBindTemporaryExpr, { }) DEF_TRAVERSE_STMT(CXXBoolLiteralExpr, { }) DEF_TRAVERSE_STMT(CXXDefaultArgExpr, { }) +DEF_TRAVERSE_STMT(CXXDefaultInitExpr, { }) DEF_TRAVERSE_STMT(CXXDeleteExpr, { }) DEF_TRAVERSE_STMT(ExprWithCleanups, { }) DEF_TRAVERSE_STMT(CXXNullPtrLiteralExpr, { }) diff --git a/www/cxx_status.html b/www/cxx_status.html index edd93e7f1f..8a7acc1090 100644 --- a/www/cxx_status.html +++ b/www/cxx_status.html @@ -457,7 +457,7 @@ ISO/IEC JTC1/SC22/WG21 post-Bristol mailing ships.</p> <tr> <td>Member initializers and aggregates</td> <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3653.html">N3653</a></td> - <td class="none" align="center">No</td> + <td class="svn" align="center">SVN</td> </tr> <tr> <td>Clarifying memory allocation</td> |