<div dir="ltr"><div dir="ltr">On Tue, 27 Oct 2020 at 17:08, Nico Weber <<a href="mailto:thakis@chromium.org">thakis@chromium.org</a>> wrote:<br></div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr">gcc points out that DTST is unused in<div><br></div><div>+ if (auto *DTST = dyn_cast<DeducedTemplateSpecializationType>(DeducedT)) {<br></div><div><br></div><div>Should it be used? Or should we delete the lhs of that assignment?</div></div></blockquote><div><br></div><div>The latter. Done in af2f5f99bd144ae2f70e21d53b23fc18a3d9d7d0.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Wed, Oct 21, 2020 at 6:03 PM Richard Smith via cfe-commits <<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br>
Author: Richard Smith<br>
Date: 2020-10-21T15:03:22-07:00<br>
New Revision: e97e9851b227e98e39c27c4c8f5558e331cde8b4<br>
<br>
URL: <a href="https://github.com/llvm/llvm-project/commit/e97e9851b227e98e39c27c4c8f5558e331cde8b4" rel="noreferrer" target="_blank">https://github.com/llvm/llvm-project/commit/e97e9851b227e98e39c27c4c8f5558e331cde8b4</a><br>
DIFF: <a href="https://github.com/llvm/llvm-project/commit/e97e9851b227e98e39c27c4c8f5558e331cde8b4.diff" rel="noreferrer" target="_blank">https://github.com/llvm/llvm-project/commit/e97e9851b227e98e39c27c4c8f5558e331cde8b4.diff</a><br>
<br>
LOG: [c++20] For P0732R2: permit class template argument deduction for non-type template parameters.<br>
<br>
Added: <br>
clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.class.deduct/p2.cpp<br>
clang/test/CXX/temp/temp.arg/temp.arg.nontype/p1-cxx20.cpp<br>
<br>
Modified: <br>
clang/lib/Sema/SemaTemplate.cpp<br>
clang/lib/Sema/SemaTemplateDeduction.cpp<br>
clang/lib/Sema/SemaType.cpp<br>
clang/test/SemaTemplate/deduction.cpp<br>
clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp<br>
<br>
Removed: <br>
<br>
<br>
<br>
################################################################################<br>
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp<br>
index 8bff982d66be..d23ad9f7d91d 100644<br>
--- a/clang/lib/Sema/SemaTemplate.cpp<br>
+++ b/clang/lib/Sema/SemaTemplate.cpp<br>
@@ -23,6 +23,7 @@<br>
#include "clang/Basic/Stack.h"<br>
#include "clang/Basic/TargetInfo.h"<br>
#include "clang/Sema/DeclSpec.h"<br>
+#include "clang/Sema/Initialization.h"<br>
#include "clang/Sema/Lookup.h"<br>
#include "clang/Sema/Overload.h"<br>
#include "clang/Sema/ParsedTemplate.h"<br>
@@ -6807,14 +6808,15 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,<br>
SourceLocation StartLoc = Arg->getBeginLoc();<br>
<br>
// If the parameter type somehow involves auto, deduce the type now.<br>
- if (getLangOpts().CPlusPlus17 && ParamType->isUndeducedType()) {<br>
+ DeducedType *DeducedT = ParamType->getContainedDeducedType();<br>
+ if (getLangOpts().CPlusPlus17 && DeducedT && !DeducedT->isDeduced()) {<br>
// During template argument deduction, we allow 'decltype(auto)' to<br>
// match an arbitrary dependent argument.<br>
// FIXME: The language rules don't say what happens in this case.<br>
// FIXME: We get an opaque dependent type out of decltype(auto) if the<br>
// expression is merely instantiation-dependent; is this enough?<br>
if (CTAK == CTAK_Deduced && Arg->isTypeDependent()) {<br>
- auto *AT = dyn_cast<AutoType>(ParamType);<br>
+ auto *AT = dyn_cast<AutoType>(DeducedT);<br>
if (AT && AT->isDecltypeAuto()) {<br>
Converted = TemplateArgument(Arg);<br>
return Arg;<br>
@@ -6828,14 +6830,26 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,<br>
Expr *DeductionArg = Arg;<br>
if (auto *PE = dyn_cast<PackExpansionExpr>(DeductionArg))<br>
DeductionArg = PE->getPattern();<br>
- if (DeduceAutoType(<br>
- Context.getTrivialTypeSourceInfo(ParamType, Param->getLocation()),<br>
- DeductionArg, ParamType, Depth,<br>
- // We do not check constraints right now because the<br>
- // immediately-declared constraint of the auto type is also an<br>
- // associated constraint, and will be checked along with the other<br>
- // associated constraints after checking the template argument list.<br>
- /*IgnoreConstraints=*/true) == DAR_Failed) {<br>
+ TypeSourceInfo *TSI =<br>
+ Context.getTrivialTypeSourceInfo(ParamType, Param->getLocation());<br>
+ if (auto *DTST = dyn_cast<DeducedTemplateSpecializationType>(DeducedT)) {<br>
+ InitializedEntity Entity =<br>
+ InitializedEntity::InitializeTemplateParameter(ParamType, Param);<br>
+ InitializationKind Kind = InitializationKind::CreateForInit(<br>
+ DeductionArg->getBeginLoc(), /*DirectInit*/false, DeductionArg);<br>
+ Expr *Inits[1] = {DeductionArg};<br>
+ ParamType =<br>
+ DeduceTemplateSpecializationFromInitializer(TSI, Entity, Kind, Inits);<br>
+ if (ParamType.isNull())<br>
+ return ExprError();<br>
+ } else if (DeduceAutoType(<br>
+ TSI, DeductionArg, ParamType, Depth,<br>
+ // We do not check constraints right now because the<br>
+ // immediately-declared constraint of the auto type is also<br>
+ // an associated constraint, and will be checked along with<br>
+ // the other associated constraints after checking the<br>
+ // template argument list.<br>
+ /*IgnoreConstraints=*/true) == DAR_Failed) {<br>
Diag(Arg->getExprLoc(),<br>
diag::err_non_type_template_parm_type_deduction_failure)<br>
<< Param->getDeclName() << Param->getType() << Arg->getType()<br>
@@ -6870,9 +6884,9 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,<br>
// FIXME: If the argument type contains 'auto', we carry on and fail the<br>
// type check in order to force specific types to be more specialized than<br>
// 'auto'. It's not clear how partial ordering with 'auto' is supposed to<br>
- // work.<br>
+ // work. Similarly for CTAD, when comparing 'A<x>' against 'A'.<br>
if ((ParamType->isDependentType() || Arg->isTypeDependent()) &&<br>
- !Arg->getType()->getContainedAutoType()) {<br>
+ !Arg->getType()->getContainedDeducedType()) {<br>
Converted = TemplateArgument(Arg);<br>
return Arg;<br>
}<br>
<br>
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp<br>
index 1c20ca619138..886377108e3f 100644<br>
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp<br>
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp<br>
@@ -1615,14 +1615,18 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S,<br>
return Sema::TDK_Success;<br>
}<br>
} else if (!Param->isDependentType()) {<br>
- CanQualType ParamUnqualType = CanParam.getUnqualifiedType(),<br>
- ArgUnqualType = CanArg.getUnqualifiedType();<br>
- bool Success =<br>
- (TDF & TDF_AllowCompatibleFunctionType)<br>
- ? S.isSameOrCompatibleFunctionType(ParamUnqualType, ArgUnqualType)<br>
- : ParamUnqualType == ArgUnqualType;<br>
- if (Success)<br>
+ if (!(TDF & TDF_SkipNonDependent)) {<br>
+ CanQualType ParamUnqualType = CanParam.getUnqualifiedType(),<br>
+ ArgUnqualType = CanArg.getUnqualifiedType();<br>
+ bool Success =<br>
+ (TDF & TDF_AllowCompatibleFunctionType)<br>
+ ? S.isSameOrCompatibleFunctionType(ParamUnqualType, ArgUnqualType)<br>
+ : ParamUnqualType == ArgUnqualType;<br>
+ if (Success)<br>
+ return Sema::TDK_Success;<br>
+ } else {<br>
return Sema::TDK_Success;<br>
+ }<br>
}<br>
<br>
switch (Param->getTypeClass()) {<br>
<br>
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp<br>
index 4d156634bf48..1ba1869e0fe5 100644<br>
--- a/clang/lib/Sema/SemaType.cpp<br>
+++ b/clang/lib/Sema/SemaType.cpp<br>
@@ -3375,8 +3375,9 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state,<br>
Error = 7; // Exception declaration<br>
break;<br>
case DeclaratorContext::TemplateParamContext:<br>
- if (isa<DeducedTemplateSpecializationType>(Deduced))<br>
- Error = 19; // Template parameter<br>
+ if (isa<DeducedTemplateSpecializationType>(Deduced) &&<br>
+ !SemaRef.getLangOpts().CPlusPlus20)<br>
+ Error = 19; // Template parameter (until C++20)<br>
else if (!SemaRef.getLangOpts().CPlusPlus17)<br>
Error = 8; // Template parameter (until C++17)<br>
break;<br>
<br>
diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.class.deduct/p2.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.class.deduct/p2.cpp<br>
new file mode 100644<br>
index 000000000000..8468731a3cb0<br>
--- /dev/null<br>
+++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.class.deduct/p2.cpp<br>
@@ -0,0 +1,23 @@<br>
+// RUN: %clang_cc1 -std=c++17 -verify=expected,cxx17 %s<br>
+// RUN: %clang_cc1 -std=c++20 -verify=expected,cxx20 %s<br>
+<br>
+template<typename T> struct A { constexpr A(T) {} }; // expected-note 1+{{here}}<br>
+<br>
+A a = 0;<br>
+A b(0);<br>
+A c = A(0);<br>
+A d = A{0};<br>
+auto *p = new A(0);<br>
+A *q = new A(0); // expected-error {{cannot form pointer to deduced class template specialization type}}<br>
+<br>
+struct B {<br>
+ operator A() { // expected-error {{argument deduction not allowed in conversion function type}}<br>
+ return A(0);<br>
+ }<br>
+};<br>
+<br>
+void f(A a); // expected-error {{argument deduction not allowed in function prototype}}<br>
+A f(); // expected-error {{argument deduction not allowed in function return type}}<br>
+<br>
+template<A a> // cxx17-error {{argument deduction not allowed in template parameter}}<br>
+void f();<br>
<br>
diff --git a/clang/test/CXX/temp/temp.arg/temp.arg.nontype/p1-cxx20.cpp b/clang/test/CXX/temp/temp.arg/temp.arg.nontype/p1-cxx20.cpp<br>
new file mode 100644<br>
index 000000000000..d6d2ad742d0e<br>
--- /dev/null<br>
+++ b/clang/test/CXX/temp/temp.arg/temp.arg.nontype/p1-cxx20.cpp<br>
@@ -0,0 +1,23 @@<br>
+// RUN: %clang_cc1 -std=c++20 %s -verify<br>
+<br>
+using size_t = __SIZE_TYPE__;<br>
+<br>
+namespace CTAD {<br>
+ template<typename T> struct A { constexpr A(T) {} };<br>
+ template<A a> using DeducedA = decltype(a);<br>
+<br>
+ using ATest1 = DeducedA<A(0)>;<br>
+ using ATest1 = A<int>; // expected-note {{previous}}<br>
+ using ATest1 = void; // expected-error {{<br>
diff erent}}<br>
+<br>
+ using ATest2 = DeducedA<A(0.0)>;<br>
+ using ATest2 = A<double>;<br>
+<br>
+ template <size_t N> struct B {<br>
+ constexpr B(const char (&r)[N]) { __builtin_memcpy(text, r, N); }<br>
+ char text[N];<br>
+ };<br>
+<br>
+ template<B b> constexpr const char *str() { return b.text; }<br>
+ static_assert(__builtin_strcmp("hello world", str<"hello world">()) == 0);<br>
+}<br>
<br>
diff --git a/clang/test/SemaTemplate/deduction.cpp b/clang/test/SemaTemplate/deduction.cpp<br>
index a068bcaea048..b9a1f0dccb24 100644<br>
--- a/clang/test/SemaTemplate/deduction.cpp<br>
+++ b/clang/test/SemaTemplate/deduction.cpp<br>
@@ -312,6 +312,13 @@ namespace nullptr_deduction {<br>
f(X<nullptr_t, nullptr>()); // expected-note {{instantiation of}}<br>
}<br>
<br>
+ template<template<typename T, T> class X, typename T, int *P><br>
+ void f0(X<T, P>) {} // expected-note {{deduced non-type template argument does not have the same type as the corresponding template parameter ('nullptr_t' vs 'int *')}}<br>
+ void h0() {<br>
+ f0(X<int*, nullptr>());<br>
+ f0(X<nullptr_t, nullptr>()); // expected-error {{no matching function}}<br>
+ }<br>
+<br>
template<template<typename T, T> class X, typename T, typename U, int *P><br>
void f1(X<T, P>, X<U, P>) {} // expected-note 2{{values of conflicting types}}<br>
void h() {<br>
<br>
diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp<br>
index d0ace9b2b2b5..1d9fefb6cbe5 100644<br>
--- a/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp<br>
+++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp<br>
@@ -124,8 +124,7 @@ namespace StableAddress {<br>
// FIXME: Deduction guide not needed with P1816R0.<br>
template<size_t N> str(const char (&)[N]) -> str<N>;<br>
<br>
- // FIXME: Explicit size not needed.<br>
- template<str<15> s> constexpr int sum() {<br>
+ template<str s> constexpr int sum() {<br>
int n = 0;<br>
for (char c : s.arr)<br>
n += c;<br>
@@ -184,3 +183,21 @@ namespace Diags {<br>
template<A a> struct X { static_assert(a.n == a.m); }; // expected-error {{static_assert failed due to requirement 'Diags::A{1, 2}.n == Diags::A{1, 2}.m'}}<br>
template struct X<A{1, 2}>; // expected-note {{in instantiation of template class 'Diags::X<{1, 2}>' requested here}}<br>
}<br>
+<br>
+namespace CTADPartialOrder {<br>
+ template<int> struct A {};<br>
+ template<typename T, typename U, A a> struct X; // expected-note {{declared here}}<br>
+ template<typename T, A a> struct X<T, int, a> { static constexpr int n = 1; }; // expected-note {{matches}}<br>
+ template<typename T, A a> struct X<T *, int, a> { static constexpr int n = 2; };<br>
+ template<typename T, A a> struct X<T, T, a> { static constexpr int n = 3; }; // expected-note {{matches}}<br>
+<br>
+ A<0> a;<br>
+ static_assert(X<void, int, a>::n == 1);<br>
+ static_assert(X<int*, int, a>::n == 2);<br>
+ static_assert(X<void, void, a>::n == 3);<br>
+ static_assert(X<int, int, a>::n == -1); // expected-error {{ambiguous}}<br>
+ static_assert(X<int*, void, a>::n == 2); // expected-error {{undefined}}<br>
+<br>
+ template<typename T, A<0> a> struct X<T, T, a> { static constexpr int n = 4; };<br>
+ static_assert(X<float, float, a>::n == 4);<br>
+}<br>
<br>
<br>
<br>
_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits</a><br>
</blockquote></div>
</blockquote></div></div>