[cfe-commits] r102853 - in /cfe/trunk: include/clang/AST/DeclCXX.h lib/AST/DeclCXX.cpp lib/CodeGen/CGClass.cpp lib/CodeGen/CodeGenFunction.cpp lib/CodeGen/CodeGenFunction.h lib/Sema/Sema.h lib/Sema/SemaDeclCXX.cpp lib/Sema/SemaExpr.cpp test/CXX/class.access/p4.cpp test/CodeGenCXX/implicit-copy-assign-operator.cpp test/SemaCXX/default-assignment-operator.cpp test/SemaTemplate/instantiate-default-assignment-operator.cpp

Douglas Gregor dgregor at apple.com
Sat May 1 13:49:11 PDT 2010


Author: dgregor
Date: Sat May  1 15:49:11 2010
New Revision: 102853

URL: http://llvm.org/viewvc/llvm-project?rev=102853&view=rev
Log:
Complete reimplementation of the synthesis for implicitly-defined copy
assignment operators. 

Previously, Sema provided type-checking and template instantiation for
copy assignment operators, then CodeGen would synthesize the actual
body of the copy constructor. Unfortunately, the two were not in sync,
and CodeGen might pick a copy-assignment operator that is different
from what Sema chose, leading to strange failures, e.g., link-time
failures when CodeGen called a copy-assignment operator that was not
instantiation, run-time failures when copy-assignment operators were
overloaded for const/non-const references and the wrong one was
picked, and run-time failures when by-value copy-assignment operators
did not have their arguments properly copy-initialized.

This implementation synthesizes the implicitly-defined copy assignment
operator bodies in Sema, so that the resulting ASTs encode exactly
what CodeGen needs to do; there is no longer any special code in
CodeGen to synthesize copy-assignment operators. The synthesis of the
body is relatively simple, and we generate one of three different
kinds of copy statements for each base or member:

  - For a class subobject, call the appropriate copy-assignment
    operator, after overload resolution has determined what that is.
  - For an array of scalar types or an array of class types that have
    trivial copy assignment operators, construct a call to
    __builtin_memcpy.
  - For an array of class types with non-trivial copy assignment
    operators, synthesize a (possibly nested!) for loop whose inner
    statement calls the copy constructor.
  - For a scalar type, use built-in assignment.

This patch fixes at least a few tests cases in Boost.Spirit that were
failing because CodeGen picked the wrong copy-assignment operator
(leading to link-time failures), and I suspect a number of undiagnosed
problems will also go away with this change.

Some of the diagnostics we had previously have gotten worse with this
change, since we're going through generic code for our
type-checking. I will improve this in a subsequent patch.


Added:
    cfe/trunk/test/CodeGenCXX/implicit-copy-assign-operator.cpp   (with props)
Modified:
    cfe/trunk/include/clang/AST/DeclCXX.h
    cfe/trunk/lib/AST/DeclCXX.cpp
    cfe/trunk/lib/CodeGen/CGClass.cpp
    cfe/trunk/lib/CodeGen/CodeGenFunction.cpp
    cfe/trunk/lib/CodeGen/CodeGenFunction.h
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/lib/Sema/SemaExpr.cpp
    cfe/trunk/test/CXX/class.access/p4.cpp
    cfe/trunk/test/SemaCXX/default-assignment-operator.cpp
    cfe/trunk/test/SemaTemplate/instantiate-default-assignment-operator.cpp

Modified: cfe/trunk/include/clang/AST/DeclCXX.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclCXX.h?rev=102853&r1=102852&r2=102853&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DeclCXX.h (original)
+++ cfe/trunk/include/clang/AST/DeclCXX.h Sat May  1 15:49:11 2010
@@ -966,6 +966,10 @@
   /// delete or delete[] operator with a particular signature.
   bool isUsualDeallocationFunction() const;
   
+  /// \brief Determine whether this is a copy-assignment operator, regardless
+  /// of whether it was declared implicitly or explicitly.
+  bool isCopyAssignmentOperator() const;
+  
   const CXXMethodDecl *getCanonicalDecl() const {
     return cast<CXXMethodDecl>(FunctionDecl::getCanonicalDecl());
   }

Modified: cfe/trunk/lib/AST/DeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclCXX.cpp?rev=102853&r1=102852&r2=102853&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclCXX.cpp (original)
+++ cfe/trunk/lib/AST/DeclCXX.cpp Sat May  1 15:49:11 2010
@@ -633,6 +633,27 @@
   return true;
 }
 
+bool CXXMethodDecl::isCopyAssignmentOperator() const {
+  // C++0x [class.copy]p19:
+  //  A user-declared copy assignment operator X::operator= is a non-static 
+  //  non-template member function of class X with exactly one parameter of 
+  //  type X, X&, const X&, volatile X& or const volatile X&.
+  if (/*operator=*/getOverloadedOperator() != OO_Equal ||
+      /*non-static*/ isStatic() || 
+      /*non-template*/getPrimaryTemplate() || getDescribedFunctionTemplate() ||
+      /*exactly one parameter*/getNumParams() != 1)
+    return false;
+      
+  QualType ParamType = getParamDecl(0)->getType();
+  if (const LValueReferenceType *Ref = ParamType->getAs<LValueReferenceType>())
+    ParamType = Ref->getPointeeType();
+  
+  ASTContext &Context = getASTContext();
+  QualType ClassType
+    = Context.getCanonicalType(Context.getTypeDeclType(getParent()));
+  return Context.hasSameUnqualifiedType(ClassType, ParamType);
+}
+
 void CXXMethodDecl::addOverriddenMethod(const CXXMethodDecl *MD) {
   assert(MD->isCanonicalDecl() && "Method is not canonical!");
   assert(!MD->getParent()->isDependentContext() &&

Modified: cfe/trunk/lib/CodeGen/CGClass.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGClass.cpp?rev=102853&r1=102852&r2=102853&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGClass.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGClass.cpp Sat May  1 15:49:11 2010
@@ -712,98 +712,6 @@
   InitializeVTablePointers(ClassDecl);
 }
 
-/// SynthesizeCXXCopyAssignment - Implicitly define copy assignment operator.
-/// Before the implicitly-declared copy assignment operator for a class is
-/// implicitly defined, all implicitly- declared copy assignment operators for
-/// its direct base classes and its nonstatic data members shall have been
-/// implicitly defined. [12.8-p12]
-/// The implicitly-defined copy assignment operator for class X performs
-/// memberwise assignment of its subob- jects. The direct base classes of X are
-/// assigned first, in the order of their declaration in
-/// the base-specifier-list, and then the immediate nonstatic data members of X
-/// are assigned, in the order in which they were declared in the class
-/// definition.Each subobject is assigned in the manner appropriate to its type:
-///   if the subobject is of class type, the copy assignment operator for the
-///   class is used (as if by explicit qualification; that is, ignoring any
-///   possible virtual overriding functions in more derived classes);
-///
-///   if the subobject is an array, each element is assigned, in the manner
-///   appropriate to the element type;
-///
-///   if the subobject is of scalar type, the built-in assignment operator is
-///   used.
-void CodeGenFunction::SynthesizeCXXCopyAssignment(const FunctionArgList &Args) {
-  const CXXMethodDecl *MD = cast<CXXMethodDecl>(CurGD.getDecl());
-  const CXXRecordDecl *ClassDecl = MD->getParent();
-  assert(!ClassDecl->hasUserDeclaredCopyAssignment() &&
-         "SynthesizeCXXCopyAssignment - copy assignment has user declaration");
-
-  llvm::Value *ThisPtr = LoadCXXThis();
-  llvm::Value *SrcPtr = 
-    Builder.CreateLoad(GetAddrOfLocalVar(Args[1].first));
-
-  for (CXXRecordDecl::base_class_const_iterator Base = ClassDecl->bases_begin();
-       Base != ClassDecl->bases_end(); ++Base) {
-    
-    llvm::Value *Dest = GetAddressOfBaseClass(ThisPtr, ClassDecl, 
-                                              CXXBaseSpecifierArray(Base), 
-                                              /*NullCheckValue=*/false);
-    llvm::Value *Src = GetAddressOfBaseClass(SrcPtr, ClassDecl, 
-                                             CXXBaseSpecifierArray(Base), 
-                                             /*NullCheckValue=*/false);
-    CXXRecordDecl *BaseClassDecl
-      = cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl());
-    EmitClassCopyAssignment(Dest, Src, BaseClassDecl);
-  }
-
-  for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(),
-       FieldEnd = ClassDecl->field_end();
-       Field != FieldEnd; ++Field) {
-    QualType FieldType = getContext().getCanonicalType((*Field)->getType());
-    const ConstantArrayType *Array =
-      getContext().getAsConstantArrayType(FieldType);
-    if (Array)
-      FieldType = getContext().getBaseElementType(FieldType);
-
-    if (const RecordType *FieldClassType = FieldType->getAs<RecordType>()) {
-      CXXRecordDecl *FieldClassDecl
-      = cast<CXXRecordDecl>(FieldClassType->getDecl());
-      LValue LHS = EmitLValueForField(ThisPtr, *Field, 0);
-      LValue RHS = EmitLValueForField(SrcPtr, *Field, 0);
-      if (Array) {
-        const llvm::Type *BasePtr = ConvertType(FieldType);
-        BasePtr = llvm::PointerType::getUnqual(BasePtr);
-        llvm::Value *DestBaseAddrPtr =
-          Builder.CreateBitCast(LHS.getAddress(), BasePtr);
-        llvm::Value *SrcBaseAddrPtr =
-          Builder.CreateBitCast(RHS.getAddress(), BasePtr);
-        EmitClassAggrCopyAssignment(DestBaseAddrPtr, SrcBaseAddrPtr, Array,
-                                    FieldClassDecl, FieldType);
-      }
-      else
-        EmitClassCopyAssignment(LHS.getAddress(), RHS.getAddress(),
-                                FieldClassDecl);
-      continue;
-    }
-    // Do a built-in assignment of scalar data members.
-    LValue LHS = EmitLValueForField(ThisPtr, *Field, 0);
-    LValue RHS = EmitLValueForField(SrcPtr, *Field, 0);
-    if (!hasAggregateLLVMType(Field->getType())) {
-      RValue RVRHS = EmitLoadOfLValue(RHS, Field->getType());
-      EmitStoreThroughLValue(RVRHS, LHS, Field->getType());
-    } else if (Field->getType()->isAnyComplexType()) {
-      ComplexPairTy Pair = LoadComplexFromAddr(RHS.getAddress(),
-                                               RHS.isVolatileQualified());
-      StoreComplexToAddr(Pair, LHS.getAddress(), LHS.isVolatileQualified());
-    } else {
-      EmitAggregateCopy(LHS.getAddress(), RHS.getAddress(), Field->getType());
-    }
-  }
-
-  // return *this;
-  Builder.CreateStore(ThisPtr, ReturnValue);
-}
-
 static void EmitBaseInitializer(CodeGenFunction &CGF, 
                                 const CXXRecordDecl *ClassDecl,
                                 CXXBaseOrMemberInitializer *BaseInit,

Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.cpp?rev=102853&r1=102852&r2=102853&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.cpp (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.cpp Sat May  1 15:49:11 2010
@@ -246,15 +246,8 @@
 
 void CodeGenFunction::EmitFunctionBody(FunctionArgList &Args) {
   const FunctionDecl *FD = cast<FunctionDecl>(CurGD.getDecl());
-
-  Stmt *Body = FD->getBody();
-  if (Body)
-    EmitStmt(Body);
-  else {
-    assert(FD->isImplicit() && "non-implicit function def has no body");
-    assert(FD->isCopyAssignment() && "implicit function not copy assignment");
-    SynthesizeCXXCopyAssignment(Args);
-  }
+  assert(FD->getBody());
+  EmitStmt(FD->getBody());
 }
 
 void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn) {

Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.h?rev=102853&r1=102852&r2=102853&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.h (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.h Sat May  1 15:49:11 2010
@@ -553,7 +553,6 @@
 
 
   void SynthesizeCXXCopyConstructor(const FunctionArgList &Args);
-  void SynthesizeCXXCopyAssignment(const FunctionArgList &Args);
 
   /// EmitDtorEpilogue - Emit all code that comes at the end of class's
   /// destructor. This is to call destructors on members and base classes in

Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=102853&r1=102852&r2=102853&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Sat May  1 15:49:11 2010
@@ -1899,7 +1899,8 @@
                                             const CXXScopeSpec &SS,
                                             NamedDecl *FirstQualifierInScope,
                                             LookupResult &R,
-                                 const TemplateArgumentListInfo *TemplateArgs);
+                                 const TemplateArgumentListInfo *TemplateArgs,
+                                          bool SuppressQualifierCheck = false);
 
   OwningExprResult LookupMemberExpr(LookupResult &R, Expr *&Base,
                                     bool &IsArrow, SourceLocation OpLoc,
@@ -2186,12 +2187,6 @@
   void DefineImplicitCopyAssignment(SourceLocation CurrentLocation,
                                     CXXMethodDecl *MethodDecl);
 
-  /// getAssignOperatorMethod - Returns the default copy assignmment operator
-  /// for the class.
-  CXXMethodDecl *getAssignOperatorMethod(SourceLocation CurrentLocation,
-                                         ParmVarDecl *Decl,
-                                         CXXRecordDecl *ClassDecl);
-
   /// MaybeBindToTemporary - If the passed in expression has a record type with
   /// a non-trivial destructor, this will return CXXBindTemporaryExpr. Otherwise
   /// it simply returns the passed in expression.

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=102853&r1=102852&r2=102853&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Sat May  1 15:49:11 2010
@@ -16,12 +16,13 @@
 #include "Lookup.h"
 #include "clang/AST/ASTConsumer.h"
 #include "clang/AST/ASTContext.h"
-#include "clang/AST/RecordLayout.h"
+#include "clang/AST/CharUnits.h"
 #include "clang/AST/CXXInheritance.h"
 #include "clang/AST/DeclVisitor.h"
+#include "clang/AST/RecordLayout.h"
+#include "clang/AST/StmtVisitor.h"
 #include "clang/AST/TypeLoc.h"
 #include "clang/AST/TypeOrdering.h"
-#include "clang/AST/StmtVisitor.h"
 #include "clang/Parse/DeclSpec.h"
 #include "clang/Parse/Template.h"
 #include "clang/Basic/PartialDiagnostic.h"
@@ -2634,7 +2635,7 @@
     // Add the parameter to the operator.
     ParmVarDecl *FromParam = ParmVarDecl::Create(Context, CopyAssignment,
                                                  ClassDecl->getLocation(),
-                                                 /*IdentifierInfo=*/0,
+                                                 /*Id=*/0,
                                                  ArgType, /*TInfo=*/0,
                                                  VarDecl::None,
                                                  VarDecl::None, 0);
@@ -4109,102 +4110,423 @@
   Destructor->setUsed();
 }
 
+/// \brief Builds a statement that copies the given entity from \p From to
+/// \c To.
+///
+/// This routine is used to copy the members of a class with an
+/// implicitly-declared copy assignment operator. When the entities being
+/// copied are arrays, this routine builds for loops to copy them.
+///
+/// \param S The Sema object used for type-checking.
+///
+/// \param Loc The location where the implicit copy is being generated.
+///
+/// \param T The type of the expressions being copied. Both expressions must
+/// have this type.
+///
+/// \param To The expression we are copying to.
+///
+/// \param From The expression we are copying from.
+///
+/// \param Depth Internal parameter recording the depth of the recursion.
+///
+/// \returns A statement or a loop that copies the expressions.
+static Sema::OwningStmtResult
+BuildSingleCopyAssign(Sema &S, SourceLocation Loc, QualType T, 
+                      Sema::OwningExprResult To, Sema::OwningExprResult From,
+                      unsigned Depth = 0) {
+  typedef Sema::OwningStmtResult OwningStmtResult;
+  typedef Sema::OwningExprResult OwningExprResult;
+  
+  // C++0x [class.copy]p30:
+  //   Each subobject is assigned in the manner appropriate to its type:
+  //
+  //     - if the subobject is of class type, the copy assignment operator
+  //       for the class is used (as if by explicit qualification; that is, 
+  //       ignoring any possible virtual overriding functions in more derived 
+  //       classes);
+  if (const RecordType *RecordTy = T->getAs<RecordType>()) {
+    CXXRecordDecl *ClassDecl = cast<CXXRecordDecl>(RecordTy->getDecl());
+    
+    // Look for operator=.
+    DeclarationName Name
+      = S.Context.DeclarationNames.getCXXOperatorName(OO_Equal);
+    LookupResult OpLookup(S, Name, Loc, Sema::LookupOrdinaryName);
+    S.LookupQualifiedName(OpLookup, ClassDecl, false);
+    
+    // Filter out any result that isn't a copy-assignment operator.
+    LookupResult::Filter F = OpLookup.makeFilter();
+    while (F.hasNext()) {
+      NamedDecl *D = F.next();
+      if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D))
+        if (Method->isCopyAssignmentOperator())
+          continue;
+      
+      F.erase();
+    }
+    F.done();
+    
+    // Create the nested-name-specifier that will be used to qualify the
+    // reference to operator=; this is required to suppress the virtual
+    // call mechanism.
+    CXXScopeSpec SS;
+    SS.setRange(Loc);
+    SS.setScopeRep(NestedNameSpecifier::Create(S.Context, 0, false, 
+                                               T.getTypePtr()));
+    
+    // Create the reference to operator=.
+    OwningExprResult OpEqualRef
+      = S.BuildMemberReferenceExpr(move(To), T, Loc, /*isArrow=*/false, SS, 
+                                   /*FirstQualifierInScope=*/0, OpLookup, 
+                                   /*TemplateArgs=*/0,
+                                   /*SuppressQualifierCheck=*/true);
+    if (OpEqualRef.isInvalid())
+      return S.StmtError();
+    
+    // Build the call to the assignment operator.
+    Expr *FromE = From.takeAs<Expr>();
+    OwningExprResult Call = S.BuildCallToMemberFunction(/*Scope=*/0, 
+                                                      OpEqualRef.takeAs<Expr>(),
+                                                        Loc, &FromE, 1, 0, Loc);
+    if (Call.isInvalid())
+      return S.StmtError();
+    
+    return S.Owned(Call.takeAs<Stmt>());
+  }
+
+  //     - if the subobject is of scalar type, the built-in assignment 
+  //       operator is used.
+  const ConstantArrayType *ArrayTy = S.Context.getAsConstantArrayType(T);  
+  if (!ArrayTy) {
+    OwningExprResult Assignment = S.CreateBuiltinBinOp(Loc, 
+                                                       BinaryOperator::Assign,
+                                                       To.takeAs<Expr>(),
+                                                       From.takeAs<Expr>());
+    if (Assignment.isInvalid())
+      return S.StmtError();
+    
+    return S.Owned(Assignment.takeAs<Stmt>());
+  }
+    
+  //     - if the subobject is an array, each element is assigned, in the 
+  //       manner appropriate to the element type;
+  
+  // Construct a loop over the array bounds, e.g.,
+  //
+  //   for (__SIZE_TYPE__ i0 = 0; i0 != array-size; ++i0)
+  //
+  // that will copy each of the array elements. 
+  QualType SizeType = S.Context.getSizeType();
+  
+  // Create the iteration variable.
+  IdentifierInfo *IterationVarName = 0;
+  {
+    llvm::SmallString<8> Str;
+    llvm::raw_svector_ostream OS(Str);
+    OS << "__i" << Depth;
+    IterationVarName = &S.Context.Idents.get(OS.str());
+  }
+  VarDecl *IterationVar = VarDecl::Create(S.Context, S.CurContext, Loc,
+                                          IterationVarName, SizeType,
+                            S.Context.getTrivialTypeSourceInfo(SizeType, Loc),
+                                          VarDecl::None, VarDecl::None);
+  
+  // Initialize the iteration variable to zero.
+  llvm::APInt Zero(S.Context.getTypeSize(SizeType), 0);
+  IterationVar->setInit(new (S.Context) IntegerLiteral(Zero, SizeType, Loc));
+
+  // Create a reference to the iteration variable; we'll use this several
+  // times throughout.
+  Expr *IterationVarRef
+    = S.BuildDeclRefExpr(IterationVar, SizeType, Loc).takeAs<Expr>();
+  assert(IterationVarRef && "Reference to invented variable cannot fail!");
+  
+  // Create the DeclStmt that holds the iteration variable.
+  Stmt *InitStmt = new (S.Context) DeclStmt(DeclGroupRef(IterationVar),Loc,Loc);
+  
+  // Create the comparison against the array bound.
+  llvm::APInt Upper = ArrayTy->getSize();
+  Upper.zextOrTrunc(S.Context.getTypeSize(SizeType));
+  OwningExprResult Comparison
+    = S.Owned(new (S.Context) BinaryOperator(IterationVarRef->Retain(),
+                           new (S.Context) IntegerLiteral(Upper, SizeType, Loc),
+                                    BinaryOperator::NE, S.Context.BoolTy, Loc));
+  
+  // Create the pre-increment of the iteration variable.
+  OwningExprResult Increment
+    = S.Owned(new (S.Context) UnaryOperator(IterationVarRef->Retain(),
+                                            UnaryOperator::PreInc,
+                                            SizeType, Loc));
+  
+  // Subscript the "from" and "to" expressions with the iteration variable.
+  From = S.CreateBuiltinArraySubscriptExpr(move(From), Loc,
+                                           S.Owned(IterationVarRef->Retain()),
+                                           Loc);
+  To = S.CreateBuiltinArraySubscriptExpr(move(To), Loc,
+                                         S.Owned(IterationVarRef->Retain()),
+                                         Loc);
+  assert(!From.isInvalid() && "Builtin subscripting can't fail!");
+  assert(!To.isInvalid() && "Builtin subscripting can't fail!");
+  
+  // Build the copy for an individual element of the array.
+  OwningStmtResult Copy = BuildSingleCopyAssign(S, Loc, 
+                                                ArrayTy->getElementType(),
+                                                move(To), move(From), Depth+1);
+  if (Copy.isInvalid()) {
+    InitStmt->Destroy(S.Context);
+    return S.StmtError();
+  }
+  
+  // Construct the loop that copies all elements of this array.
+  return S.ActOnForStmt(Loc, Loc, S.Owned(InitStmt), 
+                        S.MakeFullExpr(Comparison),
+                        Sema::DeclPtrTy(), 
+                        S.MakeFullExpr(Increment),
+                        Loc, move(Copy));
+}
+
 void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation,
-                                        CXXMethodDecl *MethodDecl) {
-  assert((MethodDecl->isImplicit() && MethodDecl->isOverloadedOperator() &&
-          MethodDecl->getOverloadedOperator() == OO_Equal &&
-          !MethodDecl->isUsed()) &&
+                                        CXXMethodDecl *CopyAssignOperator) {
+  assert((CopyAssignOperator->isImplicit() && 
+          CopyAssignOperator->isOverloadedOperator() &&
+          CopyAssignOperator->getOverloadedOperator() == OO_Equal &&
+          !CopyAssignOperator->isUsed()) &&
          "DefineImplicitCopyAssignment called for wrong function");
 
-  CXXRecordDecl *ClassDecl
-    = cast<CXXRecordDecl>(MethodDecl->getDeclContext());
+  CXXRecordDecl *ClassDecl = CopyAssignOperator->getParent();
+
+  if (ClassDecl->isInvalidDecl() || CopyAssignOperator->isInvalidDecl()) {
+    CopyAssignOperator->setInvalidDecl();
+    return;
+  }
+  
+  CopyAssignOperator->setUsed();
 
-  ImplicitlyDefinedFunctionScope Scope(*this, MethodDecl);
+  ImplicitlyDefinedFunctionScope Scope(*this, CopyAssignOperator);
 
-  // C++[class.copy] p12
-  // Before the implicitly-declared copy assignment operator for a class is
-  // implicitly defined, all implicitly-declared copy assignment operators
-  // for its direct base classes and its nonstatic data members shall have
-  // been implicitly defined.
-  bool err = false;
+  // C++0x [class.copy]p30:
+  //   The implicitly-defined or explicitly-defaulted copy assignment operator
+  //   for a non-union class X performs memberwise copy assignment of its 
+  //   subobjects. The direct base classes of X are assigned first, in the 
+  //   order of their declaration in the base-specifier-list, and then the 
+  //   immediate non-static data members of X are assigned, in the order in 
+  //   which they were declared in the class definition.
+  
+  // The statements that form the synthesized function body.
+  ASTOwningVector<&ActionBase::DeleteStmt> Statements(*this);
+  
+  // The parameter for the "other" object, which we are copying from.
+  ParmVarDecl *Other = CopyAssignOperator->getParamDecl(0);
+  Qualifiers OtherQuals = Other->getType().getQualifiers();
+  QualType OtherRefType = Other->getType();
+  if (const LValueReferenceType *OtherRef
+                                = OtherRefType->getAs<LValueReferenceType>()) {
+    OtherRefType = OtherRef->getPointeeType();
+    OtherQuals = OtherRefType.getQualifiers();
+  }
+  
+  // Our location for everything implicitly-generated.
+  SourceLocation Loc = CopyAssignOperator->getLocation();
+  
+  // Construct a reference to the "other" object. We'll be using this 
+  // throughout the generated ASTs.
+  Expr *OtherRef = BuildDeclRefExpr(Other, OtherRefType, Loc).takeAs<Expr>();
+  assert(OtherRef && "Reference to parameter cannot fail!");
+  
+  // Construct the "this" pointer. We'll be using this throughout the generated
+  // ASTs.
+  Expr *This = ActOnCXXThis(Loc).takeAs<Expr>();
+  assert(This && "Reference to this cannot fail!");
+  
+  // Assign base classes.
+  bool Invalid = false;
   for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(),
        E = ClassDecl->bases_end(); Base != E; ++Base) {
-    CXXRecordDecl *BaseClassDecl
-      = cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl());
-    if (CXXMethodDecl *BaseAssignOpMethod =
-          getAssignOperatorMethod(CurrentLocation, MethodDecl->getParamDecl(0), 
-                                  BaseClassDecl)) {
-      CheckDirectMemberAccess(Base->getSourceRange().getBegin(),
-                              BaseAssignOpMethod,
-                              PDiag(diag::err_access_assign_base)
-                                << Base->getType());
+    // Form the assignment:
+    //   static_cast<Base*>(this)->Base::operator=(static_cast<Base&>(other));
+    QualType BaseType = Base->getType().getUnqualifiedType();
+    CXXRecordDecl *BaseClassDecl = 0;
+    if (const RecordType *BaseRecordT = BaseType->getAs<RecordType>())
+      BaseClassDecl = cast<CXXRecordDecl>(BaseRecordT->getDecl());
+    else {
+      Invalid = true;
+      continue;
+    }
 
-      MarkDeclarationReferenced(CurrentLocation, BaseAssignOpMethod);
+    // Construct the "from" expression, which is an implicit cast to the
+    // appropriately-qualified base type.
+    Expr *From = OtherRef->Retain();
+    ImpCastExprToType(From, Context.getQualifiedType(BaseType, OtherQuals),
+                      CastExpr::CK_UncheckedDerivedToBase, /*isLvalue=*/true, 
+                      CXXBaseSpecifierArray(Base));
+
+    // Dereference "this".
+    OwningExprResult To = CreateBuiltinUnaryOp(Loc, UnaryOperator::Deref,
+                                               Owned(This->Retain()));
+    
+    // Implicitly cast "this" to the appropriately-qualified base type.
+    Expr *ToE = To.takeAs<Expr>();
+    ImpCastExprToType(ToE, 
+                      Context.getCVRQualifiedType(BaseType,
+                                      CopyAssignOperator->getTypeQualifiers()),
+                      CastExpr::CK_UncheckedDerivedToBase, 
+                      /*isLvalue=*/true, CXXBaseSpecifierArray(Base));
+    To = Owned(ToE);
+
+    // Build the copy.
+    OwningStmtResult Copy = BuildSingleCopyAssign(*this, Loc, BaseType,
+                                                  move(To), Owned(From));
+    if (Copy.isInvalid()) {
+      Invalid = true;
+      continue;
     }
+    
+    // Success! Record the copy.
+    Statements.push_back(Copy.takeAs<Expr>());
   }
+  
+  // \brief Reference to the __builtin_memcpy function.
+  Expr *BuiltinMemCpyRef = 0;
+  
+  // Assign non-static members.
   for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(),
-       E = ClassDecl->field_end(); Field != E; ++Field) {
-    QualType FieldType = Context.getCanonicalType((*Field)->getType());
-    if (const ArrayType *Array = Context.getAsArrayType(FieldType))
-      FieldType = Array->getElementType();
-    if (const RecordType *FieldClassType = FieldType->getAs<RecordType>()) {
-      CXXRecordDecl *FieldClassDecl
-        = cast<CXXRecordDecl>(FieldClassType->getDecl());
-      if (CXXMethodDecl *FieldAssignOpMethod =
-          getAssignOperatorMethod(CurrentLocation, MethodDecl->getParamDecl(0), 
-                                  FieldClassDecl)) {
-        CheckDirectMemberAccess(Field->getLocation(),
-                                FieldAssignOpMethod,
-                                PDiag(diag::err_access_assign_field)
-                                  << Field->getDeclName() << Field->getType());
-
-        MarkDeclarationReferenced(CurrentLocation, FieldAssignOpMethod);
-      }
-    } else if (FieldType->isReferenceType()) {
+                                  FieldEnd = ClassDecl->field_end(); 
+       Field != FieldEnd; ++Field) {
+    // Check for members of reference type; we can't copy those.
+    if (Field->getType()->isReferenceType()) {
       Diag(ClassDecl->getLocation(), diag::err_uninitialized_member_for_assign)
-      << Context.getTagDeclType(ClassDecl) << 0 << Field->getDeclName();
+        << Context.getTagDeclType(ClassDecl) << 0 << Field->getDeclName();
       Diag(Field->getLocation(), diag::note_declared_at);
-      Diag(CurrentLocation, diag::note_first_required_here);
-      err = true;
-    } else if (FieldType.isConstQualified()) {
+      Diag(Loc, diag::note_first_required_here);
+      Invalid = true;
+      continue;
+    }
+    
+    // Check for members of const-qualified, non-class type.
+    QualType BaseType = Context.getBaseElementType(Field->getType());
+    if (!BaseType->getAs<RecordType>() && BaseType.isConstQualified()) {
       Diag(ClassDecl->getLocation(), diag::err_uninitialized_member_for_assign)
-      << Context.getTagDeclType(ClassDecl) << 1 << Field->getDeclName();
+        << Context.getTagDeclType(ClassDecl) << 1 << Field->getDeclName();
       Diag(Field->getLocation(), diag::note_declared_at);
-      Diag(CurrentLocation, diag::note_first_required_here);
-      err = true;
+      Diag(Loc, diag::note_first_required_here);
+      Invalid = true;      
+      continue;
+    }
+    
+    QualType FieldType = Field->getType().getNonReferenceType();
+    
+    // Build references to the field in the object we're copying from and to.
+    CXXScopeSpec SS; // Intentionally empty
+    LookupResult MemberLookup(*this, Field->getDeclName(), Loc,
+                              LookupMemberName);
+    MemberLookup.addDecl(*Field);
+    MemberLookup.resolveKind();
+    OwningExprResult From = BuildMemberReferenceExpr(Owned(OtherRef->Retain()),
+                                                     OtherRefType,
+                                                     Loc, /*IsArrow=*/false,
+                                                     SS, 0, MemberLookup, 0);
+    OwningExprResult To = BuildMemberReferenceExpr(Owned(This->Retain()),
+                                                   This->getType(),
+                                                   Loc, /*IsArrow=*/true,
+                                                   SS, 0, MemberLookup, 0);
+    assert(!From.isInvalid() && "Implicit field reference cannot fail");
+    assert(!To.isInvalid() && "Implicit field reference cannot fail");
+    
+    // If the field should be copied with __builtin_memcpy rather than via
+    // explicit assignments, do so. This optimization only applies for arrays 
+    // of scalars and arrays of class type with trivial copy-assignment 
+    // operators.
+    if (FieldType->isArrayType() &&
+        (!BaseType->isRecordType() || 
+         cast<CXXRecordDecl>(BaseType->getAs<RecordType>()->getDecl())
+           ->hasTrivialCopyAssignment())) {
+      // Compute the size of the memory buffer to be copied.
+      QualType SizeType = Context.getSizeType();
+      llvm::APInt Size(Context.getTypeSize(SizeType), 
+                       Context.getTypeSizeInChars(BaseType).getQuantity());
+      for (const ConstantArrayType *Array
+              = Context.getAsConstantArrayType(FieldType);
+           Array; 
+           Array = Context.getAsConstantArrayType(Array->getElementType())) {
+        llvm::APInt ArraySize = Array->getSize();
+        ArraySize.zextOrTrunc(Size.getBitWidth());
+        Size *= ArraySize;
+      }
+          
+      // Take the address of the field references for "from" and "to".
+      From = CreateBuiltinUnaryOp(Loc, UnaryOperator::AddrOf, move(From));
+      To = CreateBuiltinUnaryOp(Loc, UnaryOperator::AddrOf, move(To));
+      
+      // Create a reference to the __builtin_memcpy builtin function.
+      if (!BuiltinMemCpyRef) {
+        LookupResult R(*this, &Context.Idents.get("__builtin_memcpy"), Loc,
+                       LookupOrdinaryName);
+        LookupName(R, TUScope, true);
+        
+        FunctionDecl *BuiltinMemCpy = R.getAsSingle<FunctionDecl>();
+        if (!BuiltinMemCpy) {
+          // Something went horribly wrong earlier, and we will have complained
+          // about it.
+          Invalid = true;
+          continue;
+        }
+
+        BuiltinMemCpyRef = BuildDeclRefExpr(BuiltinMemCpy, 
+                                            BuiltinMemCpy->getType(),
+                                            Loc, 0).takeAs<Expr>();
+        assert(BuiltinMemCpyRef && "Builtin reference cannot fail");
+      }
+          
+      ASTOwningVector<&ActionBase::DeleteExpr> CallArgs(*this);
+      CallArgs.push_back(To.takeAs<Expr>());
+      CallArgs.push_back(From.takeAs<Expr>());
+      CallArgs.push_back(new (Context) IntegerLiteral(Size, SizeType, Loc));
+      llvm::SmallVector<SourceLocation, 4> Commas; // FIXME: Silly
+      Commas.push_back(Loc);
+      Commas.push_back(Loc);
+      OwningExprResult Call = ActOnCallExpr(/*Scope=*/0, 
+                                            Owned(BuiltinMemCpyRef->Retain()),
+                                            Loc, move_arg(CallArgs), 
+                                            Commas.data(), Loc);
+      assert(!Call.isInvalid() && "Call to __builtin_memcpy cannot fail!");
+      Statements.push_back(Call.takeAs<Expr>());
+      continue;
+    }
+    
+    // Build the copy of this field.
+    OwningStmtResult Copy = BuildSingleCopyAssign(*this, Loc, FieldType, 
+                                                  move(To), move(From));
+    if (Copy.isInvalid()) {
+      Invalid = true;
+      continue;
+    }
+    
+    // Success! Record the copy.
+    Statements.push_back(Copy.takeAs<Stmt>());
+  }
+
+  if (!Invalid) {
+    // Add a "return *this;"
+    OwningExprResult ThisObj = CreateBuiltinUnaryOp(Loc, UnaryOperator::Deref,
+                                                    Owned(This->Retain()));
+    
+    OwningStmtResult Return = ActOnReturnStmt(Loc, move(ThisObj));
+    if (Return.isInvalid())
+      Invalid = true;
+    else {
+      Statements.push_back(Return.takeAs<Stmt>());
     }
   }
-  if (!err)
-    MethodDecl->setUsed();
-}
 
