From: Douglas Gregor Date: Tue, 16 Feb 2010 21:39:57 +0000 (+0000) Subject: Introduce a new kind of failed result for isLvalue/isModifiableLvalue X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e873fb74219f48407ae0b8fa083aa7f0b6ff1427;p=clang Introduce a new kind of failed result for isLvalue/isModifiableLvalue which describes temporary objects of class type in C++. Use this to provide a more-specific, remappable diagnostic when takin the address of such a temporary. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@96396 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index 114a19800f..23076b93e1 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -150,7 +150,8 @@ public: LV_InvalidExpression, LV_MemberFunction, LV_SubObjCPropertySetting, - LV_SubObjCPropertyGetterSetting + LV_SubObjCPropertyGetterSetting, + LV_ClassTemporary }; isLvalueResult isLvalue(ASTContext &Ctx) const; @@ -181,7 +182,8 @@ public: MLV_NoSetterProperty, MLV_MemberFunction, MLV_SubObjCPropertySetting, - MLV_SubObjCPropertyGetterSetting + MLV_SubObjCPropertyGetterSetting, + MLV_ClassTemporary }; isModifiableLvalueResult isModifiableLvalue(ASTContext &Ctx, SourceLocation *Loc = 0) const; diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index 82f9eca129..3fb011c28b 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -18,8 +18,9 @@ def Implicit : DiagGroup<"implicit", [ -// Empty DiagGroups: these are recognized by clang but ignored. +// Empty DiagGroups are recognized by clang but ignored. def : DiagGroup<"address">; +def AddressOfTemporary : DiagGroup<"address-of-temporary">; def : DiagGroup<"aggregate-return">; def : DiagGroup<"attributes">; def : DiagGroup<"bad-function-cast">; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index a53b259829..3af80e4c1b 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1783,6 +1783,11 @@ def err_unqualified_pointer_member_function : Error< "must explicitly qualify member function %0 when taking its address">; def err_typecheck_invalid_lvalue_addrof : Error< "address expression must be an lvalue or a function designator">; +def ext_typecheck_addrof_class_temporary : ExtWarn< + "taking the address of a temporary object of type %0">, + InGroup>, DefaultError; +def err_typecheck_addrof_class_temporary : Error< + "taking the address of a temporary object of type %0">; def err_typecheck_unary_expr : Error< "invalid argument type %0 to unary expression">; def err_typecheck_indirection_requires_pointer : Error< diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 4cb0aa4560..fac65064c0 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1120,8 +1120,15 @@ Expr::isLvalueResult Expr::isLvalueInternal(ASTContext &Ctx) const { return LV_Valid; break; case ImplicitCastExprClass: - return cast(this)->isLvalueCast()? LV_Valid - : LV_InvalidExpression; + if (cast(this)->isLvalueCast()) + return LV_Valid; + + // If this is a conversion to a class temporary, make a note of + // that. + if (Ctx.getLangOptions().CPlusPlus && getType()->isRecordType()) + return LV_ClassTemporary; + + break; case ParenExprClass: // C99 6.5.1p5 return cast(this)->getSubExpr()->isLvalue(Ctx); case BinaryOperatorClass: @@ -1171,9 +1178,15 @@ Expr::isLvalueResult Expr::isLvalueInternal(ASTContext &Ctx) const { if (ReturnType->isLValueReferenceType()) return LV_Valid; + // If the function is returning a class temporary, make a note of + // that. + if (Ctx.getLangOptions().CPlusPlus && ReturnType->isRecordType()) + return LV_ClassTemporary; + break; } case CompoundLiteralExprClass: // C99 6.5.2.5p5 + // FIXME: Is this what we want in C++? return LV_Valid; case ChooseExprClass: // __builtin_choose_expr is an lvalue if the selected operand is. @@ -1207,6 +1220,13 @@ Expr::isLvalueResult Expr::isLvalueInternal(ASTContext &Ctx) const { if (cast(this)->getTypeAsWritten()-> isLValueReferenceType()) return LV_Valid; + + // If this is a conversion to a class temporary, make a note of + // that. + if (Ctx.getLangOptions().CPlusPlus && + cast(this)->getTypeAsWritten()->isRecordType()) + return LV_ClassTemporary; + break; case CXXTypeidExprClass: // C++ 5.2.8p1: The result of a typeid expression is an lvalue of ... @@ -1253,6 +1273,11 @@ Expr::isLvalueResult Expr::isLvalueInternal(ASTContext &Ctx) const { return LV_Valid; break; + case Expr::CXXConstructExprClass: + case Expr::CXXTemporaryObjectExprClass: + case Expr::CXXZeroInitValueExprClass: + return LV_ClassTemporary; + default: break; } @@ -1296,6 +1321,8 @@ Expr::isModifiableLvalue(ASTContext &Ctx, SourceLocation *Loc) const { case LV_SubObjCPropertySetting: return MLV_SubObjCPropertySetting; case LV_SubObjCPropertyGetterSetting: return MLV_SubObjCPropertyGetterSetting; + case LV_ClassTemporary: + return MLV_ClassTemporary; } // The following is illegal: diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 633884f673..e950be0485 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -5697,7 +5697,6 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) { unsigned Diag = 0; bool NeedType = false; switch (IsLV) { // C99 6.5.16p2 - default: assert(0 && "Unknown result from isModifiableLvalue!"); case Expr::MLV_ConstQualified: Diag = diag::err_typecheck_assign_const; break; case Expr::MLV_ArrayType: Diag = diag::err_typecheck_array_not_modifiable_lvalue; @@ -5710,7 +5709,11 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) { case Expr::MLV_LValueCast: Diag = diag::err_typecheck_lvalue_casts_not_supported; break; + case Expr::MLV_Valid: + llvm_unreachable("did not take early return for MLV_Valid"); case Expr::MLV_InvalidExpression: + case Expr::MLV_MemberFunction: + case Expr::MLV_ClassTemporary: Diag = diag::err_typecheck_expression_not_modifiable_lvalue; break; case Expr::MLV_IncompleteType: @@ -5995,6 +5998,12 @@ QualType Sema::CheckAddressOfOperand(Expr *op, SourceLocation OpLoc) { return Context.getMemberPointerType(op->getType(), Context.getTypeDeclType(cast(dcl->getDeclContext())) .getTypePtr()); + } else if (lval == Expr::LV_ClassTemporary) { + Diag(OpLoc, isSFINAEContext()? diag::err_typecheck_addrof_class_temporary + : diag::ext_typecheck_addrof_class_temporary) + << op->getType() << op->getSourceRange(); + if (isSFINAEContext()) + return QualType(); } else if (lval != Expr::LV_Valid && lval != Expr::LV_IncompleteVoidType) { // C99 6.5.3.2p1 // The operand must be either an l-value or a function designator diff --git a/test/SemaCXX/address-of-temporary.cpp b/test/SemaCXX/address-of-temporary.cpp new file mode 100644 index 0000000000..decdc955b6 --- /dev/null +++ b/test/SemaCXX/address-of-temporary.cpp @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -fsyntax-only -Wno-error=address-of-temporary -verify %s +struct X { + X(); + X(int); + X(int, int); +}; + +void *f0() { return &X(); } // expected-warning{{taking the address of a temporary object}} +void *f1() { return &X(1); } // expected-warning{{taking the address of a temporary object}} +void *f2() { return &X(1, 2); } // expected-warning{{taking the address of a temporary object}} +void *f3() { return &(X)1; } // expected-warning{{taking the address of a temporary object}} +