diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h index 83c6554c64d7..f677afe8e26f 100644 --- a/clang/include/clang/Sema/Overload.h +++ b/clang/include/clang/Sema/Overload.h @@ -145,7 +145,8 @@ namespace clang { /// pointer-to-member conversion, or boolean conversion. ImplicitConversionKind Second : 8; - /// Third - The third conversion can be a qualification conversion. + /// Third - The third conversion can be a qualification conversion + /// or a function 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 1dd415d3497e..08e6a29ca11e 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -8954,13 +8954,15 @@ public: ExprResult &cond, ExprResult &lhs, ExprResult &rhs, ExprValueKind &VK, ExprObjectKind &OK, SourceLocation questionLoc); QualType FindCompositePointerType(SourceLocation Loc, Expr *&E1, Expr *&E2, - bool *NonStandardCompositeType = nullptr); + bool *NonStandardCompositeType = nullptr, + bool ConvertArgs = true); QualType FindCompositePointerType(SourceLocation Loc, ExprResult &E1, ExprResult &E2, - bool *NonStandardCompositeType = nullptr) { + bool *NonStandardCompositeType = nullptr, + bool ConvertArgs = true) { Expr *E1Tmp = E1.get(), *E2Tmp = E2.get(); - QualType Composite = FindCompositePointerType(Loc, E1Tmp, E2Tmp, - NonStandardCompositeType); + QualType Composite = FindCompositePointerType( + Loc, E1Tmp, E2Tmp, NonStandardCompositeType, ConvertArgs); E1 = E1Tmp; E2 = E2Tmp; return Composite; diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index b660bd348652..83474cb7f83d 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -3610,16 +3610,6 @@ 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()) { @@ -3866,6 +3856,7 @@ 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: @@ -3878,6 +3869,16 @@ 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. @@ -5393,6 +5394,20 @@ 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()) { + Qualifiers Qs = LTy.getQualifiers(); + LTy = FindCompositePointerType(QuestionLoc, LHS, RHS, nullptr, + /*ConvertArgs*/false); + LTy = Context.getQualifiedType(LTy, Qs); + + 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; } @@ -5447,6 +5462,14 @@ 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; } @@ -5517,6 +5540,78 @@ 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 @@ -5531,9 +5626,12 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, /// 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 *NonStandardCompositeType, + bool ConvertArgs) { if (NonStandardCompositeType) *NonStandardCompositeType = false; @@ -5560,16 +5658,18 @@ 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)) { - E2 = ImpCastExprToType(E2, T1, T1->isMemberPointerType() - ? CK_NullToMemberPointer - : CK_NullToPointer).get(); + if (ConvertArgs) + 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(); + if (ConvertArgs) + E1 = ImpCastExprToType(E1, T2, T2->isMemberPointerType() + ? CK_NullToMemberPointer + : CK_NullToPointer).get(); return T2; } @@ -5615,8 +5715,8 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc, // exists. SmallVector QualifierUnion; SmallVector, 4> MemberOfClass; - QualType Composite1 = Context.getCanonicalType(T1); - QualType Composite2 = Context.getCanonicalType(T2); + QualType Composite1 = T1; + QualType Composite2 = T2; unsigned NeedConstBefore = 0; while (true) { const PointerType *Ptr1, *Ptr2; @@ -5662,6 +5762,41 @@ 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 @@ -5711,25 +5846,28 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc, E1ToC(S, Entity, Kind, E1), E2ToC(S, Entity, Kind, E2), Viable(E1ToC && E2ToC) {} - QualType perform() { + bool perform() { ExprResult E1Result = E1ToC.Perform(S, Entity, Kind, E1); if (E1Result.isInvalid()) - return QualType(); + return true; E1 = E1Result.getAs(); ExprResult E2Result = E2ToC.Perform(S, Entity, Kind, E2); if (E2Result.isInvalid()) - return QualType(); + return true; E2 = E2Result.getAs(); - return Composite; + return false; } }; // 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(); + if (C1.Viable && Context.hasSameType(Composite1, Composite2)) { + if (ConvertArgs && C1.perform()) + return QualType(); + return C1.Composite; + } Conversion C2(*this, Loc, E1, E2, Composite2); if (C1.Viable == C2.Viable) { @@ -5740,7 +5878,10 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc, } // Convert to the chosen type. - return (C1.Viable ? C1 : C2).perform(); + if (ConvertArgs && (C1.Viable ? C1 : C2).perform()) + return QualType(); + + return C1.Viable ? C1.Composite : C2.Composite; } ExprResult Sema::MaybeBindToTemporary(Expr *E) { diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index db97f3d57f55..8cc592e21e53 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -1405,6 +1405,7 @@ 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(); @@ -1417,8 +1418,13 @@ bool Sema::IsFunctionConversion(QualType FromType, QualType ToType, CanTo = CanTo.getAs()->getPointeeType(); CanFrom = CanFrom.getAs()->getPointeeType(); } else if (TyClass == Type::MemberPointer) { - CanTo = CanTo.getAs()->getPointeeType(); - CanFrom = CanFrom.getAs()->getPointeeType(); + 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(); } else { return false; } @@ -1757,10 +1763,6 @@ 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)) { @@ -1782,34 +1784,36 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, } SCS.setToType(1, FromType); - QualType CanonFrom; - QualType CanonTo; - // The third conversion can be a qualification conversion (C++ 4p1). + // The third conversion can be a function pointer conversion or a + // qualification conversion (C++ [conv.fctptr], [conv.qual]). bool ObjCLifetimeConversion; - if (S.IsQualificationConversion(FromType, ToType, CStyle, - 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)) { 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. [...] - CanonFrom = S.Context.getCanonicalType(FromType); - 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. [...] + 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; + } + SCS.setToType(2, FromType); if (CanonFrom == CanonTo) diff --git a/clang/test/CXX/expr/p13.cpp b/clang/test/CXX/expr/p13.cpp new file mode 100644 index 000000000000..b5f1587f9f2b --- /dev/null +++ b/clang/test/CXX/expr/p13.cpp @@ -0,0 +1,51 @@ +// 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; + + const A ak = a; + const B bk = b; + const A &ak2 = k ? ak : ak; + const A &ak3 = k ? ak : bk; // expected-error {{could not bind}} + const B &bk3 = k ? ak : bk; +} + +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; + } +}