From: Richard Smith Date: Sat, 18 Feb 2012 22:04:06 +0000 (+0000) Subject: Implement constant expression support for __real__ and __imag__ on lvalue X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=86024013d4c3728122c58fa07a2a67e6c15837ef;p=clang Implement constant expression support for __real__ and __imag__ on lvalue complex numbers. Treat complex numbers as arrays of the corresponding component type, in order to make std::complex behave properly if implemented in terms of _Complex T. Apparently libstdc++'s std::complex is implemented this way, and we were rejecting a member like this: constexpr double real() { return __real__ val; } because it was marked constexpr but unable to produce a constant expression. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@150895 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticASTKinds.td b/include/clang/Basic/DiagnosticASTKinds.td index 8beac716cc..9cfe5efae2 100644 --- a/include/clang/Basic/DiagnosticASTKinds.td +++ b/include/clang/Basic/DiagnosticASTKinds.td @@ -58,12 +58,14 @@ def note_constexpr_past_end : Note< "%select{temporary|%2}1 is not a constant expression">; def note_constexpr_past_end_subobject : Note< "cannot %select{access base class of|access derived class of|access field of|" - "access array element of|ERROR|call member function on}0 " + "access array element of|ERROR|call member function on|" + "access real component of|access imaginary component of}0 " "pointer past the end of object">; def note_constexpr_null_subobject : Note< "cannot %select{access base class of|access derived class of|access field of|" "access array element of|perform pointer arithmetic on|" - "call member function on}0 null pointer">; + "call member function on|access real component of|" + "access imaginary component of}0 null pointer">; def note_constexpr_var_init_non_constant : Note< "initializer of %0 is not a constant expression">; def note_constexpr_typeid_polymorphic : Note< diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index fcba037afc..dda6e84922 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -105,6 +105,11 @@ namespace { Type = CAT->getElementType(); ArraySize = CAT->getSize().getZExtValue(); MostDerivedLength = I + 1; + } else if (Type->isAnyComplexType()) { + const ComplexType *CT = Type->castAs(); + Type = CT->getElementType(); + ArraySize = 2; + MostDerivedLength = I + 1; } else if (const FieldDecl *FD = getAsField(Path[I])) { Type = FD->getType(); ArraySize = 0; @@ -120,7 +125,7 @@ namespace { // The order of this enum is important for diagnostics. enum CheckSubobjectKind { CSK_Base, CSK_Derived, CSK_Field, CSK_ArrayToPointer, CSK_ArrayIndex, - CSK_This + CSK_This, CSK_Real, CSK_Imag }; /// A path from a glvalue to a subobject of that glvalue. @@ -221,6 +226,18 @@ namespace { MostDerivedPathLength = Entries.size(); } } + /// Update this designator to refer to the given complex component. + void addComplexUnchecked(QualType EltTy, bool Imag) { + PathEntry Entry; + Entry.ArrayIndex = Imag; + Entries.push_back(Entry); + + // This is technically a most-derived object, though in practice this + // is unlikely to matter. + MostDerivedType = EltTy; + MostDerivedArraySize = 2; + MostDerivedPathLength = Entries.size(); + } void diagnosePointerArithmetic(EvalInfo &Info, const Expr *E, uint64_t N); /// Add N to the address of this subobject. void adjustIndex(EvalInfo &Info, const Expr *E, uint64_t N) { @@ -792,6 +809,10 @@ namespace { checkSubobject(Info, E, CSK_ArrayToPointer); Designator.addArrayUnchecked(CAT); } + void addComplex(EvalInfo &Info, const Expr *E, QualType EltTy, bool Imag) { + checkSubobject(Info, E, Imag ? CSK_Imag : CSK_Real); + Designator.addComplexUnchecked(EltTy, Imag); + } void adjustIndex(EvalInfo &Info, const Expr *E, uint64_t N) { if (!checkNullPointer(Info, E, CSK_ArrayIndex)) return; @@ -1420,6 +1441,24 @@ static bool HandleLValueArrayAdjustment(EvalInfo &Info, const Expr *E, return true; } +/// Update an lvalue to refer to a component of a complex number. +/// \param Info - Information about the ongoing evaluation. +/// \param LVal - The lvalue to be updated. +/// \param EltTy - The complex number's component type. +/// \param Imag - False for the real component, true for the imaginary. +static bool HandleLValueComplexElement(EvalInfo &Info, const Expr *E, + LValue &LVal, QualType EltTy, + bool Imag) { + if (Imag) { + CharUnits SizeOfComponent; + if (!HandleSizeof(Info, E->getExprLoc(), EltTy, SizeOfComponent)) + return false; + LVal.Offset += SizeOfComponent; + } + LVal.addComplex(Info, E, EltTy, Imag); + return true; +} + /// Try to evaluate the initializer for a variable declaration. static bool EvaluateVarDeclInit(EvalInfo &Info, const Expr *E, const VarDecl *VD, @@ -1566,6 +1605,25 @@ static bool ExtractSubobject(EvalInfo &Info, const Expr *E, else O = &O->getArrayFiller(); ObjType = CAT->getElementType(); + } else if (ObjType->isAnyComplexType()) { + // Next subobject is a complex number. + uint64_t Index = Sub.Entries[I].ArrayIndex; + if (Index > 1) { + Info.Diag(E->getExprLoc(), Info.getLangOpts().CPlusPlus0x ? + (unsigned)diag::note_constexpr_read_past_end : + (unsigned)diag::note_invalid_subexpr_in_const_expr); + return false; + } + assert(I == N - 1 && "extracting subobject of scalar?"); + if (O->isComplexInt()) { + Obj = CCValue(Index ? O->getComplexIntImag() + : O->getComplexIntReal()); + } else { + assert(O->isComplexFloat()); + Obj = CCValue(Index ? O->getComplexFloatImag() + : O->getComplexFloatReal()); + } + return true; } else if (const FieldDecl *Field = getAsField(Sub.Entries[I])) { if (Field->isMutable()) { Info.Diag(E->getExprLoc(), diag::note_constexpr_ltor_mutable, 1) @@ -1628,13 +1686,17 @@ static unsigned FindDesignatorMismatch(QualType ObjType, bool &WasArrayIndex) { unsigned I = 0, N = std::min(A.Entries.size(), B.Entries.size()); for (/**/; I != N; ++I) { - if (!ObjType.isNull() && ObjType->isArrayType()) { + if (!ObjType.isNull() && + (ObjType->isArrayType() || ObjType->isAnyComplexType())) { // Next subobject is an array element. if (A.Entries[I].ArrayIndex != B.Entries[I].ArrayIndex) { WasArrayIndex = true; return I; } - ObjType = ObjType->castAsArrayTypeUnsafe()->getElementType(); + if (ObjType->isAnyComplexType()) + ObjType = ObjType->castAs()->getElementType(); + else + ObjType = ObjType->castAsArrayTypeUnsafe()->getElementType(); } else { if (A.Entries[I].BaseOrMember != B.Entries[I].BaseOrMember) { WasArrayIndex = false; @@ -2870,6 +2932,8 @@ public: bool VisitCXXTypeidExpr(const CXXTypeidExpr *E); bool VisitArraySubscriptExpr(const ArraySubscriptExpr *E); bool VisitUnaryDeref(const UnaryOperator *E); + bool VisitUnaryReal(const UnaryOperator *E); + bool VisitUnaryImag(const UnaryOperator *E); bool VisitCastExpr(const CastExpr *E) { switch (E->getCastKind()) { @@ -2889,9 +2953,6 @@ public: return HandleBaseToDerivedCast(Info, E, Result); } } - - // FIXME: Missing: __real__, __imag__ - }; } // end anonymous namespace @@ -3015,6 +3076,24 @@ bool LValueExprEvaluator::VisitUnaryDeref(const UnaryOperator *E) { return EvaluatePointer(E->getSubExpr(), Result, Info); } +bool LValueExprEvaluator::VisitUnaryReal(const UnaryOperator *E) { + if (!Visit(E->getSubExpr())) + return false; + // __real is a no-op on scalar lvalues. + if (E->getSubExpr()->getType()->isAnyComplexType()) + HandleLValueComplexElement(Info, E, Result, E->getType(), false); + return true; +} + +bool LValueExprEvaluator::VisitUnaryImag(const UnaryOperator *E) { + assert(E->getSubExpr()->getType()->isAnyComplexType() && + "lvalue __imag__ on scalar?"); + if (!Visit(E->getSubExpr())) + return false; + HandleLValueComplexElement(Info, E, Result, E->getType(), true); + return true; +} + //===----------------------------------------------------------------------===// // Pointer Evaluation //===----------------------------------------------------------------------===// diff --git a/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp index b7ba68890d..78be5d292d 100644 --- a/test/SemaCXX/constant-expression-cxx11.cpp +++ b/test/SemaCXX/constant-expression-cxx11.cpp @@ -1055,6 +1055,28 @@ namespace ComplexConstexpr { constexpr _Complex int test6 = {5,6}; typedef _Complex float fcomplex; constexpr fcomplex test7 = fcomplex(); + + constexpr const double &t2r = __real test3; + constexpr const double &t2i = __imag test3; + static_assert(&t2r + 1 == &t2i, ""); + static_assert(t2r == 1.0, ""); + static_assert(t2i == 2.0, ""); + constexpr const double *t2p = &t2r; + static_assert(t2p[-1] == 0.0, ""); // expected-error {{constant expr}} expected-note {{cannot refer to element -1 of array of 2 elements}} + static_assert(t2p[0] == 1.0, ""); + static_assert(t2p[1] == 2.0, ""); + static_assert(t2p[2] == 0.0, ""); // expected-error {{constant expr}} expected-note {{one-past-the-end pointer}} + static_assert(t2p[3] == 0.0, ""); // expected-error {{constant expr}} expected-note {{cannot refer to element 3 of array of 2 elements}} + constexpr _Complex float *p = 0; + constexpr float pr = __real *p; // expected-error {{constant expr}} expected-note {{cannot access real component of null}} + constexpr float pi = __imag *p; // expected-error {{constant expr}} expected-note {{cannot access imaginary component of null}} + constexpr const _Complex double *q = &test3 + 1; + constexpr double qr = __real *q; // expected-error {{constant expr}} expected-note {{cannot access real component of pointer past the end}} + constexpr double qi = __imag *q; // expected-error {{constant expr}} expected-note {{cannot access imaginary component of pointer past the end}} + + static_assert(__real test6 == 5, ""); + static_assert(__imag test6 == 6, ""); + static_assert(&__imag test6 == &__real test6 + 1, ""); } namespace InstantiateCaseStmt {