From: Richard Smith Date: Wed, 3 Feb 2016 20:40:30 +0000 (+0000) Subject: Ensure that we substitute into the declaration of a template parameter pack X-Git-Tag: llvmorg-3.9.0-rc1~15193 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=df18ee9620617192705da8791d4b63209e42bae7;p=platform%2Fupstream%2Fllvm.git Ensure that we substitute into the declaration of a template parameter pack (that is not a pack expansion) during template argument deduction, even if we deduced that the pack would be empty. llvm-svn: 259688 --- diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 2dc4fdb..0b090c8 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2119,8 +2119,25 @@ ConvertDeducedTemplateArgument(Sema &S, NamedDecl *Param, PackedArgsBuilder.push_back(Output.pop_back_val()); } - // FIXME: If the pack is empty and this is a template template parameter, - // we still need to substitute into the parameter itself. + // If the pack is empty, we still need to substitute into the parameter + // itself, in case that substitution fails. For non-type parameters, we did + // this above. For type parameters, no substitution is ever required. + auto *TTP = dyn_cast(Param); + if (TTP && PackedArgsBuilder.empty()) { + // Set up a template instantiation context. + LocalInstantiationScope Scope(S); + Sema::InstantiatingTemplate Inst(S, Template->getLocation(), Template, + TTP, Output, + Template->getSourceRange()); + if (Inst.isInvalid()) + return true; + + TemplateArgumentList TemplateArgs(TemplateArgumentList::OnStack, + Output.data(), Output.size()); + if (!S.SubstDecl(TTP, S.CurContext, + MultiLevelTemplateArgumentList(TemplateArgs))) + return true; + } // Create the resulting argument pack. Output.push_back( @@ -2808,11 +2825,22 @@ Sema::FinishTemplateArgumentDeduction(FunctionTemplateDecl *FunctionTemplate, Builder.push_back(TemplateArgument( llvm::makeArrayRef(ExplicitArgs, NumExplicitArgs))); - // Forget the partially-substituted pack; it's substitution is now + // Forget the partially-substituted pack; its substitution is now // complete. CurrentInstantiationScope->ResetPartiallySubstitutedPack(); } else { - Builder.push_back(TemplateArgument::getEmptyPack()); + // Go through the motions of checking the empty argument pack against + // the parameter pack. + DeducedTemplateArgument DeducedPack(TemplateArgument::getEmptyPack()); + if (ConvertDeducedTemplateArgument(*this, Param, DeducedPack, + FunctionTemplate, Info, true, + Builder)) { + Info.Param = makeTemplateParameter(Param); + // FIXME: These template arguments are temporary. Free them! + Info.reset(TemplateArgumentList::CreateCopy(Context, Builder.data(), + Builder.size())); + return TDK_SubstitutionFailure; + } } continue; } diff --git a/clang/test/CXX/temp/temp.fct.spec/temp.deduct/p7.cpp b/clang/test/CXX/temp/temp.fct.spec/temp.deduct/p7.cpp new file mode 100644 index 0000000..bc074ba --- /dev/null +++ b/clang/test/CXX/temp/temp.fct.spec/temp.deduct/p7.cpp @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -std=c++11 -verify %s + +struct Q { typedef int type; }; + +// "The substitution occurs in all types and expressions that are used in [...] +// template parameter declarations." In particular, we must substitute into the +// type of a parameter pack that is not a pack expansion, even if we know the +// corresponding argument pack is empty. +template void a(T); +int &a(...); +int &a_disabled = a(0); +int &a_enabled = a(Q()); // expected-error {{cannot bind to a temporary of type 'void'}} + +template class ...X> void b(T); +int &b(...); +int &b_disabled = b(0); +int &b_enabled = b(Q()); // expected-error {{cannot bind to a temporary of type 'void'}} + +template class ...X> void c(T); +int &c(...); +int &c_disabled = c(0); +int &c_enabled = c(Q()); // expected-error {{cannot bind to a temporary of type 'void'}}