r316292 - [C++17] Fix PR34970 - tweak overload resolution for class template deduction-guides in line with WG21's p0620r0.

Faisal Vali via cfe-commits cfe-commits at lists.llvm.org
Sun Oct 22 07:45:08 PDT 2017


Author: faisalv
Date: Sun Oct 22 07:45:08 2017
New Revision: 316292

URL: http://llvm.org/viewvc/llvm-project?rev=316292&view=rev
Log:
[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

Modified:
    cfe/trunk/include/clang/AST/Decl.h
    cfe/trunk/include/clang/AST/DeclCXX.h
    cfe/trunk/lib/Sema/SemaOverload.cpp
    cfe/trunk/lib/Sema/SemaTemplate.cpp
    cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp
    cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
    cfe/trunk/lib/Serialization/ASTWriterDecl.cpp
    cfe/trunk/test/CXX/over/over.match/over.match.best/p1.cpp
    cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.class.deduct/p2.cpp

Modified: cfe/trunk/include/clang/AST/Decl.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=316292&r1=316291&r2=316292&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Decl.h (original)
+++ cfe/trunk/include/clang/AST/Decl.h Sun Oct 22 07:45:08 2017
@@ -1678,10 +1678,18 @@ private:
   /// skipped.
   unsigned HasSkippedBody : 1;
 
-  /// Indicates if the function declaration will have a body, once we're done
-  /// parsing it.
-  unsigned WillHaveBody : 1;
-
+protected:
+  // Since a Deduction Guide [C++17] will never have a body, we can share the
+  // storage, and use a different name.
+  union {
+    /// Indicates if the function declaration will have a body, once we're done
+    /// parsing it.
+    unsigned WillHaveBody : 1;
+    /// Indicates that the Deduction Guide is the implicitly generated 'copy
+    /// deduction candidate' (is used during overload resolution).
+    unsigned IsCopyDeductionCandidate : 1;
+  };
+private:
   /// \brief End part of this FunctionDecl's source range.
   ///
   /// We could compute the full range in getSourceRange(). However, when we're

Modified: cfe/trunk/include/clang/AST/DeclCXX.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclCXX.h?rev=316292&r1=316291&r2=316292&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DeclCXX.h (original)
+++ cfe/trunk/include/clang/AST/DeclCXX.h Sun Oct 22 07:45:08 2017
@@ -1881,6 +1881,10 @@ private:
     if (EndLocation.isValid())
       setRangeEnd(EndLocation);
     IsExplicitSpecified = IsExplicit;
+
+    // IsCopyDeductionCandidate is a union variant member, so ensure it is the
+    // active member by storing to it.
+    IsCopyDeductionCandidate = false; 
   }
 
 public:
@@ -1903,6 +1907,12 @@ public:
     return getDeclName().getCXXDeductionGuideTemplate();
   }
 
+  void setIsCopyDeductionCandidate() {
+    IsCopyDeductionCandidate = true;
+  }
+
+  bool isCopyDeductionCandidate() const { return IsCopyDeductionCandidate; }
+
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
   static bool classofKind(Kind K) { return K == CXXDeductionGuide; }

Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=316292&r1=316291&r2=316292&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Sun Oct 22 07:45:08 2017
@@ -8965,12 +8965,6 @@ bool clang::isBetterOverloadCandidate(
     // C++14 [over.match.best]p1 section 2 bullet 3.
   }
 
-  //    -- F1 is generated from a deduction-guide and F2 is not
-  auto *Guide1 = dyn_cast_or_null<CXXDeductionGuideDecl>(Cand1.Function);
-  auto *Guide2 = dyn_cast_or_null<CXXDeductionGuideDecl>(Cand2.Function);
-  if (Guide1 && Guide2 && Guide1->isImplicit() != Guide2->isImplicit())
-    return Guide2->isImplicit();
-
   //    -- F1 is a non-template function and F2 is a function template
   //       specialization, or, if not that,
   bool Cand1IsSpecialization = Cand1.Function &&
@@ -9015,6 +9009,23 @@ bool clang::isBetterOverloadCandidate(
     // Inherited from sibling base classes: still ambiguous.
   }
 
