[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 16 18:27:13 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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.
>From 4a0c669a618f75013fc791d69b11edf51e6d9053 Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Mon, 9 Dec 2024 17:01:49 -0800
Subject: [PATCH 15/24] cl -> clang-cl
---
clang/docs/LanguageExtensions.rst | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 78d52243da3248..5f032817510113 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -6044,6 +6044,6 @@ 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.
+This MSVC extension can be enabled with ``-fms-reference-binding`` with the clang or clang-cl driver.
+This MSVC extension can be enabled with ``/Zc:referenceBinding-`` with the clang-cl driver.
>>>>>>> 43d9c2ac844e (PR Feedback on ext warning)
>From 08227eab44b24d343eb47084034558e7459ea990 Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Tue, 10 Dec 2024 17:25:12 -0800
Subject: [PATCH 16/24] Add unit tests for default arguments
---
clang/test/SemaCXX/ms-reference-binding.cpp | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/clang/test/SemaCXX/ms-reference-binding.cpp b/clang/test/SemaCXX/ms-reference-binding.cpp
index 9b774c2d4989ee..c8ba7fb0564d03 100644
--- a/clang/test/SemaCXX/ms-reference-binding.cpp
+++ b/clang/test/SemaCXX/ms-reference-binding.cpp
@@ -33,6 +33,9 @@ void test2() {
struct A {};
struct B : A {};
+void fADefaultArgRef(A& = A{});
+void fBDefaultArgRef(A& = B{});
+
B fB();
A fA();
@@ -148,4 +151,13 @@ void test4() {
NS::fIntConstArray({ 1 });
}
+void test5() {
+ fADefaultArgRef();
+ fADefaultArgRef(A());
+
+ fBDefaultArgRef();
+ fBDefaultArgRef(B());
+ fBDefaultArgRef(A());
+}
+
#endif
>From 2d72da07daad75139a07cc8d5eeee1e6e12697a6 Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Tue, 10 Dec 2024 17:29:45 -0800
Subject: [PATCH 17/24] Add another default arg unit test
---
clang/test/SemaCXX/ms-reference-binding.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/clang/test/SemaCXX/ms-reference-binding.cpp b/clang/test/SemaCXX/ms-reference-binding.cpp
index c8ba7fb0564d03..a9231324876e7c 100644
--- a/clang/test/SemaCXX/ms-reference-binding.cpp
+++ b/clang/test/SemaCXX/ms-reference-binding.cpp
@@ -42,6 +42,8 @@ A fA();
A&& fARvalueRef();
A(&&fARvalueRefArray())[1];
+void fADefaultArgRef2(A& = fARvalueRef());
+
void fARef(A&) {}
// expected-note at +2 {{candidate function not viable: expects an lvalue for 1st argument}}
>From d8da7651b5b426db91c583651d2eec9514c7065f Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Tue, 10 Dec 2024 17:44:54 -0800
Subject: [PATCH 18/24] Convert to addOptInFlag
---
clang/lib/Driver/ToolChains/Clang.cpp | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 61642071ea3d94..c7e6617e0b97a6 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7381,10 +7381,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back("-fdelayed-template-parsing");
}
- if (Args.hasFlag(options::OPT_fms_reference_binding,
- options::OPT_fno_ms_reference_binding,
- false))
- CmdArgs.push_back("-fms-reference-binding");
+ Args.addOptInFlag(CmdArgs, options::OPT_fms_reference_binding,
+ options::OPT_fno_ms_reference_binding);
if (Args.hasFlag(options::OPT_fpch_validate_input_files_content,
options::OPT_fno_pch_validate_input_files_content, false))
>From a0c36f83fa64576230e166446180d82ca3377b39 Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Tue, 10 Dec 2024 19:32:04 -0800
Subject: [PATCH 19/24] Typedef lvalue ref tests
---
clang/test/SemaCXX/ms-reference-binding.cpp | 26 +++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/clang/test/SemaCXX/ms-reference-binding.cpp b/clang/test/SemaCXX/ms-reference-binding.cpp
index a9231324876e7c..6207c716917468 100644
--- a/clang/test/SemaCXX/ms-reference-binding.cpp
+++ b/clang/test/SemaCXX/ms-reference-binding.cpp
@@ -33,9 +33,13 @@ void test2() {
struct A {};
struct B : A {};
+typedef A AAlias;
+
void fADefaultArgRef(A& = A{});
void fBDefaultArgRef(A& = B{});
+void fAAliasDefaultArgRef(AAlias& = AAlias{});
+
B fB();
A fA();
@@ -45,6 +49,7 @@ A(&&fARvalueRefArray())[1];
void fADefaultArgRef2(A& = fARvalueRef());
void fARef(A&) {}
+void fAAliasRef(AAlias&) {}
// 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}}
@@ -61,6 +66,7 @@ void fIntConstArray(const int (&)[1]);
namespace NS {
void fARef(A&) {}
+ void fAAliasRef(AAlias&) {}
// expected-note at +2 {{passing argument to parameter here}}
// expected-note at +1 {{passing argument to parameter here}}
@@ -114,29 +120,45 @@ void test2() {
void test3() {
A& a1 = A();
+ AAlias& aalias1 = A();
fARef(A());
fARef(static_cast<A&&>(a1));
+ fAAliasRef(A());
+ fAAliasRef(static_cast<A&&>(a1));
+ fAAliasRef(AAlias());
+ fAAliasRef(static_cast<AAlias&&>(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());
+ fAAliasRef(B());
NS::fARef(A());
NS::fARef(static_cast<A&&>(a1));
+ NS::fAAliasRef(A());
+ NS::fAAliasRef(static_cast<A&&>(a1));
+ NS::fAAliasRef(AAlias());
+ NS::fAAliasRef(static_cast<AAlias&&>(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());
+ NS::fAAliasRef(B());
A& a2 = fA();
+ AAlias& aalias2 = fA();
A& a3 = fARvalueRef();
+ AAlias& aalias3 = fARvalueRef();
const A& rca = fB();
A& ra = fB();
+ AAlias& raalias = fB();
}
void test4() {
@@ -160,6 +182,10 @@ void test5() {
fBDefaultArgRef();
fBDefaultArgRef(B());
fBDefaultArgRef(A());
+
+ fAAliasDefaultArgRef(A());
+ fAAliasDefaultArgRef(B());
+ fAAliasDefaultArgRef(AAlias());
}
#endif
>From ffe5bd4de6f6184616064b94f383419591e5c12c Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Wed, 11 Dec 2024 23:49:32 -0800
Subject: [PATCH 20/24] Add ref related derived to base cv binding test
---
.../test/CodeGenCXX/ms-reference-binding.cpp | 23 ++++++++++++-------
1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/clang/test/CodeGenCXX/ms-reference-binding.cpp b/clang/test/CodeGenCXX/ms-reference-binding.cpp
index bca13f24dc918c..c4a4067b5f34fb 100644
--- a/clang/test/CodeGenCXX/ms-reference-binding.cpp
+++ b/clang/test/CodeGenCXX/ms-reference-binding.cpp
@@ -12,8 +12,14 @@ void fBPickConstRef(const A&) {}
void fAPickRef(A&) {}
void fAPickRef(const volatile A&) {}
-void fAPickRef2(A&) {}
-void fAPickRef2(const volatile A&) {}
+// NOTE: MSVC incorrectly picks the `const volatile A&` overload with the mangled name
+// "?fBPickConstVolatileRef@@YAXAEDUA@@@Z" when converting a derived class `B` to base `A`.
+// This occurs even with conforming reference binding enabled so this isn't a one off
+// behaviour with non-conforming MSVC specific reference binding but appears to be a bug.
+// A bug report has been submitted to MSVC. This bug has been verified upto MSVC 19.40.
+// We are not emulating this behaviour and instead will pick the `A&` overload as intended.
+void fBPickConstVolatileRef(A&) {}
+void fBPickConstVolatileRef(const volatile A&) {}
namespace NS {
void fAPickConstRef(A&) {}
@@ -25,8 +31,9 @@ namespace NS {
void fAPickRef(A&) {}
void fAPickRef(const volatile A&) {}
- void fAPickRef2(A&) {}
- void fAPickRef2(const volatile A&) {}
+ // See the above note above the global `fBPickConstVolatileRef`
+ void fBPickConstVolatileRef(A&) {}
+ void fBPickConstVolatileRef(const volatile A&) {}
}
struct S {
@@ -50,8 +57,8 @@ void test() {
fAPickRef(A());
// CHECK: call {{.*}} @"?fAPickRef@@YAXAEAUA@@@Z"
- fAPickRef2(A());
- // CHECK: call {{.*}} @"?fAPickRef2@@YAXAEAUA@@@Z"
+ fBPickConstVolatileRef(B());
+ // CHECK: call {{.*}} @"?fBPickConstVolatileRef@@YAXAEAUA@@@Z"
NS::fAPickConstRef(A());
// CHECK: call {{.*}} @"?fAPickConstRef at NS@@YAXAEBUA@@@Z"
@@ -62,8 +69,8 @@ void test() {
NS::fAPickRef(A());
// CHECK: call {{.*}} @"?fAPickRef at NS@@YAXAEAUA@@@Z"
- NS::fAPickRef2(A());
- // CHECK: call {{.*}} @"?fAPickRef2 at NS@@YAXAEAUA@@@Z"
+ NS::fBPickConstVolatileRef(B());
+ // CHECK: call {{.*}} @"?fBPickConstVolatileRef at NS@@YAXAEAUA@@@Z"
S::fAPickConstRef(A());
// CHECK: call {{.*}} @"?fAPickConstRef at S@@SAXAEBUA@@@Z"
>From b7754e9288bbd9c8f1824512d120e48519a6fe0b Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Thu, 12 Dec 2024 23:53:25 -0800
Subject: [PATCH 21/24] Add conversion and temporary materialization unit
tests; enforece variable non-const lvalue reference init doesn't consider ref
incompatible relationships
---
clang/lib/Sema/SemaInit.cpp | 3 +-
clang/test/SemaCXX/ms-reference-binding.cpp | 34 +++++++++++++++++++++
2 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index e9f86ff552cfd5..2729dae4792ccc 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -5331,7 +5331,8 @@ static void TryReferenceInitializationCore(Sema &S,
ConvOvlResult);
else if (!InitCategory.isLValue()) {
if (T1Quals.isAddressSpaceSupersetOf(T2Quals, S.getASTContext())) {
- if (S.AllowMSLValueReferenceBinding(T1Quals, T1)) {
+ if (S.AllowMSLValueReferenceBinding(T1Quals, T1) &&
+ RefRelationship != Sema::Ref_Incompatible) {
S.Diag(DeclLoc, diag::ext_ms_lvalue_reference_binding)
<< Initializer->getSourceRange();
} else {
diff --git a/clang/test/SemaCXX/ms-reference-binding.cpp b/clang/test/SemaCXX/ms-reference-binding.cpp
index 6207c716917468..0e180bd157c25d 100644
--- a/clang/test/SemaCXX/ms-reference-binding.cpp
+++ b/clang/test/SemaCXX/ms-reference-binding.cpp
@@ -188,4 +188,38 @@ void test5() {
fAAliasDefaultArgRef(AAlias());
}
+struct C { operator A() { return A(); } };
+struct D { D(int) {} };
+
+// expected-note at +1 {{candidate function not viable: no known conversion from 'C' to 'A &' for 1st argument}}
+void fARefConvOperator(A&);
+
+// expected-note at +1 {{candidate function not viable: no known conversion from 'int' to 'D &' for 1st argument}}
+void fDRefTemp(D&);
+
+void fAConstRefConvOperator(const A&);
+void fDConstRefTemp(const D&);
+
+void test6() {
+ fARefConvOperator(C()); // expected-error{{no matching function for call to 'fARefConvOperator'}}
+ fDRefTemp(1); // expected-error{{no matching function for call to 'fDRefTemp'}}
+
+ fAConstRefConvOperator(C());
+ fDConstRefTemp(1);
+
+ const A& cARef = C();
+ A& ARef = C(); // expected-error{{non-const lvalue reference to type 'A' cannot bind to a temporary of type 'C'}}
+
+ const D& cDRef = 1;
+ D& DRef = 1; // expected-error{{non-const lvalue reference to type 'D' cannot bind to a temporary of type 'int'}}
+}
+
+A& retARef();
+struct E { operator A&() { return retARef(); } };
+
+void test7() {
+ const A& cARef = E();
+ A& ARef = E();
+}
+
#endif
>From bd1e37bbff53736640ae6aa760bb0e318b7c6d50 Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Sun, 15 Dec 2024 21:31:30 -0800
Subject: [PATCH 22/24] Add unit test for reference to pointers
---
clang/lib/Sema/SemaOverload.cpp | 3 ++-
clang/test/SemaCXX/ms-reference-binding.cpp | 23 +++++++++++++++++++++
2 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index c7d5f7b77a2b3c..8ec3921dbafc7d 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -5035,7 +5035,8 @@ Sema::CompareReferenceRelationship(SourceLocation Loc,
bool Sema::AllowMSLValueReferenceBinding(Qualifiers Quals, QualType QT) {
return getLangOpts().MSVCReferenceBinding &&
- !(Quals.hasVolatile() || QT->isBuiltinType() || QT->isArrayType());
+ !(Quals.hasVolatile() || QT->isBuiltinType() || QT->isPointerType() ||
+ QT->isArrayType());
}
/// Look for a user-defined conversion to a value reference-compatible
diff --git a/clang/test/SemaCXX/ms-reference-binding.cpp b/clang/test/SemaCXX/ms-reference-binding.cpp
index 0e180bd157c25d..13dfe536afeb0e 100644
--- a/clang/test/SemaCXX/ms-reference-binding.cpp
+++ b/clang/test/SemaCXX/ms-reference-binding.cpp
@@ -222,4 +222,27 @@ void test7() {
A& ARef = E();
}
+void testFunction() {}
+
+void refFuncPtrArg(void (* &)()) {} // expected-note{{}}
+void cRefFuncPtrArg(void (* const &)()) {}
+
+void test8() {
+ refFuncPtrArg(&testFunction); // expected-error{{no matching function for call to 'refFuncPtrArg'}}
+ cRefFuncPtrArg(&testFunction);
+
+ void (* & refFuncPtr1)() = &testFunction; // expected-error{{non-const lvalue reference to type 'void (*)()' cannot bind to a temporary of type 'void (*)()'}}
+ void (* const & cRefFuncPtr1)() = &testFunction;
+
+ void (&refFunc1)() = testFunction;
+
+ int i;
+
+ int * & refIntPtr1 = &i; // expected-error{{non-const lvalue reference to type 'int *' cannot bind to a temporary of type 'int *'}}
+ int * const & cRefIntPtr1 = &i;
+
+ decltype(nullptr) & nullptrRef = nullptr; // expected-error{{non-const lvalue reference to type 'decltype(nullptr)' (aka 'std::nullptr_t') cannot bind to a temporary of type 'std::nullptr_t'}}
+ const decltype(nullptr) & nullptrCRef = nullptr;
+}
+
#endif
>From 3f1536195650ab357dc97a9bc754ba24e71bb0fc Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Sun, 15 Dec 2024 21:33:26 -0800
Subject: [PATCH 23/24] Fix expected-note in unit test
---
clang/test/SemaCXX/ms-reference-binding.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/clang/test/SemaCXX/ms-reference-binding.cpp b/clang/test/SemaCXX/ms-reference-binding.cpp
index 13dfe536afeb0e..5d02eb880fd485 100644
--- a/clang/test/SemaCXX/ms-reference-binding.cpp
+++ b/clang/test/SemaCXX/ms-reference-binding.cpp
@@ -224,7 +224,8 @@ void test7() {
void testFunction() {}
-void refFuncPtrArg(void (* &)()) {} // expected-note{{}}
+// expected-note at +1 {{candidate function not viable: expects an lvalue for 1st argument}}
+void refFuncPtrArg(void (* &)()) {}
void cRefFuncPtrArg(void (* const &)()) {}
void test8() {
>From 11ea3d081fefc0ffb8ff6afc366bb0ce73039d11 Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Mon, 16 Dec 2024 18:26:34 -0800
Subject: [PATCH 24/24] Add member pointer tests; Add vcall tests
---
clang/lib/Sema/SemaOverload.cpp | 2 +-
clang/test/SemaCXX/ms-reference-binding.cpp | 14 ++++++++++++++
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 8ec3921dbafc7d..f5415d6e6154cb 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -5036,7 +5036,7 @@ Sema::CompareReferenceRelationship(SourceLocation Loc,
bool Sema::AllowMSLValueReferenceBinding(Qualifiers Quals, QualType QT) {
return getLangOpts().MSVCReferenceBinding &&
!(Quals.hasVolatile() || QT->isBuiltinType() || QT->isPointerType() ||
- QT->isArrayType());
+ QT->isMemberPointerType() || QT->isArrayType());
}
/// Look for a user-defined conversion to a value reference-compatible
diff --git a/clang/test/SemaCXX/ms-reference-binding.cpp b/clang/test/SemaCXX/ms-reference-binding.cpp
index 5d02eb880fd485..60ca3d010ab4a9 100644
--- a/clang/test/SemaCXX/ms-reference-binding.cpp
+++ b/clang/test/SemaCXX/ms-reference-binding.cpp
@@ -222,7 +222,10 @@ void test7() {
A& ARef = E();
}
+struct F { void test(); int i; };
+
void testFunction() {}
+void __vectorcall testVCallFunction() {};
// expected-note at +1 {{candidate function not viable: expects an lvalue for 1st argument}}
void refFuncPtrArg(void (* &)()) {}
@@ -235,8 +238,19 @@ void test8() {
void (* & refFuncPtr1)() = &testFunction; // expected-error{{non-const lvalue reference to type 'void (*)()' cannot bind to a temporary of type 'void (*)()'}}
void (* const & cRefFuncPtr1)() = &testFunction;
+ void (__vectorcall * & refFuncPtr2)() = &testVCallFunction; // expected-error{{non-const lvalue reference to type 'void (*)() __attribute__((vectorcall))' cannot bind to a temporary of type 'void (*)() __attribute__((vectorcall))'}}
+ void (__vectorcall * const & cRefFuncPtr2)() = &testVCallFunction;
+
void (&refFunc1)() = testFunction;
+ void (__vectorcall &refFunc2)() = testVCallFunction;
+
+ void (F::* & refFuncPtr3)() = &F::test; // expected-error{{non-const lvalue reference to type 'void (F::*)()' cannot bind to a temporary of type 'void (F::*)()'}}
+ void (F::* const & cRefFuncPtr3)() = &F::test;
+
+ int F::* & refPtr1 = &F::i; // expected-error{{non-const lvalue reference to type 'int F::*' cannot bind to a temporary of type 'int F::*'}}
+ int F::* const & cRefPtr1 = &F::i;
+
int i;
int * & refIntPtr1 = &i; // expected-error{{non-const lvalue reference to type 'int *' cannot bind to a temporary of type 'int *'}}
More information about the cfe-commits
mailing list