Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit f027325

Browse files
committed
[C++17] Fix PR34970 - tweak overload resolution for class template deduction-guides in line with WG21's p0620r0.
In order to identify the copy deduction candidate, I considered two approaches: - attempt to determine whether an implicit guide is a copy deduction candidate by checking certain properties of its subsituted parameter during overload-resolution. - using one of the many bits (WillHaveBody) from FunctionDecl (that CXXDeductionGuideDecl inherits from) that are otherwise irrelevant for deduction guides After some brittle gymnastics w the first strategy, I settled on the second, although to avoid confusion and to give that bit a better name, i turned it into a member of an anonymous union. Given this identification 'bit', the tweak to overload resolution was a simple reordering of the deduction guide checks (in SemaOverload.cpp::isBetterOverloadCandidate), in-line with Jason Merrill's p0620r0 drafting which made it into the working paper. Concordant with that, I made sure the copy deduction candidate is always added. References: See https://bugs.llvm.org/show_bug.cgi?id=34970 See http://wg21.link/p0620r0 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@316292 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent b997435 commit f027325

File tree

9 files changed

+96
-30
lines changed

9 files changed

+96
-30
lines changed

include/clang/AST/Decl.h

+12-4
Original file line numberDiff line numberDiff line change
@@ -1678,10 +1678,18 @@ class FunctionDecl : public DeclaratorDecl, public DeclContext,
16781678
/// skipped.
16791679
unsigned HasSkippedBody : 1;
16801680

1681-
/// Indicates if the function declaration will have a body, once we're done
1682-
/// parsing it.
1683-
unsigned WillHaveBody : 1;
1684-
1681+
protected:
1682+
// Since a Deduction Guide [C++17] will never have a body, we can share the
1683+
// storage, and use a different name.
1684+
union {
1685+
/// Indicates if the function declaration will have a body, once we're done
1686+
/// parsing it.
1687+
unsigned WillHaveBody : 1;
1688+
/// Indicates that the Deduction Guide is the implicitly generated 'copy
1689+
/// deduction candidate' (is used during overload resolution).
1690+
unsigned IsCopyDeductionCandidate : 1;
1691+
};
1692+
private:
16851693
/// \brief End part of this FunctionDecl's source range.
16861694
///
16871695
/// We could compute the full range in getSourceRange(). However, when we're

include/clang/AST/DeclCXX.h

+10
Original file line numberDiff line numberDiff line change
@@ -1881,6 +1881,10 @@ class CXXDeductionGuideDecl : public FunctionDecl {
18811881
if (EndLocation.isValid())
18821882
setRangeEnd(EndLocation);
18831883
IsExplicitSpecified = IsExplicit;
1884+
1885+
// IsCopyDeductionCandidate is a union variant member, so ensure it is the
1886+
// active member by storing to it.
1887+
IsCopyDeductionCandidate = false;
18841888
}
18851889

18861890
public:
@@ -1903,6 +1907,12 @@ class CXXDeductionGuideDecl : public FunctionDecl {
19031907
return getDeclName().getCXXDeductionGuideTemplate();
19041908
}
19051909

1910+
void setIsCopyDeductionCandidate() {
1911+
IsCopyDeductionCandidate = true;
1912+
}
1913+
1914+
bool isCopyDeductionCandidate() const { return IsCopyDeductionCandidate; }
1915+
19061916
// Implement isa/cast/dyncast/etc.
19071917
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
19081918
static bool classofKind(Kind K) { return K == CXXDeductionGuide; }

lib/Sema/SemaOverload.cpp

+17-6
Original file line numberDiff line numberDiff line change
@@ -8965,12 +8965,6 @@ bool clang::isBetterOverloadCandidate(
89658965
// C++14 [over.match.best]p1 section 2 bullet 3.
89668966
}
89678967

8968-
// -- F1 is generated from a deduction-guide and F2 is not
8969-
auto *Guide1 = dyn_cast_or_null<CXXDeductionGuideDecl>(Cand1.Function);
8970-
auto *Guide2 = dyn_cast_or_null<CXXDeductionGuideDecl>(Cand2.Function);
8971-
if (Guide1 && Guide2 && Guide1->isImplicit() != Guide2->isImplicit())
8972-
return Guide2->isImplicit();
8973-
89748968
// -- F1 is a non-template function and F2 is a function template
89758969
// specialization, or, if not that,
89768970
bool Cand1IsSpecialization = Cand1.Function &&
@@ -9015,6 +9009,23 @@ bool clang::isBetterOverloadCandidate(
90159009
// Inherited from sibling base classes: still ambiguous.
90169010
}
90179011

9012+
// Check C++17 tie-breakers for deduction guides.
9013+
{
9014+
auto *Guide1 = dyn_cast_or_null<CXXDeductionGuideDecl>(Cand1.Function);
9015+
auto *Guide2 = dyn_cast_or_null<CXXDeductionGuideDecl>(Cand2.Function);
9016+
if (Guide1 && Guide2) {
9017+
// -- F1 is generated from a deduction-guide and F2 is not
9018+
if (Guide1->isImplicit() != Guide2->isImplicit())
9019+
return Guide2->isImplicit();
9020+
9021+
// -- F1 is the copy deduction candidate(16.3.1.8) and F2 is not
9022+
if (Guide1->isCopyDeductionCandidate())
9023+
return true;
9024+
}
9025+
}
9026+
9027+
9028+
90189029
// FIXME: Work around a defect in the C++17 guaranteed copy elision wording,
90199030
// as combined with the resolution to CWG issue 243.
90209031
//

lib/Sema/SemaTemplate.cpp

+11-10
Original file line numberDiff line numberDiff line change
@@ -1837,7 +1837,6 @@ void Sema::DeclareImplicitDeductionGuides(TemplateDecl *Template,
18371837
// for which some class template parameter without a default argument never
18381838
// appears in a deduced context).
18391839
bool AddedAny = false;
1840-
bool AddedCopyOrMove = false;
18411840
for (NamedDecl *D : LookupConstructors(Transform.Primary)) {
18421841
D = D->getUnderlyingDecl();
18431842
if (D->isInvalidDecl() || D->isImplicit())
@@ -1854,20 +1853,22 @@ void Sema::DeclareImplicitDeductionGuides(TemplateDecl *Template,
18541853

18551854
Transform.transformConstructor(FTD, CD);
18561855
AddedAny = true;
1857-
1858-
AddedCopyOrMove |= CD->isCopyOrMoveConstructor();
18591856
}
18601857

1861-
// Synthesize an X() -> X<...> guide if there were no declared constructors.
1862-
// FIXME: The standard doesn't say (how) to do this.
1858+
// C++17 [over.match.class.deduct]
1859+
// -- If C is not defined or does not declare any constructors, an
1860+
// additional function template derived as above from a hypothetical
1861+
// constructor C().
18631862
if (!AddedAny)
18641863
Transform.buildSimpleDeductionGuide(None);
18651864

1866-
// Synthesize an X(X<...>) -> X<...> guide if there was no declared constructor
1867-
// resembling a copy or move constructor.
1868-
// FIXME: The standard doesn't say (how) to do this.
1869-
if (!AddedCopyOrMove)
1870-
Transform.buildSimpleDeductionGuide(Transform.DeducedType);
1865+
// -- An additional function template derived as above from a hypothetical
1866+
// constructor C(C), called the copy deduction candidate.
1867+
cast<CXXDeductionGuideDecl>(
1868+
cast<FunctionTemplateDecl>(
1869+
Transform.buildSimpleDeductionGuide(Transform.DeducedType))
1870+
->getTemplatedDecl())
1871+
->setIsCopyDeductionCandidate();
18711872
}
18721873

18731874
/// \brief Diagnose the presence of a default template argument on a

lib/Sema/SemaTemplateInstantiateDecl.cpp

+6-4
Original file line numberDiff line numberDiff line change
@@ -1651,11 +1651,13 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D,
16511651
}
16521652

