diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2016-09-28 23:55:27 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2016-09-28 23:55:27 +0000 |
commit | 5f274389d1d99b32c81773aa8357f274ba15dc85 (patch) | |
tree | ab6dc1a8cffeed6c11a8a3493a3c599f21996290 | |
parent | dc8a254663d17a2122547320ca4b4f7dcf51e360 (diff) | |
download | bcm5719-llvm-5f274389d1d99b32c81773aa8357f274ba15dc85.tar.gz bcm5719-llvm-5f274389d1d99b32c81773aa8357f274ba15dc85.zip |
P0127R2: Support type deduction for types of non-type template parameters in
C++1z.
Patch by James Touton! Some bugfixes and rebasing by me.
llvm-svn: 282651
-rw-r--r-- | clang/include/clang/Basic/DiagnosticSemaKinds.td | 13 | ||||
-rw-r--r-- | clang/lib/Lex/PPMacroExpansion.cpp | 2 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplate.cpp | 33 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplateDeduction.cpp | 83 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplateInstantiate.cpp | 4 | ||||
-rw-r--r-- | clang/lib/Sema/SemaType.cpp | 14 | ||||
-rw-r--r-- | clang/test/SemaTemplate/temp_arg_nontype_cxx11.cpp | 4 | ||||
-rw-r--r-- | clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp | 110 |
8 files changed, 231 insertions, 32 deletions
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 44a8b0532a9..00b9ca805ae 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1827,7 +1827,8 @@ def err_auto_not_allowed : Error< "|in exception declaration|in template parameter|in block literal" "|in template argument|in typedef|in type alias|in function return type" "|in conversion function type|here|in lambda parameter" - "|in type allocated by 'new'|in K&R-style function parameter}1">; + "|in type allocated by 'new'|in K&R-style function parameter}1" + "%select{|||||||| until C++1z||||||||||}1">; def err_auto_not_allowed_var_inst : Error< "'auto' variable template instantiation is not allowed">; def err_auto_var_requires_init : Error< @@ -3656,6 +3657,10 @@ def note_template_nontype_parm_prev_declaration : Note< "previous non-type template parameter with type %0 is here">; def err_template_nontype_parm_bad_type : Error< "a non-type template parameter cannot have type %0">; +def warn_cxx14_compat_template_nontype_parm_auto_type : Warning< + "non-type template parameters declared with %0 are incompatible with C++ " + "standards before C++1z">, + DefaultIgnore, InGroup<CXXPre1zCompat>; def err_template_param_default_arg_redefinition : Error< "template parameter redefines default argument">; def note_template_param_prev_default_arg : Note< @@ -3750,8 +3755,10 @@ def warn_cxx98_compat_template_arg_null : Warning< def err_template_arg_untyped_null_constant : Error< "null non-type template argument must be cast to template parameter type %0">; def err_template_arg_wrongtype_null_constant : Error< - "null non-type template argument of type %0 does not match template parameter " - "of type %1">; + "null non-type template argument of type %0 does not match template parameter " + "of type %1">; +def err_non_type_template_parm_type_deduction_failure : Error< + "non-type template parameter %0 with type %1 has incompatible initializer of type %2">; def err_deduced_non_type_template_arg_type_mismatch : Error< "deduced non-type template argument does not have the same type as the " "its corresponding template parameter%diff{ ($ vs $)|}0,1">; diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index d133bd30cf9..1be1801ac2f 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -1202,6 +1202,8 @@ static bool HasFeature(const Preprocessor &PP, StringRef Feature) { .Case("cxx_relaxed_constexpr", LangOpts.CPlusPlus14) .Case("cxx_return_type_deduction", LangOpts.CPlusPlus14) .Case("cxx_variable_templates", LangOpts.CPlusPlus14) + // C++1z features + .Case("cxx_template_auto", LangOpts.CPlusPlus1z) // C++ TSes //.Case("cxx_runtime_arrays", LangOpts.CPlusPlusTSArrays) //.Case("cxx_concepts", LangOpts.CPlusPlusTSConcepts) diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index bef36206e09..b1bb97b7586 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -730,7 +730,13 @@ Sema::CheckNonTypeTemplateParameterType(QualType T, SourceLocation Loc) { T->isNullPtrType() || // If T is a dependent type, we can't do the check now, so we // assume that it is well-formed. - T->isDependentType()) { + T->isDependentType() || + // Allow use of auto in template parameter declarations. + T->isUndeducedType()) { + if (T->isUndeducedType()) { + Diag(Loc, diag::warn_cxx14_compat_template_nontype_parm_auto_type) + << QualType(T->getContainedAutoType(), 0); + } // C++ [temp.param]p5: The top-level cv-qualifiers on the template-parameter // are ignored when determining its type. return T.getUnqualifiedType(); @@ -4975,6 +4981,29 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, CheckTemplateArgumentKind CTAK) { SourceLocation StartLoc = Arg->getLocStart(); + // If the parameter type somehow involves auto, deduce the type now. + if (getLangOpts().CPlusPlus1z && ParamType->isUndeducedType()) { + if (DeduceAutoType( + Context.getTrivialTypeSourceInfo(ParamType, Param->getLocation()), + Arg, ParamType) == DAR_Failed) { + Diag(Arg->getExprLoc(), + diag::err_non_type_template_parm_type_deduction_failure) + << Param->getDeclName() << Param->getType() << Arg->getType() + << Arg->getSourceRange(); + Diag(Param->getLocation(), diag::note_template_param_here); + return ExprError(); + } + // CheckNonTypeTemplateParameterType will produce a diagnostic if there's + // an error. The error message normally references the parameter + // declaration, but here we'll pass the argument location because that's + // where the parameter type is deduced. + ParamType = CheckNonTypeTemplateParameterType(ParamType, Arg->getExprLoc()); + if (ParamType.isNull()) { + Diag(Param->getLocation(), diag::note_template_param_here); + return ExprError(); + } + } + // If either the parameter has a dependent type or the argument is // type-dependent, there's nothing we can check now. if (ParamType->isDependentType() || Arg->isTypeDependent()) { @@ -7255,7 +7284,7 @@ Sema::CheckMemberSpecialization(NamedDecl *Member, LookupResult &Previous) { if (InstantiationFunction->isDeleted()) { assert(InstantiationFunction->getCanonicalDecl() == InstantiationFunction); - InstantiationFunction->setDeletedAsWritten(false);
+ InstantiationFunction->setDeletedAsWritten(false); } } diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 2311b2b7447..4f76a029dcd 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -100,7 +100,8 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S, SmallVectorImpl<DeducedTemplateArgument> & Deduced, unsigned TDF, - bool PartialOrdering = false); + bool PartialOrdering = false, + bool DeducedFromArrayBound = false); static Sema::TemplateDeductionResult DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, @@ -167,6 +168,12 @@ checkDeducedTemplateArguments(ASTContext &Context, Context.hasSameType(X.getAsType(), Y.getAsType())) return X; + // If one of the two arguments was deduced from an array bound, the other + // supersedes it. + if (X.wasDeducedFromArrayBound() != Y.wasDeducedFromArrayBound()) + return X.wasDeducedFromArrayBound() ? Y : X; + + // The arguments are not compatible. return DeducedTemplateArgument(); case TemplateArgument::Integral: @@ -287,7 +294,8 @@ checkDeducedTemplateArguments(ASTContext &Context, /// \brief Deduce the value of the given non-type template parameter /// from the given integral constant. static Sema::TemplateDeductionResult DeduceNonTypeTemplateArgument( - Sema &S, NonTypeTemplateParmDecl *NTTP, const llvm::APSInt &Value, + Sema &S, TemplateParameterList *TemplateParams, + NonTypeTemplateParmDecl *NTTP, const llvm::APSInt &Value, QualType ValueType, bool DeducedFromArrayBound, TemplateDeductionInfo &Info, SmallVectorImpl<DeducedTemplateArgument> &Deduced) { assert(NTTP->getDepth() == 0 && @@ -306,13 +314,20 @@ static Sema::TemplateDeductionResult DeduceNonTypeTemplateArgument( } Deduced[NTTP->getIndex()] = Result; - return Sema::TDK_Success; + return S.getLangOpts().CPlusPlus1z + ? DeduceTemplateArgumentsByTypeMatch( + S, TemplateParams, NTTP->getType(), ValueType, Info, Deduced, + TDF_ParamWithReferenceType | TDF_SkipNonDependent, + /*PartialOrdering=*/false, + /*ArrayBound=*/DeducedFromArrayBound) + : Sema::TDK_Success; } /// \brief Deduce the value of the given non-type template parameter /// from the given null pointer template argument type. static Sema::TemplateDeductionResult DeduceNullPtrTemplateArgument( - Sema &S, NonTypeTemplateParmDecl *NTTP, QualType NullPtrType, + Sema &S, TemplateParameterList *TemplateParams, + NonTypeTemplateParmDecl *NTTP, QualType NullPtrType, TemplateDeductionInfo &Info, SmallVectorImpl<DeducedTemplateArgument> &Deduced) { Expr *Value = @@ -332,7 +347,11 @@ static Sema::TemplateDeductionResult DeduceNullPtrTemplateArgument( } Deduced[NTTP->getIndex()] = Result; - return Sema::TDK_Success; + return S.getLangOpts().CPlusPlus1z + ? DeduceTemplateArgumentsByTypeMatch( + S, TemplateParams, NTTP->getType(), Value->getType(), Info, + Deduced, TDF_ParamWithReferenceType | TDF_SkipNonDependent) + : Sema::TDK_Success; } /// \brief Deduce the value of the given non-type template parameter @@ -341,6 +360,7 @@ static Sema::TemplateDeductionResult DeduceNullPtrTemplateArgument( /// \returns true if deduction succeeded, false otherwise. static Sema::TemplateDeductionResult DeduceNonTypeTemplateArgument(Sema &S, + TemplateParameterList *TemplateParams, NonTypeTemplateParmDecl *NTTP, Expr *Value, TemplateDeductionInfo &Info, @@ -363,7 +383,11 @@ DeduceNonTypeTemplateArgument(Sema &S, } Deduced[NTTP->getIndex()] = Result; - return Sema::TDK_Success; + return S.getLangOpts().CPlusPlus1z + ? DeduceTemplateArgumentsByTypeMatch( + S, TemplateParams, NTTP->getType(), Value->getType(), Info, + Deduced, TDF_ParamWithReferenceType | TDF_SkipNonDependent) + : Sema::TDK_Success; } /// \brief Deduce the value of the given non-type template parameter @@ -372,8 +396,9 @@ DeduceNonTypeTemplateArgument(Sema &S, /// \returns true if deduction succeeded, false otherwise. static Sema::TemplateDeductionResult DeduceNonTypeTemplateArgument(Sema &S, + TemplateParameterList *TemplateParams, NonTypeTemplateParmDecl *NTTP, - ValueDecl *D, + ValueDecl *D, QualType T, TemplateDeductionInfo &Info, SmallVectorImpl<DeducedTemplateArgument> &Deduced) { assert(NTTP->getDepth() == 0 && @@ -393,7 +418,11 @@ DeduceNonTypeTemplateArgument(Sema &S, } Deduced[NTTP->getIndex()] = Result; - return Sema::TDK_Success; + return S.getLangOpts().CPlusPlus1z + ? DeduceTemplateArgumentsByTypeMatch( + S, TemplateParams, NTTP->getType(), T, Info, Deduced, + TDF_ParamWithReferenceType | TDF_SkipNonDependent) + : Sema::TDK_Success; } static Sema::TemplateDeductionResult @@ -968,7 +997,8 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S, TemplateDeductionInfo &Info, SmallVectorImpl<DeducedTemplateArgument> &Deduced, unsigned TDF, - bool PartialOrdering) { + bool PartialOrdering, + bool DeducedFromArrayBound) { // We only want to look at the canonical types, since typedefs and // sugar are not part of template argument deduction. QualType Param = S.Context.getCanonicalType(ParamIn); @@ -1152,7 +1182,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S, if (RecanonicalizeArg) DeducedType = S.Context.getCanonicalType(DeducedType); - DeducedTemplateArgument NewDeduced(DeducedType); + DeducedTemplateArgument NewDeduced(DeducedType, DeducedFromArrayBound); DeducedTemplateArgument Result = checkDeducedTemplateArguments(S.Context, Deduced[Index], NewDeduced); @@ -1374,7 +1404,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S, if (const ConstantArrayType *ConstantArrayArg = dyn_cast<ConstantArrayType>(ArrayArg)) { llvm::APSInt Size(ConstantArrayArg->getSize()); - return DeduceNonTypeTemplateArgument(S, NTTP, Size, + return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP, Size, S.Context.getSizeType(), /*ArrayBound=*/true, Info, Deduced); @@ -1382,7 +1412,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S, if (const DependentSizedArrayType *DependentArrayArg = dyn_cast<DependentSizedArrayType>(ArrayArg)) if (DependentArrayArg->getSizeExpr()) - return DeduceNonTypeTemplateArgument(S, NTTP, + return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP, DependentArrayArg->getSizeExpr(), Info, Deduced); @@ -1654,8 +1684,8 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S, llvm::APSInt ArgSize(S.Context.getTypeSize(S.Context.IntTy), false); ArgSize = VectorArg->getNumElements(); - return DeduceNonTypeTemplateArgument(S, NTTP, ArgSize, S.Context.IntTy, - false, Info, Deduced); + return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP, ArgSize, + S.Context.IntTy, false, Info, Deduced); } if (const DependentSizedExtVectorType *VectorArg @@ -1674,7 +1704,8 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S, if (!NTTP) return Sema::TDK_Success; - return DeduceNonTypeTemplateArgument(S, NTTP, VectorArg->getSizeExpr(), + return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP, + VectorArg->getSizeExpr(), Info, Deduced); } @@ -1779,19 +1810,22 @@ DeduceTemplateArguments(Sema &S, if (NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr(Param.getAsExpr())) { if (Arg.getKind() == TemplateArgument::Integral) - return DeduceNonTypeTemplateArgument(S, NTTP, + return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP, Arg.getAsIntegral(), Arg.getIntegralType(), /*ArrayBound=*/false, Info, Deduced); if (Arg.getKind() == TemplateArgument::NullPtr) - return DeduceNullPtrTemplateArgument(S, NTTP, Arg.getNullPtrType(), + return DeduceNullPtrTemplateArgument(S, TemplateParams, NTTP, + Arg.getNullPtrType(), Info, Deduced); if (Arg.getKind() == TemplateArgument::Expression) - return DeduceNonTypeTemplateArgument(S, NTTP, Arg.getAsExpr(), - Info, Deduced); + return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP, + Arg.getAsExpr(), Info, Deduced); if (Arg.getKind() == TemplateArgument::Declaration) - return DeduceNonTypeTemplateArgument(S, NTTP, Arg.getAsDecl(), + return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP, + Arg.getAsDecl(), + Arg.getParamTypeForDecl(), Info, Deduced); Info.FirstArg = Param; @@ -3280,7 +3314,7 @@ DeduceFromInitializerList(Sema &S, TemplateParameterList *TemplateParams, ILE->getNumInits()); Result = DeduceNonTypeTemplateArgument( - S, NTTP, llvm::APSInt(Size), NTTP->getType(), + S, TemplateParams, NTTP, llvm::APSInt(Size), NTTP->getType(), /*ArrayBound=*/true, Info, Deduced); } } @@ -4700,6 +4734,11 @@ MarkUsedTemplateParameters(ASTContext &Ctx, if (NTTP->getDepth() == Depth) Used[NTTP->getIndex()] = true; + + // In C++1z mode, additional arguments may be deduced from the type of a + // non-type argument. + if (Ctx.getLangOpts().CPlusPlus1z) + MarkUsedTemplateParameters(Ctx, NTTP->getType(), OnlyDeduced, Depth, Used); } /// \brief Mark the template parameters that are used by the given @@ -4946,7 +4985,7 @@ MarkUsedTemplateParameters(ASTContext &Ctx, QualType T, case Type::UnaryTransform: if (!OnlyDeduced) MarkUsedTemplateParameters(Ctx, - cast<UnaryTransformType>(T)->getUnderlyingType(), + cast<UnaryTransformType>(T)->getUnderlyingType(), OnlyDeduced, Depth, Used); break; diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 50663f9f4ba..b715c13b077 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1179,8 +1179,8 @@ ExprResult TemplateInstantiator::transformNonTypeTemplateParmRef( cast<PackExpansionType>(parm->getType())->getPattern(), TemplateArgs, loc, parm->getDeclName()); } else { - type = SemaRef.SubstType(parm->getType(), TemplateArgs, - loc, parm->getDeclName()); + type = SemaRef.SubstType(VD ? arg.getParamTypeForDecl() : arg.getNullPtrType(), + TemplateArgs, loc, parm->getDeclName()); } assert(!type.isNull() && "type substitution failed for param type"); assert(!type->isDependentType() && "param type still dependent"); diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index dd833d72217..b0f18fd1bdc 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -1582,7 +1582,14 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { // template type parameter. Result = QualType(CorrespondingTemplateParam->getTypeForDecl(), 0); } else { - Result = Context.getAutoType(QualType(), AutoTypeKeyword::Auto, false); + // If auto appears in the declaration of a template parameter, treat + // the parameter as type-dependent. + bool IsDependent = + S.getLangOpts().CPlusPlus1z && + declarator.getContext() == Declarator::TemplateParamContext; + Result = Context.getAutoType(QualType(), + AutoTypeKeyword::Auto, + IsDependent); } break; @@ -2858,7 +2865,8 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, Error = 7; // Exception declaration break; case Declarator::TemplateParamContext: - Error = 8; // Template parameter + if (!SemaRef.getLangOpts().CPlusPlus1z) + Error = 8; // Template parameter break; case Declarator::BlockLiteralContext: Error = 9; // Block literal @@ -5540,7 +5548,7 @@ static bool handleObjCOwnershipTypeAttr(TypeProcessingState &state, if (Class->isArcWeakrefUnavailable()) { S.Diag(AttrLoc, diag::err_arc_unsupported_weak_class); S.Diag(ObjT->getInterfaceDecl()->getLocation(), - diag::note_class_declared); + diag::note_class_declared); } } } diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx11.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx11.cpp index 15c9c2560a9..aa4cc8c094e 100644 --- a/clang/test/SemaTemplate/temp_arg_nontype_cxx11.cpp +++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx11.cpp @@ -23,3 +23,7 @@ namespace CanonicalNullptr { B<int> b = MakeB<int>(); C<int> c = MakeC<int>(); } + +namespace Auto { + template<auto> struct A { }; // expected-error {{until C++1z}} +} diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp index 98b3aa4710e..59b05a14d3f 100644 --- a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp +++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp @@ -155,3 +155,113 @@ namespace PR24921 { template<int> void f(int); template<> void f<e>() {} } + +namespace Auto { + namespace Basic { + // simple auto + template<auto x> constexpr auto constant = x; // expected-note {{declared here}} + + auto v1 = constant<5>; + auto v2 = constant<true>; + auto v3 = constant<'a'>; + auto v4 = constant<2.5>; // expected-error {{cannot have type 'double'}} + + using T1 = decltype(v1); + using T1 = int; + using T2 = decltype(v2); + using T2 = bool; + using T3 = decltype(v3); + using T3 = char; + + // pointers + template<auto v> class B { }; + template<auto* p> class B<p> { }; + template<auto** pp> class B<pp> { }; + template<auto* p0> int &f(B<p0> b); + template<auto** pp0> float &f(B<pp0> b); + + int a, *b = &a; + int &r = f(B<&a>()); + float &s = f(B<&b>()); + } + + namespace Chained { + // chained template argument deduction + template<long n> struct C { }; + template<class T> struct D; + template<class T, T n> struct D<C<n>> + { + using Q = T; + }; + using DQ = long; + using DQ = D<C<short(2)>>::Q; + + // chained template argument deduction from an array bound + template<typename T> struct E; + template<typename T, T n> struct E<int[n]> { + using Q = T; + }; + using EQ = E<int[short(42)]>::Q; + using EQ = decltype(sizeof 0); + + template<int N> struct F; + template<typename T, T N> int foo(F<N> *) = delete; // expected-note {{explicitly deleted}} + void foo(void *); // expected-note {{candidate function}} + void bar(F<0> *p) { + foo(p); // expected-error {{deleted function}} + } + } + + namespace ArrayToPointer { + constexpr char s[] = "test"; + template<const auto* p> struct S { }; + S<s> p; + } + + namespace DecltypeAuto { + template<auto v> struct A { }; + template<decltype(auto) v> struct DA { }; + template<auto&> struct R { }; + + auto n = 0; // expected-note + {{declared here}} + A<n> a; // expected-error {{not a constant}} expected-note {{non-const variable 'n'}} + DA<n> da1; // expected-error {{not a constant}} expected-note {{non-const variable 'n'}} + DA<(n)> da2; + R<n> r; + } + + namespace Decomposition { + double foo(int, bool); + template<auto& f> struct fn_result_type; + + template<class R, class... Args, R (& f)(Args...)> + struct fn_result_type<f> + { + using type = R; + }; + + using R1 = fn_result_type<foo>::type; + using R1 = double; + } + + namespace Variadic { + template<auto... vs> struct value_list { }; + + using size_t = decltype(sizeof 0); + template<size_t n, class List> struct nth_element; + template<size_t n, class List> constexpr auto nth_element_v = nth_element<n, List>::value; + + template<size_t n, auto v0, auto... vs> + struct nth_element<n, value_list<v0, vs...>> + { + static constexpr auto value = nth_element<n - 1, value_list<vs...>>::value; + }; + template<auto v0, auto... vs> + struct nth_element<0, value_list<v0, vs...>> + { + static constexpr auto value = v0; + }; + + static_assert(nth_element_v<2, value_list<'a', 27U, false>> == false, "value mismatch"); + } +} |