From 41f792735f1b141be619ba4104a87e61168b8d48 Mon Sep 17 00:00:00 2001 From: Fariborz Jahanian Date: Thu, 25 Jun 2009 21:45:19 +0000 Subject: [PATCH] Patch to diagnose and Mark use of implicit default assignment operator. llvm-svn: 74205 --- .../clang/Basic/DiagnosticSemaKinds.td | 6 ++ clang/lib/Sema/Sema.h | 10 +++ clang/lib/Sema/SemaDeclCXX.cpp | 88 +++++++++++++++++++ clang/lib/Sema/SemaExpr.cpp | 8 +- 4 files changed, 111 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index d9b268eb9417..babcc2660b86 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -584,6 +584,12 @@ def err_defining_default_ctor : Error< "%2 does not have any default constructor">; def note_previous_class_decl : Note< "%0 declared here">; +def err_unintialized_member_for_assign : Error< + "cannot define the implicit default assignment operator for %0, because " + "non-static %select{reference|const}1 member %2 can't use default " + "assignment operator">; +def note_first_required_here : Note< + "synthesized method is first required here">; def err_unintialized_member : Error< "cannot define the implicit default constructor for %0, because " "%select{reference|const}1 member %2 cannot be default-initialized">; diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 699316efef67..faacb9f9f773 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -1598,6 +1598,16 @@ public: void DefineImplicitCopyConstructor(SourceLocation CurrentLocation, CXXConstructorDecl *Constructor, unsigned TypeQuals); + + /// DefineImplicitOverloadedAssign - Checks for feasibility of + /// defining implicit this overloaded assignment operator. + void DefineImplicitOverloadedAssign(SourceLocation CurrentLocation, + CXXMethodDecl *MethodDecl); + + /// getAssignOperatorMethod - Returns the default copy assignmment operator + /// for the class. + CXXMethodDecl *getAssignOperatorMethod(ParmVarDecl *Decl, + CXXRecordDecl *ClassDecl); /// MaybeBindToTemporary - If the passed in expression has a record type with /// a non-trivial destructor, this will return CXXBindTemporaryExpr. Otherwise diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 186097c12718..466015a164d7 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -1942,6 +1942,94 @@ void Sema::DefineImplicitDefaultConstructor(SourceLocation CurrentLocation, Constructor->setUsed(); } +void Sema::DefineImplicitOverloadedAssign(SourceLocation CurrentLocation, + CXXMethodDecl *MethodDecl) { + assert((MethodDecl->isImplicit() && MethodDecl->isOverloadedOperator() && + MethodDecl->getOverloadedOperator() == OO_Equal && + !MethodDecl->isUsed()) && + "DefineImplicitOverloadedAssign - call it for implicit assignment op"); + + CXXRecordDecl *ClassDecl + = cast(MethodDecl->getDeclContext()); + assert(ClassDecl && "DefineImplicitOverloadedAssign - invalid constructor"); + + // C++[class.copy] p210 + // 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; + for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(); + Base != ClassDecl->bases_end(); ++Base) { + CXXRecordDecl *BaseClassDecl + = cast(Base->getType()->getAsRecordType()->getDecl()); + if (CXXMethodDecl *BaseAssignOpMethod = + getAssignOperatorMethod(MethodDecl->getParamDecl(0), BaseClassDecl)) + MarkDeclarationReferenced(CurrentLocation, BaseAssignOpMethod); + } + for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(Context); + Field != ClassDecl->field_end(Context); + ++Field) { + QualType FieldType = Context.getCanonicalType((*Field)->getType()); + if (const ArrayType *Array = Context.getAsArrayType(FieldType)) + FieldType = Array->getElementType(); + if (const RecordType *FieldClassType = FieldType->getAsRecordType()) { + CXXRecordDecl *FieldClassDecl + = cast(FieldClassType->getDecl()); + if (CXXMethodDecl *FieldAssignOpMethod = + getAssignOperatorMethod(MethodDecl->getParamDecl(0), FieldClassDecl)) + MarkDeclarationReferenced(CurrentLocation, FieldAssignOpMethod); + } + else if (FieldType->isReferenceType()) { + Diag(ClassDecl->getLocation(), diag::err_unintialized_member_for_assign) + << Context.getTagDeclType(ClassDecl) << 0 << (*Field)->getNameAsCString(); + Diag((*Field)->getLocation(), diag::note_declared_at); + Diag(CurrentLocation, diag::note_first_required_here); + err = true; + } + else if (FieldType.isConstQualified()) { + Diag(ClassDecl->getLocation(), diag::err_unintialized_member_for_assign) + << Context.getTagDeclType(ClassDecl) << 1 << (*Field)->getNameAsCString(); + Diag((*Field)->getLocation(), diag::note_declared_at); + Diag(CurrentLocation, diag::note_first_required_here); + err = true; + } + } + if (!err) + MethodDecl->setUsed(); +} + +CXXMethodDecl * +Sema::getAssignOperatorMethod(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&). + if (ParmDecl->getType().isConstQualified()) + RHSType.addConst(); + if (ParmDecl->getType().isVolatileQualified()) + RHSType.addVolatile(); + ExprOwningPtr LHS(this, new (Context) DeclRefExpr(ParmDecl, + LHSType, + SourceLocation())); + ExprOwningPtr RHS(this, new (Context) DeclRefExpr(ParmDecl, + RHSType, + SourceLocation())); + Expr *Args[2] = { &*LHS, &*RHS }; + OverloadCandidateSet CandidateSet; + AddMemberOperatorCandidates(clang::OO_Equal, SourceLocation(), Args, 2, + CandidateSet); + OverloadCandidateSet::iterator Best; + if (BestViableFunction(CandidateSet, + ClassDecl->getLocation(), Best) == OR_Success) + return cast(Best->Function); + assert(false && + "getAssignOperatorMethod - copy assignment operator method not found"); + return 0; +} + void Sema::DefineImplicitCopyConstructor(SourceLocation CurrentLocation, CXXConstructorDecl *CopyConstructor, unsigned TypeQuals) { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 5b650680bedd..d94550cc5f99 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -5547,7 +5547,13 @@ void Sema::MarkDeclarationReferenced(SourceLocation Loc, Decl *D) { // FIXME: more checking for other implicits go here. else Constructor->setUsed(true); - } + } else if (CXXMethodDecl *MethodDecl = dyn_cast(D)) { + if (MethodDecl->isImplicit() && MethodDecl->isOverloadedOperator() && + MethodDecl->getOverloadedOperator() == OO_Equal) { + if (!MethodDecl->isUsed()) + DefineImplicitOverloadedAssign(Loc, MethodDecl); + } + } if (FunctionDecl *Function = dyn_cast(D)) { // Implicit instantiation of function templates if (!Function->getBody(Context)) { -- GitLab