16531653
FunctionDecl *Function;
1654-
if (auto *DGuide = dyn_cast<CXXDeductionGuideDecl>(D))
1654+
if (auto *DGuide = dyn_cast<CXXDeductionGuideDecl>(D)) {
16551655
Function = CXXDeductionGuideDecl::Create(
1656-
SemaRef.Context, DC, D->getInnerLocStart(), DGuide->isExplicit(),
1657-
D->getNameInfo(), T, TInfo, D->getSourceRange().getEnd());
1658-
else {
1656+
SemaRef.Context, DC, D->getInnerLocStart(), DGuide->isExplicit(),
1657+
D->getNameInfo(), T, TInfo, D->getSourceRange().getEnd());
1658+
if (DGuide->isCopyDeductionCandidate())
1659+
cast<CXXDeductionGuideDecl>(Function)->setIsCopyDeductionCandidate();
1660+
} else {
16591661
Function = FunctionDecl::Create(
16601662
SemaRef.Context, DC, D->getInnerLocStart(), D->getNameInfo(), T, TInfo,
16611663
D->getCanonicalDecl()->getStorageClass(), D->isInlineSpecified(),

lib/Serialization/ASTReaderDecl.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -1863,6 +1863,8 @@ ASTDeclReader::VisitCXXRecordDeclImpl(CXXRecordDecl *D) {
18631863

18641864
void ASTDeclReader::VisitCXXDeductionGuideDecl(CXXDeductionGuideDecl *D) {
18651865
VisitFunctionDecl(D);
1866+
if (Record.readInt())
1867+
D->setIsCopyDeductionCandidate();
18661868
}
18671869

18681870
void ASTDeclReader::VisitCXXMethodDecl(CXXMethodDecl *D) {

lib/Serialization/ASTWriterDecl.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,7 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
612612

613613
void ASTDeclWriter::VisitCXXDeductionGuideDecl(CXXDeductionGuideDecl *D) {
614614
VisitFunctionDecl(D);
615+
Record.push_back(D->isCopyDeductionCandidate());
615616
Code = serialization::DECL_CXX_DEDUCTION_GUIDE;
616617
}
617618

test/CXX/over/over.match/over.match.best/p1.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@ namespace deduction_guide_example {
2525

2626
// FIXME: The standard's example is wrong; we add a remove_ref<...> here to
2727
// fix it.
28-
template<typename T, int N = remove_ref<T>::value> A(T&&, int*) -> A<T>;
28+
template<typename T, int N = remove_ref<T>::value> A(T&&, int*) -> A<T**>;
2929
A a{1, 0};
3030
extern A<int> a;
31-
A b{a, 0};
31+
A b{a, 0}; // uses the implicit ctor that is more specialized
3232

3333
A<int> *pa = &a;
34-
A<A<int>&> *pb = &b;
34+
A<int> *pb = &b;
3535
}
3636

3737
// Partial ordering of function template specializations will be tested

test/CXX/over/over.match/over.match.funcs/over.match.class.deduct/p2.cpp

+34-3
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,26 @@ namespace Explicit {
66
template<typename T> struct A {
77
A(T);
88
A(T*);
9+
A(...);
910
};
1011
template<typename T> A(T) -> A<T>;
11-
template<typename T> explicit A(T*) -> A<T>; // expected-note {{explicit}}
12+
template<typename T> explicit A(T*) -> A<T**>; // expected-note {{explicit}}
1213

1314
int *p;
1415
A a(p);
1516
A b = p;
1617
A c{p};
1718
A d = {p}; // expected-error {{selected an explicit deduction guide}}
1819

19-
using X = A<int>;
20-
using Y = A<int*>;
20+
using X = A<int**>;
21+
using Y = A<int>; // uses the implicit guide, being more specialized than the eligible user-defined deduction guides.
2122

2223
using X = decltype(a);
2324
using Y = decltype(b);
2425
using X = decltype(c);
2526
}
2627

28+
2729
namespace std {
2830
template<typename T> struct initializer_list {
2931
const T *ptr;
@@ -54,3 +56,32 @@ namespace p0702r1 {
5456
// between X<int> and X<float>.
5557
X xz = {z}; // expected-error {{no viable constructor or deduction guide}}
5658
}
59+
namespace pr34970 {
60+
//https://bugs.llvm.org/show_bug.cgi?id=34970
61+
62+
template <typename X, typename Y> struct IsSame {
63+
static constexpr bool value = false;
64+
};
65+
66+
template <typename Z> struct IsSame<Z, Z> {
67+
static constexpr bool value = true;
68+
};
69+
70+
template <typename T> struct Optional {
71+
template <typename U> Optional(U&&) { }
72+
};
73+
74+
template <typename A> Optional(A) -> Optional<A>;
75+
76+
int main() {
77+
Optional opt(1729);
78+
Optional dupe(opt);
79+
80+
static_assert(IsSame<decltype(opt), Optional<int>>::value);
81+
static_assert(IsSame<decltype(dupe), Optional<int>>::value);
82+
static_assert(!IsSame<decltype(dupe), Optional<Optional<int>>>::value);
83+
return 0;
84+
}
85+
86+
87+
}

0 commit comments

Comments
 (0)