diff options
| author | Douglas Gregor <dgregor@apple.com> | 2008-11-05 20:51:48 +0000 |
|---|---|---|
| committer | Douglas Gregor <dgregor@apple.com> | 2008-11-05 20:51:48 +0000 |
| commit | 831c93f6c08b0111e9a185058e67cdc3e88bd1ab (patch) | |
| tree | 15d851fc529d8540e479c0f4137bae713f43aa6d /clang/lib/Sema | |
| parent | b4ebbc5a865ba4b0dc6094d32f9f8731a0790658 (diff) | |
| download | bcm5719-llvm-831c93f6c08b0111e9a185058e67cdc3e88bd1ab.tar.gz bcm5719-llvm-831c93f6c08b0111e9a185058e67cdc3e88bd1ab.zip | |
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").
llvm-svn: 58767
Diffstat (limited to 'clang/lib/Sema')
| -rw-r--r-- | clang/lib/Sema/Sema.h | 8 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 100 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 232 |
3 files changed, 265 insertions, 75 deletions
diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index d14c5ffa200..68d2eeefc50 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/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/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index c8c253a3fba..38a5c3780b7 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/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<CXXRecordDecl>(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<CXXRecordDecl>(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<CXXConstructorDecl>(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<CXXConstructorDecl>(NewFD)) + return ActOnConstructorDeclarator(Constructor); + else if (CXXDestructorDecl *Destructor = dyn_cast<CXXDestructorDecl>(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/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 53051ff57fb..ea3c175f162 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/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<TypedefDecl>(DeclaratorTypeD)) { + if (TypedefD->getIdentifier() != + cast<CXXRecordDecl>(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<CXXRecordDecl>(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 //===----------------------------------------------------------------------===// |

