[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 Dec 9 16:49:43 PST 2024
https://github.com/MaxEW707 updated https://github.com/llvm/llvm-project/pull/99833
>From 0a705b1a8e9673cd5e803ffe392dacfa0f06c40f 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 01/14] 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 02284225fb4fa1..3e8f7a8f7d889e 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -419,6 +419,10 @@ New Compiler Flags
existing ``-fno-c++-static-destructors`` flag) skips all static
destructors registration.
+- ``-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 39e4851dd3814c..c0451eaae344de 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -311,6 +311,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 4bc0b97ea68f2f..4f963a18297ee7 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -3096,6 +3096,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]>,
@@ -8683,6 +8689,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 b8684d11460eda..ada5184eb71b05 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -10144,6 +10144,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 99a092d83d531a..3d6d170aee4f34 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7215,6 +7215,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 80799d1e715f07..28731fdae50285 100644
--- a/clang/lib/Driver/ToolChains/MSVC.cpp
+++ b/clang/lib/Driver/ToolChains/MSVC.cpp
@@ -947,6 +947,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 7c03a12e812809..c369a53742772c 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -5329,13 +5329,17 @@ static void TryReferenceInitializationCore(Sema &S,
Sequence.SetOverloadFailure(
InitializationSequence::FK_ReferenceInitOverloadFailed,
ConvOvlResult);
- else if (!InitCategory.isLValue())
- Sequence.SetFailed(
- T1Quals.isAddressSpaceSupersetOf(T2Quals, S.getASTContext())
- ? InitializationSequence::
- FK_NonConstLValueReferenceBindingToTemporary
- : InitializationSequence::FK_ReferenceInitDropsQualifiers);
- else {
+ else if (!InitCategory.isLValue()) {
+ if (T1Quals.isAddressSpaceSupersetOf(T2Quals, S.getASTContext())) {
+ 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:
@@ -5361,7 +5365,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 c174922a926fc6..957d667a4b5545 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -5013,6 +5013,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
@@ -5245,7 +5255,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 d88746d3a5517f..712bef71a43af1 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 5f0cd5e00819dd..d2c27975dd6591 100644
--- a/clang/test/Driver/cl-zc.cpp
+++ b/clang/test/Driver/cl-zc.cpp
@@ -125,6 +125,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 00000000000000..44e4723fd4fa3a
--- /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 7d5d0b15fce06e2631b4f526f2a31e9aa4ffc971 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 02/14] 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 c0451eaae344de..b3a706cfd7b318 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -311,7 +311,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 6ba101e8f7ab42193b074b3e587955be4128e0ed 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 03/14] 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 957d667a4b5545..7e4f1795bd3db7 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -5255,13 +5255,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 2b417ccb9dba0e3f62a4fe46f85882247b03f701 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 04/14] Reorder logic around `AllowMSLValueReferenceBinding`
---
clang/lib/Sema/SemaOverload.cpp | 26 ++++++--------------------
1 file changed, 6 insertions(+), 20 deletions(-)
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 7e4f1795bd3db7..f2c7906ae45088 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -5014,13 +5014,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
@@ -5255,19 +5250,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 ec7a263fd9c36ebcb77dd806ee8b926f8f5650d1 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 05/14] PR Feedback on ext warning
---
clang/docs/LanguageExtensions.rst | 30 +++++++++++++++++++
clang/include/clang/Basic/DiagnosticGroups.td | 1 +
.../clang/Basic/DiagnosticSemaKinds.td | 4 +++
clang/lib/Sema/SemaInit.cpp | 7 +++--
clang/test/SemaCXX/ms-reference-binding.cpp | 18 ++++++++++-
5 files changed, 57 insertions(+), 3 deletions(-)
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 6b950d05fb9bf9..e7ba4319a7d8b0 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -6015,3 +6015,33 @@ qualifications.
Note, Clang does not allow an ``_Atomic`` function type because
of explicit constraints against atomically qualified (arrays and) function
types.
+
+
+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.
+>>>>>>> 43d9c2ac844e (PR Feedback on ext warning)
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 3ac490d30371b1..c87c9d6780b50b 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1302,6 +1302,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 8020be6c57bcfb..1c2c2583896bb0 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2452,6 +2452,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 c369a53742772c..e9f86ff552cfd5 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -5331,10 +5331,13 @@ static void TryReferenceInitializationCore(Sema &S,
ConvOvlResult);
else if (!InitCategory.isLValue()) {
if (T1Quals.isAddressSpaceSupersetOf(T2Quals, S.getASTContext())) {
- if (!S.getLangOpts().MSVCReferenceBinding ||
- !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 44e4723fd4fa3a..91e25959a8754b 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
>From d811e1f806fabaa967a65c9eae2cba9a69ef5bd0 Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Mon, 29 Jul 2024 08:25:39 -0700
Subject: [PATCH 06/14] quoting
---
clang/docs/LanguageExtensions.rst | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index e7ba4319a7d8b0..f98c8d61ee237d 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -6035,11 +6035,11 @@ MSVC Reference Binding
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.
+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
+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.
>From 6d87850db848d14e22db5a1a56adbf28ba94e05e Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Mon, 29 Jul 2024 18:57:33 -0700
Subject: [PATCH 07/14] Fix doc build
---
clang/docs/LanguageExtensions.rst | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index f98c8d61ee237d..d54c0d7aecebea 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -6033,7 +6033,9 @@ MSVC Reference Binding
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.
+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.
>From 6e6f7a55df7e1ab76fab4a7eecbb0630822b9036 Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Mon, 29 Jul 2024 21:41:52 -0700
Subject: [PATCH 08/14] Default ms reference binding to on unless C++20 similar
to delayed-template-parsing
---
clang/lib/Driver/ToolChains/Clang.cpp | 11 ++++++-----
clang/lib/Driver/ToolChains/MSVC.cpp | 1 +
clang/test/Driver/cl-permissive.c | 2 +-
3 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 3d6d170aee4f34..75af8cfe887fac 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7215,11 +7215,6 @@ 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,
@@ -7386,6 +7381,12 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back("-fdelayed-template-parsing");
}
+ // -fno-ms-reference-binding is the default.
+ if (Args.hasFlag(options::OPT_fms_reference_binding,
+ options::OPT_fno_ms_reference_binding,
+ IsWindowsMSVC && !HaveCxx20))
+ CmdArgs.push_back("-fms-reference-binding");
+
if (Args.hasFlag(options::OPT_fpch_validate_input_files_content,
options::OPT_fno_pch_validate_input_files_content, false))
CmdArgs.push_back("-fvalidate-ast-input-files-content");
diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp
index 28731fdae50285..8aca222807e2e2 100644
--- a/clang/lib/Driver/ToolChains/MSVC.cpp
+++ b/clang/lib/Driver/ToolChains/MSVC.cpp
@@ -954,6 +954,7 @@ static void TranslatePermissiveMinus(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_foperator_names));
+ DAL.AddFlagArg(A, Opts.getOption(options::OPT_fno_ms_reference_binding));
}
llvm::opt::DerivedArgList *
diff --git a/clang/test/Driver/cl-permissive.c b/clang/test/Driver/cl-permissive.c
index 712bef71a43af1..e325fc02d0d50a 100644
--- a/clang/test/Driver/cl-permissive.c
+++ b/clang/test/Driver/cl-permissive.c
@@ -3,8 +3,8 @@
// RUN: %clang_cl /permissive -### -- %s 2>&1 | FileCheck -check-prefix=PERMISSIVE %s
// PERMISSIVE: "-fno-operator-names"
-// PERMISSIVE: "-fms-reference-binding"
// PERMISSIVE: "-fdelayed-template-parsing"
+// PERMISSIVE: "-fms-reference-binding"
// 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"
>From c85ecb5a20ac4350fcac3274cd5eeb7eb65032de Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Tue, 30 Jul 2024 20:07:47 -0700
Subject: [PATCH 09/14] remove old comment
---
clang/lib/Driver/ToolChains/Clang.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 75af8cfe887fac..f1f530c6c33a64 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7381,7 +7381,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back("-fdelayed-template-parsing");
}
- // -fno-ms-reference-binding is the default.
if (Args.hasFlag(options::OPT_fms_reference_binding,
options::OPT_fno_ms_reference_binding,
IsWindowsMSVC && !HaveCxx20))
>From 18cde6dcbbe67c5afba9642340ffaa9126540ab8 Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Wed, 31 Jul 2024 21:26:29 -0700
Subject: [PATCH 10/14] Fix overload resolution and only consider msvc binding
if we are an rvalue
---
clang/lib/Sema/SemaOverload.cpp | 32 ++++++++++++++---
.../test/CodeGenCXX/ms-reference-binding.cpp | 30 ++++++++++++++++
clang/test/SemaCXX/ms-reference-binding.cpp | 35 ++++++++++++++++++-
3 files changed, 91 insertions(+), 6 deletions(-)
create mode 100644 clang/test/CodeGenCXX/ms-reference-binding.cpp
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index f2c7906ae45088..003fc860c125ff 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -4552,6 +4552,24 @@ CompareStandardConversionSequences(Sema &S, SourceLocation Loc,
T1 = S.Context.getQualifiedType(UnqualT1, T1Quals);
if (isa<ArrayType>(T2) && T2Quals)
T2 = S.Context.getQualifiedType(UnqualT2, T2Quals);
+
+ if (S.getLangOpts().MSVCReferenceBinding &&
+ S.Context.hasSameType(SCS1.getFromType(), SCS2.getFromType()) &&
+ !SCS1.getFromType().hasQualifiers() && SCS1.BindsToRvalue &&
+ SCS2.BindsToRvalue) {
+
+ // When binding a user-defined type temporary to an lvalue MSVC will
+ // pick `const T&` over `T&` when binding an unqualified `T&&`.
+ // Therefore we want to pick the more qualified const overload in this
+ // specific case.
+ if (!T2.hasQualifiers() &&
+ (T1.isConstQualified() && !T1.isVolatileQualified()))
+ return ImplicitConversionSequence::Better;
+ if (!T1.hasQualifiers() &&
+ (T2.isConstQualified() && !T2.isVolatileQualified()))
+ return ImplicitConversionSequence::Worse;
+ }
+
if (T2.isMoreQualifiedThan(T1, S.getASTContext()))
return ImplicitConversionSequence::Better;
if (T1.isMoreQualifiedThan(T2, S.getASTContext()))
@@ -5250,11 +5268,15 @@ 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()) &&
- !S.AllowMSLValueReferenceBinding(T1.getQualifiers(), T1)) {
- if (InitCategory.isRValue() && RefRelationship != Sema::Ref_Incompatible)
- ICS.setBad(BadConversionSequence::lvalue_ref_to_rvalue, Init, DeclType);
- return ICS;
+ if (!isRValRef && (!T1.isConstQualified() || T1.isVolatileQualified())) {
+ if (InitCategory.isRValue() && RefRelationship != Sema::Ref_Incompatible) {
+ if (!S.AllowMSLValueReferenceBinding(T1.getQualifiers(), T1)) {
+ ICS.setBad(BadConversionSequence::lvalue_ref_to_rvalue, Init, DeclType);
+ return ICS;
+ }
+ } else {
+ return ICS;
+ }
}
// -- If the initializer expression
diff --git a/clang/test/CodeGenCXX/ms-reference-binding.cpp b/clang/test/CodeGenCXX/ms-reference-binding.cpp
new file mode 100644
index 00000000000000..6f8ca371914222
--- /dev/null
+++ b/clang/test/CodeGenCXX/ms-reference-binding.cpp
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -triple x86_64-windows-msvc %s -emit-llvm -fms-extensions -fms-compatibility -fms-reference-binding -o - | FileCheck %s
+
+struct A {};
+struct B : A {};
+
+void fAPickConstRef(A&) {}
+void fAPickConstRef(const A&) {}
+
+void fBPickConstRef(A&) {}
+void fBPickConstRef(const A&) {}
+
+void fAPickRef(A&) {}
+void fAPickRef(const volatile A&) {}
+
+void fAPickRef2(A&) {}
+void fAPickRef2(const volatile A&) {}
+
+void test() {
+ fAPickConstRef(A());
+ // CHECK: call {{.*}} @"?fAPickConstRef@@YAXAEBUA@@@Z"
+
+ fBPickConstRef(B());
+ // CHECK: call {{.*}} @"?fBPickConstRef@@YAXAEBUA@@@Z"
+
+ fAPickRef(A());
+ // CHECK: call {{.*}} @"?fAPickRef@@YAXAEAUA@@@Z"
+
+ fAPickRef2(A());
+ // CHECK: call {{.*}} @"?fAPickRef2@@YAXAEAUA@@@Z"
+}
diff --git a/clang/test/SemaCXX/ms-reference-binding.cpp b/clang/test/SemaCXX/ms-reference-binding.cpp
index 91e25959a8754b..27201ef2bf63b7 100644
--- a/clang/test/SemaCXX/ms-reference-binding.cpp
+++ b/clang/test/SemaCXX/ms-reference-binding.cpp
@@ -6,12 +6,28 @@
struct A {};
void fARef(A&) {}
-void test() {
+void test1() {
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}}
}
+void fARefDoNotWarn(A&) {}
+void fARefDoNotWarn(const A&) {}
+
+// expected-note at +2 {{candidate function not viable: 1st argument ('const A') would lose const qualifier}}
+// expected-note at +1 {{candidate function not viable: 1st argument ('const A') would lose const qualifier}}
+void fARefLoseConstQualifier(A&) {}
+
+void test2() {
+ // This should not warn since `fARef2(const A&)` is a better candidate
+ fARefDoNotWarn(A());
+
+ const A a;
+ fARefLoseConstQualifier(a); // expected-error{{no matching function for call to 'fARefLoseConstQualifier'}}
+ fARefLoseConstQualifier(static_cast<const A&&>(a)); // expected-error{{no matching function for call to 'fARefLoseConstQualifier'}}
+}
+
#else
struct A {};
@@ -22,8 +38,13 @@ A fA();
A&& fARvalueRef();
A(&&fARvalueRefArray())[1];
+
void fARef(A&) {}
+// expected-note at +2 {{candidate function not viable: expects an lvalue for 1st argument}}
+// expected-note at +1 {{candidate function not viable: expects an lvalue for 1st argument}}
+void fAVolatileRef(volatile 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}}
@@ -36,6 +57,10 @@ void fIntConstArray(const int (&)[1]);
namespace NS {
void fARef(A&) {}
+ // expected-note at +2 {{passing argument to parameter here}}
+ // expected-note at +1 {{passing argument to parameter here}}
+ void fAVolatileRef(volatile A&) {}
+
void fIntRef(int&) {} // expected-note{{passing argument to parameter here}}
void fDoubleRef(double&) {} // expected-note{{passing argument to parameter here}}
@@ -87,10 +112,18 @@ void test3() {
fARef(A());
fARef(static_cast<A&&>(a1));
+
+ fAVolatileRef(A()); // expected-error{{no matching function for call to 'fAVolatileRef'}}
+ fAVolatileRef(static_cast<A&&>(a1)); // expected-error{{no matching function for call to 'fAVolatileRef'}}
+
fARef(B());
NS::fARef(A());
NS::fARef(static_cast<A&&>(a1));
+
+ NS::fAVolatileRef(A()); // expected-error{{volatile lvalue reference to type 'volatile A' cannot bind to a temporary of type 'A'}}
+ NS::fAVolatileRef(static_cast<A&&>(a1)); // expected-error{{volatile lvalue reference to type 'volatile A' cannot bind to a temporary of type 'A'}}
+
NS::fARef(B());
A& a2 = fA();
>From a9db45696c9ed2bdff860993211b7769117c0c4e Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Wed, 31 Jul 2024 21:30:08 -0700
Subject: [PATCH 11/14] fix comment
---
clang/test/SemaCXX/ms-reference-binding.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/test/SemaCXX/ms-reference-binding.cpp b/clang/test/SemaCXX/ms-reference-binding.cpp
index 27201ef2bf63b7..9b774c2d4989ee 100644
--- a/clang/test/SemaCXX/ms-reference-binding.cpp
+++ b/clang/test/SemaCXX/ms-reference-binding.cpp
@@ -20,7 +20,7 @@ void fARefDoNotWarn(const A&) {}
void fARefLoseConstQualifier(A&) {}
void test2() {
- // This should not warn since `fARef2(const A&)` is a better candidate
+ // This should not warn since `fARefDoNotWarn(const A&)` is a better candidate
fARefDoNotWarn(A());
const A a;
>From b9c5b8d92db505c00c564cdfbc468fd57b01a235 Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Thu, 1 Aug 2024 00:09:52 -0700
Subject: [PATCH 12/14] Fix CXX method functions
---
clang/lib/Sema/SemaOverload.cpp | 4 +-
.../test/CodeGenCXX/ms-reference-binding.cpp | 52 ++++++++++++++++++-
2 files changed, 54 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 003fc860c125ff..c7d5f7b77a2b3c 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -4556,7 +4556,9 @@ CompareStandardConversionSequences(Sema &S, SourceLocation Loc,
if (S.getLangOpts().MSVCReferenceBinding &&
S.Context.hasSameType(SCS1.getFromType(), SCS2.getFromType()) &&
!SCS1.getFromType().hasQualifiers() && SCS1.BindsToRvalue &&
- SCS2.BindsToRvalue) {
+ SCS2.BindsToRvalue &&
+ !SCS1.BindsImplicitObjectArgumentWithoutRefQualifier &&
+ !SCS2.BindsImplicitObjectArgumentWithoutRefQualifier) {
// When binding a user-defined type temporary to an lvalue MSVC will
// pick `const T&` over `T&` when binding an unqualified `T&&`.
diff --git a/clang/test/CodeGenCXX/ms-reference-binding.cpp b/clang/test/CodeGenCXX/ms-reference-binding.cpp
index 6f8ca371914222..bca13f24dc918c 100644
--- a/clang/test/CodeGenCXX/ms-reference-binding.cpp
+++ b/clang/test/CodeGenCXX/ms-reference-binding.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-windows-msvc %s -emit-llvm -fms-extensions -fms-compatibility -fms-reference-binding -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-windows-msvc %s -emit-llvm -fms-extensions -fms-compatibility -fms-reference-binding -Wno-microsoft-reference-binding -o - | FileCheck %s
struct A {};
struct B : A {};
@@ -15,6 +15,31 @@ void fAPickRef(const volatile A&) {}
void fAPickRef2(A&) {}
void fAPickRef2(const volatile A&) {}
+namespace NS {
+ void fAPickConstRef(A&) {}
+ void fAPickConstRef(const A&) {}
+
+ void fBPickConstRef(A&) {}
+ void fBPickConstRef(const A&) {}
+
+ void fAPickRef(A&) {}
+ void fAPickRef(const volatile A&) {}
+
+ void fAPickRef2(A&) {}
+ void fAPickRef2(const volatile A&) {}
+}
+
+struct S {
+ void memberPickNonConst() {}
+ void memberPickNonConst() const {}
+
+ void memberPickConstRef() const & {}
+ void memberPickConstRef() & {}
+
+ static void fAPickConstRef(A&) {}
+ static void fAPickConstRef(const A&) {}
+};
+
void test() {
fAPickConstRef(A());
// CHECK: call {{.*}} @"?fAPickConstRef@@YAXAEBUA@@@Z"
@@ -27,4 +52,29 @@ void test() {
fAPickRef2(A());
// CHECK: call {{.*}} @"?fAPickRef2@@YAXAEAUA@@@Z"
+
+ NS::fAPickConstRef(A());
+ // CHECK: call {{.*}} @"?fAPickConstRef at NS@@YAXAEBUA@@@Z"
+
+ NS::fBPickConstRef(B());
+ // CHECK: call {{.*}} @"?fBPickConstRef at NS@@YAXAEBUA@@@Z"
+
+ NS::fAPickRef(A());
+ // CHECK: call {{.*}} @"?fAPickRef at NS@@YAXAEAUA@@@Z"
+
+ NS::fAPickRef2(A());
+ // CHECK: call {{.*}} @"?fAPickRef2 at NS@@YAXAEAUA@@@Z"
+
+ S::fAPickConstRef(A());
+ // CHECK: call {{.*}} @"?fAPickConstRef at S@@SAXAEBUA@@@Z"
+}
+
+void test_member_call() {
+ S s;
+
+ static_cast<S&&>(s).memberPickNonConst();
+ // CHECK: call {{.*}} @"?memberPickNonConst at S@@QEAAXXZ"
+
+ static_cast<S&&>(s).memberPickConstRef();
+ // CHECK: call {{.*}} @"?memberPickConstRef at S@@QEGBAXXZ"
}
>From 71babd19c214d01fcd299ae7a63c7629170fdd78 Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Mon, 2 Dec 2024 22:46:08 -0800
Subject: [PATCH 13/14] Disable reference binding by default and fix options
comments
---
clang/include/clang/Driver/Options.td | 8 +++++---
clang/lib/Driver/ToolChains/Clang.cpp | 2 +-
clang/lib/Driver/ToolChains/MSVC.cpp | 1 -
clang/test/Driver/cl-permissive.c | 1 -
clang/test/Driver/cl-zc.cpp | 2 ++
5 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 4f963a18297ee7..64599a7559c8d6 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -3098,7 +3098,8 @@ def fms_extensions : Flag<["-"], "fms-extensions">, Group<f_Group>,
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">,
+ 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]>;
@@ -8690,10 +8691,11 @@ def _SLASH_Zc_wchar_t : CLFlag<"Zc:wchar_t">,
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">,
+ HelpText<"Do not accept expressions that bind a non-const lvalue reference to a user-defined type temporary (default)">,
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">,
+ 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">;
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index f1f530c6c33a64..61642071ea3d94 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7383,7 +7383,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
if (Args.hasFlag(options::OPT_fms_reference_binding,
options::OPT_fno_ms_reference_binding,
- IsWindowsMSVC && !HaveCxx20))
+ false))
CmdArgs.push_back("-fms-reference-binding");
if (Args.hasFlag(options::OPT_fpch_validate_input_files_content,
diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp
index 8aca222807e2e2..e3a0434e648fae 100644
--- a/clang/lib/Driver/ToolChains/MSVC.cpp
+++ b/clang/lib/Driver/ToolChains/MSVC.cpp
@@ -947,7 +947,6 @@ 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/test/Driver/cl-permissive.c b/clang/test/Driver/cl-permissive.c
index e325fc02d0d50a..b70a98d3564231 100644
--- a/clang/test/Driver/cl-permissive.c
+++ b/clang/test/Driver/cl-permissive.c
@@ -4,7 +4,6 @@
// RUN: %clang_cl /permissive -### -- %s 2>&1 | FileCheck -check-prefix=PERMISSIVE %s
// PERMISSIVE: "-fno-operator-names"
// PERMISSIVE: "-fdelayed-template-parsing"
-// PERMISSIVE: "-fms-reference-binding"
// 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"
diff --git a/clang/test/Driver/cl-zc.cpp b/clang/test/Driver/cl-zc.cpp
index d2c27975dd6591..e6b885c4bde7ff 100644
--- a/clang/test/Driver/cl-zc.cpp
+++ b/clang/test/Driver/cl-zc.cpp
@@ -127,6 +127,8 @@
// RUN: %clang_cl -c -### /Zc:referenceBinding- -- %s 2>&1 | FileCheck -check-prefix CHECK-REFERENCE_BINDING_ %s
// CHECK-REFERENCE_BINDING_: "-fms-reference-binding"
+// RUN: %clang_cl -c -### /Zc:referenceBinding -- %s 2>&1 | FileCheck -check-prefix CHECK-REFERENCE_BINDING %s
+// CHECK-REFERENCE_BINDING-NOT: "-fms-reference-binding"
// These never warn, but don't have an effect yet.
>From 9ceb05b38efe57c1bb3ea3f708839f599efb4c24 Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Sun, 8 Dec 2024 22:23:26 -0800
Subject: [PATCH 14/14] Fix inorder spelling
---
clang/docs/LanguageExtensions.rst | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index d54c0d7aecebea..78d52243da3248 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -6020,7 +6020,7 @@ types.
MSVC Extensions
===============
-Clang supports a number of extensions inorder to imitate MSVC.
+Clang supports a number of extensions in order to imitate MSVC.
Some of these extensions are behind ``-fms-compatibility`` and ``-fms-extensions`` which
are further described in :doc:`MSVCCompatibility`.
@@ -6040,9 +6040,9 @@ 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.
+The current default behavior as of MSVC 1942 is ``/permissive``.
+As of Visual Studio 2017, ``/permissive-`` is the default for newly generated projects in Visual Studio 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.
More information about the cfe-commits
mailing list