forked from OSchip/llvm-project
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:
parent
23399d765c
commit
e6c0144208
|
@ -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
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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">;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -284,4 +284,7 @@ namespace dtors {
|
|||
void f() {
|
||||
std::initializer_list<S>{ S(), S() };
|
||||
}
|
||||
void g() {
|
||||
auto x = std::initializer_list<S>{ S(), S() };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue