<div dir="ltr">Sorry if this is expected behavior, but saw a regression that looks /plausibly/ correct & is rendered ambiguous by this change so I've gone ahead and reverted this patch in d8018233d1ea4234de68d5b4593abd773db79484<br><br>Comment from the original:<br><br>    Regressed/causes this to error due to ambiguity:<br>    <br>      void f(const int * const &);<br>      void f(int *);<br>      int main() {<br>        int * x;<br>        f(x);<br>      }<br>    <br>    (in case it's important - the original case where this turned up was a<br>    member function overload in a class template with, essentially:<br>    <br>      f(const T1&)<br>      f(T2*)<br>    <br>    (where T1 == X const *, T2 == X))<br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, Dec 19, 2019 at 6:56 PM Richard Smith via cfe-commits <<a href="mailto:cfe-commits@lists.llvm.org">cfe-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br>
Author: Richard Smith<br>
Date: 2019-12-19T18:37:55-08:00<br>
New Revision: de21704ba96fa80d3e9402f12c6505917a3885f4<br>
<br>
URL: <a href="https://github.com/llvm/llvm-project/commit/de21704ba96fa80d3e9402f12c6505917a3885f4" rel="noreferrer" target="_blank">https://github.com/llvm/llvm-project/commit/de21704ba96fa80d3e9402f12c6505917a3885f4</a><br>
DIFF: <a href="https://github.com/llvm/llvm-project/commit/de21704ba96fa80d3e9402f12c6505917a3885f4.diff" rel="noreferrer" target="_blank">https://github.com/llvm/llvm-project/commit/de21704ba96fa80d3e9402f12c6505917a3885f4.diff</a><br>
<br>
LOG: CWG2352: Allow qualification conversions during reference binding.<br>
<br>
The language wording change forgot to update overload resolution to rank<br>
implicit conversion sequences based on qualification conversions in<br>
reference bindings. The anticipated resolution for that oversight is<br>
implemented here -- we order candidates based on qualification<br>
conversion, not only on top-level cv-qualifiers.<br>
<br>
For OpenCL/C++, this allows reference binding between pointers with<br>
differing (nested) address spaces. This makes the behavior of reference<br>
binding consistent with that of implicit pointer conversions, as is the<br>
purpose of this change, but that pre-existing behavior for pointer<br>
conversions is itself probably not correct. In any case, it's now<br>
consistently the same behavior and implemented in only one place.<br>
<br>
Added: <br>
<br>
<br>
Modified: <br>
    clang/include/clang/Basic/DiagnosticSemaKinds.td<br>
    clang/lib/Sema/SemaExprCXX.cpp<br>
    clang/lib/Sema/SemaInit.cpp<br>
    clang/lib/Sema/SemaOverload.cpp<br>
    clang/test/CXX/drs/dr23xx.cpp<br>
    clang/test/CXX/drs/dr4xx.cpp<br>
    clang/test/SemaObjCXX/<a href="http://arc-overloading.mm" rel="noreferrer" target="_blank">arc-overloading.mm</a><br>
    clang/test/SemaOpenCL/<a href="http://address-spaces-conversions-cl2.0.cl" rel="noreferrer" target="_blank">address-spaces-conversions-cl2.0.cl</a><br>
    clang/www/cxx_dr_status.html<br>
    clang/www/make_cxx_dr_status<br>
<br>
Removed: <br>
<br>
<br>
<br>
################################################################################<br>
diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td<br>
index c7b501b9bb2b..315e836cd397 100644<br>
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td<br>
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td<br>
@@ -1927,7 +1927,8 @@ def err_lvalue_reference_bind_to_unrelated : Error<<br>
   "cannot bind to a value of unrelated type}1,2">;<br>
 def err_reference_bind_drops_quals : Error<<br>
   "binding reference %<br>
diff {of type $ to value of type $|to value}0,1 "<br>
-  "%select{drops %3 qualifier%plural{1:|2:|4:|:s}4|changes address space}2">;<br>
+  "%select{drops %3 qualifier%plural{1:|2:|4:|:s}4|changes address space|"<br>
+  "not permitted due to incompatible qualifiers}2">;<br>
 def err_reference_bind_failed : Error<<br>
   "reference %<br>
diff {to %select{type|incomplete type}1 $ could not bind to an "<br>
   "%select{rvalue|lvalue}2 of type $|could not bind to %select{rvalue|lvalue}2 of "<br>
<br>
diff  --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp<br>
index cfb3a05e9c14..cd78f096bb22 100644<br>
--- a/clang/lib/Sema/SemaExprCXX.cpp<br>
+++ b/clang/lib/Sema/SemaExprCXX.cpp<br>
@@ -5864,6 +5864,8 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS,<br>
   //   one of the operands is reference-compatible with the other, in order<br>
   //   to support conditionals between functions <br>
diff ering in noexcept. This<br>
   //   will similarly cover <br>
diff erence in array bounds after P0388R4.<br>
+  // FIXME: If LTy and RTy have a composite pointer type, should we convert to<br>
+  //   that instead?<br>
   ExprValueKind LVK = LHS.get()->getValueKind();<br>
   ExprValueKind RVK = RHS.get()->getValueKind();<br>
   if (!Context.hasSameType(LTy, RTy) &&<br>
<br>
diff  --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp<br>
index 94d524a63f5a..ef4fa827064e 100644<br>
--- a/clang/lib/Sema/SemaInit.cpp<br>
+++ b/clang/lib/Sema/SemaInit.cpp<br>
@@ -8919,11 +8919,17 @@ bool InitializationSequence::Diagnose(Sema &S,<br>
       S.Diag(Kind.getLocation(), diag::err_reference_bind_drops_quals)<br>
           << NonRefType << SourceType << 1 /*addr space*/<br>
           << Args[0]->getSourceRange();<br>
-    else<br>
+    else if (DroppedQualifiers.hasQualifiers())<br>
       S.Diag(Kind.getLocation(), diag::err_reference_bind_drops_quals)<br>
           << NonRefType << SourceType << 0 /*cv quals*/<br>
           << Qualifiers::fromCVRMask(DroppedQualifiers.getCVRQualifiers())<br>
           << DroppedQualifiers.getCVRQualifiers() << Args[0]->getSourceRange();<br>
+    else<br>
+      // FIXME: Consider decomposing the type and explaining which qualifiers<br>
+      // were dropped where, or on which level a 'const' is missing, etc.<br>
+      S.Diag(Kind.getLocation(), diag::err_reference_bind_drops_quals)<br>
+          << NonRefType << SourceType << 2 /*incompatible quals*/<br>
+          << Args[0]->getSourceRange();<br>
     break;<br>
   }<br>
<br>
<br>
diff  --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp<br>
index 74a0bc7c78ff..92058c3aa5fd 100644<br>
--- a/clang/lib/Sema/SemaOverload.cpp<br>
+++ b/clang/lib/Sema/SemaOverload.cpp<br>
@@ -3153,6 +3153,70 @@ static bool isNonTrivialObjCLifetimeConversion(Qualifiers FromQuals,<br>
   return true;<br>
 }<br>
<br>
+/// Perform a single iteration of the loop for checking if a qualification<br>
+/// conversion is valid.<br>
+///<br>
+/// Specifically, check whether any change between the qualifiers of \p<br>
+/// FromType and \p ToType is permissible, given knowledge about whether every<br>
+/// outer layer is const-qualified.<br>
+static bool isQualificationConversionStep(QualType FromType, QualType ToType,<br>
+                                          bool CStyle,<br>
+                                          bool &PreviousToQualsIncludeConst,<br>
+                                          bool &ObjCLifetimeConversion) {<br>
+  Qualifiers FromQuals = FromType.getQualifiers();<br>
+  Qualifiers ToQuals = ToType.getQualifiers();<br>
+<br>
+  // Ignore __unaligned qualifier if this type is void.<br>
+  if (ToType.getUnqualifiedType()->isVoidType())<br>
+    FromQuals.removeUnaligned();<br>
+<br>
+  // Objective-C ARC:<br>
+  //   Check Objective-C lifetime conversions.<br>
+  if (FromQuals.getObjCLifetime() != ToQuals.getObjCLifetime()) {<br>
+    if (ToQuals.compatiblyIncludesObjCLifetime(FromQuals)) {<br>
+      if (isNonTrivialObjCLifetimeConversion(FromQuals, ToQuals))<br>
+        ObjCLifetimeConversion = true;<br>
+      FromQuals.removeObjCLifetime();<br>
+      ToQuals.removeObjCLifetime();<br>
+    } else {<br>
+      // Qualification conversions cannot cast between <br>
diff erent<br>
+      // Objective-C lifetime qualifiers.<br>
+      return false;<br>
+    }<br>
+  }<br>
+<br>
+  // Allow addition/removal of GC attributes but not changing GC attributes.<br>
+  if (FromQuals.getObjCGCAttr() != ToQuals.getObjCGCAttr() &&<br>
+      (!FromQuals.hasObjCGCAttr() || !ToQuals.hasObjCGCAttr())) {<br>
+    FromQuals.removeObjCGCAttr();<br>
+    ToQuals.removeObjCGCAttr();<br>
+  }<br>
+<br>
+  //   -- for every j > 0, if const is in cv 1,j then const is in cv<br>
+  //      2,j, and similarly for volatile.<br>
+  if (!CStyle && !ToQuals.compatiblyIncludes(FromQuals))<br>
+    return false;<br>
+<br>
+  // For a C-style cast, just require the address spaces to overlap.<br>
+  // FIXME: Does "superset" also imply the representation of a pointer is the<br>
+  // same? We're assuming that it does here and in compatiblyIncludes.<br>
+  if (CStyle && !ToQuals.isAddressSpaceSupersetOf(FromQuals) &&<br>
+      !FromQuals.isAddressSpaceSupersetOf(ToQuals))<br>
+    return false;<br>
+<br>
+  //   -- if the cv 1,j and cv 2,j are <br>
diff erent, then const is in<br>
+  //      every cv for 0 < k < j.<br>
+  if (!CStyle && FromQuals.getCVRQualifiers() != ToQuals.getCVRQualifiers() &&<br>
+      !PreviousToQualsIncludeConst)<br>
+    return false;<br>
+<br>
+  // Keep track of whether all prior cv-qualifiers in the "to" type<br>
+  // include const.<br>
+  PreviousToQualsIncludeConst =<br>
+      PreviousToQualsIncludeConst && ToQuals.hasConst();<br>
+  return true;<br>
+}<br>
+<br>
 /// IsQualificationConversion - Determines whether the conversion from<br>
 /// an rvalue of type FromType to ToType is a qualification conversion<br>
 /// (C++ 4.4).<br>
@@ -3178,73 +3242,16 @@ Sema::IsQualificationConversion(QualType FromType, QualType ToType,<br>
   bool PreviousToQualsIncludeConst = true;<br>
   bool UnwrappedAnyPointer = false;<br>
   while (Context.UnwrapSimilarTypes(FromType, ToType)) {<br>
-    // Within each iteration of the loop, we check the qualifiers to<br>
-    // determine if this still looks like a qualification<br>
-    // conversion. Then, if all is well, we unwrap one more level of<br>
-    // pointers or pointers-to-members and do it all again<br>
-    // until there are no more pointers or pointers-to-members left to<br>
-    // unwrap.<br>
-    UnwrappedAnyPointer = true;<br>
-<br>
-    Qualifiers FromQuals = FromType.getQualifiers();<br>
-    Qualifiers ToQuals = ToType.getQualifiers();<br>
-<br>
-    // Ignore __unaligned qualifier if this type is void.<br>
-    if (ToType.getUnqualifiedType()->isVoidType())<br>
-      FromQuals.removeUnaligned();<br>
-<br>
-    // Objective-C ARC:<br>
-    //   Check Objective-C lifetime conversions.<br>
-    if (FromQuals.getObjCLifetime() != ToQuals.getObjCLifetime() &&<br>
-        UnwrappedAnyPointer) {<br>
-      if (ToQuals.compatiblyIncludesObjCLifetime(FromQuals)) {<br>
-        if (isNonTrivialObjCLifetimeConversion(FromQuals, ToQuals))<br>
-          ObjCLifetimeConversion = true;<br>
-        FromQuals.removeObjCLifetime();<br>
-        ToQuals.removeObjCLifetime();<br>
-      } else {<br>
-        // Qualification conversions cannot cast between <br>
diff erent<br>
-        // Objective-C lifetime qualifiers.<br>
-        return false;<br>
-      }<br>
-    }<br>
-<br>
-    // Allow addition/removal of GC attributes but not changing GC attributes.<br>
-    if (FromQuals.getObjCGCAttr() != ToQuals.getObjCGCAttr() &&<br>
-        (!FromQuals.hasObjCGCAttr() || !ToQuals.hasObjCGCAttr())) {<br>
-      FromQuals.removeObjCGCAttr();<br>
-      ToQuals.removeObjCGCAttr();<br>
-    }<br>
-<br>
-    //   -- for every j > 0, if const is in cv 1,j then const is in cv<br>
-    //      2,j, and similarly for volatile.<br>
-    if (!CStyle && !ToQuals.compatiblyIncludes(FromQuals))<br>
-      return false;<br>
-<br>
-    //   -- if the cv 1,j and cv 2,j are <br>
diff erent, then const is in<br>
-    //      every cv for 0 < k < j.<br>
-    if (!CStyle && FromQuals.getCVRQualifiers() != ToQuals.getCVRQualifiers()<br>
-        && !PreviousToQualsIncludeConst)<br>
+    if (!isQualificationConversionStep(FromType, ToType, CStyle,<br>
+                                       PreviousToQualsIncludeConst,<br>
+                                       ObjCLifetimeConversion))<br>
       return false;<br>
-<br>
-    // Keep track of whether all prior cv-qualifiers in the "to" type<br>
-    // include const.<br>
-    PreviousToQualsIncludeConst<br>
-      = PreviousToQualsIncludeConst && ToQuals.hasConst();<br>
-  }<br>
-<br>
-  // Allows address space promotion by language rules implemented in<br>
-  // Type::Qualifiers::isAddressSpaceSupersetOf.<br>
-  Qualifiers FromQuals = FromType.getQualifiers();<br>
-  Qualifiers ToQuals = ToType.getQualifiers();<br>
-  if (!ToQuals.isAddressSpaceSupersetOf(FromQuals) &&<br>
-      !FromQuals.isAddressSpaceSupersetOf(ToQuals)) {<br>
-    return false;<br>
+    UnwrappedAnyPointer = true;<br>
   }<br>
<br>
   // We are left with FromType and ToType being the pointee types<br>
   // after unwrapping the original FromType and ToType the same number<br>
-  // of types. If we unwrapped any pointers, and if FromType and<br>
+  // of times. If we unwrapped any pointers, and if FromType and<br>
   // ToType have the same unqualified type (since we checked<br>
   // qualifiers above), then this is a qualification conversion.<br>
   return UnwrappedAnyPointer && Context.hasSameUnqualifiedType(FromType,ToType);<br>
@@ -3990,32 +3997,41 @@ CompareStandardConversionSequences(Sema &S, SourceLocation Loc,<br>
     //      top-level cv-qualifiers, and the type to which the reference<br>
     //      initialized by S2 refers is more cv-qualified than the type<br>
     //      to which the reference initialized by S1 refers.<br>
+    // FIXME: This should have been updated by DR2352, but was overlooked. The<br>
+    // corrected rule is:<br>
+    //   -- S1 and S2 include reference bindings, and references refer to types<br>
+    //      T1 and T2, respectively, where T2 is reference-compatible with T1.<br>
     QualType T1 = SCS1.getToType(2);<br>
     QualType T2 = SCS2.getToType(2);<br>
-    T1 = S.Context.getCanonicalType(T1);<br>
-    T2 = S.Context.getCanonicalType(T2);<br>
-    Qualifiers T1Quals, T2Quals;<br>
-    QualType UnqualT1 = S.Context.getUnqualifiedArrayType(T1, T1Quals);<br>
-    QualType UnqualT2 = S.Context.getUnqualifiedArrayType(T2, T2Quals);<br>
-    if (UnqualT1 == UnqualT2) {<br>
-      // Objective-C++ ARC: If the references refer to objects with <br>
diff erent<br>
-      // lifetimes, prefer bindings that don't change lifetime.<br>
-      if (SCS1.ObjCLifetimeConversionBinding !=<br>
-                                          SCS2.ObjCLifetimeConversionBinding) {<br>
-        return SCS1.ObjCLifetimeConversionBinding<br>
-                                           ? ImplicitConversionSequence::Worse<br>
-                                           : ImplicitConversionSequence::Better;<br>
-      }<br>
<br>
-      // If the type is an array type, promote the element qualifiers to the<br>
-      // type for comparison.<br>
-      if (isa<ArrayType>(T1) && T1Quals)<br>
-        T1 = S.Context.getQualifiedType(UnqualT1, T1Quals);<br>
-      if (isa<ArrayType>(T2) && T2Quals)<br>
-        T2 = S.Context.getQualifiedType(UnqualT2, T2Quals);<br>
-      if (T2.isMoreQualifiedThan(T1))<br>
+    // Objective-C++ ARC: If the references refer to objects with <br>
diff erent<br>
+    // lifetimes, prefer bindings that don't change lifetime.<br>
+    //<br>
+    // FIXME: Should this really override ordering based on qualification<br>
+    // conversions? In the correspnding check for pointers, we treat a case<br>
+    // where one candidate has worse qualifications and the other has a<br>
+    // lifetime conversion as ambiguous.<br>
+    if (SCS1.ObjCLifetimeConversionBinding !=<br>
+            SCS2.ObjCLifetimeConversionBinding &&<br>
+        S.Context.hasSameUnqualifiedType(T1, T2)) {<br>
+      return SCS1.ObjCLifetimeConversionBinding<br>
+                 ? ImplicitConversionSequence::Worse<br>
+                 : ImplicitConversionSequence::Better;<br>
+    }<br>
+<br>
+    if (!S.Context.hasSameType(T1, T2)) {<br>
+      // FIXME: Unfortunately, there are pairs of types that admit reference<br>
+      // bindings in both directions, so we can't shortcut the second check<br>
+      // here.<br>
+      bool Better =<br>
+          S.CompareReferenceRelationship(Loc, T2, T1) == Sema::Ref_Compatible;<br>
+      bool Worse =<br>
+          S.CompareReferenceRelationship(Loc, T1, T2) == Sema::Ref_Compatible;<br>
+      if (Better && Worse)<br>
+        return ImplicitConversionSequence::Indistinguishable;<br>
+      if (Better)<br>
         return ImplicitConversionSequence::Better;<br>
-      else if (T1.isMoreQualifiedThan(T2))<br>
+      if (Worse)<br>
         return ImplicitConversionSequence::Worse;<br>
     }<br>
   }<br>
@@ -4402,10 +4418,19 @@ static bool isTypeValid(QualType T) {<br>
   return true;<br>
 }<br>
<br>
+static QualType withoutUnaligned(ASTContext &Ctx, QualType T) {<br>
+  if (!T.getQualifiers().hasUnaligned())<br>
+    return T;<br>
+<br>
+  Qualifiers Q;<br>
+  T = Ctx.getUnqualifiedArrayType(T, Q);<br>
+  Q.removeUnaligned();<br>
+  return Ctx.getQualifiedType(T, Q);<br>
+}<br>
+<br>
 /// CompareReferenceRelationship - Compare the two types T1 and T2 to<br>
-/// determine whether they are reference-related,<br>
-/// reference-compatible, reference-compatible with added<br>
-/// qualification, or incompatible, for use in C++ initialization by<br>
+/// determine whether they are reference-compatible,<br>
+/// reference-related, or incompatible, for use in C++ initialization by<br>
 /// reference (C++ [dcl.ref.init]p4). Neither type can be a reference<br>
 /// type, and the first type (T1) is the pointee type of the reference<br>
 /// type being initialized.<br>
@@ -4427,10 +4452,17 @@ Sema::CompareReferenceRelationship(SourceLocation Loc,<br>
   ReferenceConversions &Conv = ConvOut ? *ConvOut : ConvTmp;<br>
   Conv = ReferenceConversions();<br>
<br>
-  // C++ [dcl.init.ref]p4:<br>
+  // C++2a [dcl.init.ref]p4:<br>
   //   Given types "cv1 T1" and "cv2 T2," "cv1 T1" is<br>
-  //   reference-related to "cv2 T2" if T1 is the same type as T2, or<br>
+  //   reference-related to "cv2 T2" if T1 is similar to T2, or<br>
   //   T1 is a base class of T2.<br>
+  //   "cv1 T1" is reference-compatible with "cv2 T2" if<br>
+  //   a prvalue of type "pointer to cv2 T2" can be converted to the type<br>
+  //   "pointer to cv1 T1" via a standard conversion sequence.<br>
+<br>
+  // Check for standard conversions we can apply to pointers: derived-to-base<br>
+  // conversions, ObjC pointer conversions, and function pointer conversions.<br>
+  // (Qualification conversions are checked last.)<br>
   QualType ConvertedT2;<br>
   if (UnqualT1 == UnqualT2) {<br>
     // Nothing to do.<br>
@@ -4444,59 +4476,47 @@ Sema::CompareReferenceRelationship(SourceLocation Loc,<br>
     Conv |= ReferenceConversions::ObjC;<br>
   else if (UnqualT2->isFunctionType() &&<br>
            IsFunctionConversion(UnqualT2, UnqualT1, ConvertedT2)) {<br>
-    // C++1z [dcl.init.ref]p4:<br>
-    //   cv1 T1" is reference-compatible with "cv2 T2" if [...] T2 is "noexcept<br>
-    //   function" and T1 is "function"<br>
-    //<br>
-    // We extend this to also apply to 'noreturn', so allow any function<br>
-    // conversion between function types.<br>
     Conv |= ReferenceConversions::Function;<br>
+    // No need to check qualifiers; function types don't have them.<br>
     return Ref_Compatible;<br>
-  } else<br>
-    return Ref_Incompatible;<br>
-<br>
-  // At this point, we know that T1 and T2 are reference-related (at<br>
-  // least).<br>
-<br>
-  // If the type is an array type, promote the element qualifiers to the type<br>
-  // for comparison.<br>
-  if (isa<ArrayType>(T1) && T1Quals)<br>
-    T1 = Context.getQualifiedType(UnqualT1, T1Quals);<br>
-  if (isa<ArrayType>(T2) && T2Quals)<br>
-    T2 = Context.getQualifiedType(UnqualT2, T2Quals);<br>
-<br>
-  // C++ [dcl.init.ref]p4:<br>
-  //   "cv1 T1" is reference-compatible with "cv2 T2" if T1 is<br>
-  //   reference-related to T2 and cv1 is the same cv-qualification<br>
-  //   as, or greater cv-qualification than, cv2. For purposes of<br>
-  //   overload resolution, cases for which cv1 is greater<br>
-  //   cv-qualification than cv2 are identified as<br>
-  //   reference-compatible with added qualification (see 13.3.3.2).<br>
-  //<br>
-  // Note that we also require equivalence of Objective-C GC and address-space<br>
-  // qualifiers when performing these computations, so that e.g., an int in<br>
-  // address space 1 is not reference-compatible with an int in address<br>
-  // space 2.<br>
-  if (T1Quals.getObjCLifetime() != T2Quals.getObjCLifetime() &&<br>
-      T1Quals.compatiblyIncludesObjCLifetime(T2Quals)) {<br>
-    if (isNonTrivialObjCLifetimeConversion(T2Quals, T1Quals))<br>
-      Conv |= ReferenceConversions::ObjCLifetime;<br>
-<br>
-    T1Quals.removeObjCLifetime();<br>
-    T2Quals.removeObjCLifetime();<br>
   }<br>
+  bool ConvertedReferent = Conv != 0;<br>
<br>
-  // MS compiler ignores __unaligned qualifier for references; do the same.<br>
-  T1Quals.removeUnaligned();<br>
-  T2Quals.removeUnaligned();<br>
+  // We can have a qualification conversion. Compute whether the types are<br>
+  // similar at the same time.<br>
+  bool PreviousToQualsIncludeConst = true;<br>
+  do {<br>
+    if (T1 == T2)<br>
+      break;<br>
<br>
-  if (T1Quals != T2Quals)<br>
+    // We will need a qualification conversion.<br>
     Conv |= ReferenceConversions::Qualification;<br>
<br>
-  if (T1Quals.compatiblyIncludes(T2Quals))<br>
-    return Ref_Compatible;<br>
-  else<br>
-    return Ref_Related;<br>
+    // MS compiler ignores __unaligned qualifier for references; do the same.<br>
+    T1 = withoutUnaligned(Context, T1);<br>
+    T2 = withoutUnaligned(Context, T2);<br>
+<br>
+    // If we find a qualifier mismatch, the types are not reference-compatible,<br>
+    // but are still be reference-related if they're similar.<br>
+    bool ObjCLifetimeConversion = false;<br>
+    if (!isQualificationConversionStep(T2, T1, /*CStyle=*/false,<br>
+                                       PreviousToQualsIncludeConst,<br>
+                                       ObjCLifetimeConversion))<br>
+      return (ConvertedReferent || Context.hasSimilarType(T1, T2))<br>
+                 ? Ref_Related<br>
+                 : Ref_Incompatible;<br>
+<br>
+    // FIXME: Should we track this for any level other than the first?<br>
+    if (ObjCLifetimeConversion)<br>
+      Conv |= ReferenceConversions::ObjCLifetime;<br>
+  } while (Context.UnwrapSimilarTypes(T1, T2));<br>
+<br>
+  // At this point, if the types are reference-related, we must either have the<br>
+  // same inner type (ignoring qualifiers), or must have already worked out how<br>
+  // to convert the referent.<br>
+  return (ConvertedReferent || Context.hasSameUnqualifiedType(T1, T2))<br>
+             ? Ref_Compatible<br>
+             : Ref_Incompatible;<br>
 }<br>
<br>
 /// Look for a user-defined conversion to a value reference-compatible<br>
<br>
diff  --git a/clang/test/CXX/drs/dr23xx.cpp b/clang/test/CXX/drs/dr23xx.cpp<br>
index 763abd5368ef..caf3be114547 100644<br>
--- a/clang/test/CXX/drs/dr23xx.cpp<br>
+++ b/clang/test/CXX/drs/dr23xx.cpp<br>
@@ -4,9 +4,38 @@<br>
 // RUN: %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s<br>
 // RUN: %clang_cc1 -std=c++2a %s -verify -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s<br>
<br>
-#if __cplusplus <= 201103L<br>
-// expected-no-diagnostics<br>
+namespace dr2352 { // dr2352: 10<br>
+  int **p;<br>
+  const int *const *const &f1() { return p; }<br>
+  int *const *const &f2() { return p; }<br>
+  int **const &f3() { return p; }<br>
+<br>
+  const int **const &f4() { return p; } // expected-error {{reference to type 'const int **const' could not bind to an lvalue of type 'int **'}}<br>
+  const int *const *&f5() { return p; } // expected-error {{binding reference of type 'const int *const *' to value of type 'int **' not permitted due to incompatible qualifiers}}<br>
+<br>
+  // FIXME: We permit this as a speculative defect resolution, allowing<br>
+  // qualification conversions when forming a glvalue conditional expression.<br>
+  const int * const * const q = 0;<br>
+  __typeof(&(true ? p : q)) x = &(true ? p : q);<br>
+<br>
+  // FIXME: Should we compute the composite pointer type here and produce an<br>
+  // lvalue of type 'const int *const * const'?<br>
+  const int * const * r;<br>
+  void *y = &(true ? p : r); // expected-error {{rvalue of type 'const int *const *'}}<br>
+<br>
+  // FIXME: We order these as a speculative defect resolution.<br>
+  void f(const int * const * const &r);<br>
+#if __cplusplus >= 201103L<br>
+  constexpr<br>
 #endif<br>
+  int *const *const &f(int * const * const &r) { return r; }<br>
+<br>
+  // No temporary is created here.<br>
+  int *const *const &check_f = f(p);<br>
+#if __cplusplus >= 201103L<br>
+  static_assert(&p == &check_f, "");<br>
+#endif<br>
+}<br>
<br>
 namespace dr2353 { // dr2353: 9<br>
   struct X {<br>
<br>
diff  --git a/clang/test/CXX/drs/dr4xx.cpp b/clang/test/CXX/drs/dr4xx.cpp<br>
index 8eeb7715cadf..d37ece6cb882 100644<br>
--- a/clang/test/CXX/drs/dr4xx.cpp<br>
+++ b/clang/test/CXX/drs/dr4xx.cpp<br>
@@ -486,14 +486,21 @@ namespace dr433 { // dr433: yes<br>
   S<int> s;<br>
 }<br>
<br>
-namespace dr434 { // dr434: yes<br>
+namespace dr434 { // dr434: sup 2352<br>
   void f() {<br>
     const int ci = 0;<br>
     int *pi = 0;<br>
-    const int *&rpci = pi; // expected-error {{cannot bind}}<br>
+    const int *&rpci = pi; // expected-error {{incompatible qualifiers}}<br>
+    const int * const &rcpci = pi; // OK<br>
     rpci = &ci;<br>
     *pi = 1;<br>
   }<br>
+<br>
+#if __cplusplus >= 201103L<br>
+  int *pi = 0;<br>
+  const int * const &rcpci = pi;<br>
+  static_assert(&rcpci == &pi, "");<br>
+#endif<br>
 }<br>
<br>
 // dr435: na<br>
<br>
diff  --git a/clang/test/SemaObjCXX/<a href="http://arc-overloading.mm" rel="noreferrer" target="_blank">arc-overloading.mm</a> b/clang/test/SemaObjCXX/<a href="http://arc-overloading.mm" rel="noreferrer" target="_blank">arc-overloading.mm</a><br>
index 3ac9c51293b7..910b5c7be978 100644<br>
--- a/clang/test/SemaObjCXX/<a href="http://arc-overloading.mm" rel="noreferrer" target="_blank">arc-overloading.mm</a><br>
+++ b/clang/test/SemaObjCXX/<a href="http://arc-overloading.mm" rel="noreferrer" target="_blank">arc-overloading.mm</a><br>
@@ -174,6 +174,36 @@ void test_f9() {<br>
   const __autoreleasing id& ar4 = weak_a;<br>
 }<br>
<br>
+int &f10(__strong id *&); // expected-note 2{{not viable: no known conversion}}<br>
+float &f10(__autoreleasing id *&); // expected-note 2{{not viable: no known conversion}}<br>
+<br>
+void test_f10() {<br>
+  __strong id *strong_id;<br>
+  __weak id *weak_id;<br>
+  __autoreleasing id *autoreleasing_id;<br>
+  __unsafe_unretained id *unsafe_id;<br>
+<br>
+  int &ir1 = f10(strong_id);<br>
+  float &fr1 = f10(autoreleasing_id);<br>
+  float &fr2 = f10(unsafe_id); // expected-error {{no match}}<br>
+  float &fr2a = f10(weak_id); // expected-error {{no match}}<br>
+}<br>
+<br>
+int &f11(__strong id *const &); // expected-note {{not viable: 1st argument ('__weak id *') has __weak ownership, but parameter has __strong ownership}}<br>
+float &f11(const __autoreleasing id *const &); // expected-note {{not viable: 1st argument ('__weak id *') has __weak ownership, but parameter has __autoreleasing ownership}}<br>
+<br>
+void test_f11() {<br>
+  __strong id *strong_id;<br>
+  __weak id *weak_id;<br>
+  __autoreleasing id *autoreleasing_id;<br>
+  __unsafe_unretained id *unsafe_id;<br>
+<br>
+  int &ir1 = f11(strong_id);<br>
+  float &fr1 = f11(autoreleasing_id);<br>
+  float &fr2 = f11(unsafe_id);<br>
+  float &fr2a = f11(weak_id); // expected-error {{no match}}<br>
+}<br>
+<br>
 // rdar://9790531<br>
 void f9790531(void *inClientData); // expected-note {{candidate function not viable: cannot implicitly convert argument of type 'MixerEQGraphTestDelegate *const __strong' to 'void *' for 1st argument under ARC}}<br>
 void f9790531_1(struct S*inClientData); // expected-note {{candidate function not viable}}<br>
<br>
diff  --git a/clang/test/SemaOpenCL/<a href="http://address-spaces-conversions-cl2.0.cl" rel="noreferrer" target="_blank">address-spaces-conversions-cl2.0.cl</a> b/clang/test/SemaOpenCL/<a href="http://address-spaces-conversions-cl2.0.cl" rel="noreferrer" target="_blank">address-spaces-conversions-cl2.0.cl</a><br>
index a97565f3a2e5..0209e1ea24de 100644<br>
--- a/clang/test/SemaOpenCL/<a href="http://address-spaces-conversions-cl2.0.cl" rel="noreferrer" target="_blank">address-spaces-conversions-cl2.0.cl</a><br>
+++ b/clang/test/SemaOpenCL/<a href="http://address-spaces-conversions-cl2.0.cl" rel="noreferrer" target="_blank">address-spaces-conversions-cl2.0.cl</a><br>
@@ -501,12 +501,9 @@ void test_pointer_chains() {<br>
   // Case 1:<br>
   //  * address spaces of corresponded most outer pointees overlaps, their canonical types are equal<br>
   //  * CVR, address spaces and canonical types of the rest of pointees are equivalent.<br>
+  var_as_as_int = var_asc_as_int;<br>
   var_as_as_int = 0 ? var_as_as_int : var_asc_as_int;<br>
-#if __OPENCL_CPP_VERSION__<br>
-#ifdef GENERIC<br>
-// expected-error@-3{{incompatible operand types ('__generic int *__generic *' and '__generic int *__local *')}}<br>
-#endif<br>
-#endif<br>
+<br>
   // Case 2: Corresponded inner pointees has non-overlapping address spaces.<br>
   var_as_as_int = 0 ? var_as_as_int : var_asc_asn_int;<br>
 #if !__OPENCL_CPP_VERSION__<br>
@@ -516,12 +513,17 @@ void test_pointer_chains() {<br>
 #endif<br>
<br>
   // Case 3: Corresponded inner pointees has overlapping but not equivalent address spaces.<br>
+  // FIXME: Should this really be allowed in C++ mode?<br>
+  var_as_as_int = var_asc_asc_int;<br>
+#if !__OPENCL_CPP_VERSION__<br>
 #ifdef GENERIC<br>
+// expected-error@-3 {{assigning '__local int *__local *' to '__generic int *__generic *' changes address space of nested pointer}}<br>
+#endif<br>
+#endif<br>
   var_as_as_int = 0 ? var_as_as_int : var_asc_asc_int;<br>
 #if !__OPENCL_CPP_VERSION__<br>
-// expected-warning-re@-2{{pointer type mismatch ('__{{(generic|global|constant)}} int *__{{(generic|global|constant)}} *' and '__{{(local|global|constant)}} int *__{{(local|global|constant)}} *')}}<br>
-#else<br>
-// expected-error-re@-4{{incompatible operand types ('__{{generic|global|constant}} int *__{{generic|global|constant}} *' and '__{{local|global|constant}} int *__{{local|global|constant}} *')}}<br>
+#ifdef GENERIC<br>
+// expected-warning@-3{{pointer type mismatch ('__generic int *__generic *' and '__local int *__local *')}}<br>
 #endif<br>
 #endif<br>
 }<br>
<br>
diff  --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html<br>
index 23a7218e897a..c4ec45736524 100755<br>
--- a/clang/www/cxx_dr_status.html<br>
+++ b/clang/www/cxx_dr_status.html<br>
@@ -2645,7 +2645,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2><br>
     <td><a href="<a href="https://wg21.link/cwg434" rel="noreferrer" target="_blank">https://wg21.link/cwg434</a>">434</a></td><br>
     <td>NAD</td><br>
     <td>Unclear suppression of standard conversions while binding reference to lvalue</td><br>
-    <td class="full" align="center">Yes</td><br>
+    <td class="svn" align="center">Superseded by <a href="#2352">2352</a></td><br>
   </tr><br>
   <tr id="435"><br>
     <td><a href="<a href="https://wg21.link/cwg435" rel="noreferrer" target="_blank">https://wg21.link/cwg435</a>">435</a></td><br>
@@ -13927,7 +13927,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2><br>
     <td><a href="<a href="https://wg21.link/cwg2352" rel="noreferrer" target="_blank">https://wg21.link/cwg2352</a>">2352</a></td><br>
     <td>DR</td><br>
     <td>Similar types and reference binding</td><br>
-    <td class="none" align="center">Unknown</td><br>
+    <td class="svn" align="center">SVN</td><br>
   </tr><br>
   <tr id="2353"><br>
     <td><a href="<a href="https://wg21.link/cwg2353" rel="noreferrer" target="_blank">https://wg21.link/cwg2353</a>">2353</a></td><br>
<br>
diff  --git a/clang/www/make_cxx_dr_status b/clang/www/make_cxx_dr_status<br>
index 4351d659e41a..fd5eb7fbabb4 100755<br>
--- a/clang/www/make_cxx_dr_status<br>
+++ b/clang/www/make_cxx_dr_status<br>
@@ -28,7 +28,7 @@ def parse(dr):<br>
   _, url, issue = issue_link.split('"', 2)<br>
   url = url.strip()<br>
   issue = int(issue.split('>', 1)[1].split('<', 1)[0])<br>
-  title = title.replace('<issue_title>', '').replace('</issue_title>', '').strip()<br>
+  title = title.replace('<issue_title>', '').replace('</issue_title>', '').replace('\r\n', '\n').strip()<br>
   return DR(section, issue, url, status, title)<br>
<br>
 status_re = re.compile(r'\bdr([0-9]+): (.*)')<br>
@@ -171,7 +171,7 @@ for dr in drs:<br>
<br>
   print >> out_file, '''\<br>
   <tr%s id="%s"><br>
-    <td><a href="<a href="http://wg21.link/cwg%s" rel="noreferrer" target="_blank">http://wg21.link/cwg%s</a>">%s</a></td><br>
+    <td><a href="<a href="https://wg21.link/cwg%s" rel="noreferrer" target="_blank">https://wg21.link/cwg%s</a>">%s</a></td><br>
     <td>%s</td><br>
     <td>%s</td><br>
     <td%s align="center">%s</td><br>
<br>
<br>
<br>
_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits</a><br>
</blockquote></div>