r288301 - PR31081: ignore exception specifications when deducing function template

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Wed Nov 30 18:11:50 PST 2016


Author: rsmith
Date: Wed Nov 30 20:11:49 2016
New Revision: 288301

URL: http://llvm.org/viewvc/llvm-project?rev=288301&view=rev
Log:
PR31081: ignore exception specifications when deducing function template
arguments from a declaration; despite what the standard says, this form of
deduction should not be considering exception specifications.

Modified:
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaExprCXX.cpp
    cfe/trunk/lib/Sema/SemaOverload.cpp
    cfe/trunk/lib/Sema/SemaTemplate.cpp
    cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp
    cfe/trunk/test/SemaCXX/cxx1z-noexcept-function-type.cpp

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=288301&r1=288300&r2=288301&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Wed Nov 30 20:11:49 2016
@@ -6515,7 +6515,12 @@ public:
   // C++ Template Argument Deduction (C++ [temp.deduct])
   //===--------------------------------------------------------------------===//
 
-  QualType adjustCCAndNoReturn(QualType ArgFunctionType, QualType FunctionType);
+  /// Adjust the type \p ArgFunctionType to match the calling convention,
+  /// noreturn, and optionally the exception specification of \p FunctionType.
+  /// Deduction often wants to ignore these properties when matching function
+  /// types.
+  QualType adjustCCAndNoReturn(QualType ArgFunctionType, QualType FunctionType,
+                               bool AdjustExceptionSpec = false);
 
   /// \brief Describes the result of template argument deduction.
   ///
@@ -6624,7 +6629,7 @@ public:
                           QualType ArgFunctionType,
                           FunctionDecl *&Specialization,
                           sema::TemplateDeductionInfo &Info,
-                          bool InOverloadResolution = false);
+                          bool IsAddressOfFunction = false);
 
   TemplateDeductionResult
   DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate,
@@ -6637,7 +6642,7 @@ public:
                           TemplateArgumentListInfo *ExplicitTemplateArgs,
                           FunctionDecl *&Specialization,
                           sema::TemplateDeductionInfo &Info,
-                          bool InOverloadResolution = false);
+                          bool IsAddressOfFunction = false);
 
   /// \brief Substitute Replacement for \p auto in \p TypeWithAuto
   QualType SubstAutoType(QualType TypeWithAuto, QualType Replacement);

