[clang] 6afcc4a - [c++] implements DR692, DR1395 and tentatively DR1432, about partial ordering of variadic template partial specialization or function template

Yuanfang Chen via cfe-commits cfe-commits at lists.llvm.org
Sun Aug 14 14:38:44 PDT 2022


Author: Yuanfang Chen
Date: 2022-08-14T14:37:40-07:00
New Revision: 6afcc4a459ead8809a0d6d9b4bf7b64bcc13582b

URL: https://github.com/llvm/llvm-project/commit/6afcc4a459ead8809a0d6d9b4bf7b64bcc13582b
DIFF: https://github.com/llvm/llvm-project/commit/6afcc4a459ead8809a0d6d9b4bf7b64bcc13582b.diff

LOG: [c++] implements DR692, DR1395 and tentatively DR1432, about partial ordering of variadic template partial specialization or function template

DR692 handles two cases: pack expansion (for class/var template) and function parameter pack. The former needs DR1432 as a fix, and the latter needs DR1395 as a fix. However, DR1432 has not yet made a wording change. so I made a tentative fix for DR1432 with the same spirit as DR1395.

Reviewed By: aaron.ballman, erichkeane, #clang-language-wg

Differential Revision: https://reviews.llvm.org/D128745

Added: 
    clang/test/CodeGen/partial-order-variadic.cpp

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/Sema/SemaTemplateDeduction.cpp
    clang/test/CXX/drs/dr6xx.cpp
    clang/www/cxx_dr_status.html

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 6970149444d1..a635b734c47a 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -135,6 +135,9 @@ C2x Feature Support
 C++ Language Changes in Clang
 -----------------------------
 
+- Implemented DR692, DR1395 and DR1432. Use the ``-fclang-abi-compat=14`` option
+  to get the old partial ordering behavior regarding packs.
+
 C++20 Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
 

diff  --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 09e5d7b80127..6836f7c090be 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -55,6 +55,7 @@
 #include <algorithm>
 #include <cassert>
 #include <tuple>
+#include <type_traits>
 #include <utility>
 
 namespace clang {
@@ -1100,6 +1101,18 @@ DeduceTemplateArguments(Sema &S,
       return Result;
   }
 
+  // DR692, DR1395
+  // C++0x [temp.deduct.type]p10:
+  // If the parameter-declaration corresponding to P_i ...
+  // During partial ordering, if Ai was originally a function parameter pack:
+  // - if P does not contain a function parameter type corresponding to Ai then
+  //   Ai is ignored;
+  bool ClangABICompat14 = S.Context.getLangOpts().getClangABICompat() <=
+                          LangOptions::ClangABI::Ver14;
+  if (!ClangABICompat14 && PartialOrdering && ArgIdx + 1 == NumArgs &&
+      isa<PackExpansionType>(Args[ArgIdx]))
+    return Sema::TDK_Success;
+
   // Make sure we don't have any extra arguments.
   if (ArgIdx < NumArgs)
     return Sema::TDK_MiscellaneousDeductionFailure;
@@ -1755,7 +1768,7 @@ static Sema::TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
       if (auto Result = DeduceTemplateArguments(
               S, TemplateParams, FPP->param_type_begin(), FPP->getNumParams(),
               FPA->param_type_begin(), FPA->getNumParams(), Info, Deduced,
-              TDF & TDF_TopLevelParameterTypeList))
+              TDF & TDF_TopLevelParameterTypeList, PartialOrdering))
         return Result;
 
       if (TDF & TDF_AllowCompatibleFunctionType)
