From 13dc8f98f6108dca8aaa9721567ed5a2d9911e0f Mon Sep 17 00:00:00 2001 From: Sebastian Redl Date: Sun, 27 Nov 2011 16:50:07 +0000 Subject: [PATCH] Reference initialization with initializer lists. This supports single-element initializer lists for references according to DR1288, as well as creating temporaries and binding to them for other initializer lists. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@145186 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 3 + include/clang/Sema/Initialization.h | 14 +- lib/AST/ExprClassification.cpp | 34 ++- lib/CodeGen/CGExpr.cpp | 13 +- lib/Sema/SemaDecl.cpp | 2 +- lib/Sema/SemaInit.cpp | 235 ++++++++++++++++-- .../cxx0x-initializer-references.cpp | 69 +++++ test/SemaCXX/cxx0x-initializer-references.cpp | 31 +++ 8 files changed, 362 insertions(+), 39 deletions(-) create mode 100644 test/CodeGenCXX/cxx0x-initializer-references.cpp create mode 100644 test/SemaCXX/cxx0x-initializer-references.cpp diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 99319b0463..6a5cd22930 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1034,6 +1034,9 @@ def err_lvalue_to_rvalue_ambig_ref : Error<"rvalue reference cannot bind to lval def err_not_reference_to_const_init : Error< "%select{non-const|volatile}0 lvalue reference to type %1 cannot be " "initialized with a %select{value|temporary}2 of type %3">; +def err_lvalue_reference_bind_to_initlist : Error< + "%select{non-const|volatile}0 lvalue reference to type %1 cannot bind to an " + "initializer list temporary">; def err_lvalue_reference_bind_to_temporary : Error< "%select{non-const|volatile}0 lvalue reference to type %1 cannot bind to a " "temporary of type %2">; diff --git a/include/clang/Sema/Initialization.h b/include/clang/Sema/Initialization.h index 91d7677387..99bed038f1 100644 --- a/include/clang/Sema/Initialization.h +++ b/include/clang/Sema/Initialization.h @@ -530,6 +530,10 @@ public: SK_ListInitialization, /// \brief Perform list-initialization with a constructor. SK_ListConstructorCall, + /// \brief Unwrap the single-element initializer list for a reference. + SK_UnwrapInitList, + /// \brief Rewrap the single-element initializer list for a reference. + SK_RewrapInitList, /// \brief Perform initialization via a constructor. SK_ConstructorInitialization, /// \brief Zero-initialize the object @@ -579,8 +583,12 @@ public: } Function; /// \brief When Kind = SK_ConversionSequence, the implicit conversion - /// sequence + /// sequence. ImplicitConversionSequence *ICS; + + /// \brief When Kind = SK_RewrapInitList, the syntactic form of the + /// wrapping list. + InitListExpr *WrappingSyntacticList; }; void Destroy(); @@ -853,6 +861,10 @@ public: /// retaining it). void AddProduceObjCObjectStep(QualType T); + /// \brief Add steps to unwrap a initializer list for a reference around a + /// single element and rewrap it at the end. + void RewrapReferenceInitList(QualType T, InitListExpr *Syntactic); + /// \brief Note that this initialization sequence failed. void SetFailed(FailureKind Failure) { SequenceKind = FailedSequence; diff --git a/lib/AST/ExprClassification.cpp b/lib/AST/ExprClassification.cpp index 1251b96fba..f5ee3e5dee 100644 --- a/lib/AST/ExprClassification.cpp +++ b/lib/AST/ExprClassification.cpp @@ -93,7 +93,6 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { const LangOptions &Lang = Ctx.getLangOptions(); switch (E->getStmtClass()) { - // First come the expressions that are always lvalues, unconditionally. case Stmt::NoStmtClass: #define ABSTRACT_STMT(Kind) #define STMT(Kind, Base) case Expr::Kind##Class: @@ -101,6 +100,8 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { #include "clang/AST/StmtNodes.inc" llvm_unreachable("cannot classify a statement"); break; + + // First come the expressions that are always lvalues, unconditionally. case Expr::ObjCIsaExprClass: // C++ [expr.prim.general]p1: A string literal is an lvalue. case Expr::StringLiteralClass: @@ -122,6 +123,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { // FIXME: ObjC++0x might have different rules case Expr::ObjCIvarRefExprClass: return Cl::CL_LValue; + // C99 6.5.2.5p5 says that compound literals are lvalues. // In C++, they're class temporaries. case Expr::CompoundLiteralExprClass: @@ -157,7 +159,6 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::ObjCProtocolExprClass: case Expr::ObjCStringLiteralClass: case Expr::ParenListExprClass: - case Expr::InitListExprClass: case Expr::SizeOfPackExprClass: case Expr::SubstNonTypeTemplateParmPackExprClass: case Expr::AsTypeExprClass: @@ -229,8 +230,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { } case Expr::OpaqueValueExprClass: - return ClassifyExprValueKind(Lang, E, - cast(E)->getValueKind()); + return ClassifyExprValueKind(Lang, E, E->getValueKind()); // Pseudo-object expressions can produce l-values with reference magic. case Expr::PseudoObjectExprClass: @@ -240,8 +240,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { // Implicit casts are lvalues if they're lvalue casts. Other than that, we // only specifically record class temporaries. case Expr::ImplicitCastExprClass: - return ClassifyExprValueKind(Lang, E, - cast(E)->getValueKind()); + return ClassifyExprValueKind(Lang, E, E->getValueKind()); // C++ [expr.prim.general]p4: The presence of parentheses does not affect // whether the expression is an lvalue. @@ -337,29 +336,40 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::VAArgExprClass: return ClassifyUnnamed(Ctx, E->getType()); - + case Expr::DesignatedInitExprClass: return ClassifyInternal(Ctx, cast(E)->getInit()); - + case Expr::StmtExprClass: { const CompoundStmt *S = cast(E)->getSubStmt(); if (const Expr *LastExpr = dyn_cast_or_null(S->body_back())) return ClassifyUnnamed(Ctx, LastExpr->getType()); return Cl::CL_PRValue; } - + case Expr::CXXUuidofExprClass: return Cl::CL_LValue; - + case Expr::PackExpansionExprClass: return ClassifyInternal(Ctx, cast(E)->getPattern()); - + case Expr::MaterializeTemporaryExprClass: return cast(E)->isBoundToLvalueReference() ? Cl::CL_LValue : Cl::CL_XValue; + + case Expr::InitListExprClass: + // An init list can be an lvalue if it is bound to a reference and + // contains only one element. In that case, we look at that element + // for an exact classification. Init list creation takes care of the + // value kind for us, so we only need to fine-tune. + if (E->isRValue()) + return ClassifyExprValueKind(Lang, E, E->getValueKind()); + assert(cast(E)->getNumInits() == 1 && + "Only 1-element init lists can be glvalues."); + return ClassifyInternal(Ctx, cast(E)->getInit(0)); } - + llvm_unreachable("unhandled expression kind in classification"); return Cl::CL_LValue; } diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index e4f81b7a3f..bf0a34a6d0 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -210,6 +210,13 @@ EmitExprForReferenceBinding(CodeGenFunction &CGF, const Expr *E, const CXXDestructorDecl *&ReferenceTemporaryDtor, QualType &ObjCARCReferenceLifetimeType, const NamedDecl *InitializedDecl) { + // 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(E)) { + if (ILE->getNumInits() == 1 && ILE->isGLValue()) + E = ILE->getInit(0); + } + // Look through expressions for materialized temporaries (for now). if (const MaterializeTemporaryExpr *M = dyn_cast(E)) { @@ -669,6 +676,10 @@ LValue CodeGenFunction::EmitLValue(const Expr *E) { return EmitObjCEncodeExprLValue(cast(E)); case Expr::PseudoObjectExprClass: return EmitPseudoObjectLValue(cast(E)); + case Expr::InitListExprClass: + assert(cast(E)->getNumInits() == 1 && + "Only single-element init list can be lvalue."); + return EmitLValue(cast(E)->getInit(0)); case Expr::BlockDeclRefExprClass: return EmitBlockDeclRefLValue(cast(E)); @@ -728,7 +739,7 @@ LValue CodeGenFunction::EmitLValue(const Expr *E) { case Expr::CXXConstCastExprClass: case Expr::ObjCBridgedCastExprClass: return EmitCastLValue(cast(E)); - + case Expr::MaterializeTemporaryExprClass: return EmitMaterializeTemporaryExpr(cast(E)); } diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index cd2f071515..eaf778d130 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -6199,7 +6199,7 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, // "ary" transitions from a VariableArrayType to a ConstantArrayType. if (!VDecl->isInvalidDecl() && (DclT != SavT)) { VDecl->setType(DclT); - Init->setType(DclT); + Init->setType(DclT.getNonReferenceType()); } // Check any implicit conversions within the expression. diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 9480f63712..a95a257131 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -989,8 +989,7 @@ void InitListChecker::CheckReferenceType(const InitializedEntity &Entity, } Expr *expr = IList->getInit(Index); - if (isa(expr)) { - // FIXME: Allowed in C++11. + if (isa(expr) && !SemaRef.getLangOptions().CPlusPlus0x) { if (!VerifyOnly) SemaRef.Diag(IList->getLocStart(), diag::err_init_non_aggr_init_list) << DeclType << IList->getSourceRange(); @@ -2385,6 +2384,8 @@ void InitializationSequence::Step::Destroy() { case SK_QualificationConversionLValue: case SK_ListInitialization: case SK_ListConstructorCall: + case SK_UnwrapInitList: + case SK_RewrapInitList: case SK_ConstructorInitialization: case SK_ZeroInitialization: case SK_CAssignment: @@ -2761,6 +2762,21 @@ void InitializationSequence::AddProduceObjCObjectStep(QualType T) { Steps.push_back(S); } +void InitializationSequence::RewrapReferenceInitList(QualType T, + InitListExpr *Syntactic) { + assert(Syntactic->getNumInits() == 1 && + "Can only rewrap trivial init lists."); + Step S; + S.Kind = SK_UnwrapInitList; + S.Type = Syntactic->getInit(0)->getType(); + Steps.insert(Steps.begin(), S); + + S.Kind = SK_RewrapInitList; + S.Type = T; + S.WrappingSyntacticList = Syntactic; + Steps.push_back(S); +} + void InitializationSequence::SetOverloadFailure(FailureKind Failure, OverloadingResult Result) { setSequenceKind(FailedSequence); @@ -2799,6 +2815,114 @@ static void MaybeProduceObjCObject(Sema &S, } } +static bool +ResolveOverloadedFunctionForReferenceBinding(Sema &S, + Expr *Initializer, + QualType &SourceType, + QualType &UnqualifiedSourceType, + QualType UnqualifiedTargetType, + InitializationSequence &Sequence) { + if (S.Context.getCanonicalType(UnqualifiedSourceType) == + S.Context.OverloadTy) { + DeclAccessPair Found; + bool HadMultipleCandidates = false; + if (FunctionDecl *Fn + = S.ResolveAddressOfOverloadedFunction(Initializer, + UnqualifiedTargetType, + false, Found, + &HadMultipleCandidates)) { + Sequence.AddAddressOverloadResolutionStep(Fn, Found, + HadMultipleCandidates); + SourceType = Fn->getType(); + UnqualifiedSourceType = SourceType.getUnqualifiedType(); + } else if (!UnqualifiedTargetType->isRecordType()) { + Sequence.SetFailed(InitializationSequence::FK_AddressOfOverloadFailed); + return true; + } + } + return false; +} + +static void TryReferenceInitializationCore(Sema &S, + const InitializedEntity &Entity, + const InitializationKind &Kind, + Expr *Initializer, + QualType cv1T1, QualType T1, + Qualifiers T1Quals, + QualType cv2T2, QualType T2, + Qualifiers T2Quals, + InitializationSequence &Sequence); + +static void TryListInitialization(Sema &S, + const InitializedEntity &Entity, + const InitializationKind &Kind, + InitListExpr *InitList, + InitializationSequence &Sequence); + +/// \brief Attempt list initialization of a reference. +static void TryReferenceListInitialization(Sema &S, + const InitializedEntity &Entity, + const InitializationKind &Kind, + InitListExpr *InitList, + InitializationSequence &Sequence) +{ + // First, catch C++03 where this isn't possible. + if (!S.getLangOptions().CPlusPlus0x) { + Sequence.SetFailed(InitializationSequence::FK_ReferenceBindingToInitList); + return; + } + + QualType DestType = Entity.getType(); + QualType cv1T1 = DestType->getAs()->getPointeeType(); + Qualifiers T1Quals; + QualType T1 = S.Context.getUnqualifiedArrayType(cv1T1, T1Quals); + + // Reference initialization via an initializer list works thus: + // If the initializer list consists of a single element that is + // reference-related to the referenced type, bind directly to that element + // (possibly creating temporaries). + // Otherwise, initialize a temporary with the initializer list and + // bind to that. + if (InitList->getNumInits() == 1) { + Expr *Initializer = InitList->getInit(0); + QualType cv2T2 = Initializer->getType(); + Qualifiers T2Quals; + QualType T2 = S.Context.getUnqualifiedArrayType(cv2T2, T2Quals); + + // If this fails, creating a temporary wouldn't work either. + if (ResolveOverloadedFunctionForReferenceBinding(S, Initializer, cv2T2, T2, + T1, Sequence)) + return; + + SourceLocation DeclLoc = Initializer->getLocStart(); + bool dummy1, dummy2, dummy3; + Sema::ReferenceCompareResult RefRelationship + = S.CompareReferenceRelationship(DeclLoc, cv1T1, cv2T2, dummy1, + dummy2, dummy3); + if (RefRelationship >= Sema::Ref_Related) { + // Try to bind the reference here. + TryReferenceInitializationCore(S, Entity, Kind, Initializer, cv1T1, T1, + T1Quals, cv2T2, T2, T2Quals, Sequence); + if (Sequence) + Sequence.RewrapReferenceInitList(cv1T1, InitList); + return; + } + } + + // Not reference-related. Create a temporary and bind to that. + InitializedEntity TempEntity = InitializedEntity::InitializeTemporary(cv1T1); + + TryListInitialization(S, TempEntity, Kind, InitList, Sequence); + if (Sequence) { + if (DestType->isRValueReferenceType() || + (T1Quals.hasConst() && !T1Quals.hasVolatile())) + Sequence.AddReferenceBindingStep(cv1T1, /*bindingTemporary=*/true); + else + Sequence.SetFailed( + InitializationSequence::FK_NonConstLValueReferenceBindingToTemporary); + } +} + /// \brief Attempt list initialization (C++0x [dcl.init.list]) static void TryListInitialization(Sema &S, const InitializedEntity &Entity, @@ -2814,11 +2938,11 @@ static void TryListInitialization(Sema &S, Sequence.SetFailed(InitializationSequence::FK_TooManyInitsForScalar); return; } - // FIXME: C++0x defines behavior for these two cases. if (DestType->isReferenceType()) { - Sequence.SetFailed(InitializationSequence::FK_ReferenceBindingToInitList); + TryReferenceListInitialization(S, Entity, Kind, InitList, Sequence); return; } + // FIXME: C++11 defines behavior for this case. if (DestType->isRecordType() && !DestType->isAggregateType()) { Sequence.SetFailed(InitializationSequence::FK_InitListBadDestinationType); return; @@ -3041,27 +3165,31 @@ static void TryReferenceInitialization(Sema &S, QualType cv2T2 = Initializer->getType(); Qualifiers T2Quals; QualType T2 = S.Context.getUnqualifiedArrayType(cv2T2, T2Quals); - SourceLocation DeclLoc = Initializer->getLocStart(); // If the initializer is the address of an overloaded function, try // to resolve the overloaded function. If all goes well, T2 is the // type of the resulting function. - if (S.Context.getCanonicalType(T2) == S.Context.OverloadTy) { - DeclAccessPair Found; - bool HadMultipleCandidates = false; - if (FunctionDecl *Fn - = S.ResolveAddressOfOverloadedFunction(Initializer, T1, false, Found, - &HadMultipleCandidates)) { - Sequence.AddAddressOverloadResolutionStep(Fn, Found, - HadMultipleCandidates); - cv2T2 = Fn->getType(); - T2 = cv2T2.getUnqualifiedType(); - } else if (!T1->isRecordType()) { - Sequence.SetFailed(InitializationSequence::FK_AddressOfOverloadFailed); - return; - } - } + if (ResolveOverloadedFunctionForReferenceBinding(S, Initializer, cv2T2, T2, + T1, Sequence)) + return; + + // Delegate everything else to a subfunction. + TryReferenceInitializationCore(S, Entity, Kind, Initializer, cv1T1, T1, + T1Quals, cv2T2, T2, T2Quals, Sequence); +} +/// \brief Reference initialization without resolving overloaded functions. +static void TryReferenceInitializationCore(Sema &S, + const InitializedEntity &Entity, + const InitializationKind &Kind, + Expr *Initializer, + QualType cv1T1, QualType T1, + Qualifiers T1Quals, + QualType cv2T2, QualType T2, + Qualifiers T2Quals, + InitializationSequence &Sequence) { + QualType DestType = Entity.getType(); + SourceLocation DeclLoc = Initializer->getLocStart(); // Compute some basic properties of the types and the initializer. bool isLValueRef = DestType->isLValueReferenceType(); bool isRValueRef = !isLValueRef; @@ -4486,6 +4614,8 @@ InitializationSequence::Perform(Sema &S, case SK_ConversionSequence: case SK_ListConstructorCall: case SK_ListInitialization: + case SK_UnwrapInitList: + case SK_RewrapInitList: case SK_CAssignment: case SK_StringInit: case SK_ObjCObjectConversion: @@ -4746,22 +4876,59 @@ InitializationSequence::Perform(Sema &S, case SK_ListInitialization: { InitListExpr *InitList = cast(CurInit.get()); - QualType Ty = Step->Type; - InitListChecker PerformInitList(S, Entity, InitList, - ResultType ? *ResultType : Ty, /*VerifyOnly=*/false, + // Hack: We must pass *ResultType if available in order to set the type + // of arrays, e.g. in 'int ar[] = {1, 2, 3};'. + // But in 'const X &x = {1, 2, 3};' we're supposed to initialize a + // temporary, not a reference, so we should pass Ty. + // Worst case: 'const int (&arref)[] = {1, 2, 3};'. + // Since this step is never used for a reference directly, we explicitly + // unwrap references here and rewrap them afterwards. + // We also need to create a InitializeTemporary entity for this. + QualType Ty = ResultType ? ResultType->getNonReferenceType() : Step->Type; + bool IsTemporary = ResultType && (*ResultType)->isReferenceType(); + InitializedEntity TempEntity = InitializedEntity::InitializeTemporary(Ty); + InitListChecker PerformInitList(S, IsTemporary ? TempEntity : Entity, + InitList, Ty, /*VerifyOnly=*/false, Kind.getKind() != InitializationKind::IK_Direct || !S.getLangOptions().CPlusPlus0x); if (PerformInitList.HadError()) return ExprError(); + if (ResultType) { + if ((*ResultType)->isRValueReferenceType()) + Ty = S.Context.getRValueReferenceType(Ty); + else if ((*ResultType)->isLValueReferenceType()) + Ty = S.Context.getLValueReferenceType(Ty, + (*ResultType)->getAs()->isSpelledAsLValue()); + *ResultType = Ty; + } + + InitListExpr *StructuredInitList = + PerformInitList.getFullyStructuredList(); CurInit.release(); - CurInit = S.Owned(PerformInitList.getFullyStructuredList()); + CurInit = S.Owned(StructuredInitList); break; } case SK_ListConstructorCall: assert(false && "List constructor calls not yet supported."); + case SK_UnwrapInitList: + CurInit = S.Owned(cast(CurInit.take())->getInit(0)); + break; + + case SK_RewrapInitList: { + Expr *E = CurInit.take(); + InitListExpr *Syntactic = Step->WrappingSyntacticList; + InitListExpr *ILE = new (S.Context) InitListExpr(S.Context, + Syntactic->getLBraceLoc(), &E, 1, Syntactic->getRBraceLoc()); + ILE->setSyntacticForm(Syntactic); + ILE->setType(E->getType()); + ILE->setValueKind(E->getValueKind()); + CurInit = S.Owned(ILE); + break; + } + case SK_ConstructorInitialization: { unsigned NumArgs = Args.size(); CXXConstructorDecl *Constructor @@ -5081,6 +5248,16 @@ bool InitializationSequence::Diagnose(Sema &S, break; case FK_NonConstLValueReferenceBindingToTemporary: + if (isa(Args[0])) { + S.Diag(Kind.getLocation(), + diag::err_lvalue_reference_bind_to_initlist) + << DestType.getNonReferenceType().isVolatileQualified() + << DestType.getNonReferenceType() + << Args[0]->getSourceRange(); + break; + } + // Intentional fallthrough + case FK_NonConstLValueReferenceBindingToUnrelated: S.Diag(Kind.getLocation(), Failure == FK_NonConstLValueReferenceBindingToTemporary @@ -5446,9 +5623,11 @@ void InitializationSequence::dump(raw_ostream &OS) const { case SK_QualificationConversionRValue: OS << "qualification conversion (rvalue)"; + break; case SK_QualificationConversionXValue: OS << "qualification conversion (xvalue)"; + break; case SK_QualificationConversionLValue: OS << "qualification conversion (lvalue)"; @@ -5468,6 +5647,14 @@ void InitializationSequence::dump(raw_ostream &OS) const { OS << "list initialization via constructor"; break; + case SK_UnwrapInitList: + OS << "unwrap reference initializer list"; + break; + + case SK_RewrapInitList: + OS << "rewrap reference initializer list"; + break; + case SK_ConstructorInitialization: OS << "constructor initialization"; break; diff --git a/test/CodeGenCXX/cxx0x-initializer-references.cpp b/test/CodeGenCXX/cxx0x-initializer-references.cpp new file mode 100644 index 0000000000..4c847b8e58 --- /dev/null +++ b/test/CodeGenCXX/cxx0x-initializer-references.cpp @@ -0,0 +1,69 @@ +// RUN: %clang_cc1 -std=c++11 -S -emit-llvm -o - %s | FileCheck %s + +namespace reference { + struct A { + int i1, i2; + }; + + void single_init() { + // No superfluous instructions allowed here, they could be + // hiding extra temporaries. + + // CHECK: store i32 1, i32* + // CHECK-NEXT: store i32* %{{.*}}, i32** + const int &cri2a = 1; + + // CHECK-NEXT: store i32 1, i32* + // CHECK-NEXT: store i32* %{{.*}}, i32** + const int &cri1a = {1}; + + // CHECK-NEXT: store i32 1, i32* + int i = 1; + // CHECK-NEXT: store i32* %{{.*}}, i32** + int &ri1a = {i}; + + // CHECK-NEXT: bitcast + // CHECK-NEXT: memcpy + A a{1, 2}; + // CHECK-NEXT: store %{{.*}}* %{{.*}}, %{{.*}}** % + A &ra1a = {a}; + + // CHECK-NEXT: ret + } + + void reference_to_aggregate() { + // CHECK: getelementptr {{.*}}, i32 0, i32 0 + // CHECK-NEXT: store i32 1 + // CHECK-NEXT: getelementptr {{.*}}, i32 0, i32 1 + // CHECK-NEXT: store i32 2 + // CHECK-NEXT: store %{{.*}}* %{{.*}}, %{{.*}}** %{{.*}}, align + const A &ra1{1, 2}; + + // CHECK-NEXT: getelementptr inbounds [3 x i32]* %{{.*}}, i{{32|64}} 0, i{{32|64}} 0 + // CHECK-NEXT: store i32 1 + // CHECK-NEXT: getelementptr inbounds i32* %{{.*}}, i{{32|64}} 1 + // CHECK-NEXT: store i32 2 + // CHECK-NEXT: getelementptr inbounds i32* %{{.*}}, i{{32|64}} 1 + // CHECK-NEXT: store i32 3 + // CHECK-NEXT: store [3 x i32]* %{{.*}}, [3 x i32]** %{{.*}}, align + const int (&arrayRef)[] = {1, 2, 3}; + + // CHECK-NEXT: ret + } + + struct B { + B(); + ~B(); + }; + + void single_init_temp_cleanup() + { + // Ensure lifetime extension. + + // CHECK: call void @_ZN9reference1BC1Ev + // CHECK-NEXT: store %{{.*}}* %{{.*}}, %{{.*}}** % + const B &rb{ B() }; + // CHECK: call void @_ZN9reference1BD1Ev + } + +} diff --git a/test/SemaCXX/cxx0x-initializer-references.cpp b/test/SemaCXX/cxx0x-initializer-references.cpp new file mode 100644 index 0000000000..5bc355f8e4 --- /dev/null +++ b/test/SemaCXX/cxx0x-initializer-references.cpp @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s + +namespace reference { + struct A { + int i1, i2; + }; + + void single_init() { + const int &cri1a = {1}; + const int &cri1b{1}; + + int i = 1; + int &ri1a = {i}; + int &ri1b{i}; + + int &ri2 = {1}; // expected-error {{cannot bind to an initializer list temporary}} + + A a{1, 2}; + A &ra1a = {a}; + A &ra1b{a}; + } + + void reference_to_aggregate() { + const A &ra1{1, 2}; + A &ra2{1, 2}; // expected-error {{cannot bind to an initializer list temporary}} + + const int (&arrayRef)[] = {1, 2, 3}; + static_assert(sizeof(arrayRef) == 3 * sizeof(int), "bad array size"); + } + +} -- 2.40.0