When we get around to modeling non-trivial temporaries in the static analyzer, this will probably be very, very useful. Thanks for doing all this.
On Jun 4, 2013, at 17:46 , Richard Smith <[email protected]> wrote: > Author: rsmith > Date: Tue Jun 4 19:46:14 2013 > New Revision: 183283 > > URL: http://llvm.org/viewvc/llvm-project?rev=183283&view=rev > Log: > Model temporary lifetime-extension explicitly in the AST. Use this model to > handle temporaries which have been lifetime-extended to static storage > duration > within constant expressions. This correctly handles nested lifetime extension > (through reference members of aggregates in aggregate initializers) but > non-constant-expression emission hasn't yet been updated to do the same. > > Modified: > cfe/trunk/include/clang/AST/ASTContext.h > cfe/trunk/include/clang/AST/Decl.h > cfe/trunk/include/clang/AST/ExprCXX.h > cfe/trunk/include/clang/AST/Stmt.h > cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td > cfe/trunk/include/clang/Basic/Specifiers.h > cfe/trunk/include/clang/Sema/Initialization.h > cfe/trunk/lib/AST/ASTContext.cpp > cfe/trunk/lib/AST/ASTDumper.cpp > cfe/trunk/lib/AST/ExprConstant.cpp > cfe/trunk/lib/CodeGen/CGExprConstant.cpp > cfe/trunk/lib/CodeGen/CodeGenModule.cpp > cfe/trunk/lib/CodeGen/CodeGenModule.h > cfe/trunk/lib/Sema/SemaExpr.cpp > cfe/trunk/lib/Sema/SemaInit.cpp > cfe/trunk/lib/Serialization/ASTReaderStmt.cpp > cfe/trunk/lib/Serialization/ASTWriterStmt.cpp > cfe/trunk/test/CodeGenCXX/const-init-cxx11.cpp > cfe/trunk/test/CodeGenCXX/const-init-cxx1y.cpp > cfe/trunk/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp > cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp > cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp > > Modified: cfe/trunk/include/clang/AST/ASTContext.h > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ASTContext.h?rev=183283&r1=183282&r2=183283&view=diff > ============================================================================== > --- cfe/trunk/include/clang/AST/ASTContext.h (original) > +++ cfe/trunk/include/clang/AST/ASTContext.h Tue Jun 4 19:46:14 2013 > @@ -55,6 +55,7 @@ namespace clang { > class ExternalASTSource; > class ASTMutationListener; > class IdentifierTable; > + class MaterializeTemporaryExpr; > class SelectorTable; > class TargetInfo; > class CXXABI; > @@ -163,6 +164,11 @@ class ASTContext : public RefCountedBase > llvm::DenseMap<const FunctionDecl*, FunctionDecl*> > ClassScopeSpecializationPattern; > > + /// \brief Mapping from materialized temporaries with static storage > duration > + /// that appear in constant initializers to their evaluated values. > + llvm::DenseMap<const MaterializeTemporaryExpr*, APValue> > + MaterializedTemporaryValues; > + > /// \brief Representation of a "canonical" template template parameter that > /// is used in canonical template names. > class CanonicalTemplateTemplateParm : public llvm::FoldingSetNode { > @@ -2117,7 +2123,12 @@ public: > /// \brief Used by ParmVarDecl to retrieve on the side the > /// index of the parameter when it exceeds the size of the normal bitfield. > unsigned getParameterIndex(const ParmVarDecl *D) const; > - > + > + /// \brief Get the storage for the constant value of a materialized > temporary > + /// of static storage duration. > + APValue *getMaterializedTemporaryValue(const MaterializeTemporaryExpr *E, > + bool MayCreate); > + > > //===--------------------------------------------------------------------===// > // Statistics > > //===--------------------------------------------------------------------===// > > Modified: cfe/trunk/include/clang/AST/Decl.h > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=183283&r1=183282&r2=183283&view=diff > ============================================================================== > --- cfe/trunk/include/clang/AST/Decl.h (original) > +++ cfe/trunk/include/clang/AST/Decl.h Tue Jun 4 19:46:14 2013 > @@ -811,7 +811,7 @@ public: > bool hasLocalStorage() const { > if (getStorageClass() == SC_None) > // Second check is for C++11 [dcl.stc]p4. > - return !isFileVarDecl() && getTSCSpec() != TSCS_thread_local; > + return !isFileVarDecl() && getTSCSpec() == TSCS_unspecified; > > // Return true for: Auto, Register. > // Return false for: Extern, Static, PrivateExtern, OpenCLWorkGroupLocal. > @@ -840,6 +840,12 @@ public: > /// as static variables declared within a function. > bool hasGlobalStorage() const { return !hasLocalStorage(); } > > + /// \brief Get the storage duration of this variable, per C++ [basid.stc]. > + StorageDuration getStorageDuration() const { > + return hasLocalStorage() ? SD_Automatic : > + getTSCSpec() ? SD_Thread : SD_Static; > + } > + > /// Compute the language linkage. > LanguageLinkage getLanguageLinkage() const; > > > Modified: cfe/trunk/include/clang/AST/ExprCXX.h > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ExprCXX.h?rev=183283&r1=183282&r2=183283&view=diff > ============================================================================== > --- cfe/trunk/include/clang/AST/ExprCXX.h (original) > +++ cfe/trunk/include/clang/AST/ExprCXX.h Tue Jun 4 19:46:14 2013 > @@ -3824,30 +3824,60 @@ public: > /// binds to the temporary. \c MaterializeTemporaryExprs are always glvalues > /// (either an lvalue or an xvalue, depending on the kind of reference binding > /// to it), maintaining the invariant that references always bind to glvalues. > +/// > +/// Reference binding and copy-elision can both extend the lifetime of a > +/// temporary. When either happens, the expression will also track the > +/// declaration which is responsible for the lifetime extension. > class MaterializeTemporaryExpr : public Expr { > +public: > /// \brief The temporary-generating expression whose value will be > /// materialized. > Stmt *Temporary; > > + /// \brief The declaration which lifetime-extended this reference, if any. > + /// Either a VarDecl, or (for a ctor-initializer) a FieldDecl. > + const ValueDecl *ExtendingDecl; > + > friend class ASTStmtReader; > friend class ASTStmtWriter; > > public: > MaterializeTemporaryExpr(QualType T, Expr *Temporary, > - bool BoundToLvalueReference) > + bool BoundToLvalueReference, > + const ValueDecl *ExtendedBy) > : Expr(MaterializeTemporaryExprClass, T, > BoundToLvalueReference? VK_LValue : VK_XValue, OK_Ordinary, > Temporary->isTypeDependent(), Temporary->isValueDependent(), > Temporary->isInstantiationDependent(), > Temporary->containsUnexpandedParameterPack()), > - Temporary(Temporary) { } > + Temporary(Temporary), ExtendingDecl(ExtendedBy) { > + } > > MaterializeTemporaryExpr(EmptyShell Empty) > : Expr(MaterializeTemporaryExprClass, Empty) { } > > /// \brief Retrieve the temporary-generating subexpression whose value will > /// be materialized into a glvalue. > - Expr *GetTemporaryExpr() const { return reinterpret_cast<Expr > *>(Temporary); } > + Expr *GetTemporaryExpr() const { return static_cast<Expr *>(Temporary); } > + > + /// \brief Retrieve the storage duration for the materialized temporary. > + StorageDuration getStorageDuration() const { > + if (!ExtendingDecl) > + return SD_FullExpression; > + // FIXME: This is not necessarily correct for a temporary materialized > + // within a default initializer. > + if (isa<FieldDecl>(ExtendingDecl)) > + return SD_Automatic; > + return cast<VarDecl>(ExtendingDecl)->getStorageDuration(); > + } > + > + /// \brief Get the declaration which triggered the lifetime-extension of > this > + /// temporary, if any. > + const ValueDecl *getExtendingDecl() const { return ExtendingDecl; } > + > + void setExtendingDecl(const ValueDecl *ExtendedBy) { > + ExtendingDecl = ExtendedBy; > + } > > /// \brief Determine whether this materialized temporary is bound to an > /// lvalue reference; otherwise, it's bound to an rvalue reference. > > Modified: cfe/trunk/include/clang/AST/Stmt.h > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Stmt.h?rev=183283&r1=183282&r2=183283&view=diff > ============================================================================== > --- cfe/trunk/include/clang/AST/Stmt.h (original) > +++ cfe/trunk/include/clang/AST/Stmt.h Tue Jun 4 19:46:14 2013 > @@ -289,7 +289,7 @@ protected: > /// \brief The number of arguments to this type trait. > unsigned NumArgs : 32 - 8 - 1 - NumExprBits; > }; > - > + > union { > // FIXME: this is wasteful on 64-bit platforms. > void *Aligner; > > Modified: cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td?rev=183283&r1=183282&r2=183283&view=diff > ============================================================================== > --- cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td (original) > +++ cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td Tue Jun 4 19:46:14 > 2013 > @@ -117,6 +117,10 @@ def note_constexpr_access_inactive_union > "%select{read of|assignment to|increment of|decrement of}0 " > "member %1 of union with %select{active member %3|no active member}2 " > "is not allowed in a constant expression">; > +def note_constexpr_access_static_temporary : Note< > + "%select{read of|assignment to|increment of|decrement of}0 temporary " > + "is not allowed in a constant expression outside the expression that " > + "created the temporary">; > def note_constexpr_modify_global : Note< > "a constant expression cannot modify an object that is visible outside " > "that expression">; > > Modified: cfe/trunk/include/clang/Basic/Specifiers.h > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Specifiers.h?rev=183283&r1=183282&r2=183283&view=diff > ============================================================================== > --- cfe/trunk/include/clang/Basic/Specifiers.h (original) > +++ cfe/trunk/include/clang/Basic/Specifiers.h Tue Jun 4 19:46:14 2013 > @@ -212,6 +212,14 @@ namespace clang { > CC_IntelOclBicc // __attribute__((intel_ocl_bicc)) > }; > > + /// \brief The storage duration for an object (per C++ [basic.stc]). > + enum StorageDuration { > + SD_FullExpression, ///< Full-expression storage duration (for > temporaries). > + SD_Automatic, ///< Automatic storage duration (most local > variables). > + SD_Thread, ///< Thread storage duration. > + SD_Static, ///< Static storage duration. > + SD_Dynamic ///< Dynamic storage duration. > + }; > } // end namespace clang > > #endif // LLVM_CLANG_BASIC_SPECIFIERS_H > > Modified: cfe/trunk/include/clang/Sema/Initialization.h > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Initialization.h?rev=183283&r1=183282&r2=183283&view=diff > ============================================================================== > --- cfe/trunk/include/clang/Sema/Initialization.h (original) > +++ cfe/trunk/include/clang/Sema/Initialization.h Tue Jun 4 19:46:14 2013 > @@ -384,6 +384,13 @@ public: > assert(getKind() == EK_LambdaCapture && "Not a lambda capture!"); > return SourceLocation::getFromRawEncoding(Capture.Location); > } > + > + /// Dump a representation of the initialized entity to standard error, > + /// for debugging purposes. > + void dump() const; > + > +private: > + unsigned dumpImpl(raw_ostream &OS) const; > }; > > /// \brief Describes the kind of initialization being performed, along with > > Modified: cfe/trunk/lib/AST/ASTContext.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=183283&r1=183282&r2=183283&view=diff > ============================================================================== > --- cfe/trunk/lib/AST/ASTContext.cpp (original) > +++ cfe/trunk/lib/AST/ASTContext.cpp Tue Jun 4 19:46:14 2013 > @@ -8029,6 +8029,19 @@ unsigned ASTContext::getParameterIndex(c > return I->second; > } > > +APValue * > +ASTContext::getMaterializedTemporaryValue(const MaterializeTemporaryExpr *E, > + bool MayCreate) { > + assert(E && E->getStorageDuration() == SD_Static && > + "don't need to cache the computed value for this temporary"); > + if (MayCreate) > + return &MaterializedTemporaryValues[E]; > + > + llvm::DenseMap<const MaterializeTemporaryExpr *, APValue>::iterator I = > + MaterializedTemporaryValues.find(E); > + return I == MaterializedTemporaryValues.end() ? 0 : &I->second; > +} > + > bool ASTContext::AtomicUsesUnsupportedLibcall(const AtomicExpr *E) const { > const llvm::Triple &T = getTargetInfo().getTriple(); > if (!T.isOSDarwin()) > > Modified: cfe/trunk/lib/AST/ASTDumper.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTDumper.cpp?rev=183283&r1=183282&r2=183283&view=diff > ============================================================================== > --- cfe/trunk/lib/AST/ASTDumper.cpp (original) > +++ cfe/trunk/lib/AST/ASTDumper.cpp Tue Jun 4 19:46:14 2013 > @@ -274,6 +274,7 @@ namespace { > void VisitCXXFunctionalCastExpr(const CXXFunctionalCastExpr *Node); > void VisitCXXConstructExpr(const CXXConstructExpr *Node); > void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *Node); > + void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *Node); > void VisitExprWithCleanups(const ExprWithCleanups *Node); > void VisitUnresolvedLookupExpr(const UnresolvedLookupExpr *Node); > void dumpCXXTemporary(const CXXTemporary *Temporary); > @@ -1682,6 +1683,15 @@ void ASTDumper::VisitCXXBindTemporaryExp > dumpCXXTemporary(Node->getTemporary()); > } > > +void > +ASTDumper::VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr > *Node) { > + VisitExpr(Node); > + if (const ValueDecl *VD = Node->getExtendingDecl()) { > + OS << " extended by "; > + dumpBareDeclRef(VD); > + } > +} > + > void ASTDumper::VisitExprWithCleanups(const ExprWithCleanups *Node) { > VisitExpr(Node); > for (unsigned i = 0, e = Node->getNumObjects(); i != e; ++i) > > Modified: cfe/trunk/lib/AST/ExprConstant.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=183283&r1=183282&r2=183283&view=diff > ============================================================================== > --- cfe/trunk/lib/AST/ExprConstant.cpp (original) > +++ cfe/trunk/lib/AST/ExprConstant.cpp Tue Jun 4 19:46:14 2013 > @@ -1001,6 +1001,10 @@ static bool IsGlobalLValue(APValue::LVal > const CompoundLiteralExpr *CLE = cast<CompoundLiteralExpr>(E); > return CLE->isFileScope() && CLE->isLValue(); > } > + case Expr::MaterializeTemporaryExprClass: > + // A materialized temporary might have been lifetime-extended to static > + // storage duration. > + return cast<MaterializeTemporaryExpr>(E)->getStorageDuration() == > SD_Static; > // A string literal has static storage duration. > case Expr::StringLiteralClass: > case Expr::PredefinedExprClass: > @@ -1182,7 +1186,10 @@ const ValueDecl *GetLValueBaseDecl(const > } > > static bool IsLiteralLValue(const LValue &Value) { > - return Value.Base.dyn_cast<const Expr*>() && !Value.CallIndex; > + if (Value.CallIndex) > + return false; > + const Expr *E = Value.Base.dyn_cast<const Expr*>(); > + return E && !isa<MaterializeTemporaryExpr>(E); > } > > static bool IsWeakLValue(const LValue &Value) { > @@ -2279,11 +2286,44 @@ CompleteObject findCompleteObject(EvalIn > const Expr *Base = LVal.Base.dyn_cast<const Expr*>(); > > if (!Frame) { > - Info.Diag(E); > - return CompleteObject(); > - } > + if (const MaterializeTemporaryExpr *MTE = > + dyn_cast<MaterializeTemporaryExpr>(Base)) { > + assert(MTE->getStorageDuration() == SD_Static && > + "should have a frame for a non-global materialized > temporary"); > + > + // Per C++1y [expr.const]p2: > + // an lvalue-to-rvalue conversion [is not allowed unless it applies > to] > + // - a [...] glvalue of integral or enumeration type that refers to > + // a non-volatile const object [...] > + // [...] > + // - a [...] glvalue of literal type that refers to a non-volatile > + // object whose lifetime began within the evaluation of e. > + // > + // C++11 misses the 'began within the evaluation of e' check and > + // instead allows all temporaries, including things like: > + // int &&r = 1; > + // int x = ++r; > + // constexpr int k = r; > + // Therefore we use the C++1y rules in C++11 too. > + const ValueDecl *VD = Info.EvaluatingDecl.dyn_cast<const > ValueDecl*>(); > + const ValueDecl *ED = MTE->getExtendingDecl(); > + if (!(BaseType.isConstQualified() && > + BaseType->isIntegralOrEnumerationType()) && > + !(VD && VD->getCanonicalDecl() == ED->getCanonicalDecl())) { > + Info.Diag(E, diag::note_constexpr_access_static_temporary, 1) << > AK; > + Info.Note(MTE->getExprLoc(), diag::note_constexpr_temporary_here); > + return CompleteObject(); > + } > > - BaseVal = &Frame->Temporaries[Base]; > + BaseVal = Info.Ctx.getMaterializedTemporaryValue(MTE, false); > + assert(BaseVal && "got reference to unevaluated temporary"); > + } else { > + Info.Diag(E); > + return CompleteObject(); > + } > + } else { > + BaseVal = &Frame->Temporaries[Base]; > + } > > // Volatile temporary objects cannot be accessed in constant expressions. > if (BaseType.isVolatileQualified()) { > @@ -3940,6 +3980,8 @@ public: > // * Any Expr, with a CallIndex indicating the function in which the > temporary > // was evaluated, for cases where the MaterializeTemporaryExpr is missing > // from the AST (FIXME). > +// * A MaterializeTemporaryExpr that has static storage duration, with no > +// CallIndex, for a lifetime-extended temporary. > // plus an offset in bytes. > //===----------------------------------------------------------------------===// > namespace { > @@ -4045,9 +4087,19 @@ bool LValueExprEvaluator::VisitMateriali > if (!EvaluateIgnoredValue(Info, CommaLHSs[I])) > return false; > > + // A materialized temporary with static storage duration can appear within > the > + // result of a constant expression evaluation, so we need to preserve its > + // value for use outside this evaluation. > + APValue *Value; > + if (E->getStorageDuration() == SD_Static) { > + Value = Info.Ctx.getMaterializedTemporaryValue(E, true); > + Result.set(E); > + } else { > + Value = &Info.CurrentCall->Temporaries[E]; > + Result.set(E, Info.CurrentCall->Index); > + } > + > // Materialize the temporary itself. > - APValue *Value = &Info.CurrentCall->Temporaries[E]; > - Result.set(E, Info.CurrentCall->Index); > if (!EvaluateInPlace(*Value, Info, Result, Inner)) > return false; > > @@ -7608,10 +7660,10 @@ void Expr::EvaluateForOverflow(const AST > } > } > > - bool Expr::EvalResult::isGlobalLValue() const { > - assert(Val.isLValue()); > - return IsGlobalLValue(Val.getLValueBase()); > - } > +bool Expr::EvalResult::isGlobalLValue() const { > + assert(Val.isLValue()); > + return IsGlobalLValue(Val.getLValueBase()); > +} > > > /// isIntegerConstantExpr - this recursive routine will test if an expression > is > > Modified: cfe/trunk/lib/CodeGen/CGExprConstant.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGExprConstant.cpp?rev=183283&r1=183282&r2=183283&view=diff > ============================================================================== > --- cfe/trunk/lib/CodeGen/CGExprConstant.cpp (original) > +++ cfe/trunk/lib/CodeGen/CGExprConstant.cpp Tue Jun 4 19:46:14 2013 > @@ -1003,6 +1003,15 @@ public: > case Expr::CXXUuidofExprClass: { > return CGM.GetAddrOfUuidDescriptor(cast<CXXUuidofExpr>(E)); > } > + case Expr::MaterializeTemporaryExprClass: { > + MaterializeTemporaryExpr *MTE = cast<MaterializeTemporaryExpr>(E); > + assert(MTE->getStorageDuration() == SD_Static); > + SmallVector<const Expr *, 2> CommaLHSs; > + SmallVector<SubobjectAdjustment, 2> Adjustments; > + const Expr *Inner = MTE->GetTemporaryExpr() > + ->skipRValueSubobjectAdjustments(CommaLHSs, Adjustments); > + return CGM.GetAddrOfGlobalTemporary(MTE, Inner); > + } > } > > return 0; > > Modified: cfe/trunk/lib/CodeGen/CodeGenModule.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenModule.cpp?rev=183283&r1=183282&r2=183283&view=diff > ============================================================================== > --- cfe/trunk/lib/CodeGen/CodeGenModule.cpp (original) > +++ cfe/trunk/lib/CodeGen/CodeGenModule.cpp Tue Jun 4 19:46:14 2013 > @@ -2735,6 +2735,63 @@ llvm::Constant *CodeGenModule::GetAddrOf > return GetAddrOfConstantString(StrWithNull, GlobalName, Alignment); > } > > +llvm::Constant *CodeGenModule::GetAddrOfGlobalTemporary( > + const MaterializeTemporaryExpr *E, const Expr *Inner) { > + assert((E->getStorageDuration() == SD_Static || > + E->getStorageDuration() == SD_Thread) && "not a global temporary"); > + const VarDecl *VD = cast<VarDecl>(E->getExtendingDecl()); > + > + // If we're not materializing a subobject of the temporary, keep the > + // cv-qualifiers from the type of the MaterializeTemporaryExpr. > + if (Inner == E->GetTemporaryExpr()) > + Inner = E; > + > + llvm::Constant *&Slot = MaterializedGlobalTemporaryMap[E]; > + if (Slot) > + return Slot; > + > + // FIXME: If an externally-visible declaration extends multiple > temporaries, > + // we need to give each temporary the same name in every translation unit > (and > + // we also need to make the temporaries externally-visible). > + SmallString<256> Name; > + llvm::raw_svector_ostream Out(Name); > + getCXXABI().getMangleContext().mangleReferenceTemporary(VD, Out); > + Out.flush(); > + > + llvm::Constant *InitialValue = 0; > + APValue *Value = 0; > + if (E->getStorageDuration() == SD_Static) { > + // We might have a constant initializer for this temporary. > + Value = getContext().getMaterializedTemporaryValue(E, false); > + if (Value && Value->isUninit()) > + Value = 0; > + } > + > + bool Constant; > + if (Value) { > + // The temporary has a constant initializer, use it. > + InitialValue = EmitConstantValue(*Value, Inner->getType(), 0); > + Constant = isTypeConstant(Inner->getType(), /*ExcludeCtor*/Value); > + } else { > + // No constant initializer, the initialization will be provided when we > + // initialize the declaration which performed lifetime extension. > + InitialValue = EmitNullConstant(Inner->getType()); > + Constant = false; > + } > + > + // Create a global variable for this lifetime-extended temporary. > + llvm::GlobalVariable *GV = > + new llvm::GlobalVariable(getModule(), InitialValue->getType(), Constant, > + llvm::GlobalValue::PrivateLinkage, InitialValue, > + Name.c_str()); > + GV->setAlignment( > + getContext().getTypeAlignInChars(Inner->getType()).getQuantity()); > + if (VD->getTLSKind()) > + setTLSMode(GV, *VD); > + Slot = GV; > + return GV; > +} > + > /// EmitObjCPropertyImplementations - Emit information for synthesized > /// properties for an implementation. > void CodeGenModule::EmitObjCPropertyImplementations(const > > Modified: cfe/trunk/lib/CodeGen/CodeGenModule.h > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenModule.h?rev=183283&r1=183282&r2=183283&view=diff > ============================================================================== > --- cfe/trunk/lib/CodeGen/CodeGenModule.h (original) > +++ cfe/trunk/lib/CodeGen/CodeGenModule.h Tue Jun 4 19:46:14 2013 > @@ -307,7 +307,8 @@ class CodeGenModule : public CodeGenType > llvm::StringMap<llvm::GlobalVariable*> ConstantStringMap; > llvm::DenseMap<const Decl*, llvm::Constant *> StaticLocalDeclMap; > llvm::DenseMap<const Decl*, llvm::GlobalVariable*> StaticLocalDeclGuardMap; > - > + llvm::DenseMap<const Expr*, llvm::Constant *> > MaterializedGlobalTemporaryMap; > + > llvm::DenseMap<QualType, llvm::Constant *> AtomicSetterHelperFnMap; > llvm::DenseMap<QualType, llvm::Constant *> AtomicGetterHelperFnMap; > > @@ -731,7 +732,12 @@ public: > /// GetAddrOfConstantCompoundLiteral - Returns a pointer to a constant > global > /// variable for the given file-scope compound literal expression. > llvm::Constant *GetAddrOfConstantCompoundLiteral(const > CompoundLiteralExpr*E); > - > + > + /// \brief Returns a pointer to a global variable representing a temporary > + /// with static or thread storage duration. > + llvm::Constant *GetAddrOfGlobalTemporary(const MaterializeTemporaryExpr *E, > + const Expr *Inner); > + > /// \brief Retrieve the record type that describes the state of an > /// Objective-C fast enumeration loop (for..in). > QualType getObjCFastEnumerationStateType(); > > Modified: cfe/trunk/lib/Sema/SemaExpr.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=183283&r1=183282&r2=183283&view=diff > ============================================================================== > --- cfe/trunk/lib/Sema/SemaExpr.cpp (original) > +++ cfe/trunk/lib/Sema/SemaExpr.cpp Tue Jun 4 19:46:14 2013 > @@ -8353,7 +8353,7 @@ static QualType CheckAddressOfOperand(Se > return QualType(); > // Materialize the temporary as an lvalue so that we can take its address. > OrigOp = op = new (S.Context) > - MaterializeTemporaryExpr(op->getType(), OrigOp.take(), true); > + MaterializeTemporaryExpr(op->getType(), OrigOp.take(), true, 0); > } else if (isa<ObjCSelectorExpr>(op)) { > return S.Context.getPointerType(op->getType()); > } else if (lval == Expr::LV_MemberFunction) { > > Modified: cfe/trunk/lib/Sema/SemaInit.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaInit.cpp?rev=183283&r1=183282&r2=183283&view=diff > ============================================================================== > --- cfe/trunk/lib/Sema/SemaInit.cpp (original) > +++ cfe/trunk/lib/Sema/SemaInit.cpp Tue Jun 4 19:46:14 2013 > @@ -2502,6 +2502,46 @@ bool InitializedEntity::allowsNRVO() con > return false; > } > > +unsigned InitializedEntity::dumpImpl(raw_ostream &OS) const { > + unsigned Depth = getParent() ? getParent()->dumpImpl(OS) : 0; > + for (unsigned I = 0; I != Depth; ++I) > + OS << "`-"; > + > + switch (getKind()) { > + case EK_Variable: OS << "Variable"; break; > + case EK_Parameter: OS << "Parameter"; break; > + case EK_Result: OS << "Result"; break; > + case EK_Exception: OS << "Exception"; break; > + case EK_Member: OS << "Member"; break; > + case EK_New: OS << "New"; break; > + case EK_Temporary: OS << "Temporary"; break; > + case EK_CompoundLiteralInit: OS << "CompoundLiteral";break; > + case EK_Base: OS << "Base"; break; > + case EK_Delegating: OS << "Delegating"; break; > + case EK_ArrayElement: OS << "ArrayElement " << Index; break; > + case EK_VectorElement: OS << "VectorElement " << Index; break; > + case EK_ComplexElement: OS << "ComplexElement " << Index; break; > + case EK_BlockElement: OS << "Block"; break; > + case EK_LambdaCapture: > + OS << "LambdaCapture "; > + getCapturedVar()->printName(OS); > + break; > + } > + > + if (Decl *D = getDecl()) { > + OS << " "; > + cast<NamedDecl>(D)->printQualifiedName(OS); > + } > + > + OS << " '" << getType().getAsString() << "'\n"; > + > + return Depth + 1; > +} > + > +void InitializedEntity::dump() const { > + dumpImpl(llvm::errs()); > +} > + > //===----------------------------------------------------------------------===// > // Initialization sequence > //===----------------------------------------------------------------------===// > @@ -5089,6 +5129,143 @@ InitializedEntityOutlivesFullExpression( > llvm_unreachable("unknown entity kind"); > } > > +/// Determine the declaration which an initialized entity ultimately refers > to, > +/// for the purpose of lifetime-extending a temporary bound to a reference in > +/// the initialization of \p Entity. > +static const ValueDecl * > +getDeclForTemporaryLifetimeExtension(const InitializedEntity &Entity, > + const ValueDecl *FallbackDecl = 0) { > + // C++11 [class.temporary]p5: > + switch (Entity.getKind()) { > + case InitializedEntity::EK_Variable: > + // The temporary [...] persists for the lifetime of the reference > + return Entity.getDecl(); > + > + case InitializedEntity::EK_Member: > + // For subobjects, we look at the complete object. > + if (Entity.getParent()) > + return getDeclForTemporaryLifetimeExtension(*Entity.getParent(), > + Entity.getDecl()); > + > + // except: > + // -- A temporary bound to a reference member in a constructor's > + // ctor-initializer persists until the constructor exits. > + return Entity.getDecl(); > + > + case InitializedEntity::EK_Parameter: > + // -- A temporary bound to a reference parameter in a function call > + // persists until the completion of the full-expression containing > + // the call. > + case InitializedEntity::EK_Result: > + // -- The lifetime of a temporary bound to the returned value in a > + // function return statement is not extended; the temporary is > + // destroyed at the end of the full-expression in the return > statement. > + case InitializedEntity::EK_New: > + // -- A temporary bound to a reference in a new-initializer persists > + // until the completion of the full-expression containing the > + // new-initializer. > + return 0; > + > + case InitializedEntity::EK_Temporary: > + case InitializedEntity::EK_CompoundLiteralInit: > + // We don't yet know the storage duration of the surrounding temporary. > + // Assume it's got full-expression duration for now, it will patch up our > + // storage duration if that's not correct. > + return 0; > + > + case InitializedEntity::EK_ArrayElement: > + // For subobjects, we look at the complete object. > + return getDeclForTemporaryLifetimeExtension(*Entity.getParent(), > + FallbackDecl); > + > + case InitializedEntity::EK_Base: > + case InitializedEntity::EK_Delegating: > + // We can reach this case for aggregate initialization in a constructor: > + // struct A { int &&r; }; > + // struct B : A { B() : A{0} {} }; > + // In this case, use the innermost field decl as the context. > + return FallbackDecl; > + > + case InitializedEntity::EK_BlockElement: > + case InitializedEntity::EK_LambdaCapture: > + case InitializedEntity::EK_Exception: > + case InitializedEntity::EK_VectorElement: > + case InitializedEntity::EK_ComplexElement: > + llvm_unreachable("should not materialize a temporary to initialize > this"); > + } > +} > + > +static void performLifetimeExtension(Expr *Init, const ValueDecl > *ExtendingD); > + > +/// Update a glvalue expression that is used as the initializer of a > reference > +/// to note that its lifetime is extended. > +static void performReferenceExtension(Expr *Init, const ValueDecl > *ExtendingD) { > + if (InitListExpr *ILE = dyn_cast<InitListExpr>(Init)) { > + if (ILE->getNumInits() == 1 && ILE->isGLValue()) { > + // This is just redundant braces around an initializer. Step over it. > + Init = ILE->getInit(0); > + } > + } > + > + if (MaterializeTemporaryExpr *ME = > dyn_cast<MaterializeTemporaryExpr>(Init)) { > + // Update the storage duration of the materialized temporary. > + // FIXME: Rebuild the expression instead of mutating it. > + ME->setExtendingDecl(ExtendingD); > + performLifetimeExtension(ME->GetTemporaryExpr(), ExtendingD); > + } > +} > + > +/// Update a prvalue expression that is going to be materialized as a > +/// lifetime-extended temporary. > +static void performLifetimeExtension(Expr *Init, const ValueDecl > *ExtendingD) { > + // Dig out the expression which constructs the extended temporary. > + SmallVector<const Expr *, 2> CommaLHSs; > + SmallVector<SubobjectAdjustment, 2> Adjustments; > + Init = const_cast<Expr *>( > + Init->skipRValueSubobjectAdjustments(CommaLHSs, Adjustments)); > + > + if (InitListExpr *ILE = dyn_cast<InitListExpr>(Init)) { > + if (ILE->initializesStdInitializerList()) { > + // FIXME: If this is an InitListExpr which creates a > std::initializer_list > + // object, we also need to lifetime-extend the underlying array > + // itself. Fix the representation to explicitly materialize an > + // array temporary so we can model this properly. > + for (unsigned I = 0, N = ILE->getNumInits(); I != N; ++I) > + performLifetimeExtension(ILE->getInit(I), ExtendingD); > + return; > + } > + > + CXXRecordDecl *RD = Init->getType()->getAsCXXRecordDecl(); > + if (RD) { > + assert(RD->isAggregate() && "aggregate init on non-aggregate"); > + > + // If we lifetime-extend a braced initializer which is initializing an > + // aggregate, and that aggregate contains reference members which are > + // bound to temporaries, those temporaries are also lifetime-extended. > + if (RD->isUnion() && ILE->getInitializedFieldInUnion() && > + ILE->getInitializedFieldInUnion()->getType()->isReferenceType()) > + performReferenceExtension(ILE->getInit(0), ExtendingD); > + else { > + unsigned Index = 0; > + for (RecordDecl::field_iterator I = RD->field_begin(), > + E = RD->field_end(); > + I != E; ++I) { > + if (I->isUnnamedBitfield()) > + continue; > + if (I->getType()->isReferenceType()) > + performReferenceExtension(ILE->getInit(Index), ExtendingD); > + else if (isa<InitListExpr>(ILE->getInit(Index))) > + // This may be either aggregate-initialization of a member or > + // initialization of a std::initializer_list object. Either way, > + // we should recursively lifetime-extend that initializer. > + performLifetimeExtension(ILE->getInit(Index), ExtendingD); > + ++Index; > + } > + } > + } > + } > +} > + > ExprResult > InitializationSequence::Perform(Sema &S, > const InitializedEntity &Entity, > @@ -5326,7 +5503,7 @@ InitializationSequence::Perform(Sema &S, > > break; > > - case SK_BindReferenceToTemporary: > + case SK_BindReferenceToTemporary: { > // Make sure the "temporary" is actually an rvalue. > assert(CurInit.get()->isRValue() && "not a temporary"); > > @@ -5334,11 +5511,17 @@ InitializationSequence::Perform(Sema &S, > if (S.CheckExceptionSpecCompatibility(CurInit.get(), DestType)) > return ExprError(); > > + // Maybe lifetime-extend the temporary's subobjects to match the > + // entity's lifetime. > + const ValueDecl *ExtendingDecl = > + getDeclForTemporaryLifetimeExtension(Entity); > + if (ExtendingDecl) > + performLifetimeExtension(CurInit.get(), ExtendingDecl); > + > // Materialize the temporary into memory. > CurInit = new (S.Context) MaterializeTemporaryExpr( > - > Entity.getType().getNonReferenceType(), > - CurInit.get(), > - > Entity.getType()->isLValueReferenceType()); > + Entity.getType().getNonReferenceType(), CurInit.get(), > + Entity.getType()->isLValueReferenceType(), ExtendingDecl); > > // If we're binding to an Objective-C object that has lifetime, we > // need cleanups. > @@ -5347,6 +5530,7 @@ InitializationSequence::Perform(Sema &S, > S.ExprNeedsCleanups = true; > > break; > + } > > case SK_ExtraneousCopyToTemporary: > CurInit = CopyObject(S, Step->Type, Entity, CurInit, > > Modified: cfe/trunk/lib/Serialization/ASTReaderStmt.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderStmt.cpp?rev=183283&r1=183282&r2=183283&view=diff > ============================================================================== > --- cfe/trunk/lib/Serialization/ASTReaderStmt.cpp (original) > +++ cfe/trunk/lib/Serialization/ASTReaderStmt.cpp Tue Jun 4 19:46:14 2013 > @@ -1576,6 +1576,7 @@ void ASTStmtReader::VisitFunctionParmPac > void ASTStmtReader::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr > *E) { > VisitExpr(E); > E->Temporary = Reader.ReadSubExpr(); > + E->ExtendingDecl = ReadDeclAs<ValueDecl>(Record, Idx); > } > > void ASTStmtReader::VisitOpaqueValueExpr(OpaqueValueExpr *E) { > > Modified: cfe/trunk/lib/Serialization/ASTWriterStmt.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriterStmt.cpp?rev=183283&r1=183282&r2=183283&view=diff > ============================================================================== > --- cfe/trunk/lib/Serialization/ASTWriterStmt.cpp (original) > +++ cfe/trunk/lib/Serialization/ASTWriterStmt.cpp Tue Jun 4 19:46:14 2013 > @@ -1572,6 +1572,7 @@ void ASTStmtWriter::VisitFunctionParmPac > void ASTStmtWriter::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr > *E) { > VisitExpr(E); > Writer.AddStmt(E->Temporary); > + Writer.AddDeclRef(E->ExtendingDecl, Record); > Code = serialization::EXPR_MATERIALIZE_TEMPORARY; > } > > > Modified: cfe/trunk/test/CodeGenCXX/const-init-cxx11.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/const-init-cxx11.cpp?rev=183283&r1=183282&r2=183283&view=diff > ============================================================================== > --- cfe/trunk/test/CodeGenCXX/const-init-cxx11.cpp (original) > +++ cfe/trunk/test/CodeGenCXX/const-init-cxx11.cpp Tue Jun 4 19:46:14 2013 > @@ -224,12 +224,50 @@ namespace LiteralReference { > constexpr Lit() : n(5) {} > int n; > }; > - // FIXME: This should have static initialization, but we do not implement > - // that yet. For now, just check that we don't set the (pointer) value of > - // the reference to 5! > - // > - // CHECK: @_ZN16LiteralReference3litE = global {{.*}} null > + > + // This creates a non-const temporary and binds a reference to it. > + // CHECK: @[[TEMP:.*]] = private global {{.*}} { i32 5 }, align 4 > + // CHECK: @_ZN16LiteralReference3litE = constant {{.*}} @[[TEMP]], align 8 > const Lit &lit = Lit(); > + > + // This creates a const temporary as part of the reference initialization. > + // CHECK: @[[TEMP:.*]] = private constant {{.*}} { i32 5 }, align 4 > + // CHECK: @_ZN16LiteralReference4lit2E = constant {{.*}} @[[TEMP]], align 8 > + const Lit &lit2 = {}; > + > + struct A { int &&r1; const int &&r2; }; > + struct B { A &&a1; const A &&a2; }; > + B b = { { 0, 1 }, { 2, 3 } }; > + // CHECK: @[[TEMP0:.*]] = private global i32 0, align 4 > + // CHECK: @[[TEMP1:.*]] = private constant i32 1, align 4 > + // CHECK: @[[TEMPA1:.*]] = private global {{.*}} { i32* @[[TEMP0]], i32* > @[[TEMP1]] }, align 8 > + // CHECK: @[[TEMP2:.*]] = private global i32 2, align 4 > + // CHECK: @[[TEMP3:.*]] = private constant i32 3, align 4 > + // CHECK: @[[TEMPA2:.*]] = private constant {{.*}} { i32* @[[TEMP2]], i32* > @[[TEMP3]] }, align 8 > + // CHECK: @_ZN16LiteralReference1bE = global {{.*}} { {{.*}}* @[[TEMPA1]], > {{.*}}* @[[TEMPA2]] }, align 8 > + > + struct Subobj { > + int a, b, c; > + }; > + // CHECK: @[[TEMP:.*]] = private global {{.*}} { i32 1, i32 2, i32 3 }, > align 4 > + // CHECK: @_ZN16LiteralReference2soE = constant {{.*}} (i8* getelementptr > {{.*}} @[[TEMP]]{{.*}}, i64 4) > + constexpr int &&so = Subobj{ 1, 2, 3 }.b; > + > + struct Dummy { int padding; }; > + struct Derived : Dummy, Subobj { > + constexpr Derived() : Dummy{200}, Subobj{4, 5, 6} {} > + }; > + using ConstDerived = const Derived; > + // CHECK: @[[TEMPCOMMA:.*]] = private constant {{.*}} { i32 200, i32 4, > i32 5, i32 6 } > + // CHECK: @_ZN16LiteralReference5commaE = constant {{.*}} getelementptr > {{.*}} @[[TEMPCOMMA]]{{.*}}, i64 8) > + constexpr const int &comma = (1, (2, ConstDerived{}).b); > + > + // CHECK: @[[TEMPDERIVED:.*]] = private global {{.*}} { i32 200, i32 4, > i32 5, i32 6 } > + // CHECK: @_ZN16LiteralReference4baseE = constant {{.*}} getelementptr > {{.*}} @[[TEMPDERIVED]]{{.*}}, i64 4) > + constexpr Subobj &&base = Derived{}; > + > + // CHECK: @_ZN16LiteralReference7derivedE = constant {{.*}} > @[[TEMPDERIVED]] > + constexpr Derived &derived = static_cast<Derived&>(base); > } > > namespace NonLiteralConstexpr { > @@ -330,6 +368,17 @@ namespace PR13273 { > extern const S s {}; > } > > +namespace UnemittedTemporaryDecl { > + constexpr int &&ref = 0; > + extern constexpr int &ref2 = ref; > + // CHECK: @_ZGRN22UnemittedTemporaryDecl3refE = private global i32 0 > + > + // FIXME: This declaration should not be emitted -- it isn't odr-used. > + // CHECK: @_ZN22UnemittedTemporaryDecl3refE > + > + // CHECK: @_ZN22UnemittedTemporaryDecl4ref2E = constant i32* > @_ZGRN22UnemittedTemporaryDecl3refE > +} > + > // CHECK: @_ZZN12LocalVarInit3aggEvE1a = internal constant {{.*}} i32 101 > // CHECK: @_ZZN12LocalVarInit4ctorEvE1a = internal constant {{.*}} i32 102 > // CHECK: @_ZZN12LocalVarInit8mutable_EvE1a = private unnamed_addr constant > {{.*}} i32 103 > > Modified: cfe/trunk/test/CodeGenCXX/const-init-cxx1y.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/const-init-cxx1y.cpp?rev=183283&r1=183282&r2=183283&view=diff > ============================================================================== > --- cfe/trunk/test/CodeGenCXX/const-init-cxx1y.cpp (original) > +++ cfe/trunk/test/CodeGenCXX/const-init-cxx1y.cpp Tue Jun 4 19:46:14 2013 > @@ -17,4 +17,13 @@ B b; > > // CHECK: @b = global {{.*}} i32 1, {{.*}} { i32 2 }, {{.*}} { i32 3 }, > {{.*}} { i32 4 } > // CHECK-NOT: _ZN1BC > -// CHECK: __cxa_atexit > + > +namespace ModifyStaticTemporary { > + struct A { int &&temporary; int x; }; > + constexpr int f(int &r) { r *= 9; return r - 12; } > + A a = { 6, f(a.temporary) }; > + // CHECK: @_ZGRN21ModifyStaticTemporary1aE = private global i32 54 > + // CHECK: @_ZN21ModifyStaticTemporary1aE = global {{.*}} i32* > @_ZGRN21ModifyStaticTemporary1aE, i32 42 > +} > + > +// CHECK: __cxa_atexit({{.*}} @_ZN1BD1Ev {{.*}} @b > > Modified: cfe/trunk/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp?rev=183283&r1=183282&r2=183283&view=diff > ============================================================================== > --- cfe/trunk/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp > (original) > +++ cfe/trunk/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp Tue > Jun 4 19:46:14 2013 > @@ -284,4 +284,7 @@ namespace dtors { > void f() { > std::initializer_list<S>{ S(), S() }; > } > + void g() { > + auto x = std::initializer_list<S>{ S(), S() }; > + } > } > > Modified: cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp?rev=183283&r1=183282&r2=183283&view=diff > ============================================================================== > --- cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp (original) > +++ cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp Tue Jun 4 19:46:14 > 2013 > @@ -339,6 +339,30 @@ static_assert(!same(4, 4), ""); > static_assert(same(n, n), ""); > static_assert(sameTemporary(9), ""); > > +struct A { int &&r; }; > +struct B { A &&a1; A &&a2; }; > + > +constexpr B b1 { { 1 }, { 2 } }; // expected-note {{temporary created here}} > +static_assert(&b1.a1 != &b1.a2, ""); > +static_assert(&b1.a1.r != &b1.a2.r, ""); // expected-error {{constant > expression}} expected-note {{outside the expression that created the > temporary}} > + > +constexpr B &&b2 { { 3 }, { 4 } }; // expected-note {{temporary created > here}} > +static_assert(&b1 != &b2, ""); > +static_assert(&b1.a1 != &b2.a1, ""); // expected-error {{constant > expression}} expected-note {{outside the expression that created the > temporary}} > + > +constexpr thread_local B b3 { { 1 }, { 2 } }; // expected-error {{constant > expression}} expected-note {{reference to temporary}} expected-note {{here}} > +void foo() { > + constexpr static B b1 { { 1 }, { 2 } }; // ok > + constexpr thread_local B b2 { { 1 }, { 2 } }; // expected-error {{constant > expression}} expected-note {{reference to temporary}} expected-note {{here}} > + constexpr B b3 { { 1 }, { 2 } }; // expected-error {{constant expression}} > expected-note {{reference to temporary}} expected-note {{here}} > +} > + > +constexpr B &&b4 = ((1, 2), 3, 4, B { {10}, {{20}} }); // expected-warning > 4{{unused}} > +static_assert(&b4 != &b2, ""); > + > +// Proposed DR: copy-elision doesn't trigger lifetime extension. > +constexpr B b5 = B{ {0}, {0} }; // expected-error {{constant expression}} > expected-note {{reference to temporary}} expected-note {{here}} > + > } > > constexpr int strcmp_ce(const char *p, const char *q) { > @@ -572,7 +596,7 @@ struct E { > constexpr E() : p(&p) {} > void *p; > }; > -constexpr const E &e1 = E(); // expected-error {{constant expression}} > expected-note {{reference to temporary is not a constant expression}} > expected-note {{temporary created here}} > +constexpr const E &e1 = E(); > // This is a constant expression if we elide the copy constructor call, and > // is not a constant expression if we don't! But we do, so it is. > constexpr E e2 = E(); > @@ -1282,8 +1306,23 @@ struct Wrap { > constexpr const Wrap &g(const Wrap &w) { return w; } > constexpr int k2 = g({0}).value; // ok > > -constexpr const int &i = 0; // expected-error {{constant expression}} > expected-note {{temporary}} expected-note 2{{here}} > -constexpr const int j = i; // expected-error {{constant expression}} > expected-note {{initializer of 'i' is not a constant expression}} > +// The temporary here has static storage duration, so we can bind a constexpr > +// reference to it. > +constexpr const int &i = 1; > +constexpr const int j = i; > +static_assert(j == 1, ""); > + > +// The temporary here is not const, so it can't be read outside the > expression > +// in which it was created (per the C++14 rules, which we use to avoid a > C++11 > +// defect). > +constexpr int &&k = 1; // expected-note {{temporary created here}} > +constexpr const int l = k; // expected-error {{constant expression}} > expected-note {{read of temporary}} > + > +void f() { > + // The temporary here has automatic storage duration, so we can't bind a > + // constexpr reference to it. > + constexpr const int &i = 1; // expected-error {{constant expression}} > expected-note 2{{temporary}} > +} > > } > > > Modified: cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp?rev=183283&r1=183282&r2=183283&view=diff > ============================================================================== > --- cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp (original) > +++ cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp Tue Jun 4 19:46:14 > 2013 > @@ -715,3 +715,13 @@ namespace deduced_return_type { > static_assert(f() == 0, ""); > static_assert(g(true), ""); > } > + > +namespace modify_temporary_during_construction { > + struct A { int &&temporary; int x; int y; }; > + constexpr int f(int &r) { r *= 9; return r - 12; } > + // FIXME: The 'uninitialized' warning here is bogus. > + constexpr A a = { 6, f(a.temporary), a.temporary }; // expected-warning > {{uninitialized}} expected-note {{temporary created here}} > + static_assert(a.x == 42, ""); > + static_assert(a.y == 54, ""); > + constexpr int k = a.temporary++; // expected-error {{constant expression}} > expected-note {{outside the expression that created the temporary}} > +} > > > _______________________________________________ > cfe-commits mailing list > [email protected] > http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits _______________________________________________ cfe-commits mailing list [email protected] http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
