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