[clang] [clang-cl] [Sema] Support MSVC non-const lvalue to user-defined temporary reference (PR #99833)

Max Winkler via cfe-commits cfe-commits at lists.llvm.org
Mon Jul 29 08:24:44 PDT 2024


https://github.com/MaxEW707 updated https://github.com/llvm/llvm-project/pull/99833

>From e0528ecc441e33822426b8b3d6522d056c95bb54 Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Fri, 21 Jun 2024 20:37:40 -0700
Subject: [PATCH 1/5] Support MSVC lvalue to temporary reference binding

---
 clang/docs/ReleaseNotes.rst                 |   4 +
 clang/include/clang/Basic/LangOptions.def   |   1 +
 clang/include/clang/Driver/Options.td       |  12 +++
 clang/include/clang/Sema/Sema.h             |   2 +
 clang/lib/Driver/ToolChains/Clang.cpp       |   5 +
 clang/lib/Driver/ToolChains/MSVC.cpp        |   1 +
 clang/lib/Sema/SemaInit.cpp                 |  22 +++--
 clang/lib/Sema/SemaOverload.cpp             |  16 ++-
 clang/test/Driver/cl-permissive.c           |   7 ++
 clang/test/Driver/cl-zc.cpp                 |   2 +
 clang/test/SemaCXX/ms-reference-binding.cpp | 102 ++++++++++++++++++++
 11 files changed, 165 insertions(+), 9 deletions(-)
 create mode 100644 clang/test/SemaCXX/ms-reference-binding.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 4638b91b48f95..567effa83f845 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -486,6 +486,10 @@ New Compiler Flags
 - ``-fpointer-tbaa`` enables emission of distinct type-based alias
   analysis tags for incompatible pointers.
 
+- ``-fms-reference-binding`` and its clang-cl counterpart ``/Zc:referenceBinding``.
+  Implements the MSVC extension where expressions that bind a user-defined type temporary
+  to a non-const lvalue reference are allowed.
+
 Deprecated Compiler Flags
 -------------------------
 
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index a6f36b23f07dc..188cea2c900c7 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -306,6 +306,7 @@ LANGOPT(HIPStdParInterposeAlloc, 1, 0, "Replace allocations / deallocations with
 LANGOPT(OpenACC           , 1, 0, "OpenACC Enabled")
 
 LANGOPT(MSVCEnableStdcMacro , 1, 0, "Define __STDC__ with '-fms-compatibility'")
+LANGOPT(MSVCReferenceBinding , 1, 0, "Accept expressions that bind a non-const lvalue reference to a temporary")
 LANGOPT(SizedDeallocation , 1, 0, "sized deallocation")
 LANGOPT(AlignedAllocation , 1, 0, "aligned allocation")
 LANGOPT(AlignedAllocationUnavailable, 1, 0, "aligned allocation functions are unavailable")
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 8707e71f2a319..cd568c9f8f60f 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -3036,6 +3036,12 @@ def fms_extensions : Flag<["-"], "fms-extensions">, Group<f_Group>,
   Visibility<[ClangOption, CC1Option, CLOption]>,
   HelpText<"Accept some non-standard constructs supported by the Microsoft compiler">,
   MarshallingInfoFlag<LangOpts<"MicrosoftExt">>, ImpliedByAnyOf<[fms_compatibility.KeyPath]>;
+def fms_reference_binding : Flag<["-"], "fms-reference-binding">, Group<f_Group>,
+  Visibility<[ClangOption, CC1Option, CLOption]>,
+  HelpText<"Accept expressions that bind a non-const lvalue reference to a user-defined type temporary as supported by the Microsoft Compiler">,
+  MarshallingInfoFlag<LangOpts<"MSVCReferenceBinding">>;
+def fno_ms_reference_binding : Flag<["-"], "fno-ms-reference-binding">, Group<f_Group>,
+  Visibility<[ClangOption, CLOption]>;
 defm asm_blocks : BoolFOption<"asm-blocks",
   LangOpts<"AsmBlocks">, Default<fms_extensions.KeyPath>,
   PosFlag<SetTrue, [], [ClangOption, CC1Option]>,
@@ -8467,6 +8473,12 @@ def _SLASH_Zc_wchar_t : CLFlag<"Zc:wchar_t">,
   HelpText<"Enable C++ builtin type wchar_t (default)">;
 def _SLASH_Zc_wchar_t_ : CLFlag<"Zc:wchar_t-">,
   HelpText<"Disable C++ builtin type wchar_t">;
+def _SLASH_Zc_referenceBinding : CLFlag<"Zc:referenceBinding">,
+  HelpText<"Do not accept expressions that bind a non-const lvalue reference to a user-defined type temporary">,
+  Alias<fno_ms_reference_binding>;
+def _SLASH_Zc_referenceBinding_ : CLFlag<"Zc:referenceBinding-">,
+  HelpText<"Accept expressions that bind a non-const lvalue reference to a user-defined type temporary">,
+  Alias<fms_reference_binding>;
 def _SLASH_Z7 : CLFlag<"Z7">, Alias<g_Flag>,
   HelpText<"Enable CodeView debug information in object files">;
 def _SLASH_ZH_MD5 : CLFlag<"ZH:MD5">,
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index d638d31e050dc..34ea43c3fa2e2 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -10158,6 +10158,8 @@ class Sema final : public SemaBase {
   CompareReferenceRelationship(SourceLocation Loc, QualType T1, QualType T2,
                                ReferenceConversions *Conv = nullptr);
 
+  bool AllowMSLValueReferenceBinding(Qualifiers Quals, QualType QT);
+
   /// AddOverloadCandidate - Adds the given function to the set of
   /// candidate functions, using the given function call arguments.  If
   /// @p SuppressUserConversions, then don't allow user-defined
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 71cdaa10416f4..f1cf00b03ae96 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7007,6 +7007,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
                    IsWindowsMSVC))
     CmdArgs.push_back("-fms-extensions");
 
+  // -fno-ms-reference-binding is the default.
+  if (Args.hasFlag(options::OPT_fms_reference_binding,
+                   options::OPT_fno_ms_reference_binding, false))
+    CmdArgs.push_back("-fms-reference-binding");
+
   // -fms-compatibility=0 is default.
   bool IsMSVCCompat = Args.hasFlag(
       options::OPT_fms_compatibility, options::OPT_fno_ms_compatibility,
diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp
index ca266e3e1d1d3..63ec69647c364 100644
--- a/clang/lib/Driver/ToolChains/MSVC.cpp
+++ b/clang/lib/Driver/ToolChains/MSVC.cpp
@@ -950,6 +950,7 @@ static void TranslatePermissive(Arg *A, llvm::opt::DerivedArgList &DAL,
                                 const OptTable &Opts) {
   DAL.AddFlagArg(A, Opts.getOption(options::OPT__SLASH_Zc_twoPhase_));
   DAL.AddFlagArg(A, Opts.getOption(options::OPT_fno_operator_names));
+  DAL.AddFlagArg(A, Opts.getOption(options::OPT_fms_reference_binding));
 }
 
 static void TranslatePermissiveMinus(Arg *A, llvm::opt::DerivedArgList &DAL,
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index dc2ba039afe7f..0ccb2f285e766 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -5213,13 +5213,17 @@ static void TryReferenceInitializationCore(Sema &S,
       Sequence.SetOverloadFailure(
                         InitializationSequence::FK_ReferenceInitOverloadFailed,
                                   ConvOvlResult);
-    else if (!InitCategory.isLValue())
-      Sequence.SetFailed(
-          T1Quals.isAddressSpaceSupersetOf(T2Quals)
-              ? InitializationSequence::
-                    FK_NonConstLValueReferenceBindingToTemporary
-              : InitializationSequence::FK_ReferenceInitDropsQualifiers);
-    else {
+    else if (!InitCategory.isLValue()) {
+      if (T1Quals.isAddressSpaceSupersetOf(T2Quals)) {
+        if (!S.getLangOpts().MSVCReferenceBinding ||
+            !S.AllowMSLValueReferenceBinding(T1Quals, T1))
+          Sequence.SetFailed(InitializationSequence::
+                                 FK_NonConstLValueReferenceBindingToTemporary);
+      } else {
+        Sequence.SetFailed(
+            InitializationSequence::FK_ReferenceInitDropsQualifiers);
+      }
+    } else {
       InitializationSequence::FailureKind FK;
       switch (RefRelationship) {
       case Sema::Ref_Compatible:
@@ -5245,7 +5249,9 @@ static void TryReferenceInitializationCore(Sema &S,
       }
       Sequence.SetFailed(FK);
     }
-    return;
+
+    if (Sequence.Failed())
+      return;
   }
 
   //    - If the initializer expression
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index a8d250fbabfed..70802848e789a 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -4906,6 +4906,16 @@ Sema::CompareReferenceRelationship(SourceLocation Loc,
              : Ref_Incompatible;
 }
 
+bool Sema::AllowMSLValueReferenceBinding(Qualifiers Quals, QualType QT) {
+  if (Quals.hasVolatile())
+    return false;
+  if (Quals.hasConst())
+    return true;
+  if (QT->isBuiltinType() || QT->isArrayType())
+    return false;
+  return true;
+}
+
 /// Look for a user-defined conversion to a value reference-compatible
 ///        with DeclType. Return true if something definite is found.
 static bool
@@ -5138,7 +5148,11 @@ TryReferenceInit(Sema &S, Expr *Init, QualType DeclType,
   //     -- Otherwise, the reference shall be an lvalue reference to a
   //        non-volatile const type (i.e., cv1 shall be const), or the reference
   //        shall be an rvalue reference.
-  if (!isRValRef && (!T1.isConstQualified() || T1.isVolatileQualified())) {
+  const bool CanBindLValueRef =
+      !S.getLangOpts().MSVCReferenceBinding
+          ? (T1.isConstQualified() && !T1.isVolatileQualified())
+          : S.AllowMSLValueReferenceBinding(T1.getQualifiers(), T1);
+  if (!isRValRef && !CanBindLValueRef) {
     if (InitCategory.isRValue() && RefRelationship != Sema::Ref_Incompatible)
       ICS.setBad(BadConversionSequence::lvalue_ref_to_rvalue, Init, DeclType);
     return ICS;
diff --git a/clang/test/Driver/cl-permissive.c b/clang/test/Driver/cl-permissive.c
index d88746d3a5517..712bef71a43af 100644
--- a/clang/test/Driver/cl-permissive.c
+++ b/clang/test/Driver/cl-permissive.c
@@ -3,9 +3,11 @@
 
 // RUN: %clang_cl /permissive -### -- %s 2>&1 | FileCheck -check-prefix=PERMISSIVE %s
 // PERMISSIVE: "-fno-operator-names"
+// PERMISSIVE: "-fms-reference-binding"
 // PERMISSIVE: "-fdelayed-template-parsing"
 // RUN: %clang_cl /permissive- -### -- %s 2>&1 | FileCheck -check-prefix=PERMISSIVE-MINUS %s
 // PERMISSIVE-MINUS-NOT: "-fno-operator-names"
+// PERMISSIVE-MINUS-NOT: "-fms-reference-binding"
 // PERMISSIVE-MINUS-NOT: "-fdelayed-template-parsing"
 
 // The switches set by permissive may then still be manually enabled or disabled
@@ -15,3 +17,8 @@
 // RUN: %clang_cl /permissive- /Zc:twoPhase- -### -- %s 2>&1 | FileCheck -check-prefix=PERMISSIVE-MINUS-OVERWRITE %s
 // PERMISSIVE-MINUS-OVERWRITE-NOT: "-fno-operator-names"
 // PERMISSIVE-MINUS-OVERWRITE: "-fdelayed-template-parsing"
+
+// RUN: %clang_cl /permissive -fno-ms-reference-binding -### -- %s 2>&1 | FileCheck -check-prefix=PERMISSIVE-OVERWRITE-REF-BINDING %s
+// PERMISSIVE-OVERWRITE-REF-BINDING-NOT: "-fms-reference-binding"
+// RUN: %clang_cl /permissive- -fms-reference-binding -### -- %s 2>&1 | FileCheck -check-prefix=PERMISSIVE-MINUS-OVERWRITE-REF-BINDING %s
+// PERMISSIVE-MINUS-OVERWRITE-REF-BINDING: "-fms-reference-binding"
diff --git a/clang/test/Driver/cl-zc.cpp b/clang/test/Driver/cl-zc.cpp
index c7cf5b1b6525b..862d7a6e649c6 100644
--- a/clang/test/Driver/cl-zc.cpp
+++ b/clang/test/Driver/cl-zc.cpp
@@ -122,6 +122,8 @@
 // RUN: %clang_cl -c -### /Zc:char8_t- -- %s 2>&1 | FileCheck -check-prefix CHECK-CHAR8_T_ %s
 // CHECK-CHAR8_T_: "-fno-char8_t"
 
+// RUN: %clang_cl -c -### /Zc:referenceBinding- -- %s 2>&1 | FileCheck -check-prefix CHECK-REFERENCE_BINDING_ %s
+// CHECK-REFERENCE_BINDING_: "-fms-reference-binding"
 
 
 // These never warn, but don't have an effect yet.
diff --git a/clang/test/SemaCXX/ms-reference-binding.cpp b/clang/test/SemaCXX/ms-reference-binding.cpp
new file mode 100644
index 0000000000000..44e4723fd4fa3
--- /dev/null
+++ b/clang/test/SemaCXX/ms-reference-binding.cpp
@@ -0,0 +1,102 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -fms-reference-binding %s
+
+struct A {};
+struct B : A {};
+
+B fB();
+A fA();
+
+A&& fARvalueRef();
+A(&&fARvalueRefArray())[1];
+void fARef(A&) {}
+
+void fIntRef(int&) {} // expected-note{{candidate function not viable: expects an lvalue for 1st argument}}
+void fDoubleRef(double&) {} // expected-note{{candidate function not viable: expects an lvalue for 1st argument}}
+
+void fIntConstRef(const int&) {}
+void fDoubleConstRef(const double&) {}
+
+void fIntArray(int (&)[1]); // expected-note{{candidate function not viable: expects an lvalue for 1st argument}}
+void fIntConstArray(const int (&)[1]);
+
+namespace NS {
+  void fARef(A&) {}
+
+  void fIntRef(int&) {} // expected-note{{passing argument to parameter here}}
+  void fDoubleRef(double&) {} // expected-note{{passing argument to parameter here}}
+
+  void fIntConstRef(const int&) {}
+  void fDoubleConstRef(const double&) {}
+
+  A(&&fARvalueRefArray())[1];
+
+  void fIntArray(int (&)[1]); // expected-note{{passing argument to parameter here}}
+
+  void fIntConstArray(const int (&)[1]);
+}
+
+void test1() {
+  double& rd2 = 2.0; // expected-error{{non-const lvalue reference to type 'double' cannot bind to a temporary of type 'double'}}
+  int& i1 = 0; // expected-error{{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}}
+
+  fIntRef(0); // expected-error{{no matching function for call to 'fIntRef'}}
+  fDoubleRef(0.0); // expected-error{{no matching function for call to 'fDoubleRef'}}
+
+  NS::fIntRef(0); // expected-error{{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}}
+  NS::fDoubleRef(0.0); // expected-error{{non-const lvalue reference to type 'double' cannot bind to a temporary of type 'double'}}
+
+  int i2 = 2;
+  double& rd3 = i2; // expected-error{{non-const lvalue reference to type 'double' cannot bind to a value of unrelated type 'int'}}
+}
+
+void test2() {
+  fIntConstRef(0);
+  fDoubleConstRef(0.0);
+
+  NS::fIntConstRef(0);
+  NS::fDoubleConstRef(0.0);
+
+  int i = 0;
+  const int ci = 0;
+  volatile int vi = 0;
+  const volatile int cvi = 0;
+  bool b = true;
+
+  const volatile int &cvir1 = b ? ci : vi; // expected-error{{volatile lvalue reference to type 'const volatile int' cannot bind to a temporary of type 'int'}}
+
+  volatile int& vir1 = 0; // expected-error{{volatile lvalue reference to type 'volatile int' cannot bind to a temporary of type 'int'}}
+  const volatile int& cvir2 = 0; // expected-error{{volatile lvalue reference to type 'const volatile int' cannot bind to a temporary of type 'int'}}
+}
+
+void test3() {
+  A& a1 = A();
+
+  fARef(A());
+  fARef(static_cast<A&&>(a1));
+  fARef(B());
+
+  NS::fARef(A());
+  NS::fARef(static_cast<A&&>(a1));
+  NS::fARef(B());
+
+  A& a2 = fA();
+
+  A& a3 = fARvalueRef();
+
+  const A& rca = fB();
+  A& ra = fB();
+}
+
+void test4() {
+  A (&array1)[1] = fARvalueRefArray(); // expected-error{{non-const lvalue reference to type 'A[1]' cannot bind to a temporary of type 'A[1]'}}
+  const A (&array2)[1] = fARvalueRefArray();
+
+  A (&array3)[1] = NS::fARvalueRefArray(); // expected-error{{non-const lvalue reference to type 'A[1]' cannot bind to a temporary of type 'A[1]'}}
+  const A (&array4)[1] = NS::fARvalueRefArray();
+
+  fIntArray({ 1 }); // expected-error{{no matching function for call to 'fIntArray'}}
+  NS::fIntArray({ 1 }); // expected-error{{non-const lvalue reference to type 'int[1]' cannot bind to an initializer list temporary}}
+
+  fIntConstArray({ 1 });
+  NS::fIntConstArray({ 1 });
+}

>From b688562d5d88805da468f990b126894df77985aa Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Sat, 20 Jul 2024 23:47:51 -0700
Subject: [PATCH 2/5] update description of lang opt

---
 clang/include/clang/Basic/LangOptions.def | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index 188cea2c900c7..ac4cabb9a82c7 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -306,7 +306,7 @@ LANGOPT(HIPStdParInterposeAlloc, 1, 0, "Replace allocations / deallocations with
 LANGOPT(OpenACC           , 1, 0, "OpenACC Enabled")
 
 LANGOPT(MSVCEnableStdcMacro , 1, 0, "Define __STDC__ with '-fms-compatibility'")
-LANGOPT(MSVCReferenceBinding , 1, 0, "Accept expressions that bind a non-const lvalue reference to a temporary")
+LANGOPT(MSVCReferenceBinding , 1, 0, "Accept expressions that bind a non-const lvalue reference to a user-defined type temporary")
 LANGOPT(SizedDeallocation , 1, 0, "sized deallocation")
 LANGOPT(AlignedAllocation , 1, 0, "aligned allocation")
 LANGOPT(AlignedAllocationUnavailable, 1, 0, "aligned allocation functions are unavailable")

>From 8d072ec7ff07b8edda16b278dc05f34ced5fb40e Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Mon, 22 Jul 2024 21:44:33 -0700
Subject: [PATCH 3/5] PR Feedback on SemaOverload.cpp if check variable scoping

---
 clang/lib/Sema/SemaOverload.cpp | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 70802848e789a..61e2f2084a9c5 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -5148,13 +5148,19 @@ TryReferenceInit(Sema &S, Expr *Init, QualType DeclType,
   //     -- Otherwise, the reference shall be an lvalue reference to a
   //        non-volatile const type (i.e., cv1 shall be const), or the reference
   //        shall be an rvalue reference.
-  const bool CanBindLValueRef =
-      !S.getLangOpts().MSVCReferenceBinding
-          ? (T1.isConstQualified() && !T1.isVolatileQualified())
-          : S.AllowMSLValueReferenceBinding(T1.getQualifiers(), T1);
+  if (!isRValRef) {
+    const bool CanBindLValueRef =
+        !S.getLangOpts().MSVCReferenceBinding
+            ? (T1.isConstQualified() && !T1.isVolatileQualified())
+            : S.AllowMSLValueReferenceBinding(T1.getQualifiers(), T1);
+    if (!CanBindLValueRef) {
+      if (InitCategory.isRValue() && RefRelationship != Sema::Ref_Incompatible)
+        ICS.setBad(BadConversionSequence::lvalue_ref_to_rvalue, Init, DeclType);
+      return ICS;
+    }
+  }
+
   if (!isRValRef && !CanBindLValueRef) {
-    if (InitCategory.isRValue() && RefRelationship != Sema::Ref_Incompatible)
-      ICS.setBad(BadConversionSequence::lvalue_ref_to_rvalue, Init, DeclType);
     return ICS;
   }
 

>From 3e1032116870fb54ffd30389e6be6aa2a8de65f8 Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Tue, 23 Jul 2024 20:40:06 -0700
Subject: [PATCH 4/5] Reorder logic around `AllowMSLValueReferenceBinding`

---
 clang/lib/Sema/SemaInit.cpp     |  3 +--
 clang/lib/Sema/SemaOverload.cpp | 26 ++++++--------------------
 2 files changed, 7 insertions(+), 22 deletions(-)

diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 0ccb2f285e766..91fe36491f18e 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -5215,8 +5215,7 @@ static void TryReferenceInitializationCore(Sema &S,
                                   ConvOvlResult);
     else if (!InitCategory.isLValue()) {
       if (T1Quals.isAddressSpaceSupersetOf(T2Quals)) {
-        if (!S.getLangOpts().MSVCReferenceBinding ||
-            !S.AllowMSLValueReferenceBinding(T1Quals, T1))
+        if (!S.AllowMSLValueReferenceBinding(T1Quals, T1))
           Sequence.SetFailed(InitializationSequence::
                                  FK_NonConstLValueReferenceBindingToTemporary);
       } else {
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 61e2f2084a9c5..3ba5f2139e3e2 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -4907,13 +4907,8 @@ Sema::CompareReferenceRelationship(SourceLocation Loc,
 }
 
 bool Sema::AllowMSLValueReferenceBinding(Qualifiers Quals, QualType QT) {
-  if (Quals.hasVolatile())
-    return false;
-  if (Quals.hasConst())
-    return true;
-  if (QT->isBuiltinType() || QT->isArrayType())
-    return false;
-  return true;
+  return getLangOpts().MSVCReferenceBinding &&
+         !(Quals.hasVolatile() || QT->isBuiltinType() || QT->isArrayType());
 }
 
 /// Look for a user-defined conversion to a value reference-compatible
@@ -5148,19 +5143,10 @@ TryReferenceInit(Sema &S, Expr *Init, QualType DeclType,
   //     -- Otherwise, the reference shall be an lvalue reference to a
   //        non-volatile const type (i.e., cv1 shall be const), or the reference
   //        shall be an rvalue reference.
-  if (!isRValRef) {
-    const bool CanBindLValueRef =
-        !S.getLangOpts().MSVCReferenceBinding
-            ? (T1.isConstQualified() && !T1.isVolatileQualified())
-            : S.AllowMSLValueReferenceBinding(T1.getQualifiers(), T1);
-    if (!CanBindLValueRef) {
-      if (InitCategory.isRValue() && RefRelationship != Sema::Ref_Incompatible)
-        ICS.setBad(BadConversionSequence::lvalue_ref_to_rvalue, Init, DeclType);
-      return ICS;
-    }
-  }
-
-  if (!isRValRef && !CanBindLValueRef) {
+  if (!isRValRef && (!T1.isConstQualified() || T1.isVolatileQualified()) &&
+      !S.AllowMSLValueReferenceBinding(T1.getQualifiers(), T1)) {
+    if (InitCategory.isRValue() && RefRelationship != Sema::Ref_Incompatible)
+      ICS.setBad(BadConversionSequence::lvalue_ref_to_rvalue, Init, DeclType);
     return ICS;
   }
 

>From 30024aa81f86166ba12a4cae1f8c9b3396482651 Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Mon, 29 Jul 2024 08:23:24 -0700
Subject: [PATCH 5/5] PR Feedback on ext warning

---
 clang/docs/LanguageExtensions.rst             | 28 +++++++++++++++++++
 clang/include/clang/Basic/DiagnosticGroups.td |  1 +
 .../clang/Basic/DiagnosticSemaKinds.td        |  4 +++
 clang/lib/Sema/SemaInit.cpp                   |  6 +++-
 clang/test/SemaCXX/ms-reference-binding.cpp   | 18 +++++++++++-
 5 files changed, 55 insertions(+), 2 deletions(-)

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 81784c75081ba..8599f8c4c23d1 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -5820,3 +5820,31 @@ specify the starting offset to begin embedding from. The resources is treated
 as being empty if the specified offset is larger than the number of bytes in
 the resource. The offset will be applied *before* any ``limit`` parameters are
 applied.
+
+MSVC Extensions
+===============
+
+Clang supports a number of extensions inorder to imitate MSVC.
+Some of these extensions are behind ``-fms-compatibility`` and ``-fms-extensions`` which
+are further described in :doc:`MSVCCompatibility`.
+
+MSVC Reference Binding
+----------------------
+
+.. code-block:: c++
+
+  struct A {};
+
+  A& a = A();
+
+Please see the `MSDN doc<https://learn.microsoft.com/en-us/cpp/build/reference/zc-referencebinding-enforce-reference-binding-rules>`_ for more information on this non-conforming C++ extension.
+
+MSVC allows user-defined type temporaries to be bound to non-const lvalue references when `/permissive`
+or `/Zc:referenceBinding-` are given on the command line.
+
+The current default behavior as of MSVC 1940 is `/permissive`.
+As of Visual Studio 2017, `/permissive-` is the default for projects meaning C++ conformance is enforced when
+building with MSVC in Visual Studio.
+
+This MSVC extension can be enabled with ``-fms-reference-binding`` with the clang or cl driver.
+This MSVC extension can be enabled with ``/Zc:referenceBinding-`` with the cl driver.
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 19c3f1e043349..eb38f89f040b5 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1289,6 +1289,7 @@ def MicrosoftStaticAssert : DiagGroup<"microsoft-static-assert">;
 def MicrosoftInitFromPredefined : DiagGroup<"microsoft-init-from-predefined">;
 def MicrosoftStringLiteralFromPredefined : DiagGroup<
     "microsoft-string-literal-from-predefined">;
+def MicrosoftReferenceBinding : DiagGroup<"microsoft-reference-binding">;
 
 // Aliases.
 def : DiagGroup<"msvc-include", [MicrosoftInclude]>;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d60f32674ca3a..f02d1af48bb73 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2424,6 +2424,10 @@ def note_explicit_ctor_deduction_guide_here : Note<
   "explicit %select{constructor|deduction guide}0 declared here">;
 def note_implicit_deduction_guide : Note<"implicit deduction guide declared as '%0'">;
 
+def ext_ms_lvalue_reference_binding : ExtWarn<
+  "binding a user-defined type temporary to a non-const lvalue is a "
+  "Microsoft extension">, InGroup<MicrosoftReferenceBinding>;
+
 // C++11 auto
 def warn_cxx98_compat_auto_type_specifier : Warning<
   "'auto' type specifier is incompatible with C++98">,
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 91fe36491f18e..9d831bcf67c49 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -5215,9 +5215,13 @@ static void TryReferenceInitializationCore(Sema &S,
                                   ConvOvlResult);
     else if (!InitCategory.isLValue()) {
       if (T1Quals.isAddressSpaceSupersetOf(T2Quals)) {
-        if (!S.AllowMSLValueReferenceBinding(T1Quals, T1))
+        if (S.AllowMSLValueReferenceBinding(T1Quals, T1)) {
+          S.Diag(DeclLoc, diag::ext_ms_lvalue_reference_binding)
+              << Initializer->getSourceRange();
+        } else {
           Sequence.SetFailed(InitializationSequence::
                                  FK_NonConstLValueReferenceBindingToTemporary);
+        }
       } else {
         Sequence.SetFailed(
             InitializationSequence::FK_ReferenceInitDropsQualifiers);
diff --git a/clang/test/SemaCXX/ms-reference-binding.cpp b/clang/test/SemaCXX/ms-reference-binding.cpp
index 44e4723fd4fa3..91e25959a8754 100644
--- a/clang/test/SemaCXX/ms-reference-binding.cpp
+++ b/clang/test/SemaCXX/ms-reference-binding.cpp
@@ -1,4 +1,18 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -fms-reference-binding %s
+// RUN: %clang_cc1 -fsyntax-only -Wno-microsoft-reference-binding -verify -fms-reference-binding %s
+// RUN: %clang_cc1 -DEXTWARN -fsyntax-only -verify -fms-reference-binding %s
+
+#ifdef EXTWARN
+
+struct A {};
+void fARef(A&) {}
+
+void test() {
+  A& a1 = A(); // expected-warning{{binding a user-defined type temporary to a non-const lvalue is a Microsoft extension}}
+
+  fARef(A()); // expected-warning{{binding a user-defined type temporary to a non-const lvalue is a Microsoft extension}}
+}
+
+#else
 
 struct A {};
 struct B : A {};
@@ -100,3 +114,5 @@ void test4() {
   fIntConstArray({ 1 });
   NS::fIntConstArray({ 1 });
 }
+
+#endif



More information about the cfe-commits mailing list