From 2b81f42a76a58a23c358f6d72b65385c0073f94f Mon Sep 17 00:00:00 2001 From: Hans Wennborg Date: Thu, 20 Oct 2016 20:54:32 +0000 Subject: [PATCH] Revert r284753 "[c++1z] Teach composite pointer type computation how to compute the composite" It caused PR30749. llvm-svn: 284778 --- clang/include/clang/Sema/Overload.h | 3 +- clang/include/clang/Sema/Sema.h | 10 +- clang/lib/Sema/SemaExprCXX.cpp | 190 ++++------------------------ clang/lib/Sema/SemaOverload.cpp | 54 ++++---- clang/test/CXX/expr/p13.cpp | 45 ------- 5 files changed, 56 insertions(+), 246 deletions(-) delete mode 100644 clang/test/CXX/expr/p13.cpp diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h index f677afe8e26fb..83c6554c64d7d 100644 --- a/clang/include/clang/Sema/Overload.h +++ b/clang/include/clang/Sema/Overload.h @@ -145,8 +145,7 @@ namespace clang { /// pointer-to-member conversion, or boolean conversion. ImplicitConversionKind Second : 8; - /// Third - The third conversion can be a qualification conversion - /// or a function conversion. + /// Third - The third conversion can be a qualification conversion. ImplicitConversionKind Third : 8; /// \brief Whether this is the deprecated conversion of a diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 08e6a29ca11e3..1dd415d3497e9 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -8954,15 +8954,13 @@ class Sema { ExprResult &cond, ExprResult &lhs, ExprResult &rhs, ExprValueKind &VK, ExprObjectKind &OK, SourceLocation questionLoc); QualType FindCompositePointerType(SourceLocation Loc, Expr *&E1, Expr *&E2, - bool *NonStandardCompositeType = nullptr, - bool ConvertArgs = true); + bool *NonStandardCompositeType = nullptr); QualType FindCompositePointerType(SourceLocation Loc, ExprResult &E1, ExprResult &E2, - bool *NonStandardCompositeType = nullptr, - bool ConvertArgs = true) { + bool *NonStandardCompositeType = nullptr) { Expr *E1Tmp = E1.get(), *E2Tmp = E2.get(); - QualType Composite = FindCompositePointerType( - Loc, E1Tmp, E2Tmp, NonStandardCompositeType, ConvertArgs); + QualType Composite = FindCompositePointerType(Loc, E1Tmp, E2Tmp, + NonStandardCompositeType); E1 = E1Tmp; E2 = E2Tmp; return Composite; diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index ba38049bcab91..b660bd3486527 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -3610,6 +3610,16 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, // Nothing else to do. break; + case ICK_Function_Conversion: + // If both sides are functions (or pointers/references to them), there could + // be incompatible exception declarations. + if (CheckExceptionSpecCompatibility(From, ToType)) + return ExprError(); + + From = ImpCastExprToType(From, ToType, CK_NoOp, + VK_RValue, /*BasePath=*/nullptr, CCK).get(); + break; + case ICK_Integral_Promotion: case ICK_Integral_Conversion: if (ToType->isBooleanType()) { @@ -3856,7 +3866,6 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, case ICK_Lvalue_To_Rvalue: case ICK_Array_To_Pointer: case ICK_Function_To_Pointer: - case ICK_Function_Conversion: case ICK_Qualification: case ICK_Num_Conversion_Kinds: case ICK_C_Only_Conversion: @@ -3869,16 +3878,6 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, // Nothing to do. break; - case ICK_Function_Conversion: - // If both sides are functions (or pointers/references to them), there could - // be incompatible exception declarations. - if (CheckExceptionSpecCompatibility(From, ToType)) - return ExprError(); - - From = ImpCastExprToType(From, ToType, CK_NoOp, - VK_RValue, /*BasePath=*/nullptr, CCK).get(); - break; - case ICK_Qualification: { // The qualification keeps the category of the inner expression, unless the // target type isn't a reference. @@ -5394,17 +5393,6 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, if (LHS.get()->getObjectKind() == OK_BitField || RHS.get()->getObjectKind() == OK_BitField) OK = OK_BitField; - - // If we have function pointer types, unify them anyway to unify their - // exception specifications, if any. - if (LTy->isFunctionPointerType() || LTy->isMemberFunctionPointerType()) { - LTy = FindCompositePointerType(QuestionLoc, LHS, RHS, nullptr, - /*ConvertArgs*/false); - assert(!LTy.isNull() && "failed to find composite pointer type for " - "canonically equivalent function ptr types"); - assert(Context.hasSameType(LTy, RTy) && "bad composite pointer type"); - } - return LTy; } @@ -5459,14 +5447,6 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, RHS = RHSCopy; } - // If we have function pointer types, unify them anyway to unify their - // exception specifications, if any. - if (LTy->isFunctionPointerType() || LTy->isMemberFunctionPointerType()) { - LTy = FindCompositePointerType(QuestionLoc, LHS, RHS); - assert(!LTy.isNull() && "failed to find composite pointer type for " - "canonically equivalent function ptr types"); - } - return LTy; } @@ -5537,78 +5517,6 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, return QualType(); } -static FunctionProtoType::ExceptionSpecInfo -mergeExceptionSpecs(Sema &S, FunctionProtoType::ExceptionSpecInfo ESI1, - FunctionProtoType::ExceptionSpecInfo ESI2, - SmallVectorImpl &ExceptionTypeStorage) { - ExceptionSpecificationType EST1 = ESI1.Type; - ExceptionSpecificationType EST2 = ESI2.Type; - - // If either of them can throw anything, that is the result. - if (EST1 == EST_None) return ESI1; - if (EST2 == EST_None) return ESI2; - if (EST1 == EST_MSAny) return ESI1; - if (EST2 == EST_MSAny) return ESI2; - - // If either of them is non-throwing, the result is the other. - if (EST1 == EST_DynamicNone) return ESI2; - if (EST2 == EST_DynamicNone) return ESI1; - if (EST1 == EST_BasicNoexcept) return ESI2; - if (EST2 == EST_BasicNoexcept) return ESI1; - - // If either of them is a non-value-dependent computed noexcept, that - // determines the result. - if (EST2 == EST_ComputedNoexcept && ESI2.NoexceptExpr && - !ESI2.NoexceptExpr->isValueDependent()) - return !ESI2.NoexceptExpr->EvaluateKnownConstInt(S.Context) ? ESI2 : ESI1; - if (EST1 == EST_ComputedNoexcept && ESI1.NoexceptExpr && - !ESI1.NoexceptExpr->isValueDependent()) - return !ESI1.NoexceptExpr->EvaluateKnownConstInt(S.Context) ? ESI1 : ESI2; - // If we're left with value-dependent computed noexcept expressions, we're - // stuck. Before C++17, we can just drop the exception specification entirely, - // since it's not actually part of the canonical type. And this should never - // happen in C++17, because it would mean we were computing the composite - // pointer type of dependent types, which should never happen. - if (EST1 == EST_ComputedNoexcept || EST2 == EST_ComputedNoexcept) { - assert(!S.getLangOpts().CPlusPlus1z && - "computing composite pointer type of dependent types"); - return FunctionProtoType::ExceptionSpecInfo(); - } - - // Switch over the possibilities so that people adding new values know to - // update this function. - switch (EST1) { - case EST_None: - case EST_DynamicNone: - case EST_MSAny: - case EST_BasicNoexcept: - case EST_ComputedNoexcept: - llvm_unreachable("handled above"); - - case EST_Dynamic: { - // This is the fun case: both exception specifications are dynamic. Form - // the union of the two lists. - assert(EST2 == EST_Dynamic && "other cases should already be handled"); - llvm::SmallPtrSet Found; - for (auto &Exceptions : {ESI1.Exceptions, ESI2.Exceptions}) - for (QualType E : Exceptions) - if (Found.insert(S.Context.getCanonicalType(E)).second) - ExceptionTypeStorage.push_back(E); - - FunctionProtoType::ExceptionSpecInfo Result(EST_Dynamic); - Result.Exceptions = ExceptionTypeStorage; - return Result; - } - - case EST_Unevaluated: - case EST_Uninstantiated: - case EST_Unparsed: - llvm_unreachable("shouldn't see unresolved exception specifications here"); - } - - llvm_unreachable("invalid ExceptionSpecificationType"); -} - /// \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 @@ -5623,12 +5531,9 @@ mergeExceptionSpecs(Sema &S, FunctionProtoType::ExceptionSpecInfo ESI1, /// a non-standard (but still sane) composite type to which both expressions /// can be converted. When such a type is chosen, \c *NonStandardCompositeType /// will be set true. -/// -/// \param ConvertArgs If \c false, do not convert E1 and E2 to the target type. QualType Sema::FindCompositePointerType(SourceLocation Loc, Expr *&E1, Expr *&E2, - bool *NonStandardCompositeType, - bool ConvertArgs) { + bool *NonStandardCompositeType) { if (NonStandardCompositeType) *NonStandardCompositeType = false; @@ -5655,18 +5560,16 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc, // - if either p1 or p2 is a null pointer constant, T2 or T1, respectively; if (T1IsPointerLike && E2->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull)) { - if (ConvertArgs) - E2 = ImpCastExprToType(E2, T1, T1->isMemberPointerType() - ? CK_NullToMemberPointer - : CK_NullToPointer).get(); + E2 = ImpCastExprToType(E2, T1, T1->isMemberPointerType() + ? CK_NullToMemberPointer + : CK_NullToPointer).get(); return T1; } if (T2IsPointerLike && E1->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull)) { - if (ConvertArgs) - E1 = ImpCastExprToType(E1, T2, T2->isMemberPointerType() - ? CK_NullToMemberPointer - : CK_NullToPointer).get(); + E1 = ImpCastExprToType(E1, T2, T2->isMemberPointerType() + ? CK_NullToMemberPointer + : CK_NullToPointer).get(); return T2; } @@ -5712,8 +5615,8 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc, // exists. SmallVector QualifierUnion; SmallVector, 4> MemberOfClass; - QualType Composite1 = T1; - QualType Composite2 = T2; + QualType Composite1 = Context.getCanonicalType(T1); + QualType Composite2 = Context.getCanonicalType(T2); unsigned NeedConstBefore = 0; while (true) { const PointerType *Ptr1, *Ptr2; @@ -5759,41 +5662,6 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc, break; } - // Apply the function pointer conversion to unify the types. We've already - // unwrapped down to the function types, and we want to merge rather than - // just convert, so do this ourselves rather than calling - // IsFunctionConversion. - // - // FIXME: In order to match the standard wording as closely as possible, we - // currently only do this under a single level of pointers. Ideally, we would - // allow this in general, and set NeedConstBefore to the relevant depth on - // the side(s) where we changed anything. - if (QualifierUnion.size() == 1) { - if (auto *FPT1 = Composite1->getAs()) { - if (auto *FPT2 = Composite2->getAs()) { - FunctionProtoType::ExtProtoInfo EPI1 = FPT1->getExtProtoInfo(); - FunctionProtoType::ExtProtoInfo EPI2 = FPT2->getExtProtoInfo(); - - // The result is noreturn if both operands are. - bool Noreturn = - EPI1.ExtInfo.getNoReturn() && EPI2.ExtInfo.getNoReturn(); - EPI1.ExtInfo = EPI1.ExtInfo.withNoReturn(Noreturn); - EPI2.ExtInfo = EPI2.ExtInfo.withNoReturn(Noreturn); - - // The result is nothrow if both operands are. - SmallVector ExceptionTypeStorage; - EPI1.ExceptionSpec = EPI2.ExceptionSpec = - mergeExceptionSpecs(*this, EPI1.ExceptionSpec, EPI2.ExceptionSpec, - ExceptionTypeStorage); - - Composite1 = Context.getFunctionType(FPT1->getReturnType(), - FPT1->getParamTypes(), EPI1); - Composite2 = Context.getFunctionType(FPT2->getReturnType(), - FPT2->getParamTypes(), EPI2); - } - } - } - if (NeedConstBefore && NonStandardCompositeType) { // Extension: Add 'const' to qualifiers that come before the first qualifier // mismatch, so that our (non-standard!) composite type meets the @@ -5843,28 +5711,25 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc, E1ToC(S, Entity, Kind, E1), E2ToC(S, Entity, Kind, E2), Viable(E1ToC && E2ToC) {} - bool perform() { + QualType perform() { ExprResult E1Result = E1ToC.Perform(S, Entity, Kind, E1); if (E1Result.isInvalid()) - return true; + return QualType(); E1 = E1Result.getAs(); ExprResult E2Result = E2ToC.Perform(S, Entity, Kind, E2); if (E2Result.isInvalid()) - return true; + return QualType(); E2 = E2Result.getAs(); - return false; + return Composite; } }; // Try to convert to each composite pointer type. Conversion C1(*this, Loc, E1, E2, Composite1); - if (C1.Viable && Context.hasSameType(Composite1, Composite2)) { - if (ConvertArgs && C1.perform()) - return QualType(); - return C1.Composite; - } + if (C1.Viable && Context.hasSameType(Composite1, Composite2)) + return C1.perform(); Conversion C2(*this, Loc, E1, E2, Composite2); if (C1.Viable == C2.Viable) { @@ -5875,10 +5740,7 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc, } // Convert to the chosen type. - if (ConvertArgs && (C1.Viable ? C1 : C2).perform()) - return QualType(); - - return C1.Viable ? C1.Composite : C2.Composite; + return (C1.Viable ? C1 : C2).perform(); } ExprResult Sema::MaybeBindToTemporary(Expr *E) { diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 8cc592e21e531..db97f3d57f551 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -1405,7 +1405,6 @@ bool Sema::IsFunctionConversion(QualType FromType, QualType ToType, // - a pointer // - a member pointer // - a block pointer - // Changes here need matching changes in FindCompositePointerType. CanQualType CanTo = Context.getCanonicalType(ToType); CanQualType CanFrom = Context.getCanonicalType(FromType); Type::TypeClass TyClass = CanTo->getTypeClass(); @@ -1418,13 +1417,8 @@ bool Sema::IsFunctionConversion(QualType FromType, QualType ToType, CanTo = CanTo.getAs()->getPointeeType(); CanFrom = CanFrom.getAs()->getPointeeType(); } else if (TyClass == Type::MemberPointer) { - auto ToMPT = CanTo.getAs(); - auto FromMPT = CanFrom.getAs(); - // A function pointer conversion cannot change the class of the function. - if (ToMPT->getClass() != FromMPT->getClass()) - return false; - CanTo = ToMPT->getPointeeType(); - CanFrom = FromMPT->getPointeeType(); + CanTo = CanTo.getAs()->getPointeeType(); + CanFrom = CanFrom.getAs()->getPointeeType(); } else { return false; } @@ -1763,6 +1757,10 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, // Compatible conversions (Clang extension for C function overloading) SCS.Second = ICK_Compatible_Conversion; FromType = ToType.getUnqualifiedType(); + } else if (S.IsFunctionConversion(FromType, ToType, FromType)) { + // Function pointer conversions (removing 'noexcept') including removal of + // 'noreturn' (Clang extension). + SCS.Second = ICK_Function_Conversion; } else if (IsTransparentUnionStandardConversion(S, From, ToType, InOverloadResolution, SCS, CStyle)) { @@ -1784,36 +1782,34 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, } SCS.setToType(1, FromType); - // The third conversion can be a function pointer conversion or a - // qualification conversion (C++ [conv.fctptr], [conv.qual]). + QualType CanonFrom; + QualType CanonTo; + // The third conversion can be a qualification conversion (C++ 4p1). bool ObjCLifetimeConversion; - if (S.IsFunctionConversion(FromType, ToType, FromType)) { - // Function pointer conversions (removing 'noexcept') including removal of - // 'noreturn' (Clang extension). - SCS.Third = ICK_Function_Conversion; - } else if (S.IsQualificationConversion(FromType, ToType, CStyle, - ObjCLifetimeConversion)) { + if (S.IsQualificationConversion(FromType, ToType, CStyle, + ObjCLifetimeConversion)) { SCS.Third = ICK_Qualification; SCS.QualificationIncludesObjCLifetime = ObjCLifetimeConversion; FromType = ToType; + CanonFrom = S.Context.getCanonicalType(FromType); + CanonTo = S.Context.getCanonicalType(ToType); } else { // No conversion required SCS.Third = ICK_Identity; - } - // C++ [over.best.ics]p6: - // [...] Any difference in top-level cv-qualification is - // subsumed by the initialization itself and does not constitute - // a conversion. [...] - QualType CanonFrom = S.Context.getCanonicalType(FromType); - QualType CanonTo = S.Context.getCanonicalType(ToType); - if (CanonFrom.getLocalUnqualifiedType() - == CanonTo.getLocalUnqualifiedType() && - CanonFrom.getLocalQualifiers() != CanonTo.getLocalQualifiers()) { - FromType = ToType; - CanonFrom = CanonTo; + // C++ [over.best.ics]p6: + // [...] Any difference in top-level cv-qualification is + // subsumed by the initialization itself and does not constitute + // a conversion. [...] + CanonFrom = S.Context.getCanonicalType(FromType); + CanonTo = S.Context.getCanonicalType(ToType); + if (CanonFrom.getLocalUnqualifiedType() + == CanonTo.getLocalUnqualifiedType() && + CanonFrom.getLocalQualifiers() != CanonTo.getLocalQualifiers()) { + FromType = ToType; + CanonFrom = CanonTo; + } } - SCS.setToType(2, FromType); if (CanonFrom == CanonTo) diff --git a/clang/test/CXX/expr/p13.cpp b/clang/test/CXX/expr/p13.cpp deleted file mode 100644 index b3489fa087497..0000000000000 --- a/clang/test/CXX/expr/p13.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// RUN: %clang_cc1 -std=c++1z -verify %s -fexceptions -fcxx-exceptions - -struct X {}; -struct Y : X {}; - -using A = void (*)() noexcept; -using B = void (*)(); -using C = void (X::*)() noexcept; -using D = void (X::*)(); -using E = void (Y::*)() noexcept; -using F = void (Y::*)(); - -void f(A a, B b, C c, D d, E e, F f, bool k) { - a = k ? a : b; // expected-error {{different exception specifications}} - b = k ? a : b; - - c = k ? c : d; // expected-error {{different exception specifications}} - d = k ? c : d; - - e = k ? c : f; // expected-error {{different exception specifications}} - e = k ? d : e; // expected-error {{different exception specifications}} - f = k ? c : f; - f = k ? d : e; -} - -namespace dynamic_exception_spec { - // Prior to P0012, we had: - // "[...] the target entity shall allow at least the exceptions allowed - // by the source value in the assignment or initialization" - // - // There's really only one way we can coherently apply this to conditional - // expressions: this must hold no matter which branch was taken. - using X = void (*)() throw(int); - using Y = void (*)() throw(float); - using Z = void (*)() throw(int, float); - void g(X x, Y y, Z z, bool k) { - x = k ? X() : Y(); // expected-error {{not superset}} - y = k ? X() : Y(); // expected-error {{not superset}} - z = k ? X() : Y(); - - x = k ? x : y; // expected-error {{not superset}} - y = k ? x : y; // expected-error {{not superset}} - z = k ? x : y; - } -}