From 74e611a5fd0b5977c664d13a07b625ae23527d0d Mon Sep 17 00:00:00 2001 From: Sebastian Redl Date: Sun, 4 Sep 2011 18:14:28 +0000 Subject: [PATCH] Add test case for defaulted copy and move structure validation. Fix bug this uncovered. Address minor comments from Doug. Enable cxx_implicit_moves feature. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@139101 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Lex/PPMacroExpansion.cpp | 2 +- lib/Parse/ParseDeclCXX.cpp | 1 - lib/Sema/SemaDecl.cpp | 2 + lib/Sema/SemaDeclCXX.cpp | 27 ++++---- .../dcl.fct/dcl.fct.def.default/p2.cpp | 62 +++++++++++++++++++ test/Lexer/has_feature_cxx0x.cpp | 9 +++ 6 files changed, 87 insertions(+), 16 deletions(-) create mode 100644 test/CXX/dcl.decl/dcl.meaning/dcl.fct/dcl.fct.def.default/p2.cpp diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index 8bc1351f6e..d0828d0d5d 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/lib/Lex/PPMacroExpansion.cpp @@ -619,7 +619,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("cxx_deleted_functions", LangOpts.CPlusPlus0x) .Case("cxx_explicit_conversions", LangOpts.CPlusPlus0x) //.Case("cxx_generalized_initializers", LangOpts.CPlusPlus0x) - //.Case("cxx_implicit_moves", false) + .Case("cxx_implicit_moves", LangOpts.CPlusPlus0x) //.Case("cxx_inheriting_constructors", false) .Case("cxx_inline_namespaces", LangOpts.CPlusPlus0x) //.Case("cxx_lambdas", false) diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index f1dec4ab3f..385683abd6 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -1979,7 +1979,6 @@ ExprResult Parser::ParseCXXMemberInitializer(bool IsFunction, return ExprResult(); } } else if (Tok.is(tok::kw_default)) { - Diag(ConsumeToken(), diag::err_default_special_members); if (IsFunction) Diag(Tok, diag::err_default_delete_in_multiple_declaration) << 0 /* default */; diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index dc16dcae72..e0b6981424 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -1529,6 +1529,8 @@ Sema::CXXSpecialMember Sema::getSpecialMember(const CXXMethodDecl *MD) { return Sema::CXXDestructor; } else if (MD->isCopyAssignmentOperator()) { return Sema::CXXCopyAssignment; + } else if (MD->isMoveAssignmentOperator()) { + return Sema::CXXMoveAssignment; } return Sema::CXXInvalid; diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index f35fdeda38..04868cee57 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -1984,7 +1984,7 @@ BuildImplicitBaseInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, BasePath.push_back(BaseSpec); CopyCtorArg = SemaRef.ImpCastExprToType(CopyCtorArg, ArgTy, CK_UncheckedDerivedToBase, - Moving ? VK_RValue : VK_LValue, + Moving ? VK_XValue : VK_LValue, &BasePath).take(); InitializationKind InitKind @@ -2055,7 +2055,7 @@ BuildImplicitMemberInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, MemberLookup.addDecl(Indirect ? cast(Indirect) : cast(Field), AS_public); MemberLookup.resolveKind(); - ExprResult CopyCtorArg + ExprResult CtorArg = SemaRef.BuildMemberReferenceExpr(MemberExprBase, ParamType, Loc, /*IsArrow=*/false, @@ -2063,14 +2063,14 @@ BuildImplicitMemberInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, /*FirstQualifierInScope=*/0, MemberLookup, /*TemplateArgs=*/0); - if (CopyCtorArg.isInvalid()) + if (CtorArg.isInvalid()) return true; // C++11 [class.copy]p15: // - if a member m has rvalue reference type T&&, it is direct-initialized // with static_cast(x.m); - if (RefersToRValueRef(CopyCtorArg.get())) { - CopyCtorArg = CastForMoving(SemaRef, CopyCtorArg.take()); + if (RefersToRValueRef(CtorArg.get())) { + CtorArg = CastForMoving(SemaRef, CtorArg.take()); } // When the field we are copying is an array, create index variables for @@ -2104,13 +2104,12 @@ BuildImplicitMemberInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, = SemaRef.BuildDeclRefExpr(IterationVar, SizeType, VK_RValue, Loc); assert(!IterationVarRef.isInvalid() && "Reference to invented variable cannot fail!"); - + // Subscript the array with this iteration variable. - CopyCtorArg = SemaRef.CreateBuiltinArraySubscriptExpr(CopyCtorArg.take(), - Loc, + CtorArg = SemaRef.CreateBuiltinArraySubscriptExpr(CtorArg.take(), Loc, IterationVarRef.take(), - Loc); - if (CopyCtorArg.isInvalid()) + Loc); + if (CtorArg.isInvalid()) return true; BaseType = Array->getElementType(); @@ -2118,7 +2117,7 @@ BuildImplicitMemberInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, // The array subscript expression is an lvalue, which is wrong for moving. if (Moving && InitializingArray) - CopyCtorArg = CastForMoving(SemaRef, CopyCtorArg.take()); + CtorArg = CastForMoving(SemaRef, CtorArg.take()); // Construct the entity that we will be initializing. For an array, this // will be first element in the array, which may require several levels @@ -2138,13 +2137,13 @@ BuildImplicitMemberInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, InitializationKind InitKind = InitializationKind::CreateDirect(Loc, SourceLocation(), SourceLocation()); - Expr *CopyCtorArgE = CopyCtorArg.takeAs(); + Expr *CtorArgE = CtorArg.takeAs(); InitializationSequence InitSeq(SemaRef, Entities.back(), InitKind, - &CopyCtorArgE, 1); + &CtorArgE, 1); ExprResult MemberInit = InitSeq.Perform(SemaRef, Entities.back(), InitKind, - MultiExprArg(&CopyCtorArgE, 1)); + MultiExprArg(&CtorArgE, 1)); MemberInit = SemaRef.MaybeCreateExprWithCleanups(MemberInit); if (MemberInit.isInvalid()) return true; diff --git a/test/CXX/dcl.decl/dcl.meaning/dcl.fct/dcl.fct.def.default/p2.cpp b/test/CXX/dcl.decl/dcl.meaning/dcl.fct/dcl.fct.def.default/p2.cpp new file mode 100644 index 0000000000..6b964dba31 --- /dev/null +++ b/test/CXX/dcl.decl/dcl.meaning/dcl.fct/dcl.fct.def.default/p2.cpp @@ -0,0 +1,62 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s + +// FIXME: test with non-std qualifiers + +namespace move { + struct Const { + Const(const Const&&) = default; // expected-error {{the parameter for an explicitly-defaulted move constructor may not be const}} + Const& operator=(const Const&&) = default; // expected-error {{the parameter for an explicitly-defaulted move assignment operator may not be const}} + }; + + struct Volatile { + Volatile(volatile Volatile&&) = default; // expected-error {{the parameter for an explicitly-defaulted move constructor may not be volatile}} + Volatile& operator=(volatile Volatile&&) = default; // expected-error {{the parameter for an explicitly-defaulted move assignment operator may not be volatile}} + }; + + struct AssignmentRet1 { + AssignmentRet1&& operator=(AssignmentRet1&&) = default; // expected-error {{an explicitly-defaulted move assignment operator must return an unqualified lvalue reference to its class type}} + }; + + struct AssignmentRet2 { + const AssignmentRet2& operator=(AssignmentRet2&&) = default; // expected-error {{an explicitly-defaulted move assignment operator must return an unqualified lvalue reference to its class type}} + }; + + struct ConstAssignment { + ConstAssignment& operator=(ConstAssignment&&) const = default; // expected-error {{an explicitly-defaulted move assignment operator may not have 'const' or 'volatile' qualifiers}} + }; +} + +namespace copy { + struct Volatile { + Volatile(const volatile Volatile&) = default; // expected-error {{the parameter for an explicitly-defaulted copy constructor may not be volatile}} + Volatile& operator=(const volatile Volatile&) = default; // expected-error {{the parameter for an explicitly-defaulted copy assignment operator may not be volatile}} + }; + + struct Const { + Const(const Const&) = default; + Const& operator=(const Const&) = default; + }; + + struct NonConst { + NonConst(NonConst&) = default; + NonConst& operator=(NonConst&) = default; + }; + + struct BadConst { + NonConst nc; // makes implicit copy non-const + BadConst(const BadConst&) = default; // expected-error {{is const, but}} + BadConst& operator=(const BadConst&) = default; // expected-error {{is const, but}} + }; + + struct AssignmentRet1 { + AssignmentRet1&& operator=(const AssignmentRet1&) = default; // expected-error {{an explicitly-defaulted copy assignment operator must return an unqualified lvalue reference to its class type}} + }; + + struct AssignmentRet2 { + const AssignmentRet2& operator=(const AssignmentRet2&) = default; // expected-error {{an explicitly-defaulted copy assignment operator must return an unqualified lvalue reference to its class type}} + }; + + struct ConstAssignment { + ConstAssignment& operator=(const ConstAssignment&) const = default; // expected-error {{an explicitly-defaulted copy assignment operator may not have 'const' or 'volatile' qualifiers}} + }; +} diff --git a/test/Lexer/has_feature_cxx0x.cpp b/test/Lexer/has_feature_cxx0x.cpp index ca5f868d9b..7625430384 100644 --- a/test/Lexer/has_feature_cxx0x.cpp +++ b/test/Lexer/has_feature_cxx0x.cpp @@ -164,3 +164,12 @@ int no_alias_templates(); // CHECK-0X: has_alias_templates // CHECK-NO-0X: no_alias_templates + +#if __has_feature(cxx_implicit_moves) +int has_implicit_moves(); +#else +int no_implicit_moves(); +#endif + +// CHECK-0X: has_implicit_moves +// CHECK-NO-0X: no_implicit_moves -- 2.40.0