r304946 - [c++1z] Support deducing B in noexcept(B).

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Wed Jun 7 14:46:23 PDT 2017


Author: rsmith
Date: Wed Jun  7 16:46:22 2017
New Revision: 304946

URL: http://llvm.org/viewvc/llvm-project?rev=304946&view=rev
Log:
[c++1z] Support deducing B in noexcept(B).

This is not required by the standard (yet), but there seems to be reasonable
support for this being a defect according to CWG discussion, and libstdc++ 7.1
relies on it working.

Modified:
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp
    cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp
    cfe/trunk/test/CXX/drs/dr13xx.cpp
    cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p4.cpp
    cfe/trunk/test/SemaCXX/cxx1z-noexcept-function-type.cpp
    cfe/trunk/test/SemaTemplate/temp_arg_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=304946&r1=304945&r2=304946&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Wed Jun  7 16:46:22 2017
@@ -7555,6 +7555,10 @@ public:
                                         unsigned ThisTypeQuals);
   void SubstExceptionSpec(FunctionDecl *New, const FunctionProtoType *Proto,
                           const MultiLevelTemplateArgumentList &Args);
+  bool SubstExceptionSpec(SourceLocation Loc,
+                          FunctionProtoType::ExceptionSpecInfo &ESI,
+                          SmallVectorImpl<QualType> &ExceptionStorage,
+                          const MultiLevelTemplateArgumentList &Args);
   ParmVarDecl *SubstParmVarDecl(ParmVarDecl *D,
                             const MultiLevelTemplateArgumentList &TemplateArgs,
                                 int indexAdjustment,

Modified: cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp?rev=304946&r1=304945&r2=304946&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp Wed Jun  7 16:46:22 2017
@@ -56,8 +56,12 @@ namespace clang {
     TDF_TopLevelParameterTypeList = 0x10,
     /// \brief Within template argument deduction from overload resolution per
     /// C++ [over.over] allow matching function types that are compatible in
-    /// terms of noreturn and default calling convention adjustments.
-    TDF_InOverloadResolution = 0x20
+    /// terms of noreturn and default calling convention adjustments, or
+    /// similarly matching a declared template specialization against a
+    /// possible template, per C++ [temp.deduct.decl]. In either case, permit
+    /// deduction where the parameter is a function type that can be converted
+    /// to the argument type.
+    TDF_AllowCompatibleFunctionType = 0x20,
   };
 }
 
