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.

llvm-svn: 183283
This commit is contained in:
Richard Smith 2013-06-05 00:46:14 +00:00
parent 23399d765c
commit e6c0144208
22 changed files with 542 additions and 33 deletions

View File

@ -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<ASTContext> {
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
//===--------------------------------------------------------------------===//

View File

@ -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;

View File

@ -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.

View File

@ -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;

View File

@ -117,6 +117,10 @@ def note_constexpr_access_inactive_union_member : Note<
"%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">;

View File

@ -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

View File

@ -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

View File

@ -8029,6 +8029,19 @@ unsigned ASTContext::getParameterIndex(const ParmVarDecl *D) const {
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())

View File

@ -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::VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *Node) {
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)

View File

@ -1001,6 +1001,10 @@ static bool IsGlobalLValue(APValue::LValueBase B) {
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 LValue &LVal) {
}
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(EvalInfo &Info, const Expr *E, AccessKinds AK,
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");
BaseVal = &Frame->Temporaries[Base];
// 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 = 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::VisitMaterializeTemporaryExpr(
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 ASTContext &Ctx,
}
}
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

View File

@ -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;

View File

@ -2735,6 +2735,63 @@ llvm::Constant *CodeGenModule::GetAddrOfConstantCString(const std::string &Str,
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

View File

@ -307,7 +307,8 @@ class CodeGenModule : public CodeGenTypeCache {
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();

View File

@ -8353,7 +8353,7 @@ static QualType CheckAddressOfOperand(Sema &S, ExprResult &OrigOp,
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) {

View File

@ -2502,6 +2502,46 @@ bool InitializedEntity::allowsNRVO() const {
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(const InitializedEntity &Entity) {
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,

View File

@ -1576,6 +1576,7 @@ void ASTStmtReader::VisitFunctionParmPackExpr(FunctionParmPackExpr *E) {
void ASTStmtReader::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E) {
VisitExpr(E);
E->Temporary = Reader.ReadSubExpr();
E->ExtendingDecl = ReadDeclAs<ValueDecl>(Record, Idx);
}
void ASTStmtReader::VisitOpaqueValueExpr(OpaqueValueExpr *E) {

View File

@ -1572,6 +1572,7 @@ void ASTStmtWriter::VisitFunctionParmPackExpr(FunctionParmPackExpr *E) {
void ASTStmtWriter::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E) {
VisitExpr(E);
Writer.AddStmt(E->Temporary);
Writer.AddDeclRef(E->ExtendingDecl, Record);
Code = serialization::EXPR_MATERIALIZE_TEMPORARY;
}

View File

@ -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

View File

@ -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

View File

@ -284,4 +284,7 @@ namespace dtors {
void f() {
std::initializer_list<S>{ S(), S() };
}
void g() {
auto x = std::initializer_list<S>{ S(), S() };
}
}

View File

@ -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}}
}
}

View File

@ -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}}
}