r284753 - [c++1z] Teach composite pointer type computation how to compute the composite

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 20 10:57:33 PDT 2016


Author: rsmith
Date: Thu Oct 20 12:57:33 2016
New Revision: 284753

URL: http://llvm.org/viewvc/llvm-project?rev=284753&view=rev
Log:
[c++1z] Teach composite pointer type computation how to compute the composite
pointer type of two function pointers with different noexcept specifications.
While I'm here, also teach it how to merge dynamic exception specifications.

Added:
    cfe/trunk/test/CXX/expr/p13.cpp
Modified:
    cfe/trunk/include/clang/Sema/Overload.h
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaExprCXX.cpp
    cfe/trunk/lib/Sema/SemaOverload.cpp

Modified: cfe/trunk/include/clang/Sema/Overload.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Overload.h?rev=284753&r1=284752&r2=284753&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Overload.h (original)
+++ cfe/trunk/include/clang/Sema/Overload.h Thu Oct 20 12:57:33 2016
@@ -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

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=284753&r1=284752&r2=284753&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Thu Oct 20 12:57:33 2016
@@ -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;

Modified: cfe/trunk/lib/Sema/SemaExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprCXX.cpp?rev=284753&r1=284752&r2=284753&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprCXX.cpp Thu Oct 20 12:57:33 2016
@@ -3610,16 +3610,6 @@ Sema::PerformImplicitConversion(Expr *Fr
     // 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 *Fr
   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 *Fr
     // 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,17 @@ QualType Sema::CXXCheckConditionalOperan
     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;
   }
 
@@ -5447,6 +5459,14 @@ QualType Sema::CXXCheckConditionalOperan
       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 +5537,78 @@ QualType Sema::CXXCheckConditionalOperan
   return QualType();
 }
 
+static FunctionProtoType::ExceptionSpecInfo
+mergeExceptionSpecs(Sema &S, FunctionProtoType::ExceptionSpecInfo ESI1,
+                    FunctionProtoType::ExceptionSpecInfo ESI2,
+                    SmallVectorImpl<QualType> &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<QualType, 8> 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 +5623,12 @@ QualType Sema::CXXCheckConditionalOperan
 /// 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 +5655,18 @@ QualType Sema::FindCompositePointerType(
   //   - 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 +5712,8 @@ QualType Sema::FindCompositePointerType(
   // exists.
   SmallVector<unsigned, 4> QualifierUnion;
   SmallVector<std::pair<const Type *, const Type *>, 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 +5759,41 @@ QualType Sema::FindCompositePointerType(
     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<FunctionProtoType>()) {
+      if (auto *FPT2 = Composite2->getAs<FunctionProtoType>()) {
+        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<QualType, 8> 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 +5843,28 @@ QualType Sema::FindCompositePointerType(
           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<Expr>();
 
       ExprResult E2Result = E2ToC.Perform(S, Entity, Kind, E2);
       if (E2Result.isInvalid())
-        return QualType();
+        return true;
       E2 = E2Result.getAs<Expr>();
 
-      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 +5875,10 @@ QualType Sema::FindCompositePointerType(
   }
 
   // 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) {

Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=284753&r1=284752&r2=284753&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Thu Oct 20 12:57:33 2016
@@ -1405,6 +1405,7 @@ bool Sema::IsFunctionConversion(QualType
   //   - 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
       CanTo = CanTo.getAs<BlockPointerType>()->getPointeeType();
       CanFrom = CanFrom.getAs<BlockPointerType>()->getPointeeType();
     } else if (TyClass == Type::MemberPointer) {
-      CanTo = CanTo.getAs<MemberPointerType>()->getPointeeType();
-      CanFrom = CanFrom.getAs<MemberPointerType>()->getPointeeType();
+      auto ToMPT = CanTo.getAs<MemberPointerType>();
+      auto FromMPT = CanFrom.getAs<MemberPointerType>();
+      // 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
     // 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
   }
   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)

Added: cfe/trunk/test/CXX/expr/p13.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/expr/p13.cpp?rev=284753&view=auto
==============================================================================
--- cfe/trunk/test/CXX/expr/p13.cpp (added)
+++ cfe/trunk/test/CXX/expr/p13.cpp Thu Oct 20 12:57:33 2016
@@ -0,0 +1,45 @@
+// 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;
+  }
+}




More information about the cfe-commits mailing list