[cfe-commits] r58538 - in /cfe/trunk: include/clang/AST/DeclCXX.h include/clang/Basic/DiagnosticKinds.def lib/AST/DeclCXX.cpp lib/Sema/SemaDeclCXX.cpp test/SemaCXX/constructor.cpp
Douglas Gregor
doug.gregor at gmail.com
Fri Oct 31 13:25:11 PDT 2008
Author: dgregor
Date: Fri Oct 31 15:25:05 2008
New Revision: 58538
URL: http://llvm.org/viewvc/llvm-project?rev=58538&view=rev
Log:
Semantic checking of constructor declarations and classification of default/copy constructors
Modified:
cfe/trunk/include/clang/AST/DeclCXX.h
cfe/trunk/include/clang/Basic/DiagnosticKinds.def
cfe/trunk/lib/AST/DeclCXX.cpp
cfe/trunk/lib/Sema/SemaDeclCXX.cpp
cfe/trunk/test/SemaCXX/constructor.cpp
Modified: cfe/trunk/include/clang/AST/DeclCXX.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclCXX.h?rev=58538&r1=58537&r2=58538&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DeclCXX.h (original)
+++ cfe/trunk/include/clang/AST/DeclCXX.h Fri Oct 31 15:25:05 2008
@@ -207,6 +207,14 @@
/// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
};
-inline void CXXRecordDecl::addConstructor(CXXConstructorDecl *ConDecl) {
- Constructors.addOverload(ConDecl);
-}
-
} // end namespace clang
#endif
Modified: cfe/trunk/include/clang/Basic/DiagnosticKinds.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticKinds.def?rev=58538&r1=58537&r2=58538&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticKinds.def (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticKinds.def Fri Oct 31 15:25:05 2008
@@ -683,6 +683,10 @@
"'%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,
Modified: cfe/trunk/lib/AST/DeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclCXX.cpp?rev=58538&r1=58537&r2=58538&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclCXX.cpp (original)
+++ cfe/trunk/lib/AST/DeclCXX.cpp Fri Oct 31 15:25:05 2008
@@ -59,6 +59,22 @@
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 @@
// 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 @@
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
Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=58538&r1=58537&r2=58538&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Fri Oct 31 15:25:05 2008
@@ -579,19 +579,50 @@
// 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;
}
Modified: cfe/trunk/test/SemaCXX/constructor.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constructor.cpp?rev=58538&r1=58537&r2=58538&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/constructor.cpp (original)
+++ cfe/trunk/test/SemaCXX/constructor.cpp Fri Oct 31 15:25:05 2008
@@ -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}}
More information about the cfe-commits
mailing list