@@ -1306,9 +1310,10 @@ DeduceTemplateArgumentsByTypeMatch(Sema
     // If the parameter type is not dependent, there is nothing to deduce.
     if (!Param->isDependentType()) {
       if (!(TDF & TDF_SkipNonDependent)) {
-        bool NonDeduced = (TDF & TDF_InOverloadResolution)?
-                          !S.isSameOrCompatibleFunctionType(CanParam, CanArg) :
-                          Param != Arg;
+        bool NonDeduced =
+            (TDF & TDF_AllowCompatibleFunctionType)
+                ? !S.isSameOrCompatibleFunctionType(CanParam, CanArg)
+                : Param != Arg;
         if (NonDeduced) {
           return Sema::TDK_NonDeducedMismatch;
         }
@@ -1318,10 +1323,10 @@ DeduceTemplateArgumentsByTypeMatch(Sema
   } else if (!Param->isDependentType()) {
     CanQualType ParamUnqualType = CanParam.getUnqualifiedType(),
                 ArgUnqualType = CanArg.getUnqualifiedType();
-    bool Success = (TDF & TDF_InOverloadResolution)?
-                   S.isSameOrCompatibleFunctionType(ParamUnqualType,
-                                                    ArgUnqualType) :
-                   ParamUnqualType == ArgUnqualType;
+    bool Success =
+        (TDF & TDF_AllowCompatibleFunctionType)
+            ? S.isSameOrCompatibleFunctionType(ParamUnqualType, ArgUnqualType)
+            : ParamUnqualType == ArgUnqualType;
     if (Success)
       return Sema::TDK_Success;
   }
@@ -1524,17 +1529,56 @@ DeduceTemplateArgumentsByTypeMatch(Sema
         return Sema::TDK_NonDeducedMismatch;
 
       // Check return types.
-      if (Sema::TemplateDeductionResult Result =
-              DeduceTemplateArgumentsByTypeMatch(
-                  S, TemplateParams, FunctionProtoParam->getReturnType(),
-                  FunctionProtoArg->getReturnType(), Info, Deduced, 0))
+      if (auto Result = DeduceTemplateArgumentsByTypeMatch(
+              S, TemplateParams, FunctionProtoParam->getReturnType(),
+              FunctionProtoArg->getReturnType(), Info, Deduced, 0))
         return Result;
 
-      return DeduceTemplateArguments(
-          S, TemplateParams, FunctionProtoParam->param_type_begin(),
-          FunctionProtoParam->getNumParams(),
-          FunctionProtoArg->param_type_begin(),
-          FunctionProtoArg->getNumParams(), Info, Deduced, SubTDF);
+      // Check parameter types.
+      if (auto Result = DeduceTemplateArguments(
+              S, TemplateParams, FunctionProtoParam->param_type_begin(),
+              FunctionProtoParam->getNumParams(),
+              FunctionProtoArg->param_type_begin(),
+              FunctionProtoArg->getNumParams(), Info, Deduced, SubTDF))
+        return Result;
+
+      if (TDF & TDF_AllowCompatibleFunctionType)
+        return Sema::TDK_Success;
+
+      // FIXME: Per core-2016/10/1019 (no corresponding core issue yet), permit
+      // deducing through the noexcept-specifier if it's part of the canonical
+      // type. libstdc++ relies on this.
+      Expr *NoexceptExpr = FunctionProtoParam->getNoexceptExpr();
+      if (NonTypeTemplateParmDecl *NTTP =
+          NoexceptExpr ? getDeducedParameterFromExpr(Info, NoexceptExpr)
+                       : nullptr) {
+        assert(NTTP->getDepth() == Info.getDeducedDepth() &&
+               "saw non-type template parameter with wrong depth");
+
+        llvm::APSInt Noexcept(1);
+        switch (FunctionProtoArg->canThrow(S.Context)) {
+        case CT_Cannot:
+          Noexcept = 1;
+          LLVM_FALLTHROUGH;
+
+        case CT_Can:
+          // We give E in noexcept(E) the "deduced from array bound" treatment.
+          // FIXME: Should we?
+          return DeduceNonTypeTemplateArgument(
+              S, TemplateParams, NTTP, Noexcept, S.Context.BoolTy,
+              /*ArrayBound*/true, Info, Deduced);
+
+        case CT_Dependent:
+          if (Expr *ArgNoexceptExpr = FunctionProtoArg->getNoexceptExpr())
+            return DeduceNonTypeTemplateArgument(
+                S, TemplateParams, NTTP, ArgNoexceptExpr, Info, Deduced);
+          // Can't deduce anything from throw(T...).
+          break;
+        }
+      }
+      // FIXME: Detect non-deduced exception specification mismatches?
+
+      return Sema::TDK_Success;
     }
 
     case Type::InjectedClassName: {
@@ -1544,7 +1588,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema
         ->getInjectedSpecializationType();
       assert(isa<TemplateSpecializationType>(Param) &&
              "injected class name is not a template specialization type");
-      // fall through
+      LLVM_FALLTHROUGH;
     }
 
     //     template-name<T> (where template-name refers to a class template)
@@ -2820,6 +2864,17 @@ Sema::SubstituteExplicitTemplateArgument
   if (FunctionType) {
     auto EPI = Proto->getExtProtoInfo();
     EPI.ExtParameterInfos = ExtParamInfos.getPointerOrNull(ParamTypes.size());
+
+    // In C++1z onwards, exception specifications are part of the function type,
+    // so substitution into the type must also substitute into the exception
+    // specification.
+    SmallVector<QualType, 4> ExceptionStorage;
+    if (getLangOpts().CPlusPlus1z &&
+        SubstExceptionSpec(
+            Function->getLocation(), EPI.ExceptionSpec, ExceptionStorage,
+            MultiLevelTemplateArgumentList(*ExplicitArgumentList)))
+      return TDK_SubstitutionFailure;
+
     *FunctionType = BuildFunctionType(ResultType, ParamTypes,
                                       Function->getLocation(),
                                       Function->getDeclName(),
@@ -3714,13 +3769,6 @@ Sema::TemplateDeductionResult Sema::Dedu
     = FunctionTemplate->getTemplateParameters();
   QualType FunctionType = Function->getType();
 
-  // 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);
   SmallVector<DeducedTemplateArgument, 4> Deduced;
@@ -3737,6 +3785,13 @@ Sema::TemplateDeductionResult Sema::Dedu
     NumExplicitlySpecified = Deduced.size();
   }
 
+  // When taking the address of a function, we require convertibility of
+  // the resulting function type. Otherwise, we allow arbitrary mismatches
+  // of calling convention and noreturn.
+  if (!IsAddressOfFunction)
+    ArgFunctionType = adjustCCAndNoReturn(ArgFunctionType, FunctionType,
+                                          /*AdjustExceptionSpec*/false);
+
   // Unevaluated SFINAE context.
   EnterExpressionEvaluationContext Unevaluated(
       *this, Sema::ExpressionEvaluationContext::Unevaluated);
@@ -3756,9 +3811,8 @@ Sema::TemplateDeductionResult Sema::Dedu
   }
 
   if (!ArgFunctionType.isNull()) {
-    unsigned TDF = TDF_TopLevelParameterTypeList;
-    if (IsAddressOfFunction)
-      TDF |= TDF_InOverloadResolution;
+    unsigned TDF =
+        TDF_TopLevelParameterTypeList | TDF_AllowCompatibleFunctionType;
     // Deduce template arguments from the function type.
     if (TemplateDeductionResult Result
           = DeduceTemplateArgumentsByTypeMatch(*this, TemplateParams,
@@ -3789,7 +3843,7 @@ Sema::TemplateDeductionResult Sema::Dedu
       !ResolveExceptionSpec(Info.getLocation(), SpecializationFPT))
     return TDK_MiscellaneousDeductionFailure;
 
-  // Adjust the exception specification of the argument again to match the
+  // Adjust the exception specification of the argument 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.)
@@ -5127,6 +5181,8 @@ MarkUsedTemplateParameters(ASTContext &C
     for (unsigned I = 0, N = Proto->getNumParams(); I != N; ++I)
       MarkUsedTemplateParameters(Ctx, Proto->getParamType(I), OnlyDeduced,
                                  Depth, Used);
+    if (auto *E = Proto->getNoexceptExpr())
+      MarkUsedTemplateParameters(Ctx, E, OnlyDeduced, Depth, Used);
     break;
   }
 

Modified: cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp?rev=304946&r1=304945&r2=304946&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp Wed Jun  7 16:46:22 2017
@@ -1692,20 +1692,26 @@ TypeSourceInfo *Sema::SubstFunctionDeclT
   return TLB.getTypeSourceInfo(Context, Result);
 }
 
+bool Sema::SubstExceptionSpec(SourceLocation Loc,
+                              FunctionProtoType::ExceptionSpecInfo &ESI,
+                              SmallVectorImpl<QualType> &ExceptionStorage,
+                              const MultiLevelTemplateArgumentList &Args) {
+  assert(ESI.Type != EST_Uninstantiated);
+
+  bool Changed = false;
+  TemplateInstantiator Instantiator(*this, Args, Loc, DeclarationName());
+  return Instantiator.TransformExceptionSpec(Loc, ESI, ExceptionStorage,
+                                             Changed);
+}
+
 void Sema::SubstExceptionSpec(FunctionDecl *New, const FunctionProtoType *Proto,
                               const MultiLevelTemplateArgumentList &Args) {
   FunctionProtoType::ExceptionSpecInfo ESI =
       Proto->getExtProtoInfo().ExceptionSpec;
-  assert(ESI.Type != EST_Uninstantiated);
-
-  TemplateInstantiator Instantiator(*this, Args, New->getLocation(),
-                                    New->getDeclName());
 
   SmallVector<QualType, 4> ExceptionStorage;
-  bool Changed = false;
-  if (Instantiator.TransformExceptionSpec(
-          New->getTypeSourceInfo()->getTypeLoc().getLocEnd(), ESI,
-          ExceptionStorage, Changed))
+  if (SubstExceptionSpec(New->getTypeSourceInfo()->getTypeLoc().getLocEnd(),
+                         ESI, ExceptionStorage, Args))
     // On error, recover by dropping the exception specification.
     ESI.Type = EST_None;
 

Modified: cfe/trunk/test/CXX/drs/dr13xx.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr13xx.cpp?rev=304946&r1=304945&r2=304946&view=diff
==============================================================================
--- cfe/trunk/test/CXX/drs/dr13xx.cpp (original)
+++ cfe/trunk/test/CXX/drs/dr13xx.cpp Wed Jun  7 16:46:22 2017
@@ -192,7 +192,7 @@ namespace dr1330 { // dr1330: 4 c++11
   static_assert(!noexcept(B<Q>().g()), "");
 #endif
 
-  template<typename T> int f() throw(typename T::error) { return 0; } // expected-error 1-4{{prior to '::'}} expected-note 0-1{{instantiation of}}
+  template<typename T> int f() throw(typename T::error) { return 0; } // expected-error 1-4{{prior to '::'}} expected-note 0-1{{prior to '::'}} expected-note 0-1{{requested here}}
 #if __cplusplus > 201402L
     // expected-error at -2 0-1{{C++1z}} expected-note at -2 0-1{{noexcept}}
 #endif
@@ -203,7 +203,16 @@ namespace dr1330 { // dr1330: 4 c++11
   decltype(f<char>()) f2; // expected-note {{instantiation of}}
   bool f3 = noexcept(f<float>()); // expected-note {{instantiation of}}
 #endif
-  template int f<short>(); // expected-note {{instantiation of}}
+  // In C++1z onwards, substituting explicit template arguments into the
+  // function type substitutes into the exception specification (because it's
+  // part of the type). In earlier languages, we don't notice there's a problem
+  // until we've already started to instantiate.
+  template int f<short>();
+#if __cplusplus >= 201703L
+  // expected-error at -2 {{does not refer to a function template}}
+#else
+  // expected-note at -4 {{instantiation of}}
+#endif
 
   template<typename T> struct C {
     C() throw(typename T::type); // expected-error 1-2{{prior to '::'}}

Modified: cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p4.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p4.cpp?rev=304946&r1=304945&r2=304946&view=diff
==============================================================================
--- cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p4.cpp (original)
+++ cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p4.cpp Wed Jun  7 16:46:22 2017
@@ -46,10 +46,11 @@ namespace noexcept_conversion {
   template <class T> int h(T *, T *); // expected-note {{deduced conflicting types for parameter 'T' ('void () noexcept' vs. 'void ()')}}
   int x = h(g1, g2); // expected-error {{no matching function}}
 
-  // FIXME: It seems like a defect that B is not deducible here.
-  template<bool B> int i(void () noexcept(B)); // expected-note 2{{couldn't infer template argument 'B'}}
-  int i1 = i(g1); // expected-error {{no matching function}}
-  int i2 = i(g2); // expected-error {{no matching function}}
+  // We consider it a defect that deduction does not support the following.
+  // FIXME: Check that the defect is resolved as we expect.
+  template<bool B> int i(void () noexcept(B));
+  int i1 = i(g1);
+  int i2 = i(g2);
 }
 #else
 // expected-no-diagnostics

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=304946&r1=304945&r2=304946&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/cxx1z-noexcept-function-type.cpp (original)
+++ cfe/trunk/test/SemaCXX/cxx1z-noexcept-function-type.cpp Wed Jun  7 16:46:22 2017
@@ -16,7 +16,7 @@ template<typename A, typename B> void re
 
 typedef int I;
 template<bool B> void redecl4(I) noexcept(B);
-template<bool B> void redecl4(I) noexcept(B); // expected-note {{failed template argument deduction}}
+template<bool B> void redecl4(I) noexcept(B); // expected-note {{could not match 'void (I) noexcept(false)' (aka 'void (int) noexcept(false)') against 'void (int) noexcept'}}
 
 void (*init_with_exact_type_a)(int) noexcept = redecl4<true>;
 void (*init_with_mismatched_type_a)(int) = redecl4<true>;

Modified: cfe/trunk/test/SemaTemplate/temp_arg_type.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/temp_arg_type.cpp?rev=304946&r1=304945&r2=304946&view=diff
==============================================================================
--- cfe/trunk/test/SemaTemplate/temp_arg_type.cpp (original)
+++ cfe/trunk/test/SemaTemplate/temp_arg_type.cpp Wed Jun  7 16:46:22 2017
@@ -1,6 +1,7 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z %s
 
 template<typename T> class A; // expected-note 2 {{template parameter is declared here}} expected-note{{template is declared here}}
 
@@ -53,3 +54,56 @@ A1<Array<int, 17>::type> ax;
 
 // FIXME: [temp.arg.type]p3. The check doesn't really belong here (it
 // belongs somewhere in the template instantiation section).
+
+#if __cplusplus >= 201703
+// As a defect resolution, we support deducing B in noexcept(B).
+namespace deduce_noexcept {
+  template<typename> struct function;
+  template<typename R, typename ...A, bool N>
+  struct function<R(A...) noexcept(N)> {
+    static constexpr bool Noexcept = N;
+  };
+  static_assert(function<int(float, double) noexcept>::Noexcept);
+  static_assert(!function<int(float, double)>::Noexcept);
+
+  void noexcept_function() noexcept;
+  void throwing_function();
+
+  template<typename T, bool B> float &deduce_function(T(*)() noexcept(B)); // expected-note {{candidate}}
+  template<typename T> int &deduce_function(T(*)() noexcept); // expected-note {{candidate}}
+  void test_function_deduction() {
+    // FIXME: This should probably unambiguously select the second overload.
+    int &r = deduce_function(noexcept_function); // expected-error {{ambiguous}}
+    float &s = deduce_function(throwing_function);
+  }
+
+  namespace low_priority_deduction {
+    template<int> struct A {};
+    template<auto B> void f(A<B>, void(*)() noexcept(B)) {
+      using T = decltype(B);
+      using T = int;
+    }
+    void g() { f(A<0>(), g); } // ok, deduce B as an int
+  }
+
+  // FIXME: It's not clear whether this should work. We're told to deduce with
+  // P being the function template type and A being the declared type, which
+  // would accept this, but considering the exception specification in such
+  // cases breaks new/delete matching.
+  template<bool Noexcept> void dep() noexcept(Noexcept) {} // expected-note 3{{couldn't infer template argument 'Noexcept'}}
+  template void dep(); // expected-error {{does not refer to a function template}}
+  template void dep() noexcept(true); // expected-error {{does not refer to a function template}}
+  template void dep() noexcept(false); // expected-error {{does not refer to a function template}}
+
+  // FIXME: It's also not clear whether this should be valid: do we substitute
+  // into the function type (including the exception specification) or not?
+  template<typename T> typename T::type1 f() noexcept(T::a);
+  template<typename T> typename T::type2 f() noexcept(T::b) {}
+  struct X {
+    static constexpr bool b = true;
+    using type1 = void;
+    using type2 = void;
+  };
+  template void f<X>();
+}
+#endif




More information about the cfe-commits mailing list