Modified: cfe/trunk/lib/Sema/SemaExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprCXX.cpp?rev=288301&r1=288300&r2=288301&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprCXX.cpp Wed Nov 30 20:11:49 2016
@@ -2319,8 +2319,6 @@ bool Sema::FindAllocationFunctions(Sourc
     // To perform this comparison, we compute the function type that
     // the deallocation function should have, and use that type both
     // for template argument deduction and for comparison purposes.
-    //
-    // FIXME: this comparison should ignore CC and the like.
     QualType ExpectedFunctionType;
     {
       const FunctionProtoType *Proto
@@ -2334,7 +2332,6 @@ bool Sema::FindAllocationFunctions(Sourc
       FunctionProtoType::ExtProtoInfo EPI;
       // FIXME: This is not part of the standard's rule.
       EPI.Variadic = Proto->isVariadic();
-      EPI.ExceptionSpec.Type = EST_BasicNoexcept;
 
       ExpectedFunctionType
         = Context.getFunctionType(Context.VoidTy, ArgTypes, EPI);
@@ -2344,8 +2341,8 @@ bool Sema::FindAllocationFunctions(Sourc
                              DEnd = FoundDelete.end();
          D != DEnd; ++D) {
       FunctionDecl *Fn = nullptr;
-      if (FunctionTemplateDecl *FnTmpl
-            = dyn_cast<FunctionTemplateDecl>((*D)->getUnderlyingDecl())) {
+      if (FunctionTemplateDecl *FnTmpl =
+              dyn_cast<FunctionTemplateDecl>((*D)->getUnderlyingDecl())) {
         // Perform template argument deduction to try to match the
         // expected function type.
         TemplateDeductionInfo Info(StartLoc);
@@ -2355,7 +2352,10 @@ bool Sema::FindAllocationFunctions(Sourc
       } else
         Fn = cast<FunctionDecl>((*D)->getUnderlyingDecl());
 
-      if (Context.hasSameType(Fn->getType(), ExpectedFunctionType))
+      if (Context.hasSameType(adjustCCAndNoReturn(Fn->getType(),
+                                                  ExpectedFunctionType,
+                                                  /*AdjustExcpetionSpec*/true),
+                              ExpectedFunctionType))
         Matches.push_back(std::make_pair(D.getPair(), Fn));
     }
 

Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=288301&r1=288300&r2=288301&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Wed Nov 30 20:11:49 2016
@@ -10572,7 +10572,7 @@ private:
           = S.DeduceTemplateArguments(FunctionTemplate, 
                                       &OvlExplicitTemplateArgs,
                                       TargetFunctionType, Specialization, 
-                                      Info, /*InOverloadResolution=*/true)) {
+                                      Info, /*IsAddressOfFunction*/true)) {
       // Make a note of the failed deduction for diagnostics.
       FailedCandidates.addCandidate()
           .set(CurAccessFunPair, FunctionTemplate->getTemplatedDecl(),
@@ -10975,7 +10975,7 @@ Sema::ResolveSingleFunctionTemplateSpeci
     if (TemplateDeductionResult Result
           = DeduceTemplateArguments(FunctionTemplate, &ExplicitTemplateArgs,
                                     Specialization, Info,
-                                    /*InOverloadResolution=*/true)) {
+                                    /*IsAddressOfFunction*/true)) {
       // Make a note of the failed deduction for diagnostics.
       // TODO: Actually use the failed-deduction info?
       FailedCandidates.addCandidate()

Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=288301&r1=288300&r2=288301&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Wed Nov 30 20:11:49 2016
@@ -8075,7 +8075,8 @@ DeclResult Sema::ActOnExplicitInstantiat
     NamedDecl *Prev = *P;
     if (!HasExplicitTemplateArgs) {
       if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Prev)) {
-        QualType Adjusted = adjustCCAndNoReturn(R, Method->getType());
+        QualType Adjusted = adjustCCAndNoReturn(R, Method->getType(),
+                                                /*AdjustExceptionSpec*/true);
         if (Context.hasSameUnqualifiedType(Method->getType(), Adjusted)) {
           Matches.clear();
 

Modified: cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp?rev=288301&r1=288300&r2=288301&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp Wed Nov 30 20:11:49 2016
@@ -3571,25 +3571,42 @@ Sema::TemplateDeductionResult Sema::Dedu
 }
 
 QualType Sema::adjustCCAndNoReturn(QualType ArgFunctionType,
-                                   QualType FunctionType) {
+                                   QualType FunctionType,
+                                   bool AdjustExceptionSpec) {
   if (ArgFunctionType.isNull())
     return ArgFunctionType;
 
   const FunctionProtoType *FunctionTypeP =
       FunctionType->castAs<FunctionProtoType>();
-  CallingConv CC = FunctionTypeP->getCallConv();
-  bool NoReturn = FunctionTypeP->getNoReturnAttr();
   const FunctionProtoType *ArgFunctionTypeP =
       ArgFunctionType->getAs<FunctionProtoType>();
-  if (ArgFunctionTypeP->getCallConv() == CC &&
-      ArgFunctionTypeP->getNoReturnAttr() == NoReturn)
+
+  FunctionProtoType::ExtProtoInfo EPI = ArgFunctionTypeP->getExtProtoInfo();
+  bool Rebuild = false;
+
+  CallingConv CC = FunctionTypeP->getCallConv();
+  if (EPI.ExtInfo.getCC() != CC) {
+    EPI.ExtInfo = EPI.ExtInfo.withCallingConv(CC);
+    Rebuild = true;
+  }
+
+  bool NoReturn = FunctionTypeP->getNoReturnAttr();
+  if (EPI.ExtInfo.getNoReturn() != NoReturn) {
+    EPI.ExtInfo = EPI.ExtInfo.withNoReturn(NoReturn);
+    Rebuild = true;
+  }
+
+  if (AdjustExceptionSpec && (FunctionTypeP->hasExceptionSpec() ||
+                              ArgFunctionTypeP->hasExceptionSpec())) {
+    EPI.ExceptionSpec = FunctionTypeP->getExtProtoInfo().ExceptionSpec;
+    Rebuild = true;
+  }
+
+  if (!Rebuild)
     return ArgFunctionType;
 
-  FunctionType::ExtInfo EI = ArgFunctionTypeP->getExtInfo().withCallingConv(CC);
-  EI = EI.withNoReturn(NoReturn);
-  ArgFunctionTypeP =
-      cast<FunctionProtoType>(Context.adjustFunctionType(ArgFunctionTypeP, EI));
-  return QualType(ArgFunctionTypeP, 0);
+  return Context.getFunctionType(ArgFunctionTypeP->getReturnType(),
+                                 ArgFunctionTypeP->getParamTypes(), EPI);
 }
 
 /// \brief Deduce template arguments when taking the address of a function
@@ -3614,14 +3631,17 @@ QualType Sema::adjustCCAndNoReturn(QualT
 /// \param Info the argument will be updated to provide additional information
 /// about template argument deduction.
 ///
+/// \param IsAddressOfFunction If \c true, we are deducing as part of taking
+/// the address of a function template per [temp.deduct.funcaddr] and
+/// [over.over]. If \c false, we are looking up a function template
+/// specialization based on its signature, per [temp.deduct.decl].
+///
 /// \returns the result of template argument deduction.
-Sema::TemplateDeductionResult
-Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate,
-                              TemplateArgumentListInfo *ExplicitTemplateArgs,
-                              QualType ArgFunctionType,
-                              FunctionDecl *&Specialization,
-                              TemplateDeductionInfo &Info,
-                              bool InOverloadResolution) {
+Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
+    FunctionTemplateDecl *FunctionTemplate,
+    TemplateArgumentListInfo *ExplicitTemplateArgs, QualType ArgFunctionType,
+    FunctionDecl *&Specialization, TemplateDeductionInfo &Info,
+    bool IsAddressOfFunction) {
   if (FunctionTemplate->isInvalidDecl())
     return TDK_Invalid;
 
@@ -3629,8 +3649,13 @@ Sema::DeduceTemplateArguments(FunctionTe
   TemplateParameterList *TemplateParams
     = FunctionTemplate->getTemplateParameters();
   QualType FunctionType = Function->getType();
-  if (!InOverloadResolution)
-    ArgFunctionType = adjustCCAndNoReturn(ArgFunctionType, FunctionType);
+
+  // When taking the address of a function, we require convertibility of
+  // the resulting function type. Otherwise, we allow arbitrary mismatches
+  // of calling convention, noreturn, and noexcept.
+  if (!IsAddressOfFunction)
+    ArgFunctionType = adjustCCAndNoReturn(ArgFunctionType, FunctionType,
+                                          /*AdjustExceptionSpec*/true);
 
   // Substitute any explicit template arguments.
   LocalInstantiationScope InstScope(*this);
@@ -3655,9 +3680,11 @@ Sema::DeduceTemplateArguments(FunctionTe
   Deduced.resize(TemplateParams->size());
 
   // If the function has a deduced return type, substitute it for a dependent
-  // type so that we treat it as a non-deduced context in what follows.
+  // type so that we treat it as a non-deduced context in what follows. If we
+  // are looking up by signature, the signature type should also have a deduced
+  // return type, which we instead expect to exactly match.
   bool HasDeducedReturnType = false;
-  if (getLangOpts().CPlusPlus14 && InOverloadResolution &&
+  if (getLangOpts().CPlusPlus14 && IsAddressOfFunction &&
       Function->getReturnType()->getContainedAutoType()) {
     FunctionType = SubstAutoType(FunctionType, Context.DependentTy);
     HasDeducedReturnType = true;
@@ -3665,7 +3692,8 @@ Sema::DeduceTemplateArguments(FunctionTe
 
   if (!ArgFunctionType.isNull()) {
     unsigned TDF = TDF_TopLevelParameterTypeList;
-    if (InOverloadResolution) TDF |= TDF_InOverloadResolution;
+    if (IsAddressOfFunction)
+      TDF |= TDF_InOverloadResolution;
     // Deduce template arguments from the function type.
     if (TemplateDeductionResult Result
           = DeduceTemplateArgumentsByTypeMatch(*this, TemplateParams,
@@ -3696,16 +3724,27 @@ Sema::DeduceTemplateArguments(FunctionTe
       !ResolveExceptionSpec(Info.getLocation(), SpecializationFPT))
     return TDK_MiscellaneousDeductionFailure;
 
+  // Adjust the exception specification of the argument again to match the
+  // substituted and resolved type we just formed. (Calling convention and
+  // noreturn can't be dependent, so we don't actually need this for them
+  // right now.)
+  QualType SpecializationType = Specialization->getType();
+  if (!IsAddressOfFunction)
+    ArgFunctionType = adjustCCAndNoReturn(ArgFunctionType, SpecializationType,
+                                          /*AdjustExceptionSpec*/true);
+
   // If the requested function type does not match the actual type of the
   // specialization with respect to arguments of compatible pointer to function
   // types, template argument deduction fails.
   if (!ArgFunctionType.isNull()) {
-    if (InOverloadResolution && !isSameOrCompatibleFunctionType(
-                           Context.getCanonicalType(Specialization->getType()),
-                           Context.getCanonicalType(ArgFunctionType)))
+    if (IsAddressOfFunction &&
+        !isSameOrCompatibleFunctionType(
+            Context.getCanonicalType(SpecializationType),
+            Context.getCanonicalType(ArgFunctionType)))
       return TDK_MiscellaneousDeductionFailure;
-    else if(!InOverloadResolution &&
-            !Context.hasSameType(Specialization->getType(), ArgFunctionType))
+
+    if (!IsAddressOfFunction &&
+        !Context.hasSameType(SpecializationType, ArgFunctionType))
       return TDK_MiscellaneousDeductionFailure;
   }
 
@@ -3977,16 +4016,22 @@ Sema::DeduceTemplateArguments(FunctionTe
 /// \param Info the argument will be updated to provide additional information
 /// about template argument deduction.
 ///
+/// \param IsAddressOfFunction If \c true, we are deducing as part of taking
+/// the address of a function template in a context where we do not have a
+/// target type, per [over.over]. If \c false, we are looking up a function
+/// template specialization based on its signature, which only happens when
+/// deducing a function parameter type from an argument that is a template-id
+/// naming a function template specialization.
+///
 /// \returns the result of template argument deduction.
-Sema::TemplateDeductionResult
-Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate,
-                              TemplateArgumentListInfo *ExplicitTemplateArgs,
-                              FunctionDecl *&Specialization,
-                              TemplateDeductionInfo &Info,
-                              bool InOverloadResolution) {
+Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
+    FunctionTemplateDecl *FunctionTemplate,
+    TemplateArgumentListInfo *ExplicitTemplateArgs,
+    FunctionDecl *&Specialization, TemplateDeductionInfo &Info,
+    bool IsAddressOfFunction) {
   return DeduceTemplateArguments(FunctionTemplate, ExplicitTemplateArgs,
                                  QualType(), Specialization, Info,
-                                 InOverloadResolution);
+                                 IsAddressOfFunction);
 }
 
 namespace {

Modified: cfe/trunk/test/SemaCXX/cxx1z-noexcept-function-type.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx1z-noexcept-function-type.cpp?rev=288301&r1=288300&r2=288301&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/cxx1z-noexcept-function-type.cpp (original)
+++ cfe/trunk/test/SemaCXX/cxx1z-noexcept-function-type.cpp Wed Nov 30 20:11:49 2016
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -std=c++14 -verify %s
-// RUN: %clang_cc1 -std=c++1z -verify %s
+// RUN: %clang_cc1 -std=c++14 -verify -fexceptions -fcxx-exceptions %s
+// RUN: %clang_cc1 -std=c++1z -verify -fexceptions -fcxx-exceptions %s
 
 #if __cplusplus > 201402L
 
@@ -106,3 +106,47 @@ namespace Builtins {
   typedef int arr[strcmp("bar", "foo") + 4 * strncmp("foo", "bar", 4)];
   typedef int arr[3];
 }
+
+namespace ExplicitInstantiation {
+  template<typename T> void f() noexcept {}
+  template<typename T> struct X { void f() noexcept {} };
+  template void f<int>();
+  template void X<int>::f();
+}
+
+namespace ConversionFunction {
+  struct A { template<typename T> operator T() noexcept; };
+  int a = A().operator int();
+}
+
+using size_t = decltype(sizeof(0));
+
+namespace OperatorDelete {
+  struct W {};
+  struct X {};
+  struct Y {};
+  struct Z {};
+  template<bool N, bool D> struct T {};
+}
+void *operator new(size_t, OperatorDelete::W) noexcept(false);
+void operator delete(void*, OperatorDelete::W) noexcept(false) = delete; // expected-note {{here}}
+void *operator new(size_t, OperatorDelete::X) noexcept(false);
+void operator delete(void*, OperatorDelete::X) noexcept(true) = delete; // expected-note {{here}}
+void *operator new(size_t, OperatorDelete::Y) noexcept(true);
+void operator delete(void*, OperatorDelete::Y) noexcept(false) = delete; // expected-note {{here}}
+void *operator new(size_t, OperatorDelete::Z) noexcept(true);
+void operator delete(void*, OperatorDelete::Z) noexcept(true) = delete; // expected-note {{here}}
+template<bool N, bool D> void *operator new(size_t, OperatorDelete::T<N, D>) noexcept(N);
+template<bool N, bool D> void operator delete(void*, OperatorDelete::T<N, D>) noexcept(D) = delete; // expected-note 4{{here}}
+namespace OperatorDelete {
+  struct A { A(); };
+  A *w = new (W{}) A; // expected-error {{deleted function}}
+  A *x = new (X{}) A; // expected-error {{deleted function}}
+  A *y = new (Y{}) A; // expected-error {{deleted function}}
+  A *z = new (Z{}) A; // expected-error {{deleted function}}
+
+  A *t00 = new (T<false, false>{}) A; // expected-error {{deleted function}}
+  A *t01 = new (T<false, true>{}) A; // expected-error {{deleted function}}
+  A *t10 = new (T<true, false>{}) A; // expected-error {{deleted function}}
+  A *t11 = new (T<true, true>{}) A; // expected-error {{deleted function}}
+}




More information about the cfe-commits mailing list