-CXXMethodDecl *
-Sema::getAssignOperatorMethod(SourceLocation CurrentLocation,
-                              ParmVarDecl *ParmDecl,
-                              CXXRecordDecl *ClassDecl) {
-  QualType LHSType = Context.getTypeDeclType(ClassDecl);
-  QualType RHSType(LHSType);
-  // If class's assignment operator argument is const/volatile qualified,
-  // look for operator = (const/volatile B&). Otherwise, look for
-  // operator = (B&).
-  RHSType = Context.getCVRQualifiedType(RHSType,
-                                     ParmDecl->getType().getCVRQualifiers());
-  ExprOwningPtr<Expr> LHS(this,  new (Context) DeclRefExpr(ParmDecl,
-                                                           LHSType,
-                                                           SourceLocation()));
-  ExprOwningPtr<Expr> RHS(this,  new (Context) DeclRefExpr(ParmDecl,
-                                                           RHSType,
-                                                           CurrentLocation));
-  Expr *Args[2] = { &*LHS, &*RHS };
-  OverloadCandidateSet CandidateSet(CurrentLocation);
-  AddMemberOperatorCandidates(clang::OO_Equal, SourceLocation(), Args, 2,
-                              CandidateSet);
-  OverloadCandidateSet::iterator Best;
-  if (BestViableFunction(CandidateSet, CurrentLocation, Best) == OR_Success)
-    return cast<CXXMethodDecl>(Best->Function);
-  assert(false &&
-         "getAssignOperatorMethod - copy assignment operator method not found");
-  return 0;
+  if (Invalid) {
+    CopyAssignOperator->setInvalidDecl();
+    return;
+  }
+  
+  OwningStmtResult Body = ActOnCompoundStmt(Loc, Loc, move_arg(Statements),
+                                            /*isStmtExpr=*/false);
+  assert(!Body.isInvalid() && "Compound statement creation cannot fail");
+  CopyAssignOperator->setBody(Body.takeAs<Stmt>());
 }
 
 void Sema::DefineImplicitCopyConstructor(SourceLocation CurrentLocation,

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=102853&r1=102852&r2=102853&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Sat May  1 15:49:11 2010
@@ -2678,7 +2678,8 @@
                                const CXXScopeSpec &SS,
                                NamedDecl *FirstQualifierInScope,
                                LookupResult &R,
-                         const TemplateArgumentListInfo *TemplateArgs) {
+                         const TemplateArgumentListInfo *TemplateArgs,
+                               bool SuppressQualifierCheck) {
   Expr *BaseExpr = Base.takeAs<Expr>();
   QualType BaseType = BaseExprType;
   if (IsArrow) {
@@ -2717,6 +2718,7 @@
   if ((SS.isSet() || !BaseExpr ||
        (isa<CXXThisExpr>(BaseExpr) &&
         cast<CXXThisExpr>(BaseExpr)->isImplicit())) &&
+      !SuppressQualifierCheck &&
       CheckQualifiedMemberReference(BaseExpr, BaseType, SS, R))
     return ExprError();
 

Modified: cfe/trunk/test/CXX/class.access/p4.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/class.access/p4.cpp?rev=102853&r1=102852&r2=102853&view=diff
==============================================================================
--- cfe/trunk/test/CXX/class.access/p4.cpp (original)
+++ cfe/trunk/test/CXX/class.access/p4.cpp Sat May  1 15:49:11 2010
@@ -202,13 +202,13 @@
     void operator=(const A &); // expected-note 2 {{declared private here}}
   };
 
-  class Test1 { A a; }; // expected-error {{field of type 'test5::A' has private copy assignment operator}}
+  class Test1 { A a; }; // expected-error {{private member}}
   void test1() {
     Test1 a;
     a = Test1();
   }
 
-  class Test2 : A {}; // expected-error {{base class 'test5::A' has private copy assignment operator}}
+  class Test2 : A {}; // expected-error {{private member}}
   void test2() {
     Test2 a;
     a = Test2();

Added: cfe/trunk/test/CodeGenCXX/implicit-copy-assign-operator.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/implicit-copy-assign-operator.cpp?rev=102853&view=auto
==============================================================================
--- cfe/trunk/test/CodeGenCXX/implicit-copy-assign-operator.cpp (added)
+++ cfe/trunk/test/CodeGenCXX/implicit-copy-assign-operator.cpp Sat May  1 15:49:11 2010
@@ -0,0 +1,56 @@
+// RUN: %clang_cc1 -emit-llvm -triple x86_64-apple-darwin10.0.0 -o - %s | FileCheck %s
+struct A { 
+  A &operator=(const A&);
+  A &operator=(A&);
+};
+
+struct B {
+  B &operator=(B&);
+};
+
+struct C {
+  virtual C& operator=(const C&);
+};
+
+struct POD {
+  int array[3][4];
+};
+
+struct CopyByValue {
+  CopyByValue(const CopyByValue&);
+  CopyByValue &operator=(CopyByValue);
+};
+
+struct D : A, B, virtual C { 
+  int scalar;
+  int scalar_array[2][3];
+  B class_member;
+  C class_member_array[2][3];
+  POD pod_array[2][3];
+
+  union {
+    int x;
+    float f[3];
+  };
+
+  CopyByValue by_value;
+};
+
+void test_D(D d1, D d2) {
+  d1 = d2;
+}
+
+// CHECK: define linkonce_odr %struct.D* @_ZN1DaSERS_
+// CHECK: {{call.*_ZN1AaSERS_}}
+// CHECK: {{call.*_ZN1BaSERS_}}
+// CHECK: {{call.*_ZN1CaSERKS_}}
+// CHECK: {{call void @llvm.memcpy.p0i8.p0i8.i64.*i64 24}}
+// CHECK: {{call.*_ZN1BaSERS_}}
+// CHECK: for.cond
+// CHECK: {{call.*_ZN1CaSERKS_}}
+// CHECK: {{call void @llvm.memcpy.p0i8.p0i8.i64.*i64 288}}
+// CHECK: {{call void @llvm.memcpy.p0i8.p0i8.i64.*i64 12}}
+// CHECK: call void @_ZN11CopyByValueC1ERKS_
+// CHECK: {{call.*_ZN11CopyByValueaSES_}}
+// CHECK: ret
+

Propchange: cfe/trunk/test/CodeGenCXX/implicit-copy-assign-operator.cpp
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cfe/trunk/test/CodeGenCXX/implicit-copy-assign-operator.cpp
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cfe/trunk/test/CodeGenCXX/implicit-copy-assign-operator.cpp
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: cfe/trunk/test/SemaCXX/default-assignment-operator.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/default-assignment-operator.cpp?rev=102853&r1=102852&r2=102853&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/default-assignment-operator.cpp (original)
+++ cfe/trunk/test/SemaCXX/default-assignment-operator.cpp Sat May  1 15:49:11 2010
@@ -1,12 +1,14 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s
 
 class Base { // expected-error {{cannot define the implicit default assignment operator for 'Base', because non-static reference member 'ref' can't use default assignment operator}} \
-  // expected-warning{{class 'Base' does not declare any constructor to initialize its non-modifiable members}}
+  // expected-warning{{class 'Base' does not declare any constructor to initialize its non-modifiable members}} \
+  // expected-note {{synthesized method is first required here}}
   int &ref;  // expected-note {{declared here}} \
   // expected-note{{reference member 'ref' will never be initialized}}
 };
 
-class X  : Base {  // // expected-error {{cannot define the implicit default assignment operator for 'X', because non-static const member 'cint' can't use default assignment operator}}
+class X  : Base {  // // expected-error {{cannot define the implicit default assignment operator for 'X', because non-static const member 'cint' can't use default assignment operator}} \
+  // expected-note  {{synthesized method is first required here}}
 public:
   X();
   const int cint;  // expected-note {{declared here}}
@@ -27,7 +29,7 @@
 
 // Test1
 void f(X x, const X cx) {
-  x = cx;  // expected-note 2 {{synthesized method is first required here}}
+  x = cx;
   x = cx;
   z1 = z2;
 }
@@ -72,7 +74,9 @@
 
 // Test5
 
-class E1 { // expected-error{{cannot define the implicit default assignment operator for 'E1', because non-static const member 'a' can't use default assignment operator}}
+class E1 { // expected-error{{cannot define the implicit default assignment operator for 'E1', because non-static const member 'a' can't use default assignment operator}} \
+  // expected-note {{synthesized method is first required here}}
+
 public:
   const int a; // expected-note{{declared here}}
   E1() : a(0) {}  
@@ -82,6 +86,6 @@
 E1 e1, e2;
 
 void j() {
-  e1 = e2; // expected-note{{synthesized method is first required here}}
+  e1 = e2; 
 }
 

Modified: cfe/trunk/test/SemaTemplate/instantiate-default-assignment-operator.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/instantiate-default-assignment-operator.cpp?rev=102853&r1=102852&r2=102853&view=diff
==============================================================================
--- cfe/trunk/test/SemaTemplate/instantiate-default-assignment-operator.cpp (original)
+++ cfe/trunk/test/SemaTemplate/instantiate-default-assignment-operator.cpp Sat May  1 15:49:11 2010
@@ -5,13 +5,13 @@
   RefPtr& operator=(const PassRefPtr<T>&);
 };
 
-struct A { RefPtr<int> a; };
-struct B : RefPtr<float> { };
+struct A { RefPtr<int> a; };  // expected-note {{instantiation of member function 'RefPtr<int>::operator=' requested here}}
+struct B : RefPtr<float> { }; // expected-note {{in instantiation of member function 'RefPtr<float>::operator=' requested here}}
 
 void f() {
   A a1, a2;
-  a1 = a2; // expected-note {{instantiation of member function 'RefPtr<int>::operator=' requested here}}
+  a1 = a2;
 
   B b1, b2;
-  b1 = b2; // expected-note {{in instantiation of member function 'RefPtr<float>::operator=' requested here}}
+  b1 = b2; 
 }





More information about the cfe-commits mailing list