[cfe-commits] r138821 - in /cfe/trunk: include/clang/AST/ include/clang/Basic/ include/clang/Sema/ lib/AST/ lib/CodeGen/ lib/Sema/ test/CXX/dcl.decl/dcl.init/ test/CXX/special/class.copy/ test/CXX/special/class.inhctor/ test/CXX/stmt.stmt/stmt.iter/stmt.ranged/ test/CXX/temp/temp.decls/temp.variadic/ test/SemaCXX/
Douglas Gregor
dgregor at apple.com
Tue Aug 30 14:06:34 PDT 2011
On Aug 30, 2011, at 12:58 PM, Sebastian Redl wrote:
> Author: cornedbee
> Date: Tue Aug 30 14:58:05 2011
> New Revision: 138821
>
> URL: http://llvm.org/viewvc/llvm-project?rev=138821&view=rev
> Log:
> Declare and define implicit move constructor and assignment operator.
>
> This makes the code duplication of implicit special member handling even worse,
> but the cleanup will have to come later. For now, this works.
> Follow-up with tests for explicit defaulting and enabling the __has_feature
> flag to come.
Awesome!
> Added:
> cfe/trunk/test/CXX/special/class.copy/implicit-move-def.cpp
> cfe/trunk/test/CXX/special/class.copy/implicit-move.cpp
> Modified:
> cfe/trunk/include/clang/AST/DeclCXX.h
> cfe/trunk/include/clang/AST/Type.h
> cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
> cfe/trunk/include/clang/Sema/Sema.h
> cfe/trunk/lib/AST/DeclCXX.cpp
> cfe/trunk/lib/AST/ExprClassification.cpp
> cfe/trunk/lib/AST/Type.cpp
> cfe/trunk/lib/CodeGen/CGClass.cpp
> cfe/trunk/lib/CodeGen/CGExprCXX.cpp
> cfe/trunk/lib/CodeGen/CGExprConstant.cpp
> cfe/trunk/lib/Sema/SemaDeclCXX.cpp
> cfe/trunk/lib/Sema/SemaExpr.cpp
> cfe/trunk/lib/Sema/SemaLookup.cpp
> cfe/trunk/test/CXX/dcl.decl/dcl.init/p14-0x.cpp
> cfe/trunk/test/CXX/special/class.inhctor/p3.cpp
> cfe/trunk/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp
> cfe/trunk/test/CXX/temp/temp.decls/temp.variadic/p4.cpp
> cfe/trunk/test/SemaCXX/cast-conversion.cpp
> cfe/trunk/test/SemaCXX/explicit.cpp
>
> Modified: cfe/trunk/include/clang/AST/DeclCXX.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclCXX.h?rev=138821&r1=138820&r2=138821&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/AST/DeclCXX.h (original)
> +++ cfe/trunk/include/clang/AST/DeclCXX.h Tue Aug 30 14:58:05 2011
> @@ -468,6 +468,14 @@
> /// \brief Whether we have already declared a destructor within the class.
> bool DeclaredDestructor : 1;
>
> + /// \brief Whether an implicit move constructor was attempted to be declared
> + /// but would have been deleted.
> + bool FailedImplicitMoveConstructor : 1;
> +
> + /// \brief Whether an implicit move assignment operator was attempted to be
> + /// declared but would have been deleted.
> + bool FailedImplicitMoveAssignment : 1;
These bits aren't serialized to the AST file?
> Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=138821&r1=138820&r2=138821&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Tue Aug 30 14:58:05 2011
> @@ -1860,6 +1860,19 @@
> EllipsisLoc);
> }
>
> +// Create a static_cast\<T&&>(expr).
> +static Expr *CastForMoving(Sema &SemaRef, Expr *E) {
> + QualType ExprType = E->getType();
> + QualType TargetType = SemaRef.Context.getRValueReferenceType(ExprType);
> + SourceLocation ExprLoc = E->getLocStart();
> + TypeSourceInfo *TargetLoc = SemaRef.Context.getTrivialTypeSourceInfo(
> + TargetType, ExprLoc);
> +
> + return SemaRef.BuildCXXNamedCast(ExprLoc, tok::kw_static_cast, TargetLoc, E,
> + SourceRange(ExprLoc, ExprLoc),
> + E->getSourceRange()).take();
> +}
> +
> /// ImplicitInitializerKind - How an implicit base or member initializer should
> /// initialize its base or member.
> enum ImplicitInitializerKind {
> @@ -1890,7 +1903,9 @@
> break;
> }
>
> + case IIK_Move:
> case IIK_Copy: {
> + bool Moving = ImplicitInitKind == IIK_Move;
> ParmVarDecl *Param = Constructor->getParamDecl(0);
> QualType ParamType = Param->getType().getNonReferenceType();
>
> @@ -1898,17 +1913,22 @@
> DeclRefExpr::Create(SemaRef.Context, NestedNameSpecifierLoc(), Param,
> Constructor->getLocation(), ParamType,
> VK_LValue, 0);
> -
> +
> // Cast to the base class to avoid ambiguities.
> QualType ArgTy =
> SemaRef.Context.getQualifiedType(BaseSpec->getType().getUnqualifiedType(),
> ParamType.getQualifiers());
>
> + if (Moving) {
> + CopyCtorArg = CastForMoving(SemaRef, CopyCtorArg);
> + }
> +
> CXXCastPath BasePath;
> BasePath.push_back(BaseSpec);
> CopyCtorArg = SemaRef.ImpCastExprToType(CopyCtorArg, ArgTy,
> CK_UncheckedDerivedToBase,
> - VK_LValue, &BasePath).take();
> + Moving ? VK_RValue : VK_LValue,
> + &BasePath).take();
Shouldn't this be VK_XValue rather than VK_RValue?
> +static bool RefersToRValueRef(Expr *MemRef) {
> + ValueDecl *Referenced = cast<MemberExpr>(MemRef)->getMemberDecl();
> + return Referenced->getType()->isRValueReferenceType();
> +}
> +
> static bool
> BuildImplicitMemberInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor,
> ImplicitInitializerKind ImplicitInitKind,
> @@ -1951,7 +1973,8 @@
>
> SourceLocation Loc = Constructor->getLocation();
>
> - if (ImplicitInitKind == IIK_Copy) {
> + if (ImplicitInitKind == IIK_Copy || ImplicitInitKind == IIK_Move) {
> + bool Moving = ImplicitInitKind == IIK_Move;
> ParmVarDecl *Param = Constructor->getParamDecl(0);
> QualType ParamType = Param->getType().getNonReferenceType();
>
> @@ -1964,11 +1987,16 @@
> DeclRefExpr::Create(SemaRef.Context, NestedNameSpecifierLoc(), Param,
> Loc, ParamType, VK_LValue, 0);
>
> + if (Moving) {
> + MemberExprBase = CastForMoving(SemaRef, MemberExprBase);
> + }
> +
> // Build a reference to this field within the parameter.
> CXXScopeSpec SS;
> LookupResult MemberLookup(SemaRef, Field->getDeclName(), Loc,
> Sema::LookupMemberName);
> - MemberLookup.addDecl(Indirect? cast<ValueDecl>(Indirect) : cast<ValueDecl>(Field), AS_public);
> + MemberLookup.addDecl(Indirect ? cast<ValueDecl>(Indirect)
> + : cast<ValueDecl>(Field), AS_public);
> MemberLookup.resolveKind();
> ExprResult CopyCtorArg
> = SemaRef.BuildMemberReferenceExpr(MemberExprBase,
> @@ -1980,7 +2008,14 @@
> /*TemplateArgs=*/0);
> if (CopyCtorArg.isInvalid())
> return true;
> -
> +
> + // C++11 [class.copy]p15:
> + // - if a member m has rvalue reference type T&&, it is direct-initialized
> + // with static_cast<T&&>(x.m);
> + if (RefersToRValueRef(CopyCtorArg.get())) {
> + CopyCtorArg = CastForMoving(SemaRef, CopyCtorArg.take());
> + }
The name "CopyCtorArg" starts to lose some meaning hereā¦
> // When the field we are copying is an array, create index variables for
> // each dimension of the array. We use these index variables to subscript
> // the source array, and other clients (e.g., CodeGen) will perform the
> @@ -1988,8 +2023,10 @@
> SmallVector<VarDecl *, 4> IndexVariables;
> QualType BaseType = Field->getType();
> QualType SizeType = SemaRef.Context.getSizeType();
> + bool InitializingArray = false;
> while (const ConstantArrayType *Array
> = SemaRef.Context.getAsConstantArrayType(BaseType)) {
> + InitializingArray = true;
> // Create the iteration variable for this array index.
> IdentifierInfo *IterationVarName = 0;
> {
> @@ -2018,10 +2055,14 @@
> Loc);
> if (CopyCtorArg.isInvalid())
> return true;
> -
> +
> BaseType = Array->getElementType();
> }
> -
> +
> + // The array subscript expression is an lvalue, which is wrong for moving.
> + if (Moving && InitializingArray)
> + CopyCtorArg = CastForMoving(SemaRef, CopyCtorArg.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
> // of array-subscript entities.
> @@ -2154,10 +2195,11 @@
>
> BaseAndFieldInfo(Sema &S, CXXConstructorDecl *Ctor, bool ErrorsInInits)
> : S(S), Ctor(Ctor), AnyErrorsInInits(ErrorsInInits) {
> - // FIXME: Handle implicit move constructors.
> - if ((Ctor->isImplicit() || Ctor->isDefaulted()) &&
> - Ctor->isCopyConstructor())
> + bool Generated = Ctor->isImplicit() || Ctor->isDefaulted();
> + if (Generated && Ctor->isCopyConstructor())
> IIK = IIK_Copy;
> + else if (Generated && Ctor->isMoveConstructor())
> + IIK = IIK_Move;
> else
> IIK = IIK_Default;
> }
> @@ -2344,7 +2386,7 @@
> continue;
> }
>
> - // If we're not generating the implicit copy constructor, then we'll
> + // If we're not generating the implicit copy/move constructor, then we'll
> // handle anonymous struct/union fields based on their individual
> // indirect fields.
> if (F->isAnonymousStructOrUnion() && Info.IIK == IIK_Default)
> @@ -3158,12 +3200,14 @@
> break;
>
> case CXXMoveConstructor:
> + CheckExplicitlyDefaultedMoveConstructor(cast<CXXConstructorDecl>(*MI));
> + break;
> +
> case CXXMoveAssignment:
> - Diag(MI->getLocation(), diag::err_defaulted_move_unsupported);
> + CheckExplicitlyDefaultedMoveAssignment(*MI);
> break;
>
> - default:
> - // FIXME: Do moves once they exist
> + case CXXInvalid:
> llvm_unreachable("non-special member explicitly defaulted!");
> }
> }
> @@ -3332,7 +3376,7 @@
> Context.VoidTy, 0, 0, EPI)->getAs<FunctionProtoType>();
>
> QualType ArgType = OperType->getArgType(0);
> - if (!ArgType->isReferenceType()) {
> + if (!ArgType->isLValueReferenceType()) {
> Diag(MD->getLocation(), diag::err_defaulted_copy_assign_not_ref);
> HadError = true;
> } else {
> @@ -3384,6 +3428,155 @@
> }
> }
>
> +void Sema::CheckExplicitlyDefaultedMoveConstructor(CXXConstructorDecl *CD) {
> + assert(CD->isExplicitlyDefaulted() && CD->isMoveConstructor());
> +
> + // Whether this was the first-declared instance of the constructor.
> + bool First = CD == CD->getCanonicalDecl();
> +
> + bool HadError = false;
> + if (CD->getNumParams() != 1) {
> + Diag(CD->getLocation(), diag::err_defaulted_move_ctor_params)
> + << CD->getSourceRange();
> + HadError = true;
> + }
> +
> + ImplicitExceptionSpecification Spec(
> + ComputeDefaultedMoveCtorExceptionSpec(CD->getParent()));
> +
> + FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI();
> + const FunctionProtoType *CtorType = CD->getType()->getAs<FunctionProtoType>(),
> + *ExceptionType = Context.getFunctionType(
> + Context.VoidTy, 0, 0, EPI)->getAs<FunctionProtoType>();
> +
> + // Check for parameter type matching.
> + // This is a move ctor so we know it's a cv-qualified rvalue reference to T.
> + QualType ArgType = CtorType->getArgType(0);
> + if (ArgType->getPointeeType().isVolatileQualified()) {
> + Diag(CD->getLocation(), diag::err_defaulted_move_ctor_volatile_param);
> + HadError = true;
> + }
> + if (ArgType->getPointeeType().isConstQualified()) {
> + Diag(CD->getLocation(), diag::err_defaulted_move_ctor_const_param);
> + HadError = true;
> + }
Shouldn't we just check for any qualifiers here? We wouldn't want it to be restrict-qualifier, or address-space-256-qualified, or any other ridiculous non-C++ qualifier.
> + if (CtorType->hasExceptionSpec()) {
> + if (CheckEquivalentExceptionSpec(
> + PDiag(diag::err_incorrect_defaulted_exception_spec)
> + << CXXMoveConstructor,
> + PDiag(),
> + ExceptionType, SourceLocation(),
> + CtorType, CD->getLocation())) {
> + HadError = true;
> + }
> + } else if (First) {
> + // We set the declaration to have the computed exception spec here.
> + // We duplicate the one parameter type.
> + EPI.ExtInfo = CtorType->getExtInfo();
> + CD->setType(Context.getFunctionType(Context.VoidTy, &ArgType, 1, EPI));
> + }
> +
> + if (HadError) {
> + CD->setInvalidDecl();
> + return;
> + }
> +
> + if (ShouldDeleteMoveConstructor(CD)) {
> + if (First) {
> + CD->setDeletedAsWritten();
> + } else {
> + Diag(CD->getLocation(), diag::err_out_of_line_default_deletes)
> + << CXXMoveConstructor;
> + CD->setInvalidDecl();
> + }
> + }
> +}
> +
> +void Sema::CheckExplicitlyDefaultedMoveAssignment(CXXMethodDecl *MD) {
> + assert(MD->isExplicitlyDefaulted());
> +
> + // Whether this was the first-declared instance of the operator
> + bool First = MD == MD->getCanonicalDecl();
> +
> + bool HadError = false;
> + if (MD->getNumParams() != 1) {
> + Diag(MD->getLocation(), diag::err_defaulted_move_assign_params)
> + << MD->getSourceRange();
> + HadError = true;
> + }
> +
> + QualType ReturnType =
> + MD->getType()->getAs<FunctionType>()->getResultType();
> + if (!ReturnType->isLValueReferenceType() ||
> + !Context.hasSameType(
> + Context.getCanonicalType(ReturnType->getPointeeType()),
> + Context.getCanonicalType(Context.getTypeDeclType(MD->getParent())))) {
> + Diag(MD->getLocation(), diag::err_defaulted_move_assign_return_type);
> + HadError = true;
> + }
> +
> + ImplicitExceptionSpecification Spec(
> + ComputeDefaultedMoveCtorExceptionSpec(MD->getParent()));
> +
> + FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI();
> + const FunctionProtoType *OperType = MD->getType()->getAs<FunctionProtoType>(),
> + *ExceptionType = Context.getFunctionType(
> + Context.VoidTy, 0, 0, EPI)->getAs<FunctionProtoType>();
> +
> + QualType ArgType = OperType->getArgType(0);
> + if (!ArgType->isRValueReferenceType()) {
> + Diag(MD->getLocation(), diag::err_defaulted_move_assign_not_ref);
> + HadError = true;
> + } else {
> + if (ArgType->getPointeeType().isVolatileQualified()) {
> + Diag(MD->getLocation(), diag::err_defaulted_move_assign_volatile_param);
> + HadError = true;
> + }
> + if (ArgType->getPointeeType().isConstQualified()) {
> + Diag(MD->getLocation(), diag::err_defaulted_move_assign_const_param);
> + HadError = true;
> + }
> + }
Same comment about non-C++ qualifiers here.
Very nice!
- Doug
More information about the cfe-commits
mailing list