diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2012-06-04 22:27:30 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2012-06-04 22:27:30 +0000 |
commit | 36d02af300a207242f0486b4255420d8be796b21 (patch) | |
tree | adb96320cd351324f25e5d0f3de39541f840d7cc | |
parent | d5edd849b6003b97e0e1ee5cf964c10affbe6bce (diff) |
Add a warning for when an array-to-pointer decay is performed on an array
temporary or an array subobject of a class temporary, and the resulting value
is used to initialize a pointer which outlives the temporary. Such a pointer
is always left dangling after the initialization completes and the array's
lifetime ends.
In order to detect this situation, this change also adds an
LValueClassification of LV_ArrayTemporary for temporaries of array type which
aren't subobjects of class temporaries. These occur in C++11 T{...} and GNU C++
(T){...} expressions, when T is an array type. Previously we treated the former
as a generic prvalue and the latter as a class temporary.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@157955 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/AST/Expr.h | 9 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 4 | ||||
-rw-r--r-- | lib/AST/ExprClassification.cpp | 23 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 1 | ||||
-rw-r--r-- | lib/Sema/SemaInit.cpp | 49 | ||||
-rw-r--r-- | test/SemaCXX/address-of-temporary.cpp | 40 |
6 files changed, 117 insertions, 9 deletions
diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index 6cb34c7fb8..40da0705f3 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -213,7 +213,8 @@ public: LV_InvalidMessageExpression, LV_MemberFunction, LV_SubObjCPropertySetting, - LV_ClassTemporary + LV_ClassTemporary, + LV_ArrayTemporary }; /// Reasons why an expression might not be an l-value. LValueClassification ClassifyLValue(ASTContext &Ctx) const; @@ -242,7 +243,8 @@ public: MLV_MemberFunction, MLV_SubObjCPropertySetting, MLV_InvalidMessageExpression, - MLV_ClassTemporary + MLV_ClassTemporary, + MLV_ArrayTemporary }; isModifiableLvalueResult isModifiableLvalue(ASTContext &Ctx, SourceLocation *Loc = 0) const; @@ -261,7 +263,8 @@ public: CL_DuplicateVectorComponents, // A vector shuffle with dupes. CL_MemberFunction, // An expression referring to a member function CL_SubObjCPropertySetting, - CL_ClassTemporary, // A prvalue of class type + CL_ClassTemporary, // A temporary of class type, or subobject thereof. + CL_ArrayTemporary, // A temporary of array type. CL_ObjCMessageRValue, // ObjC message is an rvalue CL_PRValue // A prvalue for any other reason, of any other type }; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 384e8a15b0..7ed00e6b52 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1155,6 +1155,10 @@ def err_reference_bind_failed : Error< "%2">; def err_reference_bind_init_list : Error< "reference to type %0 cannot bind to an initializer list">; +def warn_temporary_array_to_pointer_decay : Warning< + "pointer is initialized by a temporary array, which will be destroyed at the " + "end of the full-expression">, + InGroup<DiagGroup<"address-of-array-temporary">>; def err_init_list_bad_dest_type : Error< "%select{|non-aggregate }0type %1 cannot be initialized with an initializer " "list">; diff --git a/lib/AST/ExprClassification.cpp b/lib/AST/ExprClassification.cpp index f958aded8d..f16d70b5da 100644 --- a/lib/AST/ExprClassification.cpp +++ b/lib/AST/ExprClassification.cpp @@ -77,6 +77,7 @@ Cl Expr::ClassifyImpl(ASTContext &Ctx, SourceLocation *Loc) const { case Cl::CL_MemberFunction: case Cl::CL_SubObjCPropertySetting: case Cl::CL_ClassTemporary: + case Cl::CL_ArrayTemporary: case Cl::CL_ObjCMessageRValue: case Cl::CL_PRValue: assert(getValueKind() == VK_RValue); break; } @@ -87,6 +88,18 @@ Cl Expr::ClassifyImpl(ASTContext &Ctx, SourceLocation *Loc) const { return Classification(kind, modifiable); } +/// Classify an expression which creates a temporary, based on its type. +static Cl::Kinds ClassifyTemporary(QualType T) { + if (T->isRecordType()) + return Cl::CL_ClassTemporary; + if (T->isArrayType()) + return Cl::CL_ArrayTemporary; + + // No special classification: these don't behave differently from normal + // prvalues. + return Cl::CL_PRValue; +} + static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { // This function takes the first stab at classifying expressions. const LangOptions &Lang = Ctx.getLangOpts(); @@ -124,10 +137,10 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { return Cl::CL_LValue; // C99 6.5.2.5p5 says that compound literals are lvalues. - // In C++, they're class temporaries. + // In C++, they're prvalue temporaries. case Expr::CompoundLiteralExprClass: - return Ctx.getLangOpts().CPlusPlus? Cl::CL_ClassTemporary - : Cl::CL_LValue; + return Ctx.getLangOpts().CPlusPlus ? ClassifyTemporary(E->getType()) + : Cl::CL_LValue; // Expressions that are prvalues. case Expr::CXXBoolLiteralExprClass: @@ -417,7 +430,7 @@ static Cl::Kinds ClassifyUnnamed(ASTContext &Ctx, QualType T) { return Cl::CL_LValue; const RValueReferenceType *RV = T->getAs<RValueReferenceType>(); if (!RV) // Could still be a class temporary, though. - return T->isRecordType() ? Cl::CL_ClassTemporary : Cl::CL_PRValue; + return ClassifyTemporary(T); return RV->getPointeeType()->isFunctionType() ? Cl::CL_LValue : Cl::CL_XValue; } @@ -602,6 +615,7 @@ Expr::LValueClassification Expr::ClassifyLValue(ASTContext &Ctx) const { case Cl::CL_MemberFunction: return LV_MemberFunction; case Cl::CL_SubObjCPropertySetting: return LV_SubObjCPropertySetting; case Cl::CL_ClassTemporary: return LV_ClassTemporary; + case Cl::CL_ArrayTemporary: return LV_ArrayTemporary; case Cl::CL_ObjCMessageRValue: return LV_InvalidMessageExpression; case Cl::CL_PRValue: return LV_InvalidExpression; } @@ -622,6 +636,7 @@ Expr::isModifiableLvalue(ASTContext &Ctx, SourceLocation *Loc) const { case Cl::CL_MemberFunction: return MLV_MemberFunction; case Cl::CL_SubObjCPropertySetting: return MLV_SubObjCPropertySetting; case Cl::CL_ClassTemporary: return MLV_ClassTemporary; + case Cl::CL_ArrayTemporary: return MLV_ArrayTemporary; case Cl::CL_ObjCMessageRValue: return MLV_InvalidMessageExpression; case Cl::CL_PRValue: return VC.getModifiable() == Cl::CM_LValueCast ? diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 362bafaa40..17ea7705af 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -7280,6 +7280,7 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) { break; case Expr::MLV_ArrayType: + case Expr::MLV_ArrayTemporary: Diag = diag::err_typecheck_array_not_modifiable_lvalue; NeedType = true; break; diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 66cc1e14e5..66a64a9e5c 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -4747,6 +4747,43 @@ PerformConstructorInitialization(Sema &S, return move(CurInit); } +/// Determine whether the specified InitializedEntity definitely has a lifetime +/// longer than the current full-expression. Conservatively returns false if +/// it's unclear. +static bool +InitializedEntityOutlivesFullExpression(const InitializedEntity &Entity) { + const InitializedEntity *Top = &Entity; + while (Top->getParent()) + Top = Top->getParent(); + + switch (Top->getKind()) { + case InitializedEntity::EK_Variable: + case InitializedEntity::EK_Result: + case InitializedEntity::EK_Exception: + case InitializedEntity::EK_Member: + case InitializedEntity::EK_New: + case InitializedEntity::EK_Base: + case InitializedEntity::EK_Delegating: + return true; + + case InitializedEntity::EK_ArrayElement: + case InitializedEntity::EK_VectorElement: + case InitializedEntity::EK_BlockElement: + case InitializedEntity::EK_ComplexElement: + // Could not determine what the full initialization is. Assume it might not + // outlive the full-expression. + return false; + + case InitializedEntity::EK_Parameter: + case InitializedEntity::EK_Temporary: + case InitializedEntity::EK_LambdaCapture: + // The entity being initialized might not outlive the full-expression. + return false; + } + + llvm_unreachable("unknown entity kind"); +} + ExprResult InitializationSequence::Perform(Sema &S, const InitializedEntity &Entity, @@ -4826,6 +4863,18 @@ InitializationSequence::Perform(Sema &S, << Init->getSourceRange(); } + // Diagnose cases where we initialize a pointer to an array temporary, and the + // pointer obviously outlives the temporary. + if (Args.size() == 1 && Args.get()[0]->getType()->isArrayType() && + Entity.getType()->isPointerType() && + InitializedEntityOutlivesFullExpression(Entity)) { + Expr *Init = Args.get()[0]; + Expr::LValueClassification Kind = Init->ClassifyLValue(S.Context); + if (Kind == Expr::LV_ClassTemporary || Kind == Expr::LV_ArrayTemporary) + S.Diag(Init->getLocStart(), diag::warn_temporary_array_to_pointer_decay) + << Init->getSourceRange(); + } + QualType DestType = Entity.getType().getNonReferenceType(); // FIXME: Ugly hack around the fact that Entity.getType() is not // the same as Entity.getDecl()->getType() in cases involving type merging, diff --git a/test/SemaCXX/address-of-temporary.cpp b/test/SemaCXX/address-of-temporary.cpp index eb5dee5059..bb6cba3187 100644 --- a/test/SemaCXX/address-of-temporary.cpp +++ b/test/SemaCXX/address-of-temporary.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -fsyntax-only -Wno-error=address-of-temporary -verify %s -struct X { +// RUN: %clang_cc1 -fsyntax-only -Wno-error=address-of-temporary -verify -std=gnu++11 %s +struct X { X(); X(int); X(int, int); @@ -10,3 +10,39 @@ void f1() { (void)&X(1); } // expected-warning{{taking the address of a temporar void f2() { (void)&X(1, 2); } // expected-warning{{taking the address of a temporary object}} void f3() { (void)&(X)1; } // expected-warning{{taking the address of a temporary object}} + +namespace PointerToArrayDecay { + struct Y { + int a[4]; + }; + + typedef int A[4]; + + template<typename T> void consume(T); + struct S { int *p; }; + + void g0() { int *p = Y().a; } // expected-warning{{pointer is initialized by a temporary array}} + void g1() { int *p = Y{}.a; } // expected-warning{{pointer is initialized by a temporary array}} + void g2() { int *p = A{}; } // expected-warning{{pointer is initialized by a temporary array}} + void g3() { int *p = (A){}; } // expected-warning{{pointer is initialized by a temporary array}} + + void h0() { consume(Y().a); } + void h1() { consume(Y{}.a); } + void h2() { consume(A{}); } + void h3() { consume((A){}); } + + void i0() { S s = { Y().a }; } // expected-warning{{pointer is initialized by a temporary array}} + void i1() { S s = { Y{}.a }; } // expected-warning{{pointer is initialized by a temporary array}} + void i2() { S s = { A{} }; } // expected-warning{{pointer is initialized by a temporary array}} + void i3() { S s = { (A){} }; } // expected-warning{{pointer is initialized by a temporary array}} + + void j0() { (void)S { Y().a }; } + void j1() { (void)S { Y{}.a }; } + void j2() { (void)S { A{} }; } + void j3() { (void)S { (A){} }; } + + void k0() { consume(S { Y().a }); } + void k1() { consume(S { Y{}.a }); } + void k2() { consume(S { A{} }); } + void k3() { consume(S { (A){} }); } +} |