@@ -2422,6 +2435,7 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
 static bool isSameTemplateArg(ASTContext &Context,
                               TemplateArgument X,
                               const TemplateArgument &Y,
+                              bool PartialOrdering,
                               bool PackExpansionMatchesPack = false) {
   // If we're checking deduced arguments (X) against original arguments (Y),
   // we will have flattened packs to non-expansions in X.
@@ -2463,16 +2477,30 @@ static bool isSameTemplateArg(ASTContext &Context,
     }
 
     case TemplateArgument::Pack:
-      if (X.pack_size() != Y.pack_size())
-        return false;
-
-      for (TemplateArgument::pack_iterator XP = X.pack_begin(),
-                                        XPEnd = X.pack_end(),
-                                           YP = Y.pack_begin();
-           XP != XPEnd; ++XP, ++YP)
-        if (!isSameTemplateArg(Context, *XP, *YP, PackExpansionMatchesPack))
+      unsigned PackIterationSize = X.pack_size();
+      if (X.pack_size() != Y.pack_size()) {
+        if (!PartialOrdering)
           return false;
 
+        // C++0x [temp.deduct.type]p9:
+        // During partial ordering, if Ai was originally a pack expansion:
+        // - if P does not contain a template argument corresponding to Ai then
+        //   Ai is ignored;
+        bool XHasMoreArg = X.pack_size() > Y.pack_size();
+        if (!(XHasMoreArg && X.pack_elements().back().isPackExpansion()) &&
+            !(!XHasMoreArg && Y.pack_elements().back().isPackExpansion()))
+          return false;
+
+        if (XHasMoreArg)
+          PackIterationSize = Y.pack_size();
+      }
+
+      ArrayRef<TemplateArgument> XP = X.pack_elements();
+      ArrayRef<TemplateArgument> YP = Y.pack_elements();
+      for (unsigned i = 0; i < PackIterationSize; ++i)
+        if (!isSameTemplateArg(Context, XP[i], YP[i], PartialOrdering,
+                               PackExpansionMatchesPack))
+          return false;
       return true;
   }
 
