Author: rsmith Date: Wed Sep 28 18:55:27 2016 New Revision: 282651 URL: http://llvm.org/viewvc/llvm-project?rev=282651&view=rev Log: P0127R2: Support type deduction for types of non-type template parameters in C++1z.
Patch by James Touton! Some bugfixes and rebasing by me. Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td cfe/trunk/lib/Lex/PPMacroExpansion.cpp cfe/trunk/lib/Sema/SemaTemplate.cpp cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp cfe/trunk/lib/Sema/SemaType.cpp cfe/trunk/test/SemaTemplate/temp_arg_nontype_cxx11.cpp cfe/trunk/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=282651&r1=282650&r2=282651&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Wed Sep 28 18:55:27 2016 @@ -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_decl "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 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">; Modified: cfe/trunk/lib/Lex/PPMacroExpansion.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/PPMacroExpansion.cpp?rev=282651&r1=282650&r2=282651&view=diff ============================================================================== --- cfe/trunk/lib/Lex/PPMacroExpansion.cpp (original) +++ cfe/trunk/lib/Lex/PPMacroExpansion.cpp Wed Sep 28 18:55:27 2016 @@ -1202,6 +1202,8 @@ static bool HasFeature(const Preprocesso .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) Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=282651&r1=282650&r2=282651&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaTemplate.cpp (original) +++ cfe/trunk/lib/Sema/SemaTemplate.cpp Wed Sep 28 18:55:27 2016 @@ -730,7 +730,13 @@ Sema::CheckNonTypeTemplateParameterType( 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(N 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(NamedDec if (InstantiationFunction->isDeleted()) { assert(InstantiationFunction->getCanonicalDecl() == InstantiationFunction); - InstantiationFunction->setDeletedAsWritten(false); + InstantiationFunction->setDeletedAsWritten(false); } } Modified: cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp?rev=282651&r1=282650&r2=282651&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp (original) +++ cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp Wed Sep 28 18:55:27 2016 @@ -100,7 +100,8 @@ DeduceTemplateArgumentsByTypeMatch(Sema 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.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 /// \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 Ded } 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 Ded } 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 Ded /// \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 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 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 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 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 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 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, Templ 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 &C 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 &C case Type::UnaryTransform: if (!OnlyDeduced) MarkUsedTemplateParameters(Ctx, - cast<UnaryTransformType>(T)->getUnderlyingType(), + cast<UnaryTransformType>(T)->getUnderlyingType(), OnlyDeduced, Depth, Used); break; Modified: cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp?rev=282651&r1=282650&r2=282651&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp (original) +++ cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp Wed Sep 28 18:55:27 2016 @@ -1179,8 +1179,8 @@ ExprResult TemplateInstantiator::transfo 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"); Modified: cfe/trunk/lib/Sema/SemaType.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaType.cpp?rev=282651&r1=282650&r2=282651&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaType.cpp (original) +++ cfe/trunk/lib/Sema/SemaType.cpp Wed Sep 28 18:55:27 2016 @@ -1582,7 +1582,14 @@ static QualType ConvertDeclSpecToType(Ty // 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 GetDeclSpecTypeForDeclar 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( 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); } } } Modified: cfe/trunk/test/SemaTemplate/temp_arg_nontype_cxx11.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/temp_arg_nontype_cxx11.cpp?rev=282651&r1=282650&r2=282651&view=diff ============================================================================== --- cfe/trunk/test/SemaTemplate/temp_arg_nontype_cxx11.cpp (original) +++ cfe/trunk/test/SemaTemplate/temp_arg_nontype_cxx11.cpp Wed Sep 28 18:55:27 2016 @@ -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}} +} Modified: cfe/trunk/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp?rev=282651&r1=282650&r2=282651&view=diff ============================================================================== --- cfe/trunk/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp (original) +++ cfe/trunk/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp Wed Sep 28 18:55:27 2016 @@ -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"); + } +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits