diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -598,8 +598,7 @@ } // C++2b features. if (LangOpts.CPlusPlus2b) { - if (!LangOpts.MSVCCompat) - Builder.defineMacro("__cpp_implicit_move", "202011L"); + Builder.defineMacro("__cpp_implicit_move", "202011L"); Builder.defineMacro("__cpp_size_t_suffix", "202011L"); } if (LangOpts.Char8) diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -3307,6 +3307,20 @@ return new (Context) BreakStmt(BreakLoc); } +static bool CheckSimplerImplicitMovesMSVCWorkaround(const Sema &S, + const Expr &E) { + if (!S.getLangOpts().MSVCCompat) + return false; + const Decl *D = E.getReferencedDeclOfCallee(); + if (!D || !S.SourceMgr.isInSystemHeader(D->getLocation())) + return false; + for (const DeclContext *DC = D->getDeclContext(); DC; DC = DC->getParent()) { + if (DC->isStdNamespace()) + return true; + } + return false; +} + /// Determine whether the given expression might be move-eligible or /// copy-elidable in either a (co_)return statement or throw expression, /// without considering function return type, if applicable. @@ -3338,8 +3352,8 @@ // as the MSVC STL has issues with this change. // We will come back later with a more targeted approach. if (Res.Candidate && !E->isXValue() && - (ForceCXX2b || - (getLangOpts().CPlusPlus2b && !getLangOpts().MSVCCompat))) { + (ForceCXX2b || (getLangOpts().CPlusPlus2b && + !CheckSimplerImplicitMovesMSVCWorkaround(*this, *E)))) { E = ImplicitCastExpr::Create(Context, VD->getType().getNonReferenceType(), CK_NoOp, E, nullptr, VK_XValue, FPOptionsOverride()); diff --git a/clang/test/SemaCXX/cxx2b-p2266-disable-with-msvc-compat.cpp b/clang/test/SemaCXX/cxx2b-p2266-disable-with-msvc-compat.cpp --- a/clang/test/SemaCXX/cxx2b-p2266-disable-with-msvc-compat.cpp +++ b/clang/test/SemaCXX/cxx2b-p2266-disable-with-msvc-compat.cpp @@ -1,50 +1,129 @@ -// RUN: %clang_cc1 -std=c++2b -fsyntax-only -fcxx-exceptions -verify=new %s -// RUN: %clang_cc1 -std=c++2b -fsyntax-only -fcxx-exceptions -fms-compatibility -verify=old %s -// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=old %s +// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=cxx2b,new %s +// RUN: %clang_cc1 -std=c++2b -fsyntax-only -fms-compatibility -verify=cxx2b,old %s +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=cxx20,old %s // FIXME: This is a test for a temporary workaround where we disable simpler implicit moves -// when compiling with -fms-compatibility, because the MSVC STL does not compile. -// A better workaround is under discussion. -// The test cases here are just a copy from `CXX/class/class.init/class.copy.elision/p3.cpp`, -// so feel free to delete this file when the workaround is not needed anymore. - -struct CopyOnly { - CopyOnly(); // new-note {{candidate constructor not viable: requires 0 arguments, but 1 was provided}} - // new-note@-1 {{candidate constructor not viable: requires 0 arguments, but 1 was provided}} - CopyOnly(CopyOnly &); // new-note {{candidate constructor not viable: expects an lvalue for 1st argument}} - // new-note@-1 {{candidate constructor not viable: expects an lvalue for 1st argument}} -}; -struct MoveOnly { - MoveOnly(); - MoveOnly(MoveOnly &&); -}; -MoveOnly &&rref(); - -MoveOnly &&test1(MoveOnly &&w) { - return w; // old-error {{cannot bind to lvalue of type}} -} - -CopyOnly test2(bool b) { - static CopyOnly w1; - CopyOnly w2; - if (b) { - return w1; - } else { - return w2; // new-error {{no matching constructor for initialization}} - } -} - -template T &&test3(T &&x) { return x; } // old-error {{cannot bind to lvalue of type}} -template MoveOnly &test3(MoveOnly &); -template MoveOnly &&test3(MoveOnly &&); // old-note {{in instantiation of function template specialization}} - -MoveOnly &&test4() { - MoveOnly &&x = rref(); - return x; // old-error {{cannot bind to lvalue of type}} -} - -void test5() try { - CopyOnly x; - throw x; // new-error {{no matching constructor for initialization}} -} catch (...) { -} +// in the STL when compiling with -fms-compatibility, because of issues with the +// implementation there. +// Feel free to delete this file when the workaround is not needed anymore. + +#if __INCLUDE_LEVEL__ == 0 + +#if __cpluscplus > 202002L && __cpp_implicit_move < 202011L +#error "__cpp_implicit_move not defined correctly" +#endif + +int &&mt1(int &&x) { return x; } // cxx20-error {{cannot bind to lvalue}} +int &mt2(int &&x) { return x; } // cxx2b-error {{cannot bind to a temporary}} + +namespace { +int &&mt1(int &&x) { return x; } // cxx20-error {{cannot bind to lvalue}} +int &mt2(int &&x) { return x; } // cxx2b-error {{cannot bind to a temporary}} +} // namespace + +namespace foo { +int &&mt1(int &&x) { return x; } // cxx20-error {{cannot bind to lvalue}} +int &mt2(int &&x) { return x; } // cxx2b-error {{cannot bind to a temporary}} +namespace std { +int &&mt1(int &&x) { return x; } // cxx20-error {{cannot bind to lvalue}} +int &mt2(int &&x) { return x; } // cxx2b-error {{cannot bind to a temporary}} +} // namespace std +} // namespace foo + +namespace std { + +int &&mt1(int &&x) { return x; } // cxx20-error {{cannot bind to lvalue}} +int &mt2(int &&x) { return x; } // cxx2b-error {{cannot bind to a temporary}} + +namespace { +int &&mt1(int &&x) { return x; } // cxx20-error {{cannot bind to lvalue}} +int &mt2(int &&x) { return x; } // cxx2b-error {{cannot bind to a temporary}} +} // namespace + +namespace foo { +int &&mt1(int &&x) { return x; } // cxx20-error {{cannot bind to lvalue}} +int &mt2(int &&x) { return x; } // cxx2b-error {{cannot bind to a temporary}} +} // namespace foo + +} // namespace std + +#include __FILE__ + +#define SYSTEM +#include __FILE__ + +#elif !defined(SYSTEM) + +int &&ut1(int &&x) { return x; } // cxx20-error {{cannot bind to lvalue}} +int &ut2(int &&x) { return x; } // cxx2b-error {{cannot bind to a temporary}} + +namespace { +int &&ut1(int &&x) { return x; } // cxx20-error {{cannot bind to lvalue}} +int &ut2(int &&x) { return x; } // cxx2b-error {{cannot bind to a temporary}} +} // namespace + +namespace foo { +int &&ut1(int &&x) { return x; } // cxx20-error {{cannot bind to lvalue}} +int &ut2(int &&x) { return x; } // cxx2b-error {{cannot bind to a temporary}} +namespace std { +int &&ut1(int &&x) { return x; } // cxx20-error {{cannot bind to lvalue}} +int &ut2(int &&x) { return x; } // cxx2b-error {{cannot bind to a temporary}} +} // namespace std +} // namespace foo + +namespace std { + +int &&ut1(int &&x) { return x; } // cxx20-error {{cannot bind to lvalue}} +int &ut2(int &&x) { return x; } // cxx2b-error {{cannot bind to a temporary}} + +namespace { +int &&ut1(int &&x) { return x; } // cxx20-error {{cannot bind to lvalue}} +int &ut2(int &&x) { return x; } // cxx2b-error {{cannot bind to a temporary}} +} // namespace + +namespace foo { +int &&ut1(int &&x) { return x; } // cxx20-error {{cannot bind to lvalue}} +int &ut2(int &&x) { return x; } // cxx2b-error {{cannot bind to a temporary}} +} // namespace foo + +} // namespace std + +#else + +#pragma GCC system_header + +int &&st1(int &&x) { return x; } // cxx20-error {{cannot bind to lvalue}} +int &st2(int &&x) { return x; } // cxx2b-error {{cannot bind to a temporary}} + +namespace { +int &&st1(int &&x) { return x; } // cxx20-error {{cannot bind to lvalue}} +int &st2(int &&x) { return x; } // cxx2b-error {{cannot bind to a temporary}} +} // namespace + +namespace foo { +int &&st1(int &&x) { return x; } // cxx20-error {{cannot bind to lvalue}} +int &st2(int &&x) { return x; } // cxx2b-error {{cannot bind to a temporary}} +namespace std { +int &&st1(int &&x) { return x; } // cxx20-error {{cannot bind to lvalue}} +int &st2(int &&x) { return x; } // cxx2b-error {{cannot bind to a temporary}} +} // namespace std +} // namespace foo + +namespace std { + +int &&st1(int &&x) { return x; } // old-error {{cannot bind to lvalue}} +int &st2(int &&x) { return x; } // new-error {{cannot bind to a temporary}} + +namespace { +int &&st1(int &&x) { return x; } // old-error {{cannot bind to lvalue}} +int &st2(int &&x) { return x; } // new-error {{cannot bind to a temporary}} +} // namespace + +namespace foo { +int &&st1(int &&x) { return x; } // old-error {{cannot bind to lvalue}} +int &st2(int &&x) { return x; } // new-error {{cannot bind to a temporary}} +} // namespace foo + +} // namespace std + +#endif