]> granicus.if.org Git - clang/commitdiff
Semantic checking of constructor declarations and classification of default/copy...
authorDouglas Gregor <dgregor@apple.com>
Fri, 31 Oct 2008 20:25:05 +0000 (20:25 +0000)
committerDouglas Gregor <dgregor@apple.com>
Fri, 31 Oct 2008 20:25:05 +0000 (20:25 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@58538 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/DeclCXX.h
include/clang/Basic/DiagnosticKinds.def
lib/AST/DeclCXX.cpp
lib/Sema/SemaDeclCXX.cpp
test/SemaCXX/constructor.cpp

index c0a40c25fd27c9a49196cab1a0c968ee6f3ed85e..b6a8758fe5f24cb8e39c9f27e9c5988d8193e334 100644 (file)
@@ -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<CXXRecordDecl>(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<CXXRecordDecl *>(
+             cast<CXXRecordDecl>(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
index 09d4f4a1b2b393cd6a55331bae5b766f57177968..b240cea77aae4164e51d82669cef681a4d89fecb 100644 (file)
@@ -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,
index 41e61ed76fc5103af0e7b82801d4e6c4df9af426..fb47904e01245190c00ccfbe3422768fc6be683b 100644 (file)
@@ -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<CXXRecordDecl*>(
-                                            cast<CXXRecordDecl>(getParent())));
+  QualType ClassTy = C.getTagDeclType(const_cast<CXXRecordDecl*>(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<CXXRecordDecl*>(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
index 6a25dd5bc3e5008d5b69ba94a89f32d21eb4d91e..2651c7a358ce96f7c04dfdaf1fa19446e9e9f278 100644 (file)
@@ -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<CXXRecordDecl>(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<CXXRecordDecl*>(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<CXXRecordDecl>(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;
 }
index 4f9dd2b79d62b6acee088472a773251a7f013f7d..1aedb29c4e0b41a08ca4811a2c4eb6569d476b36 100644 (file)
@@ -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}}