forked from OSchip/llvm-project
[c++] implements DR692, DR1395 and tentatively DR1432, about partial ordering of variadic template partial specialization or function template
DR692 handles two cases: pack expansion (for class/var template) and function parameter pack. The former needs DR1432 as a fix, and the latter needs DR1395 as a fix. However, DR1432 has not yet made a wording change. so I made a tentative fix for DR1432 with the same spirit as DR1395. Reviewed By: aaron.ballman, erichkeane, #clang-language-wg Differential Revision: https://reviews.llvm.org/D128745
This commit is contained in:
parent
40ba78679d
commit
6afcc4a459
|
@ -135,6 +135,9 @@ C2x Feature Support
|
|||
C++ Language Changes in Clang
|
||||
-----------------------------
|
||||
|
||||
- Implemented DR692, DR1395 and DR1432. Use the ``-fclang-abi-compat=14`` option
|
||||
to get the old partial ordering behavior regarding packs.
|
||||
|
||||
C++20 Feature Support
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace clang {
|
||||
|
@ -1100,6 +1101,18 @@ DeduceTemplateArguments(Sema &S,
|
|||
return Result;
|
||||
}
|
||||
|
||||
// DR692, DR1395
|
||||
// C++0x [temp.deduct.type]p10:
|
||||
// If the parameter-declaration corresponding to P_i ...
|
||||
// During partial ordering, if Ai was originally a function parameter pack:
|
||||
// - if P does not contain a function parameter type corresponding to Ai then
|
||||
// Ai is ignored;
|
||||
bool ClangABICompat14 = S.Context.getLangOpts().getClangABICompat() <=
|
||||
LangOptions::ClangABI::Ver14;
|
||||
if (!ClangABICompat14 && PartialOrdering && ArgIdx + 1 == NumArgs &&
|
||||
isa<PackExpansionType>(Args[ArgIdx]))
|
||||
return Sema::TDK_Success;
|
||||
|
||||
// Make sure we don't have any extra arguments.
|
||||
if (ArgIdx < NumArgs)
|
||||
return Sema::TDK_MiscellaneousDeductionFailure;
|
||||
|
@ -1755,7 +1768,7 @@ static Sema::TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
|
|||
if (auto Result = DeduceTemplateArguments(
|
||||
S, TemplateParams, FPP->param_type_begin(), FPP->getNumParams(),
|
||||
FPA->param_type_begin(), FPA->getNumParams(), Info, Deduced,
|
||||
TDF & TDF_TopLevelParameterTypeList))
|
||||
TDF & TDF_TopLevelParameterTypeList, PartialOrdering))
|
||||
return Result;
|
||||
|
||||
if (TDF & TDF_AllowCompatibleFunctionType)
|
||||
|
@ -2422,6 +2435,7 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
|
|||
static bool isSameTemplateArg(ASTContext &Context,
|
||||
TemplateArgument X,
|
||||
const TemplateArgument &Y,
|
||||
bool PartialOrdering,
|
||||
bool PackExpansionMatchesPack = false) {
|
||||
// If we're checking deduced arguments (X) against original arguments (Y),
|
||||
// we will have flattened packs to non-expansions in X.
|
||||
|
@ -2463,16 +2477,30 @@ static bool isSameTemplateArg(ASTContext &Context,
|
|||
}
|
||||
|
||||
case TemplateArgument::Pack:
|
||||
if (X.pack_size() != Y.pack_size())
|
||||
return false;
|
||||
|
||||
for (TemplateArgument::pack_iterator XP = X.pack_begin(),
|
||||
XPEnd = X.pack_end(),
|
||||
YP = Y.pack_begin();
|
||||
XP != XPEnd; ++XP, ++YP)
|
||||
if (!isSameTemplateArg(Context, *XP, *YP, PackExpansionMatchesPack))
|
||||
unsigned PackIterationSize = X.pack_size();
|
||||
if (X.pack_size() != Y.pack_size()) {
|
||||
if (!PartialOrdering)
|
||||
return false;
|
||||
|
||||
// C++0x [temp.deduct.type]p9:
|
||||
// During partial ordering, if Ai was originally a pack expansion:
|
||||
// - if P does not contain a template argument corresponding to Ai then
|
||||
// Ai is ignored;
|
||||
bool XHasMoreArg = X.pack_size() > Y.pack_size();
|
||||
if (!(XHasMoreArg && X.pack_elements().back().isPackExpansion()) &&
|
||||
!(!XHasMoreArg && Y.pack_elements().back().isPackExpansion()))
|
||||
return false;
|
||||
|
||||
if (XHasMoreArg)
|
||||
PackIterationSize = Y.pack_size();
|
||||
}
|
||||
|
||||
ArrayRef<TemplateArgument> XP = X.pack_elements();
|
||||
ArrayRef<TemplateArgument> YP = Y.pack_elements();
|
||||
for (unsigned i = 0; i < PackIterationSize; ++i)
|
||||
if (!isSameTemplateArg(Context, XP[i], YP[i], PartialOrdering,
|
||||
PackExpansionMatchesPack))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2875,7 +2903,8 @@ FinishTemplateArgumentDeduction(
|
|||
TemplateParameterList *TemplateParams = Template->getTemplateParameters();
|
||||
for (unsigned I = 0, E = TemplateParams->size(); I != E; ++I) {
|
||||
TemplateArgument InstArg = ConvertedInstArgs.data()[I];
|
||||
if (!isSameTemplateArg(S.Context, TemplateArgs[I], InstArg)) {
|
||||
if (!isSameTemplateArg(S.Context, TemplateArgs[I], InstArg,
|
||||
IsPartialOrdering)) {
|
||||
Info.Param = makeTemplateParameter(TemplateParams->getParam(I));
|
||||
Info.FirstArg = TemplateArgs[I];
|
||||
Info.SecondArg = InstArg;
|
||||
|
@ -2919,8 +2948,8 @@ static Sema::TemplateDeductionResult FinishTemplateArgumentDeduction(
|
|||
TemplateParameterList *TemplateParams = Template->getTemplateParameters();
|
||||
for (unsigned I = 0, E = TemplateParams->size(); I != E; ++I) {
|
||||
TemplateArgument InstArg = Builder[I];
|
||||
if (!isSameTemplateArg(S.Context, TemplateArgs[I], InstArg,
|
||||
/*PackExpansionMatchesPack*/true)) {
|
||||
if (!isSameTemplateArg(S.Context, TemplateArgs[I], InstArg, PartialOrdering,
|
||||
/*PackExpansionMatchesPack*/ true)) {
|
||||
Info.Param = makeTemplateParameter(TemplateParams->getParam(I));
|
||||
Info.FirstArg = TemplateArgs[I];
|
||||
Info.SecondArg = InstArg;
|
||||
|
@ -5109,27 +5138,6 @@ static bool isAtLeastAsSpecializedAs(Sema &S,
|
|||
return true;
|
||||
}
|
||||
|
||||
/// Determine whether this a function template whose parameter-type-list
|
||||
/// ends with a function parameter pack.
|
||||
static bool isVariadicFunctionTemplate(FunctionTemplateDecl *FunTmpl) {
|
||||
FunctionDecl *Function = FunTmpl->getTemplatedDecl();
|
||||
unsigned NumParams = Function->getNumParams();
|
||||
if (NumParams == 0)
|
||||
return false;
|
||||
|
||||
ParmVarDecl *Last = Function->getParamDecl(NumParams - 1);
|
||||
if (!Last->isParameterPack())
|
||||
return false;
|
||||
|
||||
// Make sure that no previous parameter is a parameter pack.
|
||||
while (--NumParams > 0) {
|
||||
if (Function->getParamDecl(NumParams - 1)->isParameterPack())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Returns the more specialized function template according
|
||||
/// to the rules of function template partial ordering (C++ [temp.func.order]).
|
||||
///
|
||||
|
@ -5188,13 +5196,22 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
|
|||
if (!Better1 && !Better2) // Neither is better than the other
|
||||
return JudgeByConstraints();
|
||||
|
||||
// FIXME: This mimics what GCC implements, but doesn't match up with the
|
||||
// proposed resolution for core issue 692. This area needs to be sorted out,
|
||||
// but for now we attempt to maintain compatibility.
|
||||
bool Variadic1 = isVariadicFunctionTemplate(FT1);
|
||||
bool Variadic2 = isVariadicFunctionTemplate(FT2);
|
||||
if (Variadic1 != Variadic2)
|
||||
return Variadic1? FT2 : FT1;
|
||||
// C++ [temp.deduct.partial]p11:
|
||||
// ... and if G has a trailing function parameter pack for which F does not
|
||||
// have a corresponding parameter, and if F does not have a trailing
|
||||
// function parameter pack, then F is more specialized than G.
|
||||
FunctionDecl *FD1 = FT1->getTemplatedDecl();
|
||||
FunctionDecl *FD2 = FT2->getTemplatedDecl();
|
||||
unsigned NumParams1 = FD1->getNumParams();
|
||||
unsigned NumParams2 = FD2->getNumParams();
|
||||
bool Variadic1 = NumParams1 && FD1->parameters().back()->isParameterPack();
|
||||
bool Variadic2 = NumParams2 && FD2->parameters().back()->isParameterPack();
|
||||
if (Variadic1 != Variadic2) {
|
||||
if (Variadic1 && NumParams1 > NumParams2)
|
||||
return FT2;
|
||||
if (Variadic2 && NumParams2 > NumParams1)
|
||||
return FT1;
|
||||
}
|
||||
|
||||
return JudgeByConstraints();
|
||||
}
|
||||
|
@ -5373,6 +5390,109 @@ static bool isAtLeastAsSpecializedAs(Sema &S, QualType T1, QualType T2,
|
|||
return AtLeastAsSpecialized;
|
||||
}
|
||||
|
||||
namespace {
|
||||
// A dummy pass to return nullptr instead of P2 when performing "more
|
||||
// specialized than primary" check.
|
||||
struct GetP2 {
|
||||
template <typename T1, typename T2,
|
||||
std::enable_if_t<std::is_same<T1, T2>::value, bool> = true>
|
||||
T2 *operator()(T1 *, T2 *P2) {
|
||||
return P2;
|
||||
}
|
||||
template <typename T1, typename T2,
|
||||
std::enable_if_t<!std::is_same<T1, T2>::value, bool> = true>
|
||||
T1 *operator()(T1 *, T2 *) {
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
/// Returns the more specialized template specialization between T1/P1 and
|
||||
/// T2/P2.
|
||||
/// - If IsMoreSpecialThanPrimaryCheck is true, T1/P1 is the partial
|
||||
/// specialization and T2/P2 is the primary template.
|
||||
/// - otherwise, both T1/P1 and T2/P2 are the partial specialization.
|
||||
///
|
||||
/// \param T1 the type of the first template partial specialization
|
||||
///
|
||||
/// \param T2 if IsMoreSpecialThanPrimaryCheck is true, the type of the second
|
||||
/// template partial specialization; otherwise, the type of the
|
||||
/// primary template.
|
||||
///
|
||||
/// \param P1 the first template partial specialization
|
||||
///
|
||||
/// \param P2 if IsMoreSpecialThanPrimaryCheck is true, the second template
|
||||
/// partial specialization; otherwise, the primary template.
|
||||
///
|
||||
/// \returns - If IsMoreSpecialThanPrimaryCheck is true, returns P1 if P1 is
|
||||
/// more specialized, returns nullptr if P1 is not more specialized.
|
||||
/// - otherwise, returns the more specialized template partial
|
||||
/// specialization. If neither partial specialization is more
|
||||
/// specialized, returns NULL.
|
||||
template <typename TemplateLikeDecl, typename PrimaryDel>
|
||||
static TemplateLikeDecl *
|
||||
getMoreSpecialized(Sema &S, QualType T1, QualType T2, TemplateLikeDecl *P1,
|
||||
PrimaryDel *P2, TemplateDeductionInfo &Info) {
|
||||
constexpr bool IsMoreSpecialThanPrimaryCheck =
|
||||
!std::is_same<TemplateLikeDecl, PrimaryDel>::value;
|
||||
|
||||
bool Better1 = isAtLeastAsSpecializedAs(S, T1, T2, P2, Info);
|
||||
if (IsMoreSpecialThanPrimaryCheck && !Better1)
|
||||
return nullptr;
|
||||
|
||||
bool Better2 = isAtLeastAsSpecializedAs(S, T2, T1, P1, Info);
|
||||
if (IsMoreSpecialThanPrimaryCheck && !Better2)
|
||||
return P1;
|
||||
|
||||
if (!Better1 && !Better2)
|
||||
return nullptr;
|
||||
|
||||
if (Better1 && Better2) {
|
||||
bool ClangABICompat14 = S.Context.getLangOpts().getClangABICompat() <=
|
||||
LangOptions::ClangABI::Ver14;
|
||||
if (!ClangABICompat14) {
|
||||
// Consider this a fix for CWG1432. Similar to the fix for CWG1395.
|
||||
auto *TST1 = T1->castAs<TemplateSpecializationType>();
|
||||
auto *TST2 = T2->castAs<TemplateSpecializationType>();
|
||||
if (TST1->getNumArgs()) {
|
||||
const TemplateArgument &TA1 = TST1->template_arguments().back();
|
||||
if (TA1.getKind() == TemplateArgument::Pack) {
|
||||
assert(TST1->getNumArgs() == TST2->getNumArgs());
|
||||
const TemplateArgument &TA2 = TST2->template_arguments().back();
|
||||
assert(TA2.getKind() == TemplateArgument::Pack);
|
||||
unsigned PackSize1 = TA1.pack_size();
|
||||
unsigned PackSize2 = TA2.pack_size();
|
||||
bool IsPackExpansion1 =
|
||||
PackSize1 && TA1.pack_elements().back().isPackExpansion();
|
||||
bool IsPackExpansion2 =
|
||||
PackSize2 && TA2.pack_elements().back().isPackExpansion();
|
||||
if (PackSize1 != PackSize2 && IsPackExpansion1 != IsPackExpansion2) {
|
||||
if (PackSize1 > PackSize2 && IsPackExpansion1)
|
||||
return GetP2()(P1, P2);
|
||||
if (PackSize1 < PackSize2 && IsPackExpansion2)
|
||||
return P1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
llvm::SmallVector<const Expr *, 3> AC1, AC2;
|
||||
P1->getAssociatedConstraints(AC1);
|
||||
P2->getAssociatedConstraints(AC2);
|
||||
bool AtLeastAsConstrained1, AtLeastAsConstrained2;
|
||||
if (S.IsAtLeastAsConstrained(P1, AC1, P2, AC2, AtLeastAsConstrained1) ||
|
||||
(IsMoreSpecialThanPrimaryCheck && !AtLeastAsConstrained1))
|
||||
return nullptr;
|
||||
if (S.IsAtLeastAsConstrained(P2, AC2, P1, AC1, AtLeastAsConstrained2))
|
||||
return nullptr;
|
||||
if (AtLeastAsConstrained1 == AtLeastAsConstrained2)
|
||||
return nullptr;
|
||||
return AtLeastAsConstrained1 ? P1 : GetP2()(P1, P2);
|
||||
}
|
||||
|
||||
return Better1 ? P1 : GetP2()(P1, P2);
|
||||
}
|
||||
|
||||
/// Returns the more specialized class template partial specialization
|
||||
/// according to the rules of partial ordering of class template partial
|
||||
/// specializations (C++ [temp.class.order]).
|
||||
|
@ -5392,26 +5512,7 @@ Sema::getMoreSpecializedPartialSpecialization(
|
|||
QualType PT2 = PS2->getInjectedSpecializationType();
|
||||
|
||||
TemplateDeductionInfo Info(Loc);
|
||||
bool Better1 = isAtLeastAsSpecializedAs(*this, PT1, PT2, PS2, Info);
|
||||
bool Better2 = isAtLeastAsSpecializedAs(*this, PT2, PT1, PS1, Info);
|
||||
|
||||
if (!Better1 && !Better2)
|
||||
return nullptr;
|
||||
if (Better1 && Better2) {
|
||||
llvm::SmallVector<const Expr *, 3> AC1, AC2;
|
||||
PS1->getAssociatedConstraints(AC1);
|
||||
PS2->getAssociatedConstraints(AC2);
|
||||
bool AtLeastAsConstrained1, AtLeastAsConstrained2;
|
||||
if (IsAtLeastAsConstrained(PS1, AC1, PS2, AC2, AtLeastAsConstrained1))
|
||||
return nullptr;
|
||||
if (IsAtLeastAsConstrained(PS2, AC2, PS1, AC1, AtLeastAsConstrained2))
|
||||
return nullptr;
|
||||
if (AtLeastAsConstrained1 == AtLeastAsConstrained2)
|
||||
return nullptr;
|
||||
return AtLeastAsConstrained1 ? PS1 : PS2;
|
||||
}
|
||||
|
||||
return Better1 ? PS1 : PS2;
|
||||
return getMoreSpecialized(*this, PT1, PT2, PS1, PS2, Info);
|
||||
}
|
||||
|
||||
bool Sema::isMoreSpecializedThanPrimary(
|
||||
|
@ -5419,24 +5520,12 @@ bool Sema::isMoreSpecializedThanPrimary(
|
|||
ClassTemplateDecl *Primary = Spec->getSpecializedTemplate();
|
||||
QualType PrimaryT = Primary->getInjectedClassNameSpecialization();
|
||||
QualType PartialT = Spec->getInjectedSpecializationType();
|
||||
if (!isAtLeastAsSpecializedAs(*this, PartialT, PrimaryT, Primary, Info))
|
||||
return false;
|
||||
if (!isAtLeastAsSpecializedAs(*this, PrimaryT, PartialT, Spec, Info))
|
||||
return true;
|
||||
Info.clearSFINAEDiagnostic();
|
||||
llvm::SmallVector<const Expr *, 3> PrimaryAC, SpecAC;
|
||||
Primary->getAssociatedConstraints(PrimaryAC);
|
||||
Spec->getAssociatedConstraints(SpecAC);
|
||||
bool AtLeastAsConstrainedPrimary, AtLeastAsConstrainedSpec;
|
||||
if (IsAtLeastAsConstrained(Spec, SpecAC, Primary, PrimaryAC,
|
||||
AtLeastAsConstrainedSpec))
|
||||
return false;
|
||||
if (!AtLeastAsConstrainedSpec)
|
||||
return false;
|
||||
if (IsAtLeastAsConstrained(Primary, PrimaryAC, Spec, SpecAC,
|
||||
AtLeastAsConstrainedPrimary))
|
||||
return false;
|
||||
return !AtLeastAsConstrainedPrimary;
|
||||
|
||||
ClassTemplatePartialSpecializationDecl *MaybeSpec =
|
||||
getMoreSpecialized(*this, PartialT, PrimaryT, Spec, Primary, Info);
|
||||
if (MaybeSpec)
|
||||
Info.clearSFINAEDiagnostic();
|
||||
return MaybeSpec;
|
||||
}
|
||||
|
||||
VarTemplatePartialSpecializationDecl *
|
||||
|
@ -5456,26 +5545,7 @@ Sema::getMoreSpecializedPartialSpecialization(
|
|||
CanonTemplate, PS2->getTemplateArgs().asArray());
|
||||
|
||||
TemplateDeductionInfo Info(Loc);
|
||||
bool Better1 = isAtLeastAsSpecializedAs(*this, PT1, PT2, PS2, Info);
|
||||
bool Better2 = isAtLeastAsSpecializedAs(*this, PT2, PT1, PS1, Info);
|
||||
|
||||
if (!Better1 && !Better2)
|
||||
return nullptr;
|
||||
if (Better1 && Better2) {
|
||||
llvm::SmallVector<const Expr *, 3> AC1, AC2;
|
||||
PS1->getAssociatedConstraints(AC1);
|
||||
PS2->getAssociatedConstraints(AC2);
|
||||
bool AtLeastAsConstrained1, AtLeastAsConstrained2;
|
||||
if (IsAtLeastAsConstrained(PS1, AC1, PS2, AC2, AtLeastAsConstrained1))
|
||||
return nullptr;
|
||||
if (IsAtLeastAsConstrained(PS2, AC2, PS1, AC1, AtLeastAsConstrained2))
|
||||
return nullptr;
|
||||
if (AtLeastAsConstrained1 == AtLeastAsConstrained2)
|
||||
return nullptr;
|
||||
return AtLeastAsConstrained1 ? PS1 : PS2;
|
||||
}
|
||||
|
||||
return Better1 ? PS1 : PS2;
|
||||
return getMoreSpecialized(*this, PT1, PT2, PS1, PS2, Info);
|
||||
}
|
||||
|
||||
bool Sema::isMoreSpecializedThanPrimary(
|
||||
|
@ -5494,24 +5564,11 @@ bool Sema::isMoreSpecializedThanPrimary(
|
|||
QualType PartialT = Context.getTemplateSpecializationType(
|
||||
CanonTemplate, Spec->getTemplateArgs().asArray());
|
||||
|
||||
if (!isAtLeastAsSpecializedAs(*this, PartialT, PrimaryT, Primary, Info))
|
||||
return false;
|
||||
if (!isAtLeastAsSpecializedAs(*this, PrimaryT, PartialT, Spec, Info))
|
||||
return true;
|
||||
Info.clearSFINAEDiagnostic();
|
||||
llvm::SmallVector<const Expr *, 3> PrimaryAC, SpecAC;
|
||||
Primary->getAssociatedConstraints(PrimaryAC);
|
||||
Spec->getAssociatedConstraints(SpecAC);
|
||||
bool AtLeastAsConstrainedPrimary, AtLeastAsConstrainedSpec;
|
||||
if (IsAtLeastAsConstrained(Spec, SpecAC, Primary, PrimaryAC,
|
||||
AtLeastAsConstrainedSpec))
|
||||
return false;
|
||||
if (!AtLeastAsConstrainedSpec)
|
||||
return false;
|
||||
if (IsAtLeastAsConstrained(Primary, PrimaryAC, Spec, SpecAC,
|
||||
AtLeastAsConstrainedPrimary))
|
||||
return false;
|
||||
return !AtLeastAsConstrainedPrimary;
|
||||
VarTemplatePartialSpecializationDecl *MaybeSpec =
|
||||
getMoreSpecialized(*this, PartialT, PrimaryT, Spec, Primary, Info);
|
||||
if (MaybeSpec)
|
||||
Info.clearSFINAEDiagnostic();
|
||||
return MaybeSpec;
|
||||
}
|
||||
|
||||
bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs(
|
||||
|
|
|
@ -1080,19 +1080,18 @@ namespace dr687 { // dr687 (9 c++20, but the issue is still considered open)
|
|||
}
|
||||
|
||||
namespace dr692 { // dr692: no
|
||||
// Also see dr1395.
|
||||
|
||||
namespace temp_func_order_example2 {
|
||||
template <typename T, typename U> struct A {};
|
||||
template <typename T, typename U> void f(U, A<U, T> *p = 0); // expected-note {{candidate}}
|
||||
template <typename U> int &f(U, A<U, U> *p = 0); // expected-note {{candidate}}
|
||||
template <typename T> void g(T, T = T());
|
||||
template <typename T, typename... U> void g(T, U...); // expected-error 0-1{{C++11}}
|
||||
template <typename T> void g(T, T = T()); // expected-note {{candidate}}
|
||||
template <typename T, typename... U> void g(T, U...); // expected-note {{candidate}} expected-error 0-1{{C++11}}
|
||||
void h() {
|
||||
int &r = f<int>(42, (A<int, int> *)0);
|
||||
f<int>(42); // expected-error {{ambiguous}}
|
||||
// FIXME: We should reject this due to ambiguity between the pack and the
|
||||
// default argument. Only parameters with arguments are considered during
|
||||
// partial ordering of function templates.
|
||||
g(42);
|
||||
g(42); // expected-error {{ambiguous}}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1125,20 +1124,16 @@ namespace dr692 { // dr692: no
|
|||
template <class T1, class T2> class S<T1, const T2&> {};
|
||||
S<int, const int&> s;
|
||||
|
||||
// FIXME: This should select the first partial specialization. Deduction of
|
||||
// the second from the first should succeed, because we should ignore the
|
||||
// trailing pack in A with no corresponding P.
|
||||
template<class T, class... U> struct A; // expected-error 0-1{{C++11}}
|
||||
template<class T1, class T2, class... U> struct A<T1,T2*,U...>; // expected-note {{matches}} expected-error 0-1{{C++11}}
|
||||
template<class T1, class T2> struct A<T1,T2> {}; // expected-note {{matches}}
|
||||
template struct A<int, int*>; // expected-error {{ambiguous}}
|
||||
template<class T1, class T2, class... U> struct A<T1,T2*,U...> {}; // expected-error 0-1{{C++11}}
|
||||
template<class T1, class T2> struct A<T1,T2>;
|
||||
template struct A<int, int*>;
|
||||
}
|
||||
|
||||
namespace temp_deduct_type_example3 {
|
||||
// FIXME: This should select the first template, as in the case above.
|
||||
template<class T, class... U> void f(T*, U...){} // expected-note {{candidate}} expected-error 0-1{{C++11}}
|
||||
template<class T> void f(T){} // expected-note {{candidate}}
|
||||
template void f(int*); // expected-error {{ambiguous}}
|
||||
template<class T, class... U> void f(T*, U...){} // expected-error 0-1{{C++11}}
|
||||
template<class T> void f(T){}
|
||||
template void f(int*);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fclang-abi-compat=14 -DCLANG_ABI_COMPAT=14 %s -emit-llvm -disable-llvm-passes -o - | FileCheck %s --check-prefix=CHECK-14
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown-unknown %s -emit-llvm -disable-llvm-passes -o - | FileCheck %s
|
||||
|
||||
#if defined(CLANG_ABI_COMPAT) && CLANG_ABI_COMPAT <= 14
|
||||
|
||||
// CHECK-14: define dso_local void @_ZN24temp_func_order_example31hEi(i32 noundef %i)
|
||||
// CHECK-14-NEXT: entry:
|
||||
// CHECK-14-NEXT: %i.addr = alloca i32, align 4
|
||||
// CHECK-14-NEXT: %r = alloca ptr, align 8
|
||||
// CHECK-14-NEXT: store i32 %i, ptr %i.addr, align 4
|
||||
// CHECK-14-NEXT: %call = call noundef nonnull align 4 dereferenceable(4) ptr @_ZN24temp_func_order_example31gIiJEEERiPT_DpT0_(ptr noundef %i.addr)
|
||||
// CHECK-14-NEXT: store ptr %call, ptr %r, align 8
|
||||
// CHECK-14-NEXT: ret void
|
||||
|
||||
namespace temp_func_order_example3 {
|
||||
template <typename T, typename... U> int &g(T *, U...);
|
||||
template <typename T> void g(T);
|
||||
void h(int i) {
|
||||
int &r = g(&i);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// CHECK: %"struct.temp_deduct_type_example1::A" = type { i8 }
|
||||
|
||||
// CHECK: $_ZN25temp_deduct_type_example31fIiJEEEvPT_DpT0_ = comdat any
|
||||
|
||||
// CHECK: define dso_local void @_ZN25temp_deduct_type_example11fEv()
|
||||
// CHECK-NEXT: entry:
|
||||
// CHECK-NEXT: %a = alloca %"struct.temp_deduct_type_example1::A", align 1
|
||||
// CHECK-NEXT: ret void
|
||||
|
||||
// CHECK: define weak_odr void @_ZN25temp_deduct_type_example31fIiJEEEvPT_DpT0_(ptr noundef %0)
|
||||
// CHECK-NEXT: entry:
|
||||
// CHECK-NEXT: %.addr = alloca ptr, align 8
|
||||
// CHECK-NEXT: store ptr %0, ptr %.addr, align 8
|
||||
// CHECK-NEXT: ret void
|
||||
|
||||
namespace temp_deduct_type_example1 {
|
||||
template<class T, class... U> struct A;
|
||||
template<class T1, class T2, class... U> struct A<T1,T2*,U...> {};
|
||||
template<class T1, class T2> struct A<T1,T2>;
|
||||
template struct A<int, int*>;
|
||||
void f() { A<int, int*> a; }
|
||||
}
|
||||
|
||||
namespace temp_deduct_type_example3 {
|
||||
template<class T, class... U> void f(T*, U...){}
|
||||
template<class T> void f(T){}
|
||||
template void f(int*);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -4194,7 +4194,7 @@ and <I>POD class</I></td>
|
|||
<td><a href="https://wg21.link/cwg692">692</a></td>
|
||||
<td>C++11</td>
|
||||
<td>Partial ordering of variadic class template partial specializations</td>
|
||||
<td class="none" align="center">No</td>
|
||||
<td class="unreleased" align="center">Clang 16</td>
|
||||
</tr>
|
||||
<tr id="693">
|
||||
<td><a href="https://wg21.link/cwg693">693</a></td>
|
||||
|
@ -8178,7 +8178,7 @@ and <I>POD class</I></td>
|
|||
<td><a href="https://wg21.link/cwg1395">1395</a></td>
|
||||
<td>C++17</td>
|
||||
<td>Partial ordering of variadic templates reconsidered</td>
|
||||
<td class="none" align="center">Unknown</td>
|
||||
<td class="unreleased" align="center">Clang 16</td>
|
||||
</tr>
|
||||
<tr class="open" id="1396">
|
||||
<td><a href="https://wg21.link/cwg1396">1396</a></td>
|
||||
|
|
Loading…
Reference in New Issue