diff options
author | Douglas Gregor <dgregor@apple.com> | 2008-11-03 17:51:48 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2008-11-03 17:51:48 +0000 |
commit | 0537942f3c3271027f708b7ac872e7149e15be37 (patch) | |
tree | 450f5ebc7aaaa33f962b01e4628b1c2c2031bb84 /clang/lib | |
parent | d395a1722d7cbd0170fe4b4737c93814edaf7b56 (diff) | |
download | bcm5719-llvm-0537942f3c3271027f708b7ac872e7149e15be37.tar.gz bcm5719-llvm-0537942f3c3271027f708b7ac872e7149e15be37.zip |
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
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/AST/DeclCXX.cpp | 12 | ||||
-rw-r--r-- | clang/lib/Sema/Sema.h | 2 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 106 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExprCXX.cpp | 7 | ||||
-rw-r--r-- | clang/lib/Sema/SemaOverload.cpp | 40 |
5 files changed, 152 insertions, 15 deletions
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index fb47904e012..fa4f35be7ad 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<CXXConstructorDecl>(*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 7de21a81f80..fa87479df7d 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 2651c7a358c..703522321a3 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<CXXRecordDecl>(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<CXXRecordDecl>(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<CXXRecordDecl>(static_cast<Decl *>(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 d4d4f39662d..99b0829593c 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 69c025518eb..b20c026f714 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<CXXConstructorDecl>(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; } |