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 /lib | |
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
Diffstat (limited to 'lib')
-rw-r--r-- | lib/AST/ExprClassification.cpp | 23 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 1 | ||||
-rw-r--r-- | lib/Sema/SemaInit.cpp | 49 |
3 files changed, 69 insertions, 4 deletions
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, |