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); +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits