diff options
Diffstat (limited to 'clang/lib')
| -rw-r--r-- | clang/lib/AST/DeclCXX.cpp | 35 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaInit.cpp | 8 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaStmt.cpp | 96 | 
3 files changed, 113 insertions, 26 deletions
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 4548273333f..334ee795f36 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -1116,25 +1116,40 @@ bool CXXConstructorDecl::isDefaultConstructor() const {  bool  CXXConstructorDecl::isCopyConstructor(unsigned &TypeQuals) const { +  return isCopyOrMoveConstructor(TypeQuals) && +         getParamDecl(0)->getType()->isLValueReferenceType(); +} + +bool CXXConstructorDecl::isMoveConstructor(unsigned &TypeQuals) const { +  return isCopyOrMoveConstructor(TypeQuals) && +    getParamDecl(0)->getType()->isRValueReferenceType(); +} + +/// \brief Determine whether this is a copy or move constructor. +bool CXXConstructorDecl::isCopyOrMoveConstructor(unsigned &TypeQuals) const {    // C++ [class.copy]p2:    //   A non-template constructor for class X is a copy constructor    //   if its first parameter is of type X&, const X&, volatile X& or    //   const volatile X&, and either there are no other parameters    //   or else all other parameters have default arguments (8.3.6). +  // C++0x [class.copy]p3: +  //   A non-template constructor for class X is a move constructor if its +  //   first parameter is of type X&&, const X&&, volatile X&&, or  +  //   const volatile X&&, and either there are no other parameters or else  +  //   all other parameters have default arguments.    if ((getNumParams() < 1) ||        (getNumParams() > 1 && !getParamDecl(1)->hasDefaultArg()) ||        (getPrimaryTemplate() != 0) ||        (getDescribedFunctionTemplate() != 0))      return false; - +      const ParmVarDecl *Param = getParamDecl(0); - -  // Do we have a reference type? Rvalue references don't count. -  const LValueReferenceType *ParamRefType = -    Param->getType()->getAs<LValueReferenceType>(); +   +  // Do we have a reference type?  +  const ReferenceType *ParamRefType = Param->getType()->getAs<ReferenceType>();    if (!ParamRefType)      return false; - +      // Is it a reference to our class type?    ASTContext &Context = getASTContext(); @@ -1144,12 +1159,12 @@ CXXConstructorDecl::isCopyConstructor(unsigned &TypeQuals) const {      = Context.getCanonicalType(Context.getTagDeclType(getParent()));    if (PointeeType.getUnqualifiedType() != ClassTy)      return false; - +      // FIXME: other qualifiers? - -  // We have a copy constructor. +   +  // We have a copy or move constructor.    TypeQuals = PointeeType.getCVRQualifiers(); -  return true; +  return true;    }  bool CXXConstructorDecl::isConvertingConstructor(bool AllowExplicit) const { diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 9e8a15739f1..00a13b54cf7 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -3363,20 +3363,20 @@ static ExprResult CopyObject(Sema &S,    if (S.RequireCompleteType(Loc, T, S.PDiag(diag::err_temp_copy_incomplete)))      return move(CurInit); -  // Perform overload resolution using the class's copy constructors. +  // Perform overload resolution using the class's copy/move constructors.    DeclContext::lookup_iterator Con, ConEnd;    OverloadCandidateSet CandidateSet(Loc);    for (llvm::tie(Con, ConEnd) = S.LookupConstructors(Class);         Con != ConEnd; ++Con) { -    // Only consider copy constructors and constructor templates. Per +    // Only consider copy/move constructors and constructor templates. Per      // C++0x [dcl.init]p16, second bullet to class types, this      // initialization is direct-initialization.      CXXConstructorDecl *Constructor = 0;      if ((Constructor = dyn_cast<CXXConstructorDecl>(*Con))) { -      // Handle copy constructors, only. +      // Handle copy/moveconstructors, only.        if (!Constructor || Constructor->isInvalidDecl() || -          !Constructor->isCopyConstructor() || +          !Constructor->isCopyOrMoveConstructor() ||            !Constructor->isConvertingConstructor(/*AllowExplicit=*/true))          continue; diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 7010d48d1c7..015dcbb8c92 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -1137,6 +1137,86 @@ const VarDecl *Sema::getCopyElisionCandidate(QualType ReturnType,    return 0;  } +/// \brief Perform the initialization of a return value. +/// +/// This routine implements C++0x [class.copy]p33, 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. +static ExprResult initializeReturnValue(Sema &S, +                                        const VarDecl *NRVOCandidate, +                                        SourceLocation ReturnLoc, +                                        QualType ResultType, +                                        Expr *RetValExp) { +  // C++0x [class.copy]p33: +  //   When the criteria for elision of a copy operation are met or would  +  //   be met save for the fact that the source object is a function  +  //   parameter, and the object to be copied is designated by an lvalue,  +  //   overload resolution to select the constructor for the copy is first +  //   performed as if the object were designated by an rvalue. +  InitializedEntity Entity = InitializedEntity::InitializeResult(ReturnLoc,  +                                                                 ResultType, +                                                           NRVOCandidate != 0); +   +  ExprResult Res = ExprError(); +  if (NRVOCandidate || S.getCopyElisionCandidate(ResultType, RetValExp, true)) { +    ImplicitCastExpr AsRvalue(ImplicitCastExpr::OnStack,  +                              RetValExp->getType(), CK_LValueToRValue, +                              RetValExp, VK_XValue); +     +    Expr *InitExpr = &AsRvalue; +    InitializationKind Kind  +    = InitializationKind::CreateCopy(RetValExp->getLocStart(), +                                     RetValExp->getLocStart()); +    InitializationSequence Seq(S, Entity, Kind, &InitExpr, 1); +     +    //   [...] If overload resolution fails, or if the type of the first  +    //   parameter of the selected constructor is not an rvalue reference +    //   to the object’s type (possibly cv-qualified), overload resolution  +    //   is performed again, considering the object as an lvalue. +    if (Seq.getKind() != InitializationSequence::FailedSequence) { +      for (InitializationSequence::step_iterator Step = Seq.step_begin(), +           StepEnd = Seq.step_end(); +           Step != StepEnd; ++Step) { +        if (Step->Kind  +            != InitializationSequence::SK_ConstructorInitialization) +          continue; +         +        CXXConstructorDecl *Constructor  +        = cast<CXXConstructorDecl>(Step->Function.Function); +         +        const RValueReferenceType *RRefType +        = Constructor->getParamDecl(0)->getType() +        ->getAs<RValueReferenceType>(); +         +        // If we don't meet the criteria, break out now. +        if (!RRefType ||  +            !S.Context.hasSameUnqualifiedType(RRefType->getPointeeType(), +                                              ResultType)) +          break; +         +        // Promote "AsRvalue" to the heap, since we now need this +        // expression node to persist. +        RetValExp = ImplicitCastExpr::Create(S.Context, +                                             RetValExp->getType(), +                                             CK_LValueToRValue, +                                             RetValExp, 0, VK_XValue); +         +        // Complete type-checking the initialization of the return type +        // using the constructor we found. +        Res = Seq.Perform(S, Entity, Kind, MultiExprArg(&RetValExp, 1)); +      } +    } +  } +   +  // Either we didn't meet the criteria for treating an lvalue as an rvalue, +  // above, or overload resolution failed. Either way, we need to try  +  // (again) now with the return value expression as written. +  if (Res.isInvalid()) +    Res = S.PerformCopyInitialization(Entity, SourceLocation(), RetValExp); +   +  return Res; +} +  /// ActOnBlockReturnStmt - Utility routine to figure out block's return type.  ///  StmtResult @@ -1193,12 +1273,8 @@ Sema::ActOnBlockReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {        // In C++ the return statement is handled via a copy initialization.        // the C version of which boils down to CheckSingleAssignmentConstraints.        NRVOCandidate = getCopyElisionCandidate(FnRetType, RetValExp, false); -      ExprResult Res = PerformCopyInitialization( -                               InitializedEntity::InitializeResult(ReturnLoc,  -                                                                   FnRetType, -                                                            NRVOCandidate != 0), -                               SourceLocation(), -                               Owned(RetValExp)); +      ExprResult Res = initializeReturnValue(*this, NRVOCandidate, ReturnLoc, +                                             FnRetType, RetValExp);        if (Res.isInvalid()) {          // FIXME: Cleanup temporaries here, anyway?          return StmtError(); @@ -1291,12 +1367,8 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {        // In C++ the return statement is handled via a copy initialization.        // the C version of which boils down to CheckSingleAssignmentConstraints.        NRVOCandidate = getCopyElisionCandidate(FnRetType, RetValExp, false); -      ExprResult Res = PerformCopyInitialization( -                               InitializedEntity::InitializeResult(ReturnLoc,  -                                                                   FnRetType, -                                                            NRVOCandidate != 0), -                               SourceLocation(), -                               Owned(RetValExp)); +      ExprResult Res = initializeReturnValue(*this, NRVOCandidate, ReturnLoc, +                                             FnRetType, RetValExp);        if (Res.isInvalid()) {          // FIXME: Cleanup temporaries here, anyway?          return StmtError();  | 

