From 030ff0cdad79b9e0602e143e0669364d2bad8bd2 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Fri, 31 Oct 2008 20:25:05 +0000 Subject: [PATCH] Semantic checking of constructor declarations and classification of default/copy constructors git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@58538 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclCXX.h | 74 ++++++++++++++++++++++--- include/clang/Basic/DiagnosticKinds.def | 4 ++ lib/AST/DeclCXX.cpp | 60 +++++++++++++++++++- lib/Sema/SemaDeclCXX.cpp | 53 ++++++++++++++---- test/SemaCXX/constructor.cpp | 8 ++- 5 files changed, 177 insertions(+), 22 deletions(-) diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index c0a40c25fd..b6a8758fe5 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -207,6 +207,14 @@ public: /// declarations. Second, it provides additional C++ fields, including /// storage for base classes and constructors. class CXXRecordDecl : public RecordDecl, public DeclContext { + /// UserDeclaredConstructor - True when this class has a + /// user-declared constructor. + bool UserDeclaredConstructor : 1; + + /// UserDeclaredCopyConstructor - True when this class has a + /// user-defined copy constructor. + bool UserDeclaredCopyConstructor : 1; + /// Bases - Base classes of this class. /// FIXME: This is wasted space for a union. CXXBaseSpecifier *Bases; @@ -222,7 +230,8 @@ class CXXRecordDecl : public RecordDecl, public DeclContext { CXXRecordDecl(TagKind TK, DeclContext *DC, SourceLocation L, IdentifierInfo *Id) : RecordDecl(CXXRecord, TK, DC, L, Id), DeclContext(CXXRecord), - Bases(0), NumBases(0), Constructors(DC, Id) {} + UserDeclaredConstructor(false), UserDeclaredCopyConstructor(false), + Bases(0), NumBases(0), Constructors(DC, Id) { } ~CXXRecordDecl(); @@ -273,7 +282,19 @@ public: const OverloadedFunctionDecl *getConstructors() const { return &Constructors; } /// addConstructor - Add another constructor to the list of constructors. - void addConstructor(CXXConstructorDecl *ConDecl); + void addConstructor(ASTContext &Context, CXXConstructorDecl *ConDecl); + + /// hasUserDeclaredConstructor - Whether this class has any + /// user-declared constructors. When true, a default constructor + /// will not be implicitly declared. + bool hasUserDeclaredConstructor() const { return UserDeclaredConstructor; } + + /// hasUserDeclaredCopyConstructor - Whether this class has a + /// user-declared copy constructor. When false, a copy constructor + /// will be implicitly declared. + bool hasUserDeclaredCopyConstructor() const { + return UserDeclaredCopyConstructor; + } /// viewInheritance - Renders and displays an inheritance diagram /// for this C++ class and all of its base classes (transitively) using @@ -331,6 +352,19 @@ public: void setAccess(AccessSpecifier AS) { Access = AS; } AccessSpecifier getAccess() const { return AccessSpecifier(Access); } + /// getParent - Returns the parent of this method declaration, which + /// is the class in which this method is defined. + const CXXRecordDecl *getParent() const { + return cast(FunctionDecl::getParent()); + } + + /// getParent - Returns the parent of this method declaration, which + /// is the class in which this method is defined. + CXXRecordDecl *getParent() { + return const_cast( + cast(FunctionDecl::getParent())); + } + /// getThisType - Returns the type of 'this' pointer. /// Should only be called for instance methods. QualType getThisType(ASTContext &C) const; @@ -357,7 +391,8 @@ protected: friend Decl* Decl::Create(llvm::Deserializer& D, ASTContext& C); }; -/// CXXConstructorDecl - Represents a C++ constructor within a class. For example: +/// CXXConstructorDecl - Represents a C++ constructor within a +/// class. For example: /// /// @code /// class X { @@ -422,9 +457,36 @@ public: ImplicitlyDefined = ID; } + /// isDefaultConstructor - Whether this constructor is a default + /// constructor (C++ [class.ctor]p5), which can be used to + /// default-initialize a class of this type. + bool isDefaultConstructor() const; + + /// isCopyConstructor - Whether this constructor is a copy + /// constructor (C++ [class.copy]p2, which can be used to copy the + /// class. @p TypeQuals will be set to the qualifiers on the + /// argument type. For example, @p TypeQuals would be set to @c + /// QualType::Const for the following copy constructor: + /// + /// @code + /// class X { + /// public: + /// X(const X&); + /// }; + /// @endcode + bool isCopyConstructor(ASTContext &Context, unsigned &TypeQuals) const; + + /// isCopyConstructor - Whether this constructor is a copy + /// constructor (C++ [class.copy]p2, which can be used to copy the + /// class. + bool isCopyConstructor(ASTContext &Context) const { + unsigned TypeQuals = 0; + return isCopyConstructor(Context, TypeQuals); + } + /// isConvertingConstructor - Whether this constructor is a /// converting constructor (C++ [class.conv.ctor]), which can be - /// used for (of course) user-defined conversions. + /// used for user-defined conversions. bool isConvertingConstructor() const; // Implement isa/cast/dyncast/etc. @@ -508,10 +570,6 @@ public: } }; -inline void CXXRecordDecl::addConstructor(CXXConstructorDecl *ConDecl) { - Constructors.addOverload(ConDecl); -} - } // end namespace clang #endif diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index 09d4f4a1b2..b240cea77a 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -683,6 +683,10 @@ DIAG(err_invalid_qualified_constructor, ERROR, "'%0' qualifier is not allowed on a constructor") DIAG(err_constructor_return_type, ERROR, "constructor cannot have a return type") +DIAG(err_constructor_redeclared, ERROR, + "constructor cannot be redeclared") +DIAG(err_constructor_byvalue_arg, ERROR, + "copy constructor must pass its first argument by reference") // C++ initialization DIAG(err_not_reference_to_const_init, ERROR, diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 41e61ed76f..fb47904e01 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -59,6 +59,22 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, this->Bases[i] = *Bases[i]; } +void +CXXRecordDecl::addConstructor(ASTContext &Context, + CXXConstructorDecl *ConDecl) { + if (!ConDecl->isImplicitlyDeclared()) { + // Note that we have a user-declared constructor. + UserDeclaredConstructor = true; + + // Note when we have a user-declared copy constructor, which will + // suppress the implicit declaration of a copy constructor. + if (ConDecl->isCopyConstructor(Context)) + UserDeclaredCopyConstructor = true; + } + + Constructors.addOverload(ConDecl); +} + CXXMethodDecl * CXXMethodDecl::Create(ASTContext &C, CXXRecordDecl *RD, SourceLocation L, IdentifierInfo *Id, @@ -76,8 +92,7 @@ QualType CXXMethodDecl::getThisType(ASTContext &C) const { // the type of this is const volatile X*. assert(isInstance() && "No 'this' for static methods!"); - QualType ClassTy = C.getTagDeclType(const_cast( - cast(getParent()))); + QualType ClassTy = C.getTagDeclType(const_cast(getParent())); ClassTy = ClassTy.getWithAdditionalQualifiers(getTypeQualifiers()); return C.getPointerType(ClassTy).withConst(); } @@ -92,6 +107,47 @@ CXXConstructorDecl::Create(ASTContext &C, CXXRecordDecl *RD, isImplicitlyDeclared); } +bool CXXConstructorDecl::isDefaultConstructor() const { + // C++ [class.ctor]p5: + // + // A default constructor for a class X is a constructor of class + // X that can be called without an argument. + return (getNumParams() == 0) || + (getNumParams() > 0 & getParamDecl(1)->getDefaultArg() != 0); +} + +bool +CXXConstructorDecl::isCopyConstructor(ASTContext &Context, + unsigned &TypeQuals) const { + // C++ [class.copy]p2: + // A non-template constructor for class X is a copy constructor + // if its first parameter is of type X&, const X&, volatile X& or + // const volatile X&, and either there are no other parameters + // or else all other parameters have default arguments (8.3.6). + if ((getNumParams() < 1) || + (getNumParams() > 1 && getParamDecl(1)->getDefaultArg() == 0)) + return false; + + const ParmVarDecl *Param = getParamDecl(0); + + // Do we have a reference type? + const ReferenceType *ParamRefType = Param->getType()->getAsReferenceType(); + if (!ParamRefType) + return false; + + // Is it a reference to our class type? + QualType PointeeType + = Context.getCanonicalType(ParamRefType->getPointeeType()); + QualType ClassTy + = Context.getTagDeclType(const_cast(getParent())); + if (PointeeType.getUnqualifiedType() != ClassTy) + return false; + + // We have a copy constructor. + TypeQuals = PointeeType.getCVRQualifiers(); + return true; +} + bool CXXConstructorDecl::isConvertingConstructor() const { // C++ [class.conv.ctor]p1: // A constructor declared without the function-specifier explicit diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 6a25dd5bc3..2651c7a358 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -579,19 +579,50 @@ Sema::DeclTy *Sema::ActOnConstructorDeclarator(CXXConstructorDecl *ConDecl) { // Check default arguments on the constructor CheckCXXDefaultArguments(ConDecl); - // FIXME: Make sure this constructor is an overload of the existing - // constructors and update the class to reflect the addition of this - // constructor (e.g., it now has a user-defined constructor, might - // have a user-declared copy constructor, etc.). - + CXXRecordDecl *ClassDecl = dyn_cast_or_null(CurContext); + if (!ClassDecl) { + ConDecl->setInvalidDecl(); + return ConDecl; + } + + // Make sure this constructor is an overload of the existing + // constructors. + OverloadedFunctionDecl::function_iterator MatchedDecl; + if (!IsOverload(ConDecl, ClassDecl->getConstructors(), MatchedDecl)) { + Diag(ConDecl->getLocation(), + diag::err_constructor_redeclared, + SourceRange(ConDecl->getLocation())); + Diag((*MatchedDecl)->getLocation(), + diag::err_previous_declaration, + SourceRange((*MatchedDecl)->getLocation())); + ConDecl->setInvalidDecl(); + return ConDecl; + } + + + // C++ [class.copy]p3: + // A declaration of a constructor for a class X is ill-formed if + // its first parameter is of type (optionally cv-qualified) X and + // either there are no other parameters or else all other + // parameters have default arguments. + if ((ConDecl->getNumParams() == 1) || + (ConDecl->getNumParams() > 1 && + ConDecl->getParamDecl(1)->getDefaultArg() != 0)) { + QualType ParamType = ConDecl->getParamDecl(0)->getType(); + QualType ClassTy = Context.getTagDeclType( + const_cast(ConDecl->getParent())); + if (Context.getCanonicalType(ParamType).getUnqualifiedType() == ClassTy) { + Diag(ConDecl->getLocation(), + diag::err_constructor_byvalue_arg, + SourceRange(ConDecl->getParamDecl(0)->getLocation())); + ConDecl->setInvalidDecl(); + return 0; + } + } + // Add this constructor to the set of constructors of the current // class. - if (CXXRecordDecl *ClassDecl = dyn_cast_or_null(CurContext)) { - ClassDecl->addConstructor(ConDecl); - } else { - assert(false && "Cannot add a constructor if we're not in the class!"); - } - + ClassDecl->addConstructor(Context, ConDecl); return (DeclTy *)ConDecl; } diff --git a/test/SemaCXX/constructor.cpp b/test/SemaCXX/constructor.cpp index 4f9dd2b79d..1aedb29c4e 100644 --- a/test/SemaCXX/constructor.cpp +++ b/test/SemaCXX/constructor.cpp @@ -1,11 +1,17 @@ // RUN: clang -fsyntax-only -verify %s +typedef int INT; + class Foo { Foo(); (Foo)(float) { } - explicit Foo(int); + explicit Foo(int); // expected-error{{previous declaration is here}} Foo(const Foo&); + ((Foo))(INT); // expected-error{{cannot be redeclared}} + + Foo(Foo foo, int i = 17, int j = 42); // expected-error {{copy constructor must pass its first argument by reference}} + static Foo(short, short); // expected-error{{constructor cannot be declared 'static'}} virtual Foo(double); // expected-error{{constructor cannot be declared 'virtual'}} Foo(long) const; // expected-error{{'const' qualifier is not allowed on a constructor}} -- 2.40.0