mizvekov updated this revision to Diff 359145.
mizvekov added a comment.
Herald added a subscriber: lxfind.
Patch update:
- Fixes issue where the workaround would completely supress implicit moves.
- Improve tests to cover this issue.
- Stop applying the workaround to throw expressions, as we have not observed
any problems there.
In D105951#2880654 <https://reviews.llvm.org/D105951#2880654>, @mibintc wrote:
> Intel compiles VS2019 #include files regularly with clang, and the file
> <filesystem> compiled with -std:c++latest encounters this error report, which
> @aaron.ballman suggests is related to this effort.
>
> I used creduce with the same command line, looking for that error diagnostic
> with std:c++latest versus no diagnostic with std:c++20 ; this smaller test
> case, with a few edits, is the result
Thanks for testing!
In D105951#2880920 <https://reviews.llvm.org/D105951#2880920>, @aaron.ballman
wrote:
> I think we might be in an awkward situation where some MSVC STL code needs
> the implicit cast in order to work, such as:
> ....
> (At least, Melanie's reduced example fails with this patch, but if you remove
> the system header or namespace std bits from the test, then it passes.)
No, that was totally my fault! :-)
I did not intend for the workaround to completely disable implicit moves, just
that it would fall back to the C++20 behavior.
The problem is that the code path for this is split around multiple places, and
while suppressing simpler implicit moves here, I should have undone the
suppressing of the C++20 implicit move code path elsewhere!
Sigh, I forgot it was there because I was working on another patch that
completely removed that second code path...
Sorry for that!
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D105951/new/
https://reviews.llvm.org/D105951
Files:
clang/include/clang/Sema/Sema.h
clang/lib/Frontend/InitPreprocessor.cpp
clang/lib/Sema/SemaCoroutine.cpp
clang/lib/Sema/SemaStmt.cpp
clang/test/SemaCXX/cxx2b-p2266-disable-with-msvc-compat.cpp
Index: clang/test/SemaCXX/cxx2b-p2266-disable-with-msvc-compat.cpp
===================================================================
--- clang/test/SemaCXX/cxx2b-p2266-disable-with-msvc-compat.cpp
+++ clang/test/SemaCXX/cxx2b-p2266-disable-with-msvc-compat.cpp
@@ -1,50 +1,151 @@
-// 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 <class T> T &&test3(T &&x) { return x; } // old-error {{cannot bind to lvalue of type}}
-template MoveOnly &test3<MoveOnly &>(MoveOnly &);
-template MoveOnly &&test3<MoveOnly>(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
+
+struct nocopy { nocopy(nocopy&&); };
+
+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}}
+nocopy mt3(nocopy x) { return x; }
+
+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}}
+nocopy mt3(nocopy x) { return x; }
+} // 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}}
+nocopy mt3(nocopy x) { return x; }
+} // 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}}
+nocopy mt3(nocopy x) { return x; }
+
+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}}
+nocopy mt3(nocopy x) { return x; }
+} // 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}}
+nocopy mt3(nocopy x) { return x; }
+} // 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}}
+nocopy ut3(nocopy x) { return x; }
+
+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}}
+nocopy ut3(nocopy x) { return x; }
+} // 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}}
+nocopy ut3(nocopy x) { return x; }
+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}}
+nocopy ut3(nocopy x) { return x; }
+} // 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}}
+nocopy ut3(nocopy x) { return x; }
+
+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}}
+nocopy ut3(nocopy x) { return x; }
+} // 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}}
+nocopy ut3(nocopy x) { return x; }
+} // 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}}
+nocopy st3(nocopy x) { return x; }
+
+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}}
+nocopy st3(nocopy x) { return x; }
+} // 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}}
+nocopy st3(nocopy x) { return x; }
+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}}
+nocopy st3(nocopy x) { return x; }
+} // 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}}
+nocopy st3(nocopy x) { return x; }
+
+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}}
+nocopy st3(nocopy x) { return x; }
+} // 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}}
+nocopy st3(nocopy x) { return x; }
+} // namespace foo
+
+} // namespace std
+
+#endif
Index: clang/lib/Sema/SemaStmt.cpp
===================================================================
--- clang/lib/Sema/SemaStmt.cpp
+++ clang/lib/Sema/SemaStmt.cpp
@@ -3321,7 +3321,8 @@
/// \returns An aggregate which contains the Candidate and isMoveEligible
/// and isCopyElidable methods. If Candidate is non-null, it means
/// isMoveEligible() would be true under the most permissive language standard.
-Sema::NamedReturnInfo Sema::getNamedReturnInfo(Expr *&E, bool ForceCXX2b) {
+Sema::NamedReturnInfo Sema::getNamedReturnInfo(Expr *&E,
+ SimplerImplicitMoveMode Mode) {
if (!E)
return NamedReturnInfo();
// - in a return statement in a function [where] ...
@@ -3333,13 +3334,10 @@
if (!VD)
return NamedReturnInfo();
NamedReturnInfo Res = getNamedReturnInfo(VD);
- // FIXME: We supress simpler implicit move here (unless ForceCXX2b is true)
- // in msvc compatibility mode just as a temporary work around,
- // 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))) {
+ (Mode == SimplerImplicitMoveMode::ForceOn ||
+ (Mode != SimplerImplicitMoveMode::ForceOff &&
+ getLangOpts().CPlusPlus2b))) {
E = ImplicitCastExpr::Create(Context, VD->getType().getNonReferenceType(),
CK_NoOp, E, nullptr, VK_XValue,
FPOptionsOverride());
@@ -3479,11 +3477,11 @@
/// This routine implements C++20 [class.copy.elision]p3, which attempts to
/// treat returned lvalues as rvalues in certain cases (to prefer move
/// construction), then falls back to treating them as lvalues if that failed.
-ExprResult
-Sema::PerformMoveOrCopyInitialization(const InitializedEntity &Entity,
- const NamedReturnInfo &NRInfo,
- Expr *Value) {
- if (!getLangOpts().CPlusPlus2b && NRInfo.isMoveEligible()) {
+ExprResult Sema::PerformMoveOrCopyInitialization(
+ const InitializedEntity &Entity, const NamedReturnInfo &NRInfo, Expr *Value,
+ bool SupressSimplerImplicitMoves) {
+ if ((!getLangOpts().CPlusPlus2b || SupressSimplerImplicitMoves) &&
+ NRInfo.isMoveEligible()) {
ImplicitCastExpr AsRvalue(ImplicitCastExpr::OnStack, Value->getType(),
CK_NoOp, Value, VK_XValue, FPOptionsOverride());
Expr *InitExpr = &AsRvalue;
@@ -3523,7 +3521,8 @@
///
StmtResult Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc,
Expr *RetValExp,
- NamedReturnInfo &NRInfo) {
+ NamedReturnInfo &NRInfo,
+ bool SupressSimplerImplicitMoves) {
// If this is the first return we've seen, infer the return type.
// [expr.prim.lambda]p4 in C++11; block literals follow the same rules.
CapturingScopeInfo *CurCap = cast<CapturingScopeInfo>(getCurFunction());
@@ -3654,7 +3653,8 @@
// the C version of which boils down to CheckSingleAssignmentConstraints.
InitializedEntity Entity = InitializedEntity::InitializeResult(
ReturnLoc, FnRetType, NRVOCandidate != nullptr);
- ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRInfo, RetValExp);
+ ExprResult Res = PerformMoveOrCopyInitialization(
+ Entity, NRInfo, RetValExp, SupressSimplerImplicitMoves);
if (Res.isInvalid()) {
// FIXME: Cleanup temporaries here, anyway?
return StmtError();
@@ -3864,15 +3864,37 @@
return R;
}
+static bool CheckSimplerImplicitMovesMSVCWorkaround(const Sema &S,
+ const Expr *E) {
+ if (!E || !S.getLangOpts().CPlusPlus2b || !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;
+}
+
StmtResult Sema::BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
// Check for unexpanded parameter packs.
if (RetValExp && DiagnoseUnexpandedParameterPack(RetValExp))
return StmtError();
- NamedReturnInfo NRInfo = getNamedReturnInfo(RetValExp);
+ // HACK: We supress simpler implicit move here in msvc compatibility mode
+ // just as a temporary work around, as the MSVC STL has issues with
+ // this change.
+ bool SupressSimplerImplicitMoves =
+ CheckSimplerImplicitMovesMSVCWorkaround(*this, RetValExp);
+ NamedReturnInfo NRInfo = getNamedReturnInfo(
+ RetValExp, SupressSimplerImplicitMoves ? SimplerImplicitMoveMode::ForceOff
+ : SimplerImplicitMoveMode::Normal);
if (isa<CapturingScopeInfo>(getCurFunction()))
- return ActOnCapScopeReturnStmt(ReturnLoc, RetValExp, NRInfo);
+ return ActOnCapScopeReturnStmt(ReturnLoc, RetValExp, NRInfo,
+ SupressSimplerImplicitMoves);
QualType FnRetType;
QualType RelatedRetType;
@@ -4063,8 +4085,8 @@
// we have a non-void function with an expression, continue checking
InitializedEntity Entity = InitializedEntity::InitializeResult(
ReturnLoc, RetType, NRVOCandidate != nullptr);
- ExprResult Res =
- PerformMoveOrCopyInitialization(Entity, NRInfo, RetValExp);
+ ExprResult Res = PerformMoveOrCopyInitialization(
+ Entity, NRInfo, RetValExp, SupressSimplerImplicitMoves);
if (Res.isInvalid()) {
// FIXME: Clean up temporaries here anyway?
return StmtError();
Index: clang/lib/Sema/SemaCoroutine.cpp
===================================================================
--- clang/lib/Sema/SemaCoroutine.cpp
+++ clang/lib/Sema/SemaCoroutine.cpp
@@ -977,7 +977,7 @@
VarDecl *Promise = FSI->CoroutinePromise;
ExprResult PC;
if (E && (isa<InitListExpr>(E) || !E->getType()->isVoidType())) {
- getNamedReturnInfo(E, /*ForceCXX2b=*/true);
+ getNamedReturnInfo(E, SimplerImplicitMoveMode::ForceOn);
PC = buildPromiseCall(*this, Promise, Loc, "return_value", E);
} else {
E = MakeFullDiscardedValueExpr(E).get();
Index: clang/lib/Frontend/InitPreprocessor.cpp
===================================================================
--- clang/lib/Frontend/InitPreprocessor.cpp
+++ 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)
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -4780,20 +4780,24 @@
bool isMoveEligible() const { return S != None; };
bool isCopyElidable() const { return S == MoveEligibleAndCopyElidable; }
};
- NamedReturnInfo getNamedReturnInfo(Expr *&E, bool ForceCXX2b = false);
+ enum class SimplerImplicitMoveMode { ForceOff, Normal, ForceOn };
+ NamedReturnInfo getNamedReturnInfo(
+ Expr *&E, SimplerImplicitMoveMode Mode = SimplerImplicitMoveMode::Normal);
NamedReturnInfo getNamedReturnInfo(const VarDecl *VD);
const VarDecl *getCopyElisionCandidate(NamedReturnInfo &Info,
QualType ReturnType);
- ExprResult PerformMoveOrCopyInitialization(const InitializedEntity &Entity,
- const NamedReturnInfo &NRInfo,
- Expr *Value);
+ ExprResult
+ PerformMoveOrCopyInitialization(const InitializedEntity &Entity,
+ const NamedReturnInfo &NRInfo, Expr *Value,
+ bool SupressSimplerImplicitMoves = false);
StmtResult ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp,
Scope *CurScope);
StmtResult BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp);
StmtResult ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp,
- NamedReturnInfo &NRInfo);
+ NamedReturnInfo &NRInfo,
+ bool SupressSimplerImplicitMoves);
StmtResult ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
bool IsVolatile, unsigned NumOutputs,
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits