From 0537942f3c3271027f708b7ac872e7149e15be37 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Mon, 3 Nov 2008 17:51:48 +0000 Subject: [PATCH] Add implicitly-declared default and copy constructors to C++ classes, when appropriate. Conversions for class types now make use of copy constructors. I've replaced the egregious hack allowing class-to-class conversions with a slightly less egregious hack calling these conversions standard conversions (for overloading reasons). llvm-svn: 58622 --- clang/include/clang/AST/DeclCXX.h | 4 + clang/lib/AST/DeclCXX.cpp | 12 +++ clang/lib/Sema/Sema.h | 2 + clang/lib/Sema/SemaDeclCXX.cpp | 106 +++++++++++++++++++ clang/lib/Sema/SemaExprCXX.cpp | 7 ++ clang/lib/Sema/SemaOverload.cpp | 40 ++++--- clang/test/SemaCXX/overload-call-copycon.cpp | 38 +++++++ 7 files changed, 194 insertions(+), 15 deletions(-) create mode 100644 clang/test/SemaCXX/overload-call-copycon.cpp diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index b6a8758fe5f2..9595a78021fa 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -281,6 +281,10 @@ public: /// the constructors of this class. const OverloadedFunctionDecl *getConstructors() const { return &Constructors; } + /// hasConstCopyConstructor - Determines whether this class has a + /// copy constructor that accepts a const-qualified argument. + bool hasConstCopyConstructor(ASTContext &Context) const; + /// addConstructor - Add another constructor to the list of constructors. void addConstructor(ASTContext &Context, CXXConstructorDecl *ConDecl); diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index fb47904e0124..fa4f35be7ad9 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -59,6 +59,18 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, this->Bases[i] = *Bases[i]; } +bool CXXRecordDecl::hasConstCopyConstructor(ASTContext &Context) const { + for (OverloadedFunctionDecl::function_const_iterator Con + = Constructors.function_begin(); + Con != Constructors.function_end(); ++Con) { + unsigned TypeQuals; + if (cast(*Con)->isCopyConstructor(Context, TypeQuals) && + (TypeQuals & QualType::Const != 0)) + return true; + } + return false; +} + void CXXRecordDecl::addConstructor(ASTContext &Context, CXXConstructorDecl *ConDecl) { diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 7de21a81f80e..fa87479df7d0 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -799,6 +799,8 @@ public: Declarator &D, ExprTy *BitfieldWidth, ExprTy *Init, DeclTy *LastInGroup); + void AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl); + virtual void ActOnFinishCXXMemberSpecification(Scope* S, SourceLocation RLoc, DeclTy *TagDecl, SourceLocation LBrac, diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 2651c7a358ce..703522321a31 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -552,9 +552,115 @@ void Sema::ActOnFinishCXXMemberSpecification(Scope* S, SourceLocation RLoc, FieldCollector->getCurNumFields(), LBrac, RBrac, 0); } +/// AddImplicitlyDeclaredMembersToClass - Adds any implicitly-declared +/// special functions, such as the default constructor, copy +/// constructor, or destructor, to the given C++ class (C++ +/// [special]p1). This routine can only be executed just before the +/// definition of the class is complete. +void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) { + if (!ClassDecl->hasUserDeclaredConstructor()) { + // C++ [class.ctor]p5: + // A default constructor for a class X is a constructor of class X + // that can be called without an argument. If there is no + // user-declared constructor for class X, a default constructor is + // implicitly declared. An implicitly-declared default constructor + // is an inline public member of its class. + CXXConstructorDecl *DefaultCon = + CXXConstructorDecl::Create(Context, ClassDecl, + ClassDecl->getLocation(), + ClassDecl->getIdentifier(), + Context.getFunctionType(Context.VoidTy, + 0, 0, false, 0), + /*isExplicit=*/false, + /*isInline=*/true, + /*isImplicitlyDeclared=*/true); + DefaultCon->setAccess(AS_public); + ClassDecl->addConstructor(Context, DefaultCon); + } + + if (!ClassDecl->hasUserDeclaredCopyConstructor()) { + // C++ [class.copy]p4: + // If the class definition does not explicitly declare a copy + // constructor, one is declared implicitly. + + // C++ [class.copy]p5: + // The implicitly-declared copy constructor for a class X will + // have the form + // + // X::X(const X&) + // + // if + bool HasConstCopyConstructor = true; + + // -- each direct or virtual base class B of X has a copy + // constructor whose first parameter is of type const B& or + // const volatile B&, and + for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(); + HasConstCopyConstructor && Base != ClassDecl->bases_end(); ++Base) { + const CXXRecordDecl *BaseClassDecl + = cast(Base->getType()->getAsRecordType()->getDecl()); + HasConstCopyConstructor + = BaseClassDecl->hasConstCopyConstructor(Context); + } + + // -- for all the nonstatic data members of X that are of a + // class type M (or array thereof), each such class type + // has a copy constructor whose first parameter is of type + // const M& or const volatile M&. + for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(); + HasConstCopyConstructor && Field != ClassDecl->field_end(); ++Field) { + QualType FieldType = (*Field)->getType(); + if (const ArrayType *Array = Context.getAsArrayType(FieldType)) + FieldType = Array->getElementType(); + if (const RecordType *FieldClassType = FieldType->getAsRecordType()) { + const CXXRecordDecl *FieldClassDecl + = cast(FieldClassType->getDecl()); + HasConstCopyConstructor + = FieldClassDecl->hasConstCopyConstructor(Context); + } + } + + // Otherwise, the implicitly declared copy constructor will have + // the form + // + // X::X(X&) + QualType ArgType = Context.getTypeDeclType(ClassDecl); + if (HasConstCopyConstructor) + ArgType = ArgType.withConst(); + ArgType = Context.getReferenceType(ArgType); + + // An implicitly-declared copy constructor is an inline public + // member of its class. + CXXConstructorDecl *CopyConstructor + = CXXConstructorDecl::Create(Context, ClassDecl, + ClassDecl->getLocation(), + ClassDecl->getIdentifier(), + Context.getFunctionType(Context.VoidTy, + &ArgType, 1, + false, 0), + /*isExplicit=*/false, + /*isInline=*/true, + /*isImplicitlyDeclared=*/true); + CopyConstructor->setAccess(AS_public); + + // Add the parameter to the constructor. + ParmVarDecl *FromParam = ParmVarDecl::Create(Context, CopyConstructor, + ClassDecl->getLocation(), + /*IdentifierInfo=*/0, + ArgType, VarDecl::None, 0, 0); + CopyConstructor->setParams(&FromParam, 1); + + ClassDecl->addConstructor(Context, CopyConstructor); + } + + // FIXME: Implicit destructor + // FIXME: Implicit copy assignment operator +} + void Sema::ActOnFinishCXXClassDef(DeclTy *D) { CXXRecordDecl *Rec = cast(static_cast(D)); FieldCollector->FinishClass(); + AddImplicitlyDeclaredMembersToClass(Rec); PopDeclContext(); // Everything, including inline method definitions, have been parsed. diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index d4d4f39662dd..99b0829593cf 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -982,6 +982,13 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType, ImpCastExprToType(From, FromType); break; + case ICK_Derived_To_Base: + // FIXME: This should never happen. It's a consequence of + // pretending that a user-defined conversion via copy constructor + // is actually a standard conversion. + ImpCastExprToType(From, ToType); + break; + default: assert(false && "Improper second standard conversion"); break; diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 69c025518ebd..b20c026f7146 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -350,24 +350,34 @@ Sema::TryImplicitConversion(Expr* From, QualType ToType) ImplicitConversionSequence ICS; if (IsStandardConversion(From, ToType, ICS.Standard)) ICS.ConversionKind = ImplicitConversionSequence::StandardConversion; - else if (IsUserDefinedConversion(From, ToType, ICS.UserDefined)) + else if (IsUserDefinedConversion(From, ToType, ICS.UserDefined)) { ICS.ConversionKind = ImplicitConversionSequence::UserDefinedConversion; - else { - // FIXME: This is a hack to allow a class type S to implicitly - // convert to another class type S, at least until we have proper - // support for implicitly-declared copy constructors. - QualType FromType = Context.getCanonicalType(From->getType()); - ToType = Context.getCanonicalType(ToType); - if (FromType.getUnqualifiedType() == ToType.getUnqualifiedType()) { - ICS.Standard.setAsIdentityConversion(); - ICS.Standard.FromTypePtr = From->getType().getAsOpaquePtr(); - ICS.Standard.ToTypePtr = ToType.getAsOpaquePtr(); - ICS.ConversionKind = ImplicitConversionSequence::StandardConversion; - return ICS; + // C++ [over.ics.user]p4: + // A conversion of an expression of class type to the same class + // type is given Exact Match rank, and a conversion of an + // expression of class type to a base class of that type is + // given Conversion rank, in spite of the fact that a copy + // constructor (i.e., a user-defined conversion function) is + // called for those cases. + if (CXXConstructorDecl *Constructor + = dyn_cast(ICS.UserDefined.ConversionFunction)) { + if (Constructor->isCopyConstructor(Context)) { + // FIXME: This is a temporary hack to give copy-constructor + // calls the appropriate rank (Exact Match or Conversion) by + // making them into standard conversions. To really fix this, we + // need to tweak the rank-checking logic to deal with ranking + // different kinds of user conversions. + ICS.ConversionKind = ImplicitConversionSequence::StandardConversion; + ICS.Standard.setAsIdentityConversion(); + ICS.Standard.FromTypePtr = From->getType().getAsOpaquePtr(); + ICS.Standard.ToTypePtr = ToType.getAsOpaquePtr(); + if (IsDerivedFrom(From->getType().getUnqualifiedType(), + ToType.getUnqualifiedType())) + ICS.Standard.Second = ICK_Derived_To_Base; + } } - + } else ICS.ConversionKind = ImplicitConversionSequence::BadConversion; - } return ICS; } diff --git a/clang/test/SemaCXX/overload-call-copycon.cpp b/clang/test/SemaCXX/overload-call-copycon.cpp new file mode 100644 index 000000000000..82709285770a --- /dev/null +++ b/clang/test/SemaCXX/overload-call-copycon.cpp @@ -0,0 +1,38 @@ +// RUN: clang -fsyntax-only %s +class X { }; + +int& copycon(X x); +float& copycon(...); + +void test_copycon(X x, X const xc, X volatile xv) { + int& i1 = copycon(x); + int& i2 = copycon(xc); + float& f1 = copycon(xv); +} + +class A { +public: + A(A&); +}; + +class B : public A { }; + +short& copycon2(A a); +int& copycon2(B b); +float& copycon2(...); + +void test_copycon2(A a, const A ac, B b, B const bc, B volatile bv) { + int& i1 = copycon2(b); + float& f1 = copycon2(bc); + float& f2 = copycon2(bv); + short& s1 = copycon2(a); + float& f3 = copycon2(ac); +} + +int& copycon3(A a); +float& copycon3(...); + +void test_copycon3(B b, const B bc) { + int& i1 = copycon3(b); + float& f1 = copycon3(bc); +}