diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 6b950d05fb9bf9..5f032817510113 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -6015,3 +6015,35 @@ 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 in order 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 +`_ +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 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 clang-cl driver. +This MSVC extension can be enabled with ``/Zc:referenceBinding-`` with the clang-cl driver. +>>>>>>> 43d9c2ac844e (PR Feedback on ext warning) 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/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; + // C++11 auto def warn_cxx98_compat_auto_type_specifier : Warning< "'auto' type specifier is incompatible with C++98">, diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 39e4851dd3814c..b3a706cfd7b318 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 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") diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 4bc0b97ea68f2f..64599a7559c8d6 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -3096,6 +3096,13 @@ def fms_extensions : Flag<["-"], "fms-extensions">, Group, Visibility<[ClangOption, CC1Option, CLOption]>, HelpText<"Accept some non-standard constructs supported by the Microsoft compiler">, MarshallingInfoFlag>, ImpliedByAnyOf<[fms_compatibility.KeyPath]>; +def fms_reference_binding : Flag<["-"], "fms-reference-binding">, 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>; +def fno_ms_reference_binding : Flag<["-"], "fno-ms-reference-binding">, Group, + Visibility<[ClangOption, CLOption]>; defm asm_blocks : BoolFOption<"asm-blocks", LangOpts<"AsmBlocks">, Default, PosFlag, @@ -8683,6 +8690,13 @@ 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 (default)">, + Alias; +def _SLASH_Zc_referenceBinding_ : CLFlag<"Zc:referenceBinding-">, + HelpText<"Accept expressions that bind a non-const lvalue reference to a " + "user-defined type temporary">, + Alias; def _SLASH_Z7 : CLFlag<"Z7">, Alias, 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..c7e6617e0b97a6 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -7381,6 +7381,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-fdelayed-template-parsing"); } + 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)) 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 80799d1e715f07..e3a0434e648fae 100644 --- a/clang/lib/Driver/ToolChains/MSVC.cpp +++ b/clang/lib/Driver/ToolChains/MSVC.cpp @@ -953,6 +953,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/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 7c03a12e812809..2729dae4792ccc 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -5329,13 +5329,21 @@ 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.AllowMSLValueReferenceBinding(T1Quals, T1) && + RefRelationship != Sema::Ref_Incompatible) { + S.Diag(DeclLoc, diag::ext_ms_lvalue_reference_binding) + << Initializer->getSourceRange(); + } else { + Sequence.SetFailed(InitializationSequence:: + FK_NonConstLValueReferenceBindingToTemporary); + } + } else { + Sequence.SetFailed( + InitializationSequence::FK_ReferenceInitDropsQualifiers); + } + } else { InitializationSequence::FailureKind FK; switch (RefRelationship) { case Sema::Ref_Compatible: @@ -5361,7 +5369,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..5fb2f7d8a9754f 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -4552,6 +4552,26 @@ CompareStandardConversionSequences(Sema &S, SourceLocation Loc, T1 = S.Context.getQualifiedType(UnqualT1, T1Quals); if (isa(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 && + !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&&`. + // 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())) @@ -5013,6 +5033,11 @@ Sema::CompareReferenceRelationship(SourceLocation Loc, : Ref_Incompatible; } +bool Sema::AllowMSLValueReferenceBinding(Qualifiers Quals, QualType QT) { + return getLangOpts().MSVCReferenceBinding && !Quals.hasVolatile() && + !QT->isArrayType() && QT->isRecordType(); +} + /// Look for a user-defined conversion to a value reference-compatible /// with DeclType. Return true if something definite is found. static bool @@ -5246,9 +5271,14 @@ TryReferenceInit(Sema &S, Expr *Init, QualType DeclType, // non-volatile const type (i.e., cv1 shall be const), or the reference // shall be an rvalue reference. if (!isRValRef && (!T1.isConstQualified() || T1.isVolatileQualified())) { - if (InitCategory.isRValue() && RefRelationship != Sema::Ref_Incompatible) - ICS.setBad(BadConversionSequence::lvalue_ref_to_rvalue, Init, DeclType); - return ICS; + 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..c4a4067b5f34fb --- /dev/null +++ b/clang/test/CodeGenCXX/ms-reference-binding.cpp @@ -0,0 +1,87 @@ +// 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 {}; + +void fAPickConstRef(A&) {} +void fAPickConstRef(const A&) {} + +void fBPickConstRef(A&) {} +void fBPickConstRef(const A&) {} + +void fAPickRef(A&) {} +void fAPickRef(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&) {} + void fAPickConstRef(const A&) {} + + void fBPickConstRef(A&) {} + void fBPickConstRef(const A&) {} + + void fAPickRef(A&) {} + void fAPickRef(const volatile A&) {} + + // See the above note above the global `fBPickConstVolatileRef` + void fBPickConstVolatileRef(A&) {} + void fBPickConstVolatileRef(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" + + fBPickConstRef(B()); + // CHECK: call {{.*}} @"?fBPickConstRef@@YAXAEBUA@@@Z" + + fAPickRef(A()); + // CHECK: call {{.*}} @"?fAPickRef@@YAXAEAUA@@@Z" + + fBPickConstVolatileRef(B()); + // CHECK: call {{.*}} @"?fBPickConstVolatileRef@@YAXAEAUA@@@Z" + + NS::fAPickConstRef(A()); + // CHECK: call {{.*}} @"?fAPickConstRef@NS@@YAXAEBUA@@@Z" + + NS::fBPickConstRef(B()); + // CHECK: call {{.*}} @"?fBPickConstRef@NS@@YAXAEBUA@@@Z" + + NS::fAPickRef(A()); + // CHECK: call {{.*}} @"?fAPickRef@NS@@YAXAEAUA@@@Z" + + NS::fBPickConstVolatileRef(B()); + // CHECK: call {{.*}} @"?fBPickConstVolatileRef@NS@@YAXAEAUA@@@Z" + + S::fAPickConstRef(A()); + // CHECK: call {{.*}} @"?fAPickConstRef@S@@SAXAEBUA@@@Z" +} + +void test_member_call() { + S s; + + static_cast(s).memberPickNonConst(); + // CHECK: call {{.*}} @"?memberPickNonConst@S@@QEAAXXZ" + + static_cast(s).memberPickConstRef(); + // CHECK: call {{.*}} @"?memberPickConstRef@S@@QEGBAXXZ" +} diff --git a/clang/test/Driver/cl-permissive.c b/clang/test/Driver/cl-permissive.c index d88746d3a5517f..b70a98d3564231 100644 --- a/clang/test/Driver/cl-permissive.c +++ b/clang/test/Driver/cl-permissive.c @@ -6,6 +6,7 @@ // 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 +16,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..e6b885c4bde7ff 100644 --- a/clang/test/Driver/cl-zc.cpp +++ b/clang/test/Driver/cl-zc.cpp @@ -125,6 +125,10 @@ // 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" +// 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. diff --git a/clang/test/SemaCXX/ms-reference-binding.cpp b/clang/test/SemaCXX/ms-reference-binding.cpp new file mode 100644 index 00000000000000..bc2cf79846c5ca --- /dev/null +++ b/clang/test/SemaCXX/ms-reference-binding.cpp @@ -0,0 +1,292 @@ +// 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 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@+2 {{candidate function not viable: 1st argument ('const A') would lose const qualifier}} +// expected-note@+1 {{candidate function not viable: 1st argument ('const A') would lose const qualifier}} +void fARefLoseConstQualifier(A&) {} + +void test2() { + // This should not warn since `fARefDoNotWarn(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(a)); // expected-error{{no matching function for call to 'fARefLoseConstQualifier'}} +} + +#else + +struct A {}; +struct B : A {}; + +typedef A AAlias; + +void fADefaultArgRef(A& = A{}); +void fBDefaultArgRef(A& = B{}); + +void fAAliasDefaultArgRef(AAlias& = AAlias{}); + +B fB(); +A fA(); + +A&& fARvalueRef(); +A(&&fARvalueRefArray())[1]; + +void fADefaultArgRef2(A& = fARvalueRef()); + +void fARef(A&) {} +void fAAliasRef(AAlias&) {} + +// expected-note@+2 {{candidate function not viable: expects an lvalue for 1st argument}} +// expected-note@+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}} + +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 fAAliasRef(AAlias&) {} + + // expected-note@+2 {{passing argument to parameter here}} + // expected-note@+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}} + + 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(); + AAlias& aalias1 = A(); + + fARef(A()); + fARef(static_cast(a1)); + + fAAliasRef(A()); + fAAliasRef(static_cast(a1)); + fAAliasRef(AAlias()); + fAAliasRef(static_cast(a1)); + + fAVolatileRef(A()); // expected-error{{no matching function for call to 'fAVolatileRef'}} + fAVolatileRef(static_cast(a1)); // expected-error{{no matching function for call to 'fAVolatileRef'}} + + fARef(B()); + fAAliasRef(B()); + + NS::fARef(A()); + NS::fARef(static_cast(a1)); + + NS::fAAliasRef(A()); + NS::fAAliasRef(static_cast(a1)); + NS::fAAliasRef(AAlias()); + NS::fAAliasRef(static_cast(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(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() { + 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 }); +} + +void test5() { + fADefaultArgRef(); + fADefaultArgRef(A()); + + fBDefaultArgRef(); + fBDefaultArgRef(B()); + fBDefaultArgRef(A()); + + fAAliasDefaultArgRef(A()); + fAAliasDefaultArgRef(B()); + fAAliasDefaultArgRef(AAlias()); +} + +struct C { operator A() { return A(); } }; +struct D { D(int) {} }; + +// expected-note@+1 {{candidate function not viable: no known conversion from 'C' to 'A &' for 1st argument}} +void fARefConvOperator(A&); + +// expected-note@+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(); +} + +struct F { void test(); int i; }; + +void testFunction() {} +void __vectorcall testVCallFunction() {}; + +// expected-note@+1 {{candidate function not viable: expects an lvalue for 1st argument}} +void refFuncPtrArg(void (* &)()) {} +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 (__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 *'}} + 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; +} + +class G {}; +union H {}; + +G fG(); +H fH(); + +enum class I : int {}; +enum J { J_ONE = 1, }; + +void fGRef(G&); +void fHRef(H&); + +void test9() { + G& g1 = fG(); + const G& g2 = fG(); + + H& h1 = fH(); + const H& h2 = fH(); + + fGRef(fG()); + fHRef(fH()); + + I& i1 = I{ 1 }; // expected-error{{non-const lvalue reference to type 'I' cannot bind to a temporary of type 'I'}} + const I& i2 = I{ 1 }; + + J& j1 = J_ONE; // expected-error{{non-const lvalue reference to type 'J' cannot bind to a temporary of type 'J'}} + const J& j2 = J_ONE; +} + +#endif