+  // Check C++17 tie-breakers for deduction guides.
+  {
+    auto *Guide1 = dyn_cast_or_null<CXXDeductionGuideDecl>(Cand1.Function);
+    auto *Guide2 = dyn_cast_or_null<CXXDeductionGuideDecl>(Cand2.Function);
+    if (Guide1 && Guide2) {
+      //  -- F1 is generated from a deduction-guide and F2 is not
+      if (Guide1->isImplicit() != Guide2->isImplicit())
+        return Guide2->isImplicit();
+
+      //  -- F1 is the copy deduction candidate(16.3.1.8) and F2 is not
+      if (Guide1->isCopyDeductionCandidate())
+        return true;
+    }
+  }
+  
+
+
   // FIXME: Work around a defect in the C++17 guaranteed copy elision wording,
   // as combined with the resolution to CWG issue 243.
   //

Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=316292&r1=316291&r2=316292&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Sun Oct 22 07:45:08 2017
@@ -1837,7 +1837,6 @@ void Sema::DeclareImplicitDeductionGuide
   // for which some class template parameter without a default argument never
   // appears in a deduced context).
   bool AddedAny = false;
-  bool AddedCopyOrMove = false;
   for (NamedDecl *D : LookupConstructors(Transform.Primary)) {
     D = D->getUnderlyingDecl();
     if (D->isInvalidDecl() || D->isImplicit())
@@ -1854,20 +1853,22 @@ void Sema::DeclareImplicitDeductionGuide
 
     Transform.transformConstructor(FTD, CD);
     AddedAny = true;
-
-    AddedCopyOrMove |= CD->isCopyOrMoveConstructor();
   }
 
-  // Synthesize an X() -> X<...> guide if there were no declared constructors.
-  // FIXME: The standard doesn't say (how) to do this.
+  // C++17 [over.match.class.deduct]
+  //    --  If C is not defined or does not declare any constructors, an
+  //    additional function template derived as above from a hypothetical
+  //    constructor C().
   if (!AddedAny)
     Transform.buildSimpleDeductionGuide(None);
 
-  // Synthesize an X(X<...>) -> X<...> guide if there was no declared constructor
-  // resembling a copy or move constructor.
-  // FIXME: The standard doesn't say (how) to do this.
-  if (!AddedCopyOrMove)
-    Transform.buildSimpleDeductionGuide(Transform.DeducedType);
+  //    -- An additional function template derived as above from a hypothetical
+  //    constructor C(C), called the copy deduction candidate.
+  cast<CXXDeductionGuideDecl>(
+      cast<FunctionTemplateDecl>(
+          Transform.buildSimpleDeductionGuide(Transform.DeducedType))
+          ->getTemplatedDecl())
+      ->setIsCopyDeductionCandidate();
 }
 
 /// \brief Diagnose the presence of a default template argument on a

Modified: cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp?rev=316292&r1=316291&r2=316292&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp Sun Oct 22 07:45:08 2017
@@ -1651,11 +1651,13 @@ Decl *TemplateDeclInstantiator::VisitFun
   }
 
   FunctionDecl *Function;
