From 5f70bed98b8fe5f13a077ca7bd2653fab7bc6f23 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 20 Oct 2016 01:20:00 +0000 Subject: [PATCH] Refactor and simplify Sema::FindCompositePointerType. No functionality change intended. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@284685 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaExprCXX.cpp | 249 +++++++++++++++++++-------------------- 1 file changed, 123 insertions(+), 126 deletions(-) diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 56f593439d..96e1b24de8 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -5520,7 +5520,7 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, /// \brief Find a merged pointer type and convert the two expressions to it. /// /// This finds the composite pointer type (or member pointer type) for @p E1 -/// and @p E2 according to C++11 5.9p2. It converts both expressions to this +/// and @p E2 according to C++1z 5p14. It converts both expressions to this /// type and returns it. /// It does not emit diagnostics. /// @@ -5538,69 +5538,87 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc, *NonStandardCompositeType = false; assert(getLangOpts().CPlusPlus && "This function assumes C++"); + + // C++1z [expr]p14: + // The composite pointer type of two operands p1 and p2 having types T1 + // and T2 QualType T1 = E1->getType(), T2 = E2->getType(); - // C++11 5.9p2 - // Pointer conversions and qualification conversions are performed on - // pointer operands to bring them to their composite pointer type. If - // one operand is a null pointer constant, the composite pointer type is - // std::nullptr_t if the other operand is also a null pointer constant or, - // if the other operand is a pointer, the type of the other operand. - if (!T1->isAnyPointerType() && !T1->isMemberPointerType() && - !T2->isAnyPointerType() && !T2->isMemberPointerType()) { - if (T1->isNullPtrType() && - E2->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull)) { - E2 = ImpCastExprToType(E2, T1, CK_NullToPointer).get(); - return T1; - } - if (T2->isNullPtrType() && - E1->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull)) { - E1 = ImpCastExprToType(E1, T2, CK_NullToPointer).get(); - return T2; - } + // where at least one is a pointer or pointer to member type or + // std::nullptr_t is: + bool T1IsPointerLike = T1->isAnyPointerType() || T1->isMemberPointerType() || + T1->isNullPtrType(); + bool T2IsPointerLike = T2->isAnyPointerType() || T2->isMemberPointerType() || + T2->isNullPtrType(); + if (!T1IsPointerLike && !T2IsPointerLike) return QualType(); - } - if (E1->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull)) { - if (T2->isMemberPointerType()) - E1 = ImpCastExprToType(E1, T2, CK_NullToMemberPointer).get(); - else - E1 = ImpCastExprToType(E1, T2, CK_NullToPointer).get(); - return T2; - } - if (E2->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull)) { - if (T1->isMemberPointerType()) - E2 = ImpCastExprToType(E2, T1, CK_NullToMemberPointer).get(); - else - E2 = ImpCastExprToType(E2, T1, CK_NullToPointer).get(); + // - if both p1 and p2 are null pointer constants, std::nullptr_t; + // This can't actually happen, following the standard, but we also use this + // to implement the end of [expr.conv], which hits this case. + // + // - if either p1 or p2 is a null pointer constant, T2 or T1, respectively; + if (T1IsPointerLike && + E2->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull)) { + E2 = ImpCastExprToType(E2, T1, T1->isMemberPointerType() + ? CK_NullToMemberPointer + : CK_NullToPointer).get(); return T1; } + if (T2IsPointerLike && + E1->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull)) { + E1 = ImpCastExprToType(E1, T2, T2->isMemberPointerType() + ? CK_NullToMemberPointer + : CK_NullToPointer).get(); + return T2; + } // Now both have to be pointers or member pointers. - if ((!T1->isPointerType() && !T1->isMemberPointerType()) || - (!T2->isPointerType() && !T2->isMemberPointerType())) + if (!T1IsPointerLike || !T2IsPointerLike) return QualType(); - - // Otherwise, of one of the operands has type "pointer to cv1 void," then - // the other has type "pointer to cv2 T" and the composite pointer type is - // "pointer to cv12 void," where cv12 is the union of cv1 and cv2. - // Otherwise, the composite pointer type is a pointer type similar to the - // type of one of the operands, with a cv-qualification signature that is - // the union of the cv-qualification signatures of the operand types. - // In practice, the first part here is redundant; it's subsumed by the second. - // What we do here is, we build the two possible composite types, and try the - // conversions in both directions. If only one works, or if the two composite - // types are the same, we have succeeded. + assert(!T1->isNullPtrType() && !T2->isNullPtrType() && + "nullptr_t should be a null pointer constant"); + + // - if T1 or T2 is "pointer to cv1 void" and the other type is + // "pointer to cv2 T", "pointer to cv12 void", where cv12 is + // the union of cv1 and cv2; + // - if T1 or T2 is "pointer to noexcept function" and the other type is + // "pointer to function", where the function types are otherwise the same, + // "pointer to function"; + // FIXME: This rule is defective: it should also permit removing noexcept + // from a pointer to member function. As a Clang extension, we also + // permit removing 'noreturn', so we generalize this rule to; + // - [Clang] If T1 and T2 are both of type "pointer to function" or + // "pointer to member function" and the pointee types can be unified + // by a function pointer conversion, that conversion is applied + // before checking the following rules. + // - if T1 is "pointer to cv1 C1" and T2 is "pointer to cv2 C2", where C1 + // is reference-related to C2 or C2 is reference-related to C1 (8.6.3), + // the cv-combined type of T1 and T2 or the cv-combined type of T2 and T1, + // respectively; + // - if T1 is "pointer to member of C1 of type cv1 U1" and T2 is "pointer + // to member of C2 of type cv2 U2" where C1 is reference-related to C2 or + // C2 is reference-related to C1 (8.6.3), the cv-combined type of T2 and + // T1 or the cv-combined type of T1 and T2, respectively; + // - if T1 and T2 are similar types (4.5), the cv-combined type of T1 and + // T2; + // + // If looked at in the right way, these bullets all do the same thing. + // What we do here is, we build the two possible cv-combined types, and try + // the conversions in both directions. If only one works, or if the two + // composite types are the same, we have succeeded. // FIXME: extended qualifiers? - typedef SmallVector QualifierVector; - QualifierVector QualifierUnion; - typedef SmallVector, 4> - ContainingClassVector; - ContainingClassVector MemberOfClass; - QualType Composite1 = Context.getCanonicalType(T1), - Composite2 = Context.getCanonicalType(T2); + // + // Note that this will fail to find a composite pointer type for "pointer + // to void" and "pointer to function". We can't actually perform the final + // conversion in this case, even though a composite pointer type formally + // exists. + SmallVector QualifierUnion; + SmallVector, 4> MemberOfClass; + QualType Composite1 = Context.getCanonicalType(T1); + QualType Composite2 = Context.getCanonicalType(T2); unsigned NeedConstBefore = 0; - do { + while (true) { const PointerType *Ptr1, *Ptr2; if ((Ptr1 = Composite1->getAs()) && (Ptr2 = Composite2->getAs())) { @@ -5642,7 +5660,7 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc, // Cannot unwrap any more types. break; - } while (true); + } if (NeedConstBefore && NonStandardCompositeType) { // Extension: Add 'const' to qualifiers that come before the first qualifier @@ -5657,94 +5675,73 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc, } // Rewrap the composites as pointers or member pointers with the union CVRs. - ContainingClassVector::reverse_iterator MOC - = MemberOfClass.rbegin(); - for (QualifierVector::reverse_iterator - I = QualifierUnion.rbegin(), - E = QualifierUnion.rend(); - I != E; (void)++I, ++MOC) { - Qualifiers Quals = Qualifiers::fromCVRMask(*I); - if (MOC->first && MOC->second) { + auto MOC = MemberOfClass.rbegin(); + for (unsigned CVR : llvm::reverse(QualifierUnion)) { + Qualifiers Quals = Qualifiers::fromCVRMask(CVR); + auto Classes = *MOC++; + if (Classes.first && Classes.second) { // Rebuild member pointer type Composite1 = Context.getMemberPointerType( - Context.getQualifiedType(Composite1, Quals), - MOC->first); + Context.getQualifiedType(Composite1, Quals), Classes.first); Composite2 = Context.getMemberPointerType( - Context.getQualifiedType(Composite2, Quals), - MOC->second); + Context.getQualifiedType(Composite2, Quals), Classes.second); } else { // Rebuild pointer type - Composite1 - = Context.getPointerType(Context.getQualifiedType(Composite1, Quals)); - Composite2 - = Context.getPointerType(Context.getQualifiedType(Composite2, Quals)); + Composite1 = + Context.getPointerType(Context.getQualifiedType(Composite1, Quals)); + Composite2 = + Context.getPointerType(Context.getQualifiedType(Composite2, Quals)); } } - // Try to convert to the first composite pointer type. - InitializedEntity Entity1 - = InitializedEntity::InitializeTemporary(Composite1); - InitializationKind Kind - = InitializationKind::CreateCopy(Loc, SourceLocation()); - InitializationSequence E1ToC1(*this, Entity1, Kind, E1); - InitializationSequence E2ToC1(*this, Entity1, Kind, E2); - - if (E1ToC1 && E2ToC1) { - // Conversion to Composite1 is viable. - if (!Context.hasSameType(Composite1, Composite2)) { - // Composite2 is a different type from Composite1. Check whether - // Composite2 is also viable. - InitializedEntity Entity2 - = InitializedEntity::InitializeTemporary(Composite2); - InitializationSequence E1ToC2(*this, Entity2, Kind, E1); - InitializationSequence E2ToC2(*this, Entity2, Kind, E2); - if (E1ToC2 && E2ToC2) { - // Both Composite1 and Composite2 are viable and are different; - // this is an ambiguity. - return QualType(); - } + struct Conversion { + Sema &S; + SourceLocation Loc; + Expr *&E1, *&E2; + QualType Composite; + InitializedEntity Entity = + InitializedEntity::InitializeTemporary(Composite); + InitializationKind Kind = + InitializationKind::CreateCopy(Loc, SourceLocation()); + InitializationSequence E1ToC, E2ToC; + bool Viable = E1ToC && E2ToC; + + Conversion(Sema &S, SourceLocation Loc, Expr *&E1, Expr *&E2, + QualType Composite) + : S(S), Loc(Loc), E1(E1), E2(E2), Composite(Composite), + E1ToC(S, Entity, Kind, E1), E2ToC(S, Entity, Kind, E2) { } - // Convert E1 to Composite1 - ExprResult E1Result - = E1ToC1.Perform(*this, Entity1, Kind, E1); - if (E1Result.isInvalid()) - return QualType(); - E1 = E1Result.getAs(); - - // Convert E2 to Composite1 - ExprResult E2Result - = E2ToC1.Perform(*this, Entity1, Kind, E2); - if (E2Result.isInvalid()) - return QualType(); - E2 = E2Result.getAs(); + QualType perform() { + ExprResult E1Result = E1ToC.Perform(S, Entity, Kind, E1); + if (E1Result.isInvalid()) + return QualType(); + E1 = E1Result.getAs(); - return Composite1; - } + ExprResult E2Result = E2ToC.Perform(S, Entity, Kind, E2); + if (E2Result.isInvalid()) + return QualType(); + E2 = E2Result.getAs(); - // Check whether Composite2 is viable. - InitializedEntity Entity2 - = InitializedEntity::InitializeTemporary(Composite2); - InitializationSequence E1ToC2(*this, Entity2, Kind, E1); - InitializationSequence E2ToC2(*this, Entity2, Kind, E2); - if (!E1ToC2 || !E2ToC2) - return QualType(); + return Composite; + } + }; - // Convert E1 to Composite2 - ExprResult E1Result - = E1ToC2.Perform(*this, Entity2, Kind, E1); - if (E1Result.isInvalid()) - return QualType(); - E1 = E1Result.getAs(); + // Try to convert to each composite pointer type. + Conversion C1(*this, Loc, E1, E2, Composite1); + if (C1.Viable && Context.hasSameType(Composite1, Composite2)) + return C1.perform(); + Conversion C2(*this, Loc, E1, E2, Composite2); - // Convert E2 to Composite2 - ExprResult E2Result - = E2ToC2.Perform(*this, Entity2, Kind, E2); - if (E2Result.isInvalid()) + if (C1.Viable == C2.Viable) { + // Either Composite1 and Composite2 are viable and are different, or + // neither is viable. + // FIXME: How both be viable and different? return QualType(); - E2 = E2Result.getAs(); + } - return Composite2; + // Convert to the chosen type. + return (C1.Viable ? C1 : C2).perform(); } ExprResult Sema::MaybeBindToTemporary(Expr *E) { -- 2.40.0