From 42a552f8200ba5948661aee0106fce0c04e39818 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 5 Nov 2008 20:51:48 +0000 Subject: [PATCH] Parsing, representation, and preliminary semantic analysis of destructors. Implicit declaration of destructors (when necessary). Extended Declarator to store information about parsed constructors and destructors; this will be extended to deal with declarators that name overloaded operators (e.g., "operator +") and user-defined conversion operators (e.g., "operator int"). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@58767 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclBase.h | 3 +- include/clang/AST/DeclCXX.h | 90 ++++++++- include/clang/Basic/DiagnosticKinds.def | 16 ++ include/clang/Parse/DeclSpec.h | 57 +++++- include/clang/Parse/Parser.h | 1 + lib/AST/DeclBase.cpp | 1 + lib/AST/DeclCXX.cpp | 14 ++ lib/Parse/ParseDecl.cpp | 49 ++++- lib/Parse/ParseDeclCXX.cpp | 53 ++++-- lib/Sema/Sema.h | 8 +- lib/Sema/SemaDecl.cpp | 100 +++------- lib/Sema/SemaDeclCXX.cpp | 232 +++++++++++++++++++++++- test/SemaCXX/destructor.cpp | 37 ++++ www/cxx_status.html | 29 ++- 14 files changed, 580 insertions(+), 110 deletions(-) create mode 100644 test/SemaCXX/destructor.cpp diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h index e6d4df1cb3..5a07c48d4a 100644 --- a/include/clang/AST/DeclBase.h +++ b/include/clang/AST/DeclBase.h @@ -67,6 +67,7 @@ public: Function, // [DeclContext] CXXMethod, CXXConstructor, + CXXDestructor, Var, ImplicitParam, CXXClassVar, @@ -90,7 +91,7 @@ public: TagFirst = Enum , TagLast = CXXRecord, RecordFirst = Record , RecordLast = CXXRecord, ValueFirst = EnumConstant , ValueLast = ParmVar, - FunctionFirst = Function , FunctionLast = CXXConstructor, + FunctionFirst = Function , FunctionLast = CXXDestructor, VarFirst = Var , VarLast = ParmVar }; diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 9d0065e4fb..2065677c7a 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -20,6 +20,7 @@ namespace clang { class CXXRecordDecl; class CXXConstructorDecl; +class CXXDestructorDecl; /// OverloadedFunctionDecl - An instance of this class represents a /// set of overloaded functions. All of the functions have the same @@ -230,11 +231,15 @@ class CXXRecordDecl : public RecordDecl, public DeclContext { /// CXXConstructorDecl. OverloadedFunctionDecl Constructors; + // Destructor - The destructor of this C++ class. + CXXDestructorDecl *Destructor; + CXXRecordDecl(TagKind TK, DeclContext *DC, SourceLocation L, IdentifierInfo *Id) : RecordDecl(CXXRecord, TK, DC, L, Id), DeclContext(CXXRecord), UserDeclaredConstructor(false), UserDeclaredCopyConstructor(false), - Aggregate(true), Bases(0), NumBases(0), Constructors(DC, Id) { } + Aggregate(true), Bases(0), NumBases(0), Constructors(DC, Id), + Destructor(0) { } ~CXXRecordDecl(); @@ -303,6 +308,15 @@ public: return UserDeclaredCopyConstructor; } + /// getDestructor - Retrieve the destructor for this class. + CXXDestructorDecl *getDestructor() const { return Destructor; } + + /// setDestructor - Set the destructor for this class. + void setDestructor(CXXDestructorDecl *Destructor) { + assert(!this->Destructor && "Already have a destructor!"); + this->Destructor = Destructor; + } + /// isAggregate - Whether this class is an aggregate (C++ /// [dcl.init.aggr]), which is a class with no user-declared /// constructors, no private or protected non-static data members, @@ -620,6 +634,80 @@ public: static CXXConstructorDecl* CreateImpl(llvm::Deserializer& D, ASTContext& C); }; +/// CXXDestructorDecl - Represents a C++ destructor within a +/// class. For example: +/// +/// @code +/// class X { +/// public: +/// ~X(); // represented by a CXXDestructorDecl. +/// }; +/// @endcode +class CXXDestructorDecl : public CXXMethodDecl { + /// ImplicitlyDeclared - Whether this destructor was implicitly + /// declared. When false, the destructor was declared by the user. + bool ImplicitlyDeclared : 1; + + /// ImplicitlyDefined - Whether this destructor was implicitly + /// defined by the compiler. When false, the destructor was defined + /// by the user. In C++03, this flag will have the same value as + /// ImplicitlyDeclared. In C++0x, however, a destructor that is + /// explicitly defaulted (i.e., defined with " = default") will have + /// @c !ImplicitlyDeclared && ImplicitlyDefined. + bool ImplicitlyDefined : 1; + + CXXDestructorDecl(CXXRecordDecl *RD, SourceLocation L, + IdentifierInfo *Id, QualType T, + bool isInline, bool isImplicitlyDeclared) + : CXXMethodDecl(CXXDestructor, RD, L, Id, T, false, isInline, + /*PrevDecl=*/0), + ImplicitlyDeclared(isImplicitlyDeclared), + ImplicitlyDefined(false) { } + +public: + static CXXDestructorDecl *Create(ASTContext &C, CXXRecordDecl *RD, + SourceLocation L, IdentifierInfo *Id, + QualType T, bool isInline, + bool isImplicitlyDeclared); + + /// isImplicitlyDeclared - Whether this destructor was implicitly + /// declared. If false, then this destructor was explicitly + /// declared by the user. + bool isImplicitlyDeclared() const { return ImplicitlyDeclared; } + + /// isImplicitlyDefined - Whether this destructor was implicitly + /// defined. If false, then this destructor was defined by the + /// user. This operation can only be invoked if the destructor has + /// already been defined. + bool isImplicitlyDefined() const { + assert(getBody() != 0 && + "Can only get the implicit-definition flag once the destructor has been defined"); + return ImplicitlyDefined; + } + + /// setImplicitlyDefined - Set whether this destructor was + /// implicitly defined or not. + void setImplicitlyDefined(bool ID) { + assert(getBody() != 0 && + "Can only set the implicit-definition flag once the destructor has been defined"); + ImplicitlyDefined = ID; + } + + // Implement isa/cast/dyncast/etc. + static bool classof(const Decl *D) { + return D->getKind() == CXXDestructor; + } + static bool classof(const CXXDestructorDecl *D) { return true; } + + /// EmitImpl - Serialize this CXXDestructorDecl. Called by Decl::Emit. + // FIXME: Implement this. + //virtual void EmitImpl(llvm::Serializer& S) const; + + /// CreateImpl - Deserialize a CXXDestructorDecl. Called by Decl::Create. + // FIXME: Implement this. + static CXXDestructorDecl* CreateImpl(llvm::Deserializer& D, ASTContext& C); +}; + /// CXXClassVarDecl - Represents a static data member of a struct/union/class. class CXXClassVarDecl : public VarDecl { diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index c4c3d0c986..1972716e1d 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -688,6 +688,22 @@ DIAG(err_constructor_redeclared, ERROR, DIAG(err_constructor_byvalue_arg, ERROR, "copy constructor must pass its first argument by reference") +// C++ destructors +DIAG(err_destructor_cannot_be, ERROR, + "destructor cannot be declared '%0'") +DIAG(err_invalid_qualified_destructor, ERROR, + "'%0' qualifier is not allowed on a destructor") +DIAG(err_destructor_return_type, ERROR, + "destructor cannot have a return type") +DIAG(err_destructor_redeclared, ERROR, + "destructor cannot be redeclared") +DIAG(err_destructor_with_params, ERROR, + "destructor cannot have any parameters") +DIAG(err_destructor_variadic, ERROR, + "destructor cannot be variadic") +DIAG(err_destructor_typedef_name, ERROR, + "destructor cannot be declared using a typedef '%0' of the class name") + // C++ initialization DIAG(err_not_reference_to_const_init, ERROR, "non-const reference to type '%0' cannot be initialized with a %1 of type '%2'") diff --git a/include/clang/Parse/DeclSpec.h b/include/clang/Parse/DeclSpec.h index 2f1ac37048..fab6ec3c34 100644 --- a/include/clang/Parse/DeclSpec.h +++ b/include/clang/Parse/DeclSpec.h @@ -604,11 +604,23 @@ public: ForContext, // Declaration within first part of a for loop. ConditionContext // Condition declaration in a C++ if/switch/while/for. }; + + /// DeclaratorKind - The kind of declarator this represents. + enum DeclaratorKind { + DK_Abstract, // An abstract declarator (has no identifier) + DK_Normal, // A normal declarator (has an identifier). + DK_Constructor, // A C++ constructor (identifier is the class name) + DK_Destructor // A C++ destructor (has no identifier) + }; + private: /// Context - Where we are parsing this declarator. /// TheContext Context; - + + /// Kind - What kind of declarator this is. + DeclaratorKind Kind; + /// DeclTypeInfo - This holds each type that the declarator includes as it is /// parsed. This is pushed from the identifier out, which means that element /// #0 will be the most closely bound to the identifier, and @@ -626,11 +638,15 @@ private: /// AsmLabel - The asm label, if specified. Action::ExprTy *AsmLabel; - + + // When Kind is DK_Constructor or DK_Destructor, the type associated + // with the constructor or destructor. + Action::TypeTy *Type; + public: Declarator(const DeclSpec &ds, TheContext C) - : DS(ds), Identifier(0), Context(C), InvalidType(false), - GroupingParens(false), AttrList(0), AsmLabel(0) { + : DS(ds), Identifier(0), Context(C), Kind(DK_Abstract), InvalidType(false), + GroupingParens(false), AttrList(0), AsmLabel(0), Type(0) { } ~Declarator() { @@ -649,7 +665,8 @@ public: DeclSpec &getMutableDeclSpec() { return const_cast(DS); } TheContext getContext() const { return Context; } - + DeclaratorKind getKind() const { return Kind; } + // getSourceRange - FIXME: This should be implemented. const SourceRange getSourceRange() const { return SourceRange(); } @@ -657,7 +674,8 @@ public: void clear() { Identifier = 0; IdentifierLoc = SourceLocation(); - + Kind = DK_Abstract; + for (unsigned i = 0, e = DeclTypeInfo.size(); i != e; ++i) { if (DeclTypeInfo[i].Kind == DeclaratorChunk::Function) DeclTypeInfo[i].Fun.destroy(); @@ -676,6 +694,7 @@ public: delete AttrList; AttrList = 0; AsmLabel = 0; + Type = 0; } /// mayOmitIdentifier - Return true if the identifier is either optional or @@ -710,8 +729,32 @@ public: void SetIdentifier(IdentifierInfo *ID, SourceLocation Loc) { Identifier = ID; IdentifierLoc = Loc; + if (ID) + Kind = DK_Normal; + else + Kind = DK_Abstract; } + /// SetConstructor - Set this declarator to be a C++ constructor + /// declarator. + void SetConstructor(Action::TypeTy *Ty, IdentifierInfo *ID, + SourceLocation Loc) { + Identifier = ID; + IdentifierLoc = Loc; + Kind = DK_Constructor; + Type = Ty; + } + + /// SetDestructor - Set this declarator to be a C++ destructor + /// declarator. + void SetDestructor(Action::TypeTy *Ty, IdentifierInfo *ID, + SourceLocation Loc) { + Identifier = ID; + IdentifierLoc = Loc; + Kind = DK_Destructor; + Type = Ty; + } + void AddTypeInfo(const DeclaratorChunk &TI) { DeclTypeInfo.push_back(TI); } @@ -759,6 +802,8 @@ public: void setAsmLabel(Action::ExprTy *E) { AsmLabel = E; } Action::ExprTy *getAsmLabel() const { return AsmLabel; } + Action::TypeTy *getDeclaratorIdType() const { return Type; } + void setInvalidType(bool flag) { InvalidType = flag; } bool getInvalidType() const { return InvalidType; } diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 897c09d0bc..adcc936216 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -715,6 +715,7 @@ private: //===--------------------------------------------------------------------===// // C++ 9: classes [class] and C structs/unions. + TypeTy *ParseClassName(); void ParseClassSpecifier(DeclSpec &DS); void ParseCXXMemberSpecification(SourceLocation StartLoc, unsigned TagType, DeclTy *TagDecl); diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index 3c6c91ea0a..de7a1d3ca4 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -238,6 +238,7 @@ void Decl::addDeclKind(Kind k) { // FIXME: Statistics for C++ decls. case CXXMethod: case CXXConstructor: + case CXXDestructor: case CXXClassVar: break; } diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index cf59d1af36..b0df75b404 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -44,6 +44,10 @@ void CXXRecordDecl::Destroy(ASTContext &C) { = Constructors.function_begin(); func != Constructors.function_end(); ++func) (*func)->Destroy(C); + + if (isDefinition()) + Destructor->Destroy(C); + RecordDecl::Destroy(C); } @@ -218,6 +222,16 @@ bool CXXConstructorDecl::isConvertingConstructor() const { (getNumParams() > 1 && getParamDecl(1)->getDefaultArg() != 0); } +CXXDestructorDecl * +CXXDestructorDecl::Create(ASTContext &C, CXXRecordDecl *RD, + SourceLocation L, IdentifierInfo *Id, + QualType T, bool isInline, + bool isImplicitlyDeclared) { + void *Mem = C.getAllocator().Allocate(); + return new (Mem) CXXDestructorDecl(RD, L, Id, T, isInline, + isImplicitlyDeclared); +} + CXXClassVarDecl *CXXClassVarDecl::Create(ASTContext &C, CXXRecordDecl *RD, SourceLocation L, IdentifierInfo *Id, QualType T, ScopedDecl *PrevDecl) { diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index f414f16af7..e1fbb6dfca 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -1243,7 +1243,7 @@ void Parser::ParseDeclaratorInternal(Declarator &D) { /// ParseDirectDeclarator /// direct-declarator: [C99 6.7.5] -/// identifier +/// [C99] identifier /// '(' declarator ')' /// [GNU] '(' attributes declarator ')' /// [C90] direct-declarator '[' constant-expression[opt] ']' @@ -1258,16 +1258,53 @@ void Parser::ParseDeclaratorInternal(Declarator &D) { /// [C++] direct-declarator '(' parameter-declaration-clause ')' /// cv-qualifier-seq[opt] exception-specification[opt] /// [C++] declarator-id -// -// declarator-id: [C++ 8] -// id-expression -// '::'[opt] nested-name-specifier[opt] type-name +/// +/// declarator-id: [C++ 8] +/// id-expression +/// '::'[opt] nested-name-specifier[opt] type-name +/// +/// id-expression: [C++ 5.1] +/// unqualified-id +/// qualified-id [TODO] +/// +/// unqualified-id: [C++ 5.1] +/// identifier +/// operator-function-id [TODO] +/// conversion-function-id [TODO] +/// '~' class-name +/// template-id [TODO] void Parser::ParseDirectDeclarator(Declarator &D) { // Parse the first direct-declarator seen. if (Tok.is(tok::identifier) && D.mayHaveIdentifier()) { assert(Tok.getIdentifierInfo() && "Not an identifier?"); - D.SetIdentifier(Tok.getIdentifierInfo(), Tok.getLocation()); + // Determine whether this identifier is a C++ constructor name or + // a normal identifier. + if (getLang().CPlusPlus && + CurScope->isCXXClassScope() && + Actions.isCurrentClassName(*Tok.getIdentifierInfo(), CurScope)) + D.SetConstructor(Actions.isTypeName(*Tok.getIdentifierInfo(), CurScope), + Tok.getIdentifierInfo(), Tok.getLocation()); + else + D.SetIdentifier(Tok.getIdentifierInfo(), Tok.getLocation()); ConsumeToken(); + } else if (getLang().CPlusPlus && Tok.is(tok::tilde) && + CurScope->isCXXClassScope() && D.mayHaveIdentifier()) { + // This should be a C++ destructor. + SourceLocation TildeLoc = ConsumeToken(); + + // Use the next identifier and "~" to form a name for the + // destructor. This is useful both for diagnostics and for + // correctness of the parser, since we use presence/absence of the + // identifier to determine what we parsed. + // FIXME: We could end up with a template-id here, once we parse + // templates, and will have to do something different to form the + // name of the destructor. + assert(Tok.is(tok::identifier) && "Expected identifier"); + IdentifierInfo *II = Tok.getIdentifierInfo(); + II = &PP.getIdentifierTable().get(std::string("~") + II->getName()); + + if (TypeTy *Type = ParseClassName()) + D.SetDestructor(Type, II, TildeLoc); } else if (Tok.is(tok::l_paren)) { // direct-declarator: '(' declarator ')' // direct-declarator: '(' attributes declarator ')' diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 57fa193e75..f90469acac 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -128,6 +128,37 @@ Parser::DeclTy *Parser::ParseLinkage(unsigned Context) { return Actions.ActOnLinkageSpec(Loc, LBrace, RBrace, LangBufPtr, StrSize, D); } +/// ParseClassName - Parse a C++ class-name, which names a class. Note +/// that we only check that the result names a type; semantic analysis +/// will need to verify that the type names a class. The result is +/// either a type or NULL, dependending on whether a type name was +/// found. +/// +/// class-name: [C++ 9.1] +/// identifier +/// template-id [TODO] +/// +Parser::TypeTy *Parser::ParseClassName() { + // Parse the class-name. + // FIXME: Alternatively, parse a simple-template-id. + if (Tok.isNot(tok::identifier)) { + Diag(Tok.getLocation(), diag::err_expected_class_name); + return 0; + } + + // We have an identifier; check whether it is actually a type. + TypeTy *Type = Actions.isTypeName(*Tok.getIdentifierInfo(), CurScope); + if (!Type) { + Diag(Tok.getLocation(), diag::err_expected_class_name); + return 0; + } + + // Consume the identifier. + ConsumeToken(); + + return Type; +} + /// ParseClassSpecifier - Parse a C++ class-specifier [C++ class] or /// elaborated-type-specifier [C++ dcl.type.elab]; we can't tell which /// until we reach the start of a definition or see a token that @@ -325,29 +356,17 @@ Parser::BaseResult Parser::ParseBaseSpecifier(DeclTy *ClassDecl) // FIXME: Parse optional '::' and optional nested-name-specifier. - // Parse the class-name. - // FIXME: Alternatively, parse a simple-template-id. - if (Tok.isNot(tok::identifier)) { - Diag(Tok.getLocation(), diag::err_expected_class_name); - return true; - } - - // We have an identifier; check whether it is actually a type. - TypeTy *BaseType = Actions.isTypeName(*Tok.getIdentifierInfo(), CurScope); - if (!BaseType) { - Diag(Tok.getLocation(), diag::err_expected_class_name); - return true; - } - // The location of the base class itself. SourceLocation BaseLoc = Tok.getLocation(); + + // Parse the class-name. + TypeTy *BaseType = ParseClassName(); + if (!BaseType) + return true; // Find the complete source range for the base-specifier. SourceRange Range(StartLoc, BaseLoc); - // Consume the identifier token (finally!). - ConsumeToken(); - // Notify semantic analysis that we have parsed a complete // base-specifier. return Actions.ActOnBaseSpecifier(ClassDecl, Range, IsVirtual, Access, BaseType, diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index d14c5ffa20..68d2eeefc5 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -836,7 +836,13 @@ public: virtual void ActOnFinishCXXClassDef(DeclTy *TagDecl); - virtual DeclTy *ActOnConstructorDeclarator(CXXConstructorDecl *ConDecl); + + bool CheckConstructorDeclarator(Declarator &D, QualType &R, + FunctionDecl::StorageClass& SC); + bool CheckDestructorDeclarator(Declarator &D, QualType &R, + FunctionDecl::StorageClass& SC); + DeclTy *ActOnConstructorDeclarator(CXXConstructorDecl *Constructor); + DeclTy *ActOnDestructorDeclarator(CXXDestructorDecl *Destructor); //===--------------------------------------------------------------------===// // C++ Derived Classes diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index c8c253a3fb..38a5c3780b 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -800,79 +800,16 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { } bool isInline = D.getDeclSpec().isInlineSpecified(); - bool isVirtual = D.getDeclSpec().isVirtualSpecified(); + // bool isVirtual = D.getDeclSpec().isVirtualSpecified(); bool isExplicit = D.getDeclSpec().isExplicitSpecified(); FunctionDecl *NewFD; - if (isCurrentClassName(*II, S)) { + if (D.getKind() == Declarator::DK_Constructor) { // This is a C++ constructor declaration. assert(D.getContext() == Declarator::MemberContext && "Constructors can only be declared in a member context"); - // C++ [class.ctor]p3: - // A constructor shall not be virtual (10.3) or static (9.4). A - // constructor can be invoked for a const, volatile or const - // volatile object. A constructor shall not be declared const, - // volatile, or const volatile (9.3.2). - if (isVirtual) { - Diag(D.getIdentifierLoc(), - diag::err_constructor_cannot_be, - "virtual", - SourceRange(D.getDeclSpec().getVirtualSpecLoc()), - SourceRange(D.getIdentifierLoc())); - isVirtual = false; - } - if (SC == FunctionDecl::Static) { - Diag(D.getIdentifierLoc(), - diag::err_constructor_cannot_be, - "static", - SourceRange(D.getDeclSpec().getStorageClassSpecLoc()), - SourceRange(D.getIdentifierLoc())); - isVirtual = false; - } - if (D.getDeclSpec().hasTypeSpecifier()) { - // Constructors don't have return types, but the parser will - // happily parse something like: - // - // class X { - // float X(float); - // }; - // - // The return type will be eliminated later. - Diag(D.getIdentifierLoc(), - diag::err_constructor_return_type, - SourceRange(D.getDeclSpec().getTypeSpecTypeLoc()), - SourceRange(D.getIdentifierLoc())); - } - if (R->getAsFunctionTypeProto()->getTypeQuals() != 0) { - DeclaratorChunk::FunctionTypeInfo &FTI = D.getTypeObject(0).Fun; - if (FTI.TypeQuals & QualType::Const) - Diag(D.getIdentifierLoc(), - diag::err_invalid_qualified_constructor, - "const", - SourceRange(D.getIdentifierLoc())); - if (FTI.TypeQuals & QualType::Volatile) - Diag(D.getIdentifierLoc(), - diag::err_invalid_qualified_constructor, - "volatile", - SourceRange(D.getIdentifierLoc())); - if (FTI.TypeQuals & QualType::Restrict) - Diag(D.getIdentifierLoc(), - diag::err_invalid_qualified_constructor, - "restrict", - SourceRange(D.getIdentifierLoc())); - } - - // Rebuild the function type "R" without any type qualifiers (in - // case any of the errors above fired) and with "void" as the - // return type, since constructors don't have return types. We - // *always* have to do this, because GetTypeForDeclarator will - // put in a result type of "int" when none was specified. - const FunctionTypeProto *Proto = R->getAsFunctionTypeProto(); - R = Context.getFunctionType(Context.VoidTy, Proto->arg_type_begin(), - Proto->getNumArgs(), - Proto->isVariadic(), - 0); + bool isInvalidDecl = CheckConstructorDeclarator(D, R, SC); // Create the new declaration NewFD = CXXConstructorDecl::Create(Context, @@ -881,6 +818,23 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { isExplicit, isInline, /*isImplicitlyDeclared=*/false); + if (isInvalidDecl) + NewFD->setInvalidDecl(); + } else if (D.getKind() == Declarator::DK_Destructor) { + // This is a C++ destructor declaration. + assert(D.getContext() == Declarator::MemberContext && + "Destructor can only be declared in a member context"); + + bool isInvalidDecl = CheckDestructorDeclarator(D, R, SC); + + NewFD = CXXDestructorDecl::Create(Context, + cast(CurContext), + D.getIdentifierLoc(), II, R, + isInline, + /*isImplicitlyDeclared=*/false); + + if (isInvalidDecl) + NewFD->setInvalidDecl(); } else if (D.getContext() == Declarator::MemberContext) { // This is a C++ method declaration. NewFD = CXXMethodDecl::Create(Context, cast(CurContext), @@ -969,12 +923,14 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { } } - // C++ constructors are handled by a separate routine, since they - // don't require any declaration merging (C++ [class.mfct]p2) and - // they aren't ever pushed into scope, because they can't be found - // by name lookup anyway (C++ [class.ctor]p2). - if (CXXConstructorDecl *ConDecl = dyn_cast(NewFD)) - return ActOnConstructorDeclarator(ConDecl); + // C++ constructors and destructors are handled by separate + // routines, since they don't require any declaration merging (C++ + // [class.mfct]p2) and they aren't ever pushed into scope, because + // they can't be found by name lookup anyway (C++ [class.ctor]p2). + if (CXXConstructorDecl *Constructor = dyn_cast(NewFD)) + return ActOnConstructorDeclarator(Constructor); + else if (CXXDestructorDecl *Destructor = dyn_cast(NewFD)) + return ActOnDestructorDeclarator(Destructor); // Merge the decl with the existing one if appropriate. Since C functions // are in a flat namespace, make sure we consider decls in outer scopes. diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 53051ff57f..ea3c175f16 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -383,6 +383,7 @@ void Sema::ActOnStartCXXClassDef(Scope *S, DeclTy *D, SourceLocation LBrace) { // class itself; this is known as the injected-class-name. For // purposes of access checking, the injected-class-name is treated // as if it were a public member name. + // FIXME: this should probably have its own kind of type node. TypedefDecl *InjectedClassName = TypedefDecl::Create(Context, Dcl, LBrace, Dcl->getIdentifier(), Context.getTypeDeclType(Dcl), /*PrevDecl=*/0); @@ -768,7 +769,25 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) { ClassDecl->addConstructor(Context, CopyConstructor); } - // FIXME: Implicit destructor + if (!ClassDecl->getDestructor()) { + // C++ [class.dtor]p2: + // If a class has no user-declared destructor, a destructor is + // declared implicitly. An implicitly-declared destructor is an + // inline public member of its class. + std::string DestructorName = "~"; + DestructorName += ClassDecl->getName(); + CXXDestructorDecl *Destructor + = CXXDestructorDecl::Create(Context, ClassDecl, + ClassDecl->getLocation(), + &PP.getIdentifierTable().get(DestructorName), + Context.getFunctionType(Context.VoidTy, + 0, 0, false, 0), + /*isInline=*/true, + /*isImplicitlyDeclared=*/true); + Destructor->setAccess(AS_public); + ClassDecl->setDestructor(Destructor); + } + // FIXME: Implicit copy assignment operator } @@ -783,6 +802,191 @@ void Sema::ActOnFinishCXXClassDef(DeclTy *D) { Consumer.HandleTagDeclDefinition(Rec); } +/// CheckConstructorDeclarator - Called by ActOnDeclarator to check +/// the well-formednes of the constructor declarator @p D with type @p +/// R. If there are any errors in the declarator, this routine will +/// emit diagnostics and return true. Otherwise, it will return +/// false. Either way, the type @p R will be updated to reflect a +/// well-formed type for the constructor. +bool Sema::CheckConstructorDeclarator(Declarator &D, QualType &R, + FunctionDecl::StorageClass& SC) { + bool isVirtual = D.getDeclSpec().isVirtualSpecified(); + bool isInvalid = false; + + // C++ [class.ctor]p3: + // A constructor shall not be virtual (10.3) or static (9.4). A + // constructor can be invoked for a const, volatile or const + // volatile object. A constructor shall not be declared const, + // volatile, or const volatile (9.3.2). + if (isVirtual) { + Diag(D.getIdentifierLoc(), + diag::err_constructor_cannot_be, + "virtual", + SourceRange(D.getDeclSpec().getVirtualSpecLoc()), + SourceRange(D.getIdentifierLoc())); + isInvalid = true; + } + if (SC == FunctionDecl::Static) { + Diag(D.getIdentifierLoc(), + diag::err_constructor_cannot_be, + "static", + SourceRange(D.getDeclSpec().getStorageClassSpecLoc()), + SourceRange(D.getIdentifierLoc())); + isInvalid = true; + SC = FunctionDecl::None; + } + if (D.getDeclSpec().hasTypeSpecifier()) { + // Constructors don't have return types, but the parser will + // happily parse something like: + // + // class X { + // float X(float); + // }; + // + // The return type will be eliminated later. + Diag(D.getIdentifierLoc(), + diag::err_constructor_return_type, + SourceRange(D.getDeclSpec().getTypeSpecTypeLoc()), + SourceRange(D.getIdentifierLoc())); + } + if (R->getAsFunctionTypeProto()->getTypeQuals() != 0) { + DeclaratorChunk::FunctionTypeInfo &FTI = D.getTypeObject(0).Fun; + if (FTI.TypeQuals & QualType::Const) + Diag(D.getIdentifierLoc(), + diag::err_invalid_qualified_constructor, + "const", + SourceRange(D.getIdentifierLoc())); + if (FTI.TypeQuals & QualType::Volatile) + Diag(D.getIdentifierLoc(), + diag::err_invalid_qualified_constructor, + "volatile", + SourceRange(D.getIdentifierLoc())); + if (FTI.TypeQuals & QualType::Restrict) + Diag(D.getIdentifierLoc(), + diag::err_invalid_qualified_constructor, + "restrict", + SourceRange(D.getIdentifierLoc())); + } + + // Rebuild the function type "R" without any type qualifiers (in + // case any of the errors above fired) and with "void" as the + // return type, since constructors don't have return types. We + // *always* have to do this, because GetTypeForDeclarator will + // put in a result type of "int" when none was specified. + const FunctionTypeProto *Proto = R->getAsFunctionTypeProto(); + R = Context.getFunctionType(Context.VoidTy, Proto->arg_type_begin(), + Proto->getNumArgs(), + Proto->isVariadic(), + 0); + + return isInvalid; +} + +/// CheckDestructorDeclarator - Called by ActOnDeclarator to check +/// the well-formednes of the destructor declarator @p D with type @p +/// R. If there are any errors in the declarator, this routine will +/// emit diagnostics and return true. Otherwise, it will return +/// false. Either way, the type @p R will be updated to reflect a +/// well-formed type for the destructor. +bool Sema::CheckDestructorDeclarator(Declarator &D, QualType &R, + FunctionDecl::StorageClass& SC) { + bool isInvalid = false; + + // C++ [class.dtor]p1: + // [...] A typedef-name that names a class is a class-name + // (7.1.3); however, a typedef-name that names a class shall not + // be used as the identifier in the declarator for a destructor + // declaration. + TypeDecl *DeclaratorTypeD = (TypeDecl *)D.getDeclaratorIdType(); + if (const TypedefDecl *TypedefD = dyn_cast(DeclaratorTypeD)) { + if (TypedefD->getIdentifier() != + cast(CurContext)->getIdentifier()) { + // FIXME: This would be easier if we could just look at whether + // we found the injected-class-name. + Diag(D.getIdentifierLoc(), + diag::err_destructor_typedef_name, + TypedefD->getName()); + isInvalid = true; + } + } + + // C++ [class.dtor]p2: + // A destructor is used to destroy objects of its class type. A + // destructor takes no parameters, and no return type can be + // specified for it (not even void). The address of a destructor + // shall not be taken. A destructor shall not be static. A + // destructor can be invoked for a const, volatile or const + // volatile object. A destructor shall not be declared const, + // volatile or const volatile (9.3.2). + if (SC == FunctionDecl::Static) { + Diag(D.getIdentifierLoc(), + diag::err_destructor_cannot_be, + "static", + SourceRange(D.getDeclSpec().getStorageClassSpecLoc()), + SourceRange(D.getIdentifierLoc())); + isInvalid = true; + SC = FunctionDecl::None; + } + if (D.getDeclSpec().hasTypeSpecifier()) { + // Destructors don't have return types, but the parser will + // happily parse something like: + // + // class X { + // float ~X(); + // }; + // + // The return type will be eliminated later. + Diag(D.getIdentifierLoc(), + diag::err_destructor_return_type, + SourceRange(D.getDeclSpec().getTypeSpecTypeLoc()), + SourceRange(D.getIdentifierLoc())); + } + if (R->getAsFunctionTypeProto()->getTypeQuals() != 0) { + DeclaratorChunk::FunctionTypeInfo &FTI = D.getTypeObject(0).Fun; + if (FTI.TypeQuals & QualType::Const) + Diag(D.getIdentifierLoc(), + diag::err_invalid_qualified_destructor, + "const", + SourceRange(D.getIdentifierLoc())); + if (FTI.TypeQuals & QualType::Volatile) + Diag(D.getIdentifierLoc(), + diag::err_invalid_qualified_destructor, + "volatile", + SourceRange(D.getIdentifierLoc())); + if (FTI.TypeQuals & QualType::Restrict) + Diag(D.getIdentifierLoc(), + diag::err_invalid_qualified_destructor, + "restrict", + SourceRange(D.getIdentifierLoc())); + } + + // Make sure we don't have any parameters. + if (R->getAsFunctionTypeProto()->getNumArgs() > 0) { + Diag(D.getIdentifierLoc(), diag::err_destructor_with_params); + + // Delete the parameters. + DeclaratorChunk::FunctionTypeInfo &FTI = D.getTypeObject(0).Fun; + if (FTI.NumArgs) { + delete [] FTI.ArgInfo; + FTI.NumArgs = 0; + FTI.ArgInfo = 0; + } + } + + // Make sure the destructor isn't variadic. + if (R->getAsFunctionTypeProto()->isVariadic()) + Diag(D.getIdentifierLoc(), diag::err_destructor_variadic); + + // Rebuild the function type "R" without any type qualifiers or + // parameters (in case any of the errors above fired) and with + // "void" as the return type, since destructors don't have return + // types. We *always* have to do this, because GetTypeForDeclarator + // will put in a result type of "int" when none was specified. + R = Context.getFunctionType(Context.VoidTy, 0, 0, false, 0); + + return isInvalid; +} + /// ActOnConstructorDeclarator - Called by ActOnDeclarator to complete /// the declaration of the given C++ constructor ConDecl that was /// built from declarator D. This routine is responsible for checking @@ -837,7 +1041,7 @@ Sema::DeclTy *Sema::ActOnConstructorDeclarator(CXXConstructorDecl *ConDecl) { diag::err_constructor_byvalue_arg, SourceRange(ConDecl->getParamDecl(0)->getLocation())); ConDecl->setInvalidDecl(); - return 0; + return ConDecl; } } @@ -847,6 +1051,30 @@ Sema::DeclTy *Sema::ActOnConstructorDeclarator(CXXConstructorDecl *ConDecl) { return (DeclTy *)ConDecl; } +/// ActOnDestructorDeclarator - Called by ActOnDeclarator to complete +/// the declaration of the given C++ @p Destructor. This routine is +/// responsible for recording the destructor in the C++ class, if +/// possible. +Sema::DeclTy *Sema::ActOnDestructorDeclarator(CXXDestructorDecl *Destructor) { + assert(Destructor && "Expected to receive a destructor declaration"); + + CXXRecordDecl *ClassDecl = cast(CurContext); + + // Make sure we aren't redeclaring the destructor. + if (CXXDestructorDecl *PrevDestructor = ClassDecl->getDestructor()) { + Diag(Destructor->getLocation(), diag::err_destructor_redeclared); + Diag(PrevDestructor->getLocation(), + PrevDestructor->isThisDeclarationADefinition()? + diag::err_previous_definition + : diag::err_previous_declaration); + Destructor->setInvalidDecl(); + return Destructor; + } + + ClassDecl->setDestructor(Destructor); + return (DeclTy *)Destructor; +} + //===----------------------------------------------------------------------===// // Namespace Handling //===----------------------------------------------------------------------===// diff --git a/test/SemaCXX/destructor.cpp b/test/SemaCXX/destructor.cpp new file mode 100644 index 0000000000..1eec0d5f00 --- /dev/null +++ b/test/SemaCXX/destructor.cpp @@ -0,0 +1,37 @@ +// RUN: clang -fsyntax-only -verify %s +class A { +public: + ~A(); +}; + +class B { +public: + ~B() { } +}; + +class C { +public: + (~C)() { } +}; + +struct D { + static void ~D(int, ...) const { } // \ + // expected-error{{type qualifier is not allowed on this function}} \ + // expected-error{{destructor cannot be declared 'static'}} \ + // expected-error{{destructor cannot have a return type}} \ + // expected-error{{destructor cannot have any parameters}} \ + // expected-error{{destructor cannot be variadic}} +}; + +struct E; + +typedef E E_typedef; +struct E { + ~E_typedef(); // expected-error{{destructor cannot be declared using a typedef 'E_typedef' of the class name}} +}; + +struct F { + (~F)(); // expected-error{{previous declaration is here}} + ~F(); // expected-error{{destructor cannot be redeclared}} +}; + diff --git a/www/cxx_status.html b/www/cxx_status.html index f52f3930f0..a194f44acf 100644 --- a/www/cxx_status.html +++ b/www/cxx_status.html @@ -608,18 +608,39 @@ welcome!

  11.7 [class.paths]   11.8 [class.access.nest] 12 [special] -  12.1 [class.ctor] + +   12.1 [class.ctor] + + + + + +   12.2 [class.temporary]   12.3 [class.conv] -    12.3.1 [class.conv.ctor] + +     12.3.1 [class.conv.ctor] + + + + + +     12.3.2 [class.conv.fct] -  12.4 [class.dtor] + +   12.4 [class.dtor] + + + + + Most of the semantics of destructors are unimplemented. +   12.5 [class.free]   12.6 [class.init]     12.6.1 [class.expl.init]     12.6.2 [class.base.init] - + -- 2.40.0