llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Valentyn Yukhymenko (BaLiKfromUA)

<details>
<summary>Changes</summary>

Fixes https://github.com/llvm/llvm-project/issues/124186

Fixes https://github.com/llvm/llvm-project/issues/42421

Fixes https://github.com/llvm/llvm-project/issues/53982

----

🔎 [**Compiler Explorer Link** to illustrate differences between GCC and Clang 
trunks](https://godbolt.org/z/MY4r8bMx8)

**🤖 AI usage:** I used LLM to suggest test cases to improve coverage + to find 
existing open issues which my fix might address.

**Note about my fix:** I checked existing callers of 
`ASTContext::isSameTemplateArgument` and I _think_  my change is safe. 

But please correct me if I am wrong! I will move the check somewhere to the 
relevant caller side then.


---
Full diff: https://github.com/llvm/llvm-project/pull/225239.diff


3 Files Affected:

- (modified) clang/docs/ReleaseNotes.md (+7) 
- (modified) clang/lib/AST/ASTContext.cpp (+4-1) 
- (modified) clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp (+148-14) 


``````````diff
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 303f972fcaae1..53ad36c840e71 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -737,6 +737,13 @@ features cannot lower the translation-unit ABI level;
   lookup before asserting that the name is not dependent, avoiding an 
assertion 
   after an earlier diagnostic has caused the declaration to be unavailable. 
(#GH220525)
 
+- Fixed template argument deduction incorrectly selecting a class or variable
+  template partial specialization whose non-type template argument has a
+  different type from the argument it is matched against, when the 
corresponding
+  parameter of the primary template has a placeholder type. For example, given
+  `template <class T, auto V> struct S`, the partial specialization `S<T, 0>` 
is
+  no longer selected for `S<void, 0L>`. (#GH124186), (#GH42421), (#GH53982)
+
 #### Bug Fixes to AST Handling
 
 - Fixed a non-deterministic ordering of unused local typedefs that made
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index e74423ca8c8a1..799f2bfc48a80 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -8040,7 +8040,10 @@ bool ASTContext::isSameTemplateArgument(const 
TemplateArgument &Arg1,
            getCanonicalTemplateName(Arg2.getAsTemplateOrTemplatePattern());
 
   case TemplateArgument::Integral:
-    return llvm::APSInt::isSameValue(Arg1.getAsIntegral(),
+    // The types have to match as well as the values:
+    // C++ [temp.type]p2
+    return hasSameType(Arg1.getIntegralType(), Arg2.getIntegralType()) &&
+           llvm::APSInt::isSameValue(Arg1.getAsIntegral(),
                                      Arg2.getAsIntegral());
 
   case TemplateArgument::StructuralValue:
diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp 
b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp
index a39bb02084aa7..1472aaa632400 100644
--- a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp
+++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp
@@ -267,22 +267,23 @@ namespace Auto {
   }
 
   namespace Decomposition {
-    // Types of deduced non-type template arguments must match exactly, so
-    // partial ordering fails in both directions here.
-    template<auto> struct Any;
-    template<int N> struct Any<N> { typedef int Int; }; // expected-note 
3{{match}}
-    template<short N> struct Any<N> { typedef int Short; }; // expected-note 
3{{match}}
-    Any<0>::Int is_int; // expected-error {{ambiguous}}
-    Any<(short)0>::Short is_short; // expected-error {{ambiguous}}
-    Any<(char)0>::Short is_char; // expected-error {{ambiguous}}
+    // Types of deduced non-type template arguments must match exactly, so each
+    // of these selects at most one partial specialization: the one whose
+    // parameter has the same type as the argument.
+    template<auto> struct Any; // expected-note {{template is declared here}}
+    template<int N> struct Any<N> { typedef int Int; };
+    template<short N> struct Any<N> { typedef int Short; };
+    Any<0>::Int is_int;
+    Any<(short)0>::Short is_short;
+    Any<(char)0>::Short is_char; // expected-error {{implicit instantiation of 
undefined template}}
 
     template<int, auto> struct NestedAny;
-    template<auto N> struct NestedAny<0, N>; // expected-note 3{{match}}
-    template<int N> struct NestedAny<0, N> { typedef int Int; }; // 
expected-note 3{{match}}
-    template<short N> struct NestedAny<0, N> { typedef int Short; }; // 
expected-note 3{{match}}
-    NestedAny<0, 0>::Int nested_int; // expected-error {{ambiguous}}
-    NestedAny<0, (short)0>::Short nested_short; // expected-error {{ambiguous}}
-    NestedAny<0, (char)0>::Short nested_char; // expected-error {{ambiguous}}
+    template<auto N> struct NestedAny<0, N>; // expected-note {{template is 
declared here}}
+    template<int N> struct NestedAny<0, N> { typedef int Int; };
+    template<short N> struct NestedAny<0, N> { typedef int Short; };
+    NestedAny<0, 0>::Int nested_int;
+    NestedAny<0, (short)0>::Short nested_short;
+    NestedAny<0, (char)0>::Short nested_char; // expected-error {{implicit 
instantiation of undefined template}}
 
     double foo(int, bool);
     template<auto& f> struct fn_result_type;