@@ -2875,7 +2903,8 @@ FinishTemplateArgumentDeduction(
   TemplateParameterList *TemplateParams = Template->getTemplateParameters();
   for (unsigned I = 0, E = TemplateParams->size(); I != E; ++I) {
     TemplateArgument InstArg = ConvertedInstArgs.data()[I];
-    if (!isSameTemplateArg(S.Context, TemplateArgs[I], InstArg)) {
+    if (!isSameTemplateArg(S.Context, TemplateArgs[I], InstArg,
+                           IsPartialOrdering)) {
       Info.Param = makeTemplateParameter(TemplateParams->getParam(I));
       Info.FirstArg = TemplateArgs[I];
       Info.SecondArg = InstArg;
@@ -2919,8 +2948,8 @@ static Sema::TemplateDeductionResult FinishTemplateArgumentDeduction(
   TemplateParameterList *TemplateParams = Template->getTemplateParameters();
   for (unsigned I = 0, E = TemplateParams->size(); I != E; ++I) {
     TemplateArgument InstArg = Builder[I];
-    if (!isSameTemplateArg(S.Context, TemplateArgs[I], InstArg,
-                           /*PackExpansionMatchesPack*/true)) {
+    if (!isSameTemplateArg(S.Context, TemplateArgs[I], InstArg, PartialOrdering,
+                           /*PackExpansionMatchesPack*/ true)) {
       Info.Param = makeTemplateParameter(TemplateParams->getParam(I));
       Info.FirstArg = TemplateArgs[I];
       Info.SecondArg = InstArg;
@@ -5109,27 +5138,6 @@ static bool isAtLeastAsSpecializedAs(Sema &S,
   return true;
 }
 
-/// Determine whether this a function template whose parameter-type-list
-/// ends with a function parameter pack.
-static bool isVariadicFunctionTemplate(FunctionTemplateDecl *FunTmpl) {
-  FunctionDecl *Function = FunTmpl->getTemplatedDecl();
-  unsigned NumParams = Function->getNumParams();
-  if (NumParams == 0)
-    return false;
-
-  ParmVarDecl *Last = Function->getParamDecl(NumParams - 1);
-  if (!Last->isParameterPack())
-    return false;
-
-  // Make sure that no previous parameter is a parameter pack.
-  while (--NumParams > 0) {
-    if (Function->getParamDecl(NumParams - 1)->isParameterPack())
-      return false;
-  }
-
-  return true;
-}
-
 /// Returns the more specialized function template according
 /// to the rules of function template partial ordering (C++ [temp.func.order]).
 ///
@@ -5188,13 +5196,22 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
   if (!Better1 && !Better2) // Neither is better than the other
     return JudgeByConstraints();
 
-  // FIXME: This mimics what GCC implements, but doesn't match up with the
-  // proposed resolution for core issue 692. This area needs to be sorted out,
-  // but for now we attempt to maintain compatibility.
-  bool Variadic1 = isVariadicFunctionTemplate(FT1);
-  bool Variadic2 = isVariadicFunctionTemplate(FT2);
-  if (Variadic1 != Variadic2)
-    return Variadic1? FT2 : FT1;
+  // C++ [temp.deduct.partial]p11:
+  //   ... and if G has a trailing function parameter pack for which F does not
+  //   have a corresponding parameter, and if F does not have a trailing
+  //   function parameter pack, then F is more specialized than G.
+  FunctionDecl *FD1 = FT1->getTemplatedDecl();
+  FunctionDecl *FD2 = FT2->getTemplatedDecl();
+  unsigned NumParams1 = FD1->getNumParams();
+  unsigned NumParams2 = FD2->getNumParams();
+  bool Variadic1 = NumParams1 && FD1->parameters().back()->isParameterPack();
+  bool Variadic2 = NumParams2 && FD2->parameters().back()->isParameterPack();
+  if (Variadic1 != Variadic2) {
+    if (Variadic1 && NumParams1 > NumParams2)
+      return FT2;
+    if (Variadic2 && NumParams2 > NumParams1)
+      return FT1;
+  }
 
   return JudgeByConstraints();
 }
@@ -5373,6 +5390,109 @@ static bool isAtLeastAsSpecializedAs(Sema &S, QualType T1, QualType T2,
   return AtLeastAsSpecialized;
 }
 
+namespace {
+// A dummy pass to return nullptr instead of P2 when performing "more
+// specialized than primary" check.
+struct GetP2 {
+  template <typename T1, typename T2,
+            std::enable_if_t<std::is_same<T1, T2>::value, bool> = true>
+  T2 *operator()(T1 *, T2 *P2) {
+    return P2;
+  }
+  template <typename T1, typename T2,
+            std::enable_if_t<!std::is_same<T1, T2>::value, bool> = true>
+  T1 *operator()(T1 *, T2 *) {
+    return nullptr;
+  }
+};
+} // namespace
+
+/// Returns the more specialized template specialization between T1/P1 and
+/// T2/P2.
+/// - If IsMoreSpecialThanPrimaryCheck is true, T1/P1 is the partial
+///   specialization and T2/P2 is the primary template.
+/// - otherwise, both T1/P1 and T2/P2 are the partial specialization.
+///
+/// \param T1 the type of the first template partial specialization
+///
+/// \param T2 if IsMoreSpecialThanPrimaryCheck is true, the type of the second
+///           template partial specialization; otherwise, the type of the
+///           primary template.
+///
+/// \param P1 the first template partial specialization
+///
+/// \param P2 if IsMoreSpecialThanPrimaryCheck is true, the second template
+///           partial specialization; otherwise, the primary template.
+///
+/// \returns - If IsMoreSpecialThanPrimaryCheck is true, returns P1 if P1 is
+///            more specialized, returns nullptr if P1 is not more specialized.
+///          - otherwise, returns the more specialized template partial
+///            specialization. If neither partial specialization is more
+///            specialized, returns NULL.
+template <typename TemplateLikeDecl, typename PrimaryDel>
+static TemplateLikeDecl *
+getMoreSpecialized(Sema &S, QualType T1, QualType T2, TemplateLikeDecl *P1,
+                   PrimaryDel *P2, TemplateDeductionInfo &Info) {
+  constexpr bool IsMoreSpecialThanPrimaryCheck =
+      !std::is_same<TemplateLikeDecl, PrimaryDel>::value;
+
+  bool Better1 = isAtLeastAsSpecializedAs(S, T1, T2, P2, Info);
+  if (IsMoreSpecialThanPrimaryCheck && !Better1)
+    return nullptr;
+
+  bool Better2 = isAtLeastAsSpecializedAs(S, T2, T1, P1, Info);
+  if (IsMoreSpecialThanPrimaryCheck && !Better2)
+    return P1;
+
+  if (!Better1 && !Better2)
+    return nullptr;
+
+  if (Better1 && Better2) {
+    bool ClangABICompat14 = S.Context.getLangOpts().getClangABICompat() <=
+                            LangOptions::ClangABI::Ver14;
+    if (!ClangABICompat14) {
+      // Consider this a fix for CWG1432. Similar to the fix for CWG1395.
+      auto *TST1 = T1->castAs<TemplateSpecializationType>();
+      auto *TST2 = T2->castAs<TemplateSpecializationType>();
+      if (TST1->getNumArgs()) {
+        const TemplateArgument &TA1 = TST1->template_arguments().back();
+        if (TA1.getKind() == TemplateArgument::Pack) {
+          assert(TST1->getNumArgs() == TST2->getNumArgs());
+          const TemplateArgument &TA2 = TST2->template_arguments().back();
+          assert(TA2.getKind() == TemplateArgument::Pack);
+          unsigned PackSize1 = TA1.pack_size();
+          unsigned PackSize2 = TA2.pack_size();
+          bool IsPackExpansion1 =
+              PackSize1 && TA1.pack_elements().back().isPackExpansion();
+          bool IsPackExpansion2 =
+              PackSize2 && TA2.pack_elements().back().isPackExpansion();
+          if (PackSize1 != PackSize2 && IsPackExpansion1 != IsPackExpansion2) {
+            if (PackSize1 > PackSize2 && IsPackExpansion1)
+              return GetP2()(P1, P2);
+            if (PackSize1 < PackSize2 && IsPackExpansion2)
+              return P1;
+          }
+        }
+      }
+    }
+
+    llvm::SmallVector<const Expr *, 3> AC1, AC2;
+    P1->getAssociatedConstraints(AC1);
+    P2->getAssociatedConstraints(AC2);
+    bool AtLeastAsConstrained1, AtLeastAsConstrained2;
+    if (S.IsAtLeastAsConstrained(P1, AC1, P2, AC2, AtLeastAsConstrained1) ||
+        (IsMoreSpecialThanPrimaryCheck && !AtLeastAsConstrained1))
+      return nullptr;
+    if (S.IsAtLeastAsConstrained(P2, AC2, P1, AC1, AtLeastAsConstrained2))
+      return nullptr;
+    if (AtLeastAsConstrained1 == AtLeastAsConstrained2)
+      return nullptr;
+    return AtLeastAsConstrained1 ? P1 : GetP2()(P1, P2);
+  }
+
+  return Better1 ? P1 : GetP2()(P1, P2);
+}
+
 /// Returns the more specialized class template partial specialization
 /// according to the rules of partial ordering of class template partial
 /// specializations (C++ [temp.class.order]).
@@ -5392,26 +5512,7 @@ Sema::getMoreSpecializedPartialSpecialization(
   QualType PT2 = PS2->getInjectedSpecializationType();
 
   TemplateDeductionInfo Info(Loc);
-  bool Better1 = isAtLeastAsSpecializedAs(*this, PT1, PT2, PS2, Info);
-  bool Better2 = isAtLeastAsSpecializedAs(*this, PT2, PT1, PS1, Info);
-
-  if (!Better1 && !Better2)
-      return nullptr;
-  if (Better1 && Better2) {
-    llvm::SmallVector<const Expr *, 3> AC1, AC2;
-    PS1->getAssociatedConstraints(AC1);
-    PS2->getAssociatedConstraints(AC2);
-    bool AtLeastAsConstrained1, AtLeastAsConstrained2;
-    if (IsAtLeastAsConstrained(PS1, AC1, PS2, AC2, AtLeastAsConstrained1))
-      return nullptr;
-    if (IsAtLeastAsConstrained(PS2, AC2, PS1, AC1, AtLeastAsConstrained2))
-      return nullptr;
-    if (AtLeastAsConstrained1 == AtLeastAsConstrained2)
-      return nullptr;
-    return AtLeastAsConstrained1 ? PS1 : PS2;
-  }
-
-  return Better1 ? PS1 : PS2;
+  return getMoreSpecialized(*this, PT1, PT2, PS1, PS2, Info);
 }
 
 bool Sema::isMoreSpecializedThanPrimary(
@@ -5419,24 +5520,12 @@ bool Sema::isMoreSpecializedThanPrimary(
   ClassTemplateDecl *Primary = Spec->getSpecializedTemplate();
   QualType PrimaryT = Primary->getInjectedClassNameSpecialization();
   QualType PartialT = Spec->getInjectedSpecializationType();
-  if (!isAtLeastAsSpecializedAs(*this, PartialT, PrimaryT, Primary, Info))
-    return false;
-  if (!isAtLeastAsSpecializedAs(*this, PrimaryT, PartialT, Spec, Info))
-    return true;
-  Info.clearSFINAEDiagnostic();
-  llvm::SmallVector<const Expr *, 3> PrimaryAC, SpecAC;
-  Primary->getAssociatedConstraints(PrimaryAC);
-  Spec->getAssociatedConstraints(SpecAC);
-  bool AtLeastAsConstrainedPrimary, AtLeastAsConstrainedSpec;
-  if (IsAtLeastAsConstrained(Spec, SpecAC, Primary, PrimaryAC,
-                             AtLeastAsConstrainedSpec))
-    return false;
-  if (!AtLeastAsConstrainedSpec)
-    return false;
-  if (IsAtLeastAsConstrained(Primary, PrimaryAC, Spec, SpecAC,
-                             AtLeastAsConstrainedPrimary))
-    return false;
-  return !AtLeastAsConstrainedPrimary;
+
+  ClassTemplatePartialSpecializationDecl *MaybeSpec =
+      getMoreSpecialized(*this, PartialT, PrimaryT, Spec, Primary, Info);
+  if (MaybeSpec)
+    Info.clearSFINAEDiagnostic();
+  return MaybeSpec;
 }
 
 VarTemplatePartialSpecializationDecl *
@@ -5456,26 +5545,7 @@ Sema::getMoreSpecializedPartialSpecialization(
       CanonTemplate, PS2->getTemplateArgs().asArray());
 
   TemplateDeductionInfo Info(Loc);
-  bool Better1 = isAtLeastAsSpecializedAs(*this, PT1, PT2, PS2, Info);
-  bool Better2 = isAtLeastAsSpecializedAs(*this, PT2, PT1, PS1, Info);
-
-  if (!Better1 && !Better2)
-    return nullptr;
-  if (Better1 && Better2) {
-    llvm::SmallVector<const Expr *, 3> AC1, AC2;
-    PS1->getAssociatedConstraints(AC1);
-    PS2->getAssociatedConstraints(AC2);
-    bool AtLeastAsConstrained1, AtLeastAsConstrained2;
-    if (IsAtLeastAsConstrained(PS1, AC1, PS2, AC2, AtLeastAsConstrained1))
-      return nullptr;
-    if (IsAtLeastAsConstrained(PS2, AC2, PS1, AC1, AtLeastAsConstrained2))
-      return nullptr;
-    if (AtLeastAsConstrained1 == AtLeastAsConstrained2)
-      return nullptr;
-    return AtLeastAsConstrained1 ? PS1 : PS2;
-  }
-
-  return Better1 ? PS1 : PS2;
+  return getMoreSpecialized(*this, PT1, PT2, PS1, PS2, Info);
 }
 
 bool Sema::isMoreSpecializedThanPrimary(
@@ -5494,24 +5564,11 @@ bool Sema::isMoreSpecializedThanPrimary(
   QualType PartialT = Context.getTemplateSpecializationType(
       CanonTemplate, Spec->getTemplateArgs().asArray());
 
-  if (!isAtLeastAsSpecializedAs(*this, PartialT, PrimaryT, Primary, Info))
-    return false;
-  if (!isAtLeastAsSpecializedAs(*this, PrimaryT, PartialT, Spec, Info))
-    return true;
-  Info.clearSFINAEDiagnostic();
-  llvm::SmallVector<const Expr *, 3> PrimaryAC, SpecAC;
-  Primary->getAssociatedConstraints(PrimaryAC);
-  Spec->getAssociatedConstraints(SpecAC);
-  bool AtLeastAsConstrainedPrimary, AtLeastAsConstrainedSpec;
-  if (IsAtLeastAsConstrained(Spec, SpecAC, Primary, PrimaryAC,
-                             AtLeastAsConstrainedSpec))
-    return false;
-  if (!AtLeastAsConstrainedSpec)
-    return false;
-  if (IsAtLeastAsConstrained(Primary, PrimaryAC, Spec, SpecAC,
-                             AtLeastAsConstrainedPrimary))
-    return false;
-  return !AtLeastAsConstrainedPrimary;
+  VarTemplatePartialSpecializationDecl *MaybeSpec =
+      getMoreSpecialized(*this, PartialT, PrimaryT, Spec, Primary, Info);
+  if (MaybeSpec)
+    Info.clearSFINAEDiagnostic();
+  return MaybeSpec;
 }
 
 bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs(

diff  --git a/clang/test/CXX/drs/dr6xx.cpp b/clang/test/CXX/drs/dr6xx.cpp
index 432fd6e6c5ef..9d8f4d669b61 100644
--- a/clang/test/CXX/drs/dr6xx.cpp
+++ b/clang/test/CXX/drs/dr6xx.cpp
@@ -1080,19 +1080,18 @@ namespace dr687 { // dr687 (9 c++20, but the issue is still considered open)
 }
 
 namespace dr692 { // dr692: no
+  // Also see dr1395.
+
   namespace temp_func_order_example2 {
     template <typename T, typename U> struct A {};
     template <typename T, typename U> void f(U, A<U, T> *p = 0); // expected-note {{candidate}}
     template <typename U> int &f(U, A<U, U> *p = 0); // expected-note {{candidate}}
-    template <typename T> void g(T, T = T());
-    template <typename T, typename... U> void g(T, U...); // expected-error 0-1{{C++11}}
+    template <typename T> void g(T, T = T()); // expected-note {{candidate}}
+    template <typename T, typename... U> void g(T, U...); // expected-note {{candidate}} expected-error 0-1{{C++11}}
     void h() {
       int &r = f<int>(42, (A<int, int> *)0);
       f<int>(42); // expected-error {{ambiguous}}
-      // FIXME: We should reject this due to ambiguity between the pack and the
-      // default argument. Only parameters with arguments are considered during
-      // partial ordering of function templates.
-      g(42);
+      g(42); // expected-error {{ambiguous}}
     }
   }
 
@@ -1125,20 +1124,16 @@ namespace dr692 { // dr692: no
     template <class T1, class T2> class S<T1, const T2&> {};
     S<int, const int&> s;
 
-    // FIXME: This should select the first partial specialization. Deduction of
-    // the second from the first should succeed, because we should ignore the
-    // trailing pack in A with no corresponding P.
     template<class T, class... U> struct A; // expected-error 0-1{{C++11}}
-    template<class T1, class T2, class... U> struct A<T1,T2*,U...>; // expected-note {{matches}} expected-error 0-1{{C++11}}
-    template<class T1, class T2> struct A<T1,T2> {}; // expected-note {{matches}}
-    template struct A<int, int*>; // expected-error {{ambiguous}}
+    template<class T1, class T2, class... U> struct A<T1,T2*,U...> {}; // expected-error 0-1{{C++11}}
+    template<class T1, class T2> struct A<T1,T2>;
+    template struct A<int, int*>;
   }
 
   namespace temp_deduct_type_example3 {
-    // FIXME: This should select the first template, as in the case above.
-    template<class T, class... U> void f(T*, U...){} // expected-note {{candidate}} expected-error 0-1{{C++11}}
-    template<class T> void f(T){} // expected-note {{candidate}}
-    template void f(int*); // expected-error {{ambiguous}}
+    template<class T, class... U> void f(T*, U...){} // expected-error 0-1{{C++11}}
+    template<class T> void f(T){}
+    template void f(int*);
   }
 }
 

diff  --git a/clang/test/CodeGen/partial-order-variadic.cpp b/clang/test/CodeGen/partial-order-variadic.cpp
new file mode 100644
index 000000000000..496a7da42208
--- /dev/null
+++ b/clang/test/CodeGen/partial-order-variadic.cpp
@@ -0,0 +1,54 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fclang-abi-compat=14 -DCLANG_ABI_COMPAT=14 %s -emit-llvm -disable-llvm-passes -o - | FileCheck %s --check-prefix=CHECK-14
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown %s -emit-llvm -disable-llvm-passes -o - | FileCheck %s
+
+#if defined(CLANG_ABI_COMPAT) && CLANG_ABI_COMPAT <= 14
+
+// CHECK-14: define dso_local void @_ZN24temp_func_order_example31hEi(i32 noundef %i)
+// CHECK-14-NEXT: entry:
+// CHECK-14-NEXT:   %i.addr = alloca i32, align 4
+// CHECK-14-NEXT:   %r = alloca ptr, align 8
+// CHECK-14-NEXT:   store i32 %i, ptr %i.addr, align 4
+// CHECK-14-NEXT:   %call = call noundef nonnull align 4 dereferenceable(4) ptr @_ZN24temp_func_order_example31gIiJEEERiPT_DpT0_(ptr noundef %i.addr)
+// CHECK-14-NEXT:   store ptr %call, ptr %r, align 8
+// CHECK-14-NEXT:   ret void
+
+namespace temp_func_order_example3 {
+  template <typename T, typename... U> int &g(T *, U...);
+  template <typename T> void g(T);
+  void h(int i) {
+    int &r = g(&i);
+  }
+}
+
+#else
+
+// CHECK: %"struct.temp_deduct_type_example1::A" = type { i8 }
+
+// CHECK: $_ZN25temp_deduct_type_example31fIiJEEEvPT_DpT0_ = comdat any
+
+// CHECK: define dso_local void @_ZN25temp_deduct_type_example11fEv()
+// CHECK-NEXT: entry:
+// CHECK-NEXT:   %a = alloca %"struct.temp_deduct_type_example1::A", align 1
+// CHECK-NEXT:   ret void
+
+// CHECK: define weak_odr void @_ZN25temp_deduct_type_example31fIiJEEEvPT_DpT0_(ptr noundef %0)
+// CHECK-NEXT: entry:
+// CHECK-NEXT:   %.addr = alloca ptr, align 8
+// CHECK-NEXT:   store ptr %0, ptr %.addr, align 8
+// CHECK-NEXT:   ret void
+
+namespace temp_deduct_type_example1 {
+  template<class T, class... U> struct A;
+  template<class T1, class T2, class... U> struct A<T1,T2*,U...> {};
+  template<class T1, class T2> struct A<T1,T2>;
+  template struct A<int, int*>;
+  void f() { A<int, int*> a; }
+}
+
+namespace temp_deduct_type_example3 {
+  template<class T, class... U> void f(T*, U...){}
+  template<class T> void f(T){}
+  template void f(int*);
+}
+
+#endif

diff  --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index 9db2d890f62a..afaa852e92ca 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -4194,7 +4194,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td><a href="https://wg21.link/cwg692">692</a></td>
     <td>C++11</td>
     <td>Partial ordering of variadic class template partial specializations</td>
-    <td class="none" align="center">No</td>
+    <td class="unreleased" align="center">Clang 16</td>
   </tr>
   <tr id="693">
     <td><a href="https://wg21.link/cwg693">693</a></td>
@@ -8178,7 +8178,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td><a href="https://wg21.link/cwg1395">1395</a></td>
     <td>C++17</td>
     <td>Partial ordering of variadic templates reconsidered</td>
-    <td class="none" align="center">Unknown</td>
+    <td class="unreleased" align="center">Clang 16</td>
   </tr>
   <tr class="open" id="1396">
     <td><a href="https://wg21.link/cwg1396">1396</a></td>


        


More information about the cfe-commits mailing list