aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/AST/DeclCXX.h4
-rw-r--r--lib/AST/DeclCXX.cpp21
-rw-r--r--lib/CodeGen/CGClass.cpp92
-rw-r--r--lib/CodeGen/CodeGenFunction.cpp11
-rw-r--r--lib/CodeGen/CodeGenFunction.h1
-rw-r--r--lib/Sema/Sema.h9
-rw-r--r--lib/Sema/SemaDeclCXX.cpp486
-rw-r--r--lib/Sema/SemaExpr.cpp4
-rw-r--r--test/CXX/class.access/p4.cpp4
-rw-r--r--test/CodeGenCXX/implicit-copy-assign-operator.cpp56
-rw-r--r--test/SemaCXX/default-assignment-operator.cpp14
-rw-r--r--test/SemaTemplate/instantiate-default-assignment-operator.cpp8
12 files changed, 507 insertions, 203 deletions
diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h
index 5e791c39b5..aa649c8111 100644
--- a/include/clang/AST/DeclCXX.h
+++ b/include/clang/AST/DeclCXX.h
@@ -966,6 +966,10 @@ public:
/// 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());
}
diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp
index e6e9ee6cc0..68f4a821e6 100644
--- a/lib/AST/DeclCXX.cpp
+++ b/lib/AST/DeclCXX.cpp
@@ -633,6 +633,27 @@ bool CXXMethodDecl::isUsualDeallocationFunction() const {
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() &&
diff --git a/lib/CodeGen/CGClass.cpp b/lib/CodeGen/CGClass.cpp
index dd8eec10ce..851284edc3 100644
--- a/lib/CodeGen/CGClass.cpp
+++ b/lib/CodeGen/CGClass.cpp
@@ -712,98 +712,6 @@ CodeGenFunction::SynthesizeCXXCopyConstructor(const FunctionArgList &Args) {
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,
diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp
index 110a8dc812..34b904972f 100644
--- a/lib/CodeGen/CodeGenFunction.cpp
+++ b/lib/CodeGen/CodeGenFunction.cpp
@@ -246,15 +246,8 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
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) {
diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h
index 58894ec9bf..7be895d71f 100644
--- a/lib/CodeGen/CodeGenFunction.h
+++ b/lib/CodeGen/CodeGenFunction.h
@@ -553,7 +553,6 @@ public:
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
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index 49b1899148..1c82baf617 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -1899,7 +1899,8 @@ public:
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 @@ public:
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.
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index 6f79a51c84..e4a154d400 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -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 @@ void Sema::AddImplicitlyDeclaredMembersToClass(Scope *S,
// 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 @@ void Sema::DefineImplicitDestructor(SourceLocation CurrentLocation,
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());
-
- MarkDeclarationReferenced(CurrentLocation, BaseAssignOpMethod);
+ // 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;
+ }
+
+ // 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,
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index 10d470a331..c4ab03facb 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -2678,7 +2678,8 @@ Sema::BuildMemberReferenceExpr(ExprArg Base, QualType BaseExprType,
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 @@ Sema::BuildMemberReferenceExpr(ExprArg Base, QualType BaseExprType,
if ((SS.isSet() || !BaseExpr ||
(isa<CXXThisExpr>(BaseExpr) &&
cast<CXXThisExpr>(BaseExpr)->isImplicit())) &&
+ !SuppressQualifierCheck &&
CheckQualifiedMemberReference(BaseExpr, BaseType, SS, R))
return ExprError();
diff --git a/test/CXX/class.access/p4.cpp b/test/CXX/class.access/p4.cpp
index 2786aefa28..4bf6249483 100644
--- a/