@@ -649,6 +650,139 @@ namespace GH58682 {
   static_assert(B<A<(g)>>::k == 1, "");
 } // namespace GH58682
 
+namespace GH124186 {
+  template <class T, auto V> struct S {
+    static constexpr int value = 0;
+  };
+
+  template <class T> struct S<T, 0> {
+    static constexpr int value = 1;
+  };
+
+  enum E { Zero };
+
+  static_assert(S<void, 0>::value == 1);
+  static_assert(S<void, 0L>::value == 0);
+  static_assert(S<void, 0U>::value == 0);
+  static_assert(S<void, false>::value == 0);
+  static_assert(S<void, Zero>::value == 0);
+
+  // For a parameter of non-placeholder type the argument is converted to the
+  // type of the parameter, so these all still match.
+  template <class T, int V> struct F {
+    static constexpr int value = 0;
+  };
+
+  template <class T> struct F<T, 0> {
+    static constexpr int value = 1;
+  };
+
+  static_assert(F<void, 0>::value == 1);
+  static_assert(F<void, 0L>::value == 1);
+  static_assert(F<void, 0U>::value == 1);
+
+  // Test with aliasing and cv modifiers
+  typedef int my_int;
+  using my_const_int = const int;
+
+  template <class T, auto V> struct G {
+    static constexpr int value = 0;
+  };
+
+  template <class T> struct G<T, (my_int)0> {
+    static constexpr int value = 1;
+  };
+
+  static_assert(G<void, 0>::value == 1);
+  static_assert(G<void, 0L>::value == 0);
+
+  template <auto V> struct H {
+    static constexpr int value = 0;
+  };
+
+  template <my_int V> struct H<V> {
+    static constexpr int value = 1;
+  };
+
+  template <auto V> struct I {
+    static constexpr int value = 0;
+  };
+
+  template <my_const_int V> struct I<V> {
+    static constexpr int value = 1;
+  };
+
+  static_assert(H<0>::value == 1);
+  static_assert(H<0L>::value == 0);
+  static_assert(I<0>::value == 1);
+  static_assert(I<0L>::value == 0);
+
+  // Variable templates behave the same way.
+  template <class T, auto V> constexpr int value = 0;
+  template <class T> constexpr int value<T, 0> = 1;
+
+  static_assert(value<void, 0> == 1);
+  static_assert(value<void, 0L> == 0);
+  static_assert(value<void, 0U> == 0);
+  static_assert(value<void, false> == 0);
+} // namespace GH124186
+
+namespace GH42421 {
+  template <auto V> struct S {
+    static constexpr int value = 0;
+  };
+
+  template <int I> struct S<I> {
+    static constexpr int value = 1;
+  };
+
+  static_assert(S<42>::value == 1);
+  // A long or unsigned argument does not match an int parameter.
+  static_assert(S<42L>::value == 0);
+  static_assert(S<42U>::value == 0);
+
+  // Only the partial specialization is type-sensitive here; the explicit
+  // specialization is not a candidate for Q<42U, int> either way.
+  template <auto I, class T> struct Q {
+    static constexpr int value = 0;
+  };
+
+  template <class T> struct Q<42, T> {
+    static constexpr int value = 1;
+  };
+
+  template <> struct Q<42, int> {
+    static constexpr int value = 2;
+  };
+
+  static_assert(Q<42U, int>::value == 0);
+  // The explicit specialization still wins for an int argument.
+  static_assert(Q<42, int>::value == 2);
+} // namespace GH42421
+
+namespace GH53982 {
+  enum class E1 : unsigned int { E11 = 1 };
+  enum class E2 : unsigned int { E21 = 1 };
+
+  template <int j, auto i> struct C {
+    static constexpr int value = 0;
+  };
+
+  template <int j> struct C<j, E1::E11> {
+    static constexpr int value = 1;
+  };
+
+  template <int j> struct C<j, E2::E21> {
+    static constexpr int value = 2;
+  };
+
+  static_assert(C<0, E1::E11>::value == 1);
+  static_assert(C<1, E2::E21>::value == 2);
+
+  // The shared underlying value on its own matches neither.
+  static_assert(C<0, 1U>::value == 0);
+} // namespace GH53982
+
 // C++26 [temp.deduct.type]p13, Example 8.
 namespace temp_deduct_type_p13 {
   template<long n> struct A { };

``````````

</details>


https://github.com/llvm/llvm-project/pull/225239
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to