-  if (auto *DGuide = dyn_cast<CXXDeductionGuideDecl>(D))
+  if (auto *DGuide = dyn_cast<CXXDeductionGuideDecl>(D)) {
     Function = CXXDeductionGuideDecl::Create(
-        SemaRef.Context, DC, D->getInnerLocStart(), DGuide->isExplicit(),
-        D->getNameInfo(), T, TInfo, D->getSourceRange().getEnd());
-  else {
+      SemaRef.Context, DC, D->getInnerLocStart(), DGuide->isExplicit(),
+      D->getNameInfo(), T, TInfo, D->getSourceRange().getEnd());
+    if (DGuide->isCopyDeductionCandidate())
+      cast<CXXDeductionGuideDecl>(Function)->setIsCopyDeductionCandidate();
+  } else {
     Function = FunctionDecl::Create(
         SemaRef.Context, DC, D->getInnerLocStart(), D->getNameInfo(), T, TInfo,
         D->getCanonicalDecl()->getStorageClass(), D->isInlineSpecified(),

Modified: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=316292&r1=316291&r2=316292&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp Sun Oct 22 07:45:08 2017
@@ -1863,6 +1863,8 @@ ASTDeclReader::VisitCXXRecordDeclImpl(CX
 
 void ASTDeclReader::VisitCXXDeductionGuideDecl(CXXDeductionGuideDecl *D) {
   VisitFunctionDecl(D);
+  if (Record.readInt())
+    D->setIsCopyDeductionCandidate();
 }
 
 void ASTDeclReader::VisitCXXMethodDecl(CXXMethodDecl *D) {

Modified: cfe/trunk/lib/Serialization/ASTWriterDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriterDecl.cpp?rev=316292&r1=316291&r2=316292&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTWriterDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTWriterDecl.cpp Sun Oct 22 07:45:08 2017
@@ -612,6 +612,7 @@ void ASTDeclWriter::VisitFunctionDecl(Fu
 
 void ASTDeclWriter::VisitCXXDeductionGuideDecl(CXXDeductionGuideDecl *D) {
   VisitFunctionDecl(D);
+  Record.push_back(D->isCopyDeductionCandidate());
   Code = serialization::DECL_CXX_DEDUCTION_GUIDE;
 }
 

Modified: cfe/trunk/test/CXX/over/over.match/over.match.best/p1.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/over/over.match/over.match.best/p1.cpp?rev=316292&r1=316291&r2=316292&view=diff
==============================================================================
--- cfe/trunk/test/CXX/over/over.match/over.match.best/p1.cpp (original)
+++ cfe/trunk/test/CXX/over/over.match/over.match.best/p1.cpp Sun Oct 22 07:45:08 2017
@@ -25,13 +25,13 @@ namespace deduction_guide_example {
 
   // FIXME: The standard's example is wrong; we add a remove_ref<...> here to
   // fix it.
-  template<typename T, int N = remove_ref<T>::value> A(T&&, int*) -> A<T>;
+  template<typename T, int N = remove_ref<T>::value> A(T&&, int*) -> A<T**>;
   A a{1, 0};
   extern A<int> a;
-  A b{a, 0};
+  A b{a, 0}; // uses the implicit ctor that is more specialized
 
   A<int> *pa = &a;
-  A<A<int>&> *pb = &b;
+  A<int> *pb = &b;
 }
 
 // Partial ordering of function template specializations will be tested 

Modified: cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.class.deduct/p2.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.class.deduct/p2.cpp?rev=316292&r1=316291&r2=316292&view=diff
==============================================================================
--- cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.class.deduct/p2.cpp (original)
+++ cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.class.deduct/p2.cpp Sun Oct 22 07:45:08 2017
@@ -6,9 +6,10 @@ namespace Explicit {
   template<typename T> struct A {
     A(T);
     A(T*);
+	A(...);
   };
   template<typename T> A(T) -> A<T>;
-  template<typename T> explicit A(T*) -> A<T>; // expected-note {{explicit}}
+  template<typename T> explicit A(T*) -> A<T**>; // expected-note {{explicit}}
 
   int *p;
   A a(p);
@@ -16,14 +17,15 @@ namespace Explicit {
   A c{p};
   A d = {p}; // expected-error {{selected an explicit deduction guide}}
 
-  using X = A<int>;
-  using Y = A<int*>;
+  using X = A<int**>;
+  using Y = A<int>;  // uses the implicit guide, being more specialized than the eligible user-defined deduction guides.
 
   using X = decltype(a);
   using Y = decltype(b);
   using X = decltype(c);
 }
 
+
 namespace std {
   template<typename T> struct initializer_list {
     const T *ptr;
@@ -54,3 +56,32 @@ namespace p0702r1 {
   // between X<int> and X<float>.
   X xz = {z}; // expected-error {{no viable constructor or deduction guide}}
 }
+namespace pr34970 {
+//https://bugs.llvm.org/show_bug.cgi?id=34970
+
+template <typename X, typename Y> struct IsSame {
+    static constexpr bool value = false;
+};
+
+template <typename Z> struct IsSame<Z, Z> {
+    static constexpr bool value = true;
+};
+
+template <typename T> struct Optional {
+    template <typename U> Optional(U&&) { }
+};
+
+template <typename A> Optional(A) -> Optional<A>;
+
+int main() {
+    Optional opt(1729);
+    Optional dupe(opt);
+
+    static_assert(IsSame<decltype(opt), Optional<int>>::value);
+    static_assert(IsSame<decltype(dupe), Optional<int>>::value);
+    static_assert(!IsSame<decltype(dupe), Optional<Optional<int>>>::value);
+	return 0;
+}
+
+
+}
\ No newline at end of file




More information about the cfe-commits mailing list