From 9ed6efdd751653ae46c264d1c653ef6bce2afa2c Mon Sep 17 00:00:00 2001 From: Sebastian Redl Date: Sat, 24 Jan 2009 21:16:55 +0000 Subject: [PATCH] Add support for declaring pointers to members. Add serialization support for ReferenceType. llvm-svn: 62934 --- clang/include/clang/AST/ASTContext.h | 10 ++- clang/include/clang/AST/Type.h | 70 ++++++++++++++-- clang/include/clang/Basic/DiagnosticKinds.def | 4 + clang/include/clang/Parse/DeclSpec.h | 82 ++++++++++++++----- clang/include/clang/Parse/Ownership.h | 2 +- clang/lib/AST/ASTContext.cpp | 51 +++++++++++- clang/lib/AST/Type.cpp | 42 +++++++++- clang/lib/AST/TypeSerialization.cpp | 51 ++++++++++-- clang/lib/Parse/ParseDecl.cpp | 52 +++++++++--- clang/lib/Sema/SemaCXXScopeSpec.cpp | 1 + clang/lib/Sema/SemaType.cpp | 57 +++++++++++-- clang/test/SemaCXX/member-pointer.cpp | 14 ++++ 12 files changed, 379 insertions(+), 57 deletions(-) create mode 100644 clang/test/SemaCXX/member-pointer.cpp diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 12f89c3fb96d..9ac6027570f3 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -60,6 +60,7 @@ class ASTContext { llvm::FoldingSet PointerTypes; llvm::FoldingSet BlockPointerTypes; llvm::FoldingSet ReferenceTypes; + llvm::FoldingSet MemberPointerTypes; llvm::FoldingSet ConstantArrayTypes; llvm::FoldingSet IncompleteArrayTypes; std::vector VariableArrayTypes; @@ -185,11 +186,16 @@ public: /// getBlockPointerType - Return the uniqued reference to the type for a block /// of the specified type. QualType getBlockPointerType(QualType T); - + /// getReferenceType - Return the uniqued reference to the type for a /// reference to the specified type. QualType getReferenceType(QualType T); - + + /// getMemberPointerType - Return the uniqued reference to the type for a + /// member pointer to the specified type in the specified class. The class + /// is a Type because it could be a dependent name. + QualType getMemberPointerType(QualType T, const Type *Cls); + /// getVariableArrayType - Returns a non-unique reference to the type for a /// variable array of the specified element type. QualType getVariableArrayType(QualType EltTy, Expr *NumElts, diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 29e829c190c6..baa2016f553b 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -46,6 +46,7 @@ namespace clang { class PointerType; class BlockPointerType; class ReferenceType; + class MemberPointerType; class VectorType; class ArrayType; class ConstantArrayType; @@ -66,7 +67,7 @@ namespace clang { class ObjCQualifiedIdType; class ObjCQualifiedInterfaceType; class StmtIteratorBase; - + /// QualType - For efficiency, we don't store CVR-qualified types as nodes on /// their own: instead each reference to a type stores the qualifiers. This /// greatly reduces the number of nodes we need to allocate for types (for @@ -237,7 +238,7 @@ namespace clang { class Type { public: enum TypeClass { - Builtin, Complex, Pointer, Reference, + Builtin, Complex, Pointer, Reference, MemberPointer, ConstantArray, VariableArray, IncompleteArray, DependentSizedArray, Vector, ExtVector, FunctionNoProto, FunctionProto, @@ -337,6 +338,8 @@ public: bool isBlockPointerType() const; bool isReferenceType() const; bool isFunctionPointerType() const; + bool isMemberPointerType() const; + bool isMemberFunctionPointerType() const; bool isArrayType() const; bool isConstantArrayType() const; bool isIncompleteArrayType() const; @@ -363,13 +366,14 @@ public: // Type Checking Functions: Check to see if this type is structurally the // specified type, ignoring typedefs and qualifiers, and return a pointer to // the best type we can. - const BuiltinType *getAsBuiltinType() const; - const FunctionType *getAsFunctionType() const; - const FunctionTypeProto *getAsFunctionTypeProto() const; + const BuiltinType *getAsBuiltinType() const; + const FunctionType *getAsFunctionType() const; + const FunctionTypeProto *getAsFunctionTypeProto() const; const PointerLikeType *getAsPointerLikeType() const; // Pointer or Reference. const PointerType *getAsPointerType() const; const BlockPointerType *getAsBlockPointerType() const; const ReferenceType *getAsReferenceType() const; + const MemberPointerType *getAsMemberPointerType() const; const RecordType *getAsRecordType() const; const RecordType *getAsStructureType() const; /// NOTE: getAs*ArrayType are methods on ASTContext. @@ -665,6 +669,53 @@ public: static bool classof(const Type *T) { return T->getTypeClass() == Reference; } static bool classof(const ReferenceType *) { return true; } + +protected: + virtual void EmitImpl(llvm::Serializer& S) const; + static Type* CreateImpl(ASTContext& Context, llvm::Deserializer& D); + friend class Type; +}; + +/// MemberPointerType - C++ 8.3.3 - Pointers to members +/// +class MemberPointerType : public Type, public llvm::FoldingSetNode { + QualType PointeeType; + /// The class of which the pointee is a member. Must ultimately be a + /// RecordType, but could be a typedef or a template parameter too. + const Type *Class; + + MemberPointerType(QualType Pointee, const Type *Cls, QualType CanonicalPtr) : + Type(MemberPointer, CanonicalPtr, + Cls->isDependentType() || Pointee->isDependentType()), + PointeeType(Pointee), Class(Cls) { + } + friend class ASTContext; // ASTContext creates these. +public: + + QualType getPointeeType() const { return PointeeType; } + + const Type *getClass() const { return Class; } + + virtual void getAsStringInternal(std::string &InnerString) const; + + void Profile(llvm::FoldingSetNodeID &ID) { + Profile(ID, getPointeeType(), getClass()); + } + static void Profile(llvm::FoldingSetNodeID &ID, QualType Pointee, + const Type *Class) { + ID.AddPointer(Pointee.getAsOpaquePtr()); + ID.AddPointer(Class); + } + + static bool classof(const Type *T) { + return T->getTypeClass() == MemberPointer; + } + static bool classof(const MemberPointerType *) { return true; } + +protected: + virtual void EmitImpl(llvm::Serializer& S) const; + static Type* CreateImpl(ASTContext& Context, llvm::Deserializer& D); + friend class Type; }; /// ArrayType - C99 6.7.5.2 - Array Declarators. @@ -1576,6 +1627,15 @@ inline bool Type::isFunctionPointerType() const { else return false; } +inline bool Type::isMemberPointerType() const { + return isa(CanonicalType.getUnqualifiedType()); +} +inline bool Type::isMemberFunctionPointerType() const { + if (const MemberPointerType* T = getAsMemberPointerType()) + return T->getPointeeType()->isFunctionType(); + else + return false; +} inline bool Type::isArrayType() const { return isa(CanonicalType.getUnqualifiedType()); } diff --git a/clang/include/clang/Basic/DiagnosticKinds.def b/clang/include/clang/Basic/DiagnosticKinds.def index 5cb71fc9bfb1..a2a1ca4c9fa5 100644 --- a/clang/include/clang/Basic/DiagnosticKinds.def +++ b/clang/include/clang/Basic/DiagnosticKinds.def @@ -1161,6 +1161,10 @@ DIAG(err_illegal_decl_array_of_references, ERROR, "'%0' declared as array of references") DIAG(err_illegal_decl_pointer_to_reference, ERROR, "'%0' declared as a pointer to a reference") +DIAG(err_illegal_decl_mempointer_to_void, ERROR, + "'%0' declared as a member pointer to void") +DIAG(err_illegal_decl_mempointer_in_nonclass, ERROR, + "'%0' does not point into a class") DIAG(err_illegal_decl_reference_to_reference, ERROR, "%0 declared as a reference to a reference") DIAG(err_invalid_reference_qualifier_application, ERROR, diff --git a/clang/include/clang/Parse/DeclSpec.h b/clang/include/clang/Parse/DeclSpec.h index d4ac25a25092..d4d363b0104f 100644 --- a/clang/include/clang/Parse/DeclSpec.h +++ b/clang/include/clang/Parse/DeclSpec.h @@ -429,9 +429,9 @@ typedef llvm::SmallVector CachedTokens; /// This is intended to be a small value object. struct DeclaratorChunk { enum { - Pointer, Reference, Array, Function, BlockPointer + Pointer, Reference, Array, Function, BlockPointer, MemberPointer } Kind; - + /// Loc - The place where this type was defined. SourceLocation Loc; @@ -544,39 +544,64 @@ struct DeclaratorChunk { void destroy() {} }; - union { - PointerTypeInfo Ptr; - ReferenceTypeInfo Ref; - ArrayTypeInfo Arr; - FunctionTypeInfo Fun; - BlockPointerTypeInfo Cls; + struct MemberPointerTypeInfo { + /// The type qualifiers: const/volatile/restrict. + unsigned TypeQuals : 3; + AttributeList *AttrList; + // CXXScopeSpec has a constructor, so it can't be a direct member. + // So we need some pointer-aligned storage and a bit of trickery. + union { + void *Aligner; + char Mem[sizeof(CXXScopeSpec)]; + } ScopeMem; + CXXScopeSpec &Scope() { + return *reinterpret_cast(ScopeMem.Mem); + } + const CXXScopeSpec &Scope() const { + return *reinterpret_cast(ScopeMem.Mem); + } + void destroy() { + delete AttrList; + Scope().~CXXScopeSpec(); + } }; - + + union { + PointerTypeInfo Ptr; + ReferenceTypeInfo Ref; + ArrayTypeInfo Arr; + FunctionTypeInfo Fun; + BlockPointerTypeInfo Cls; + MemberPointerTypeInfo Mem; + }; + void destroy() { switch (Kind) { default: assert(0 && "Unknown decl type!"); - case DeclaratorChunk::Function: return Fun.destroy(); - case DeclaratorChunk::Pointer: return Ptr.destroy(); - case DeclaratorChunk::BlockPointer: return Cls.destroy(); - case DeclaratorChunk::Reference: return Ref.destroy(); - case DeclaratorChunk::Array: return Arr.destroy(); + case DeclaratorChunk::Function: return Fun.destroy(); + case DeclaratorChunk::Pointer: return Ptr.destroy(); + case DeclaratorChunk::BlockPointer: return Cls.destroy(); + case DeclaratorChunk::Reference: return Ref.destroy(); + case DeclaratorChunk::Array: return Arr.destroy(); + case DeclaratorChunk::MemberPointer: return Mem.destroy(); } } - + /// getAttrs - If there are attributes applied to this declaratorchunk, return /// them. const AttributeList *getAttrs() const { switch (Kind) { default: assert(0 && "Unknown declarator kind!"); - case Pointer: return Ptr.AttrList; - case Reference: return Ref.AttrList; - case Array: return 0; - case Function: return 0; - case BlockPointer: return 0; // FIXME: Do blocks have attr list? + case Pointer: return Ptr.AttrList; + case Reference: return Ref.AttrList; + case MemberPointer: return Mem.AttrList; + case Array: return 0; + case Function: return 0; + case BlockPointer: return 0; // FIXME: Do blocks have attr list? } } - - + + /// getPointer - Return a DeclaratorChunk for a pointer. /// static DeclaratorChunk getPointer(unsigned TypeQuals, SourceLocation Loc, @@ -633,6 +658,19 @@ struct DeclaratorChunk { I.Cls.TypeQuals = TypeQuals; return I; } + + static DeclaratorChunk getMemberPointer(const CXXScopeSpec &SS, + unsigned TypeQuals, + SourceLocation Loc, + AttributeList *AL) { + DeclaratorChunk I; + I.Kind = MemberPointer; + I.Loc = Loc; + I.Mem.TypeQuals = TypeQuals; + I.Mem.AttrList = AL; + new (I.Mem.ScopeMem.Mem) CXXScopeSpec(SS); + return I; + } }; /// Declarator - Information about one declarator, including the parsed type diff --git a/clang/include/clang/Parse/Ownership.h b/clang/include/clang/Parse/Ownership.h index 2144481c3d1d..14c4bbf067f1 100644 --- a/clang/include/clang/Parse/Ownership.h +++ b/clang/include/clang/Parse/Ownership.h @@ -103,7 +103,7 @@ // conversions. // Flip this switch to measure performance impact of the smart pointers. -//#define DISABLE_SMART_POINTERS +#define DISABLE_SMART_POINTERS namespace clang { diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index d20387dd95ee..f3243c25ae71 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -85,6 +85,7 @@ void ASTContext::PrintStats() const { unsigned NumBuiltin = 0, NumPointer = 0, NumArray = 0, NumFunctionP = 0; unsigned NumVector = 0, NumComplex = 0, NumBlockPointer = 0; unsigned NumFunctionNP = 0, NumTypeName = 0, NumTagged = 0, NumReference = 0; + unsigned NumMemberPointer = 0; unsigned NumTagStruct = 0, NumTagUnion = 0, NumTagEnum = 0, NumTagClass = 0; unsigned NumObjCInterfaces = 0, NumObjCQualifiedInterfaces = 0; @@ -101,6 +102,8 @@ void ASTContext::PrintStats() const { ++NumBlockPointer; else if (isa(T)) ++NumReference; + else if (isa(T)) + ++NumMemberPointer; else if (isa(T)) ++NumComplex; else if (isa(T)) @@ -142,6 +145,7 @@ void ASTContext::PrintStats() const { fprintf(stderr, " %d pointer types\n", NumPointer); fprintf(stderr, " %d block pointer types\n", NumBlockPointer); fprintf(stderr, " %d reference types\n", NumReference); + fprintf(stderr, " %d member pointer types\n", NumMemberPointer); fprintf(stderr, " %d complex types\n", NumComplex); fprintf(stderr, " %d array types\n", NumArray); fprintf(stderr, " %d vector types\n", NumVector); @@ -164,6 +168,7 @@ void ASTContext::PrintStats() const { fprintf(stderr, "Total bytes = %d\n", int(NumBuiltin*sizeof(BuiltinType)+ NumPointer*sizeof(PointerType)+NumArray*sizeof(ArrayType)+ NumComplex*sizeof(ComplexType)+NumVector*sizeof(VectorType)+ + NumMemberPointer*sizeof(MemberPointerType)+ NumFunctionP*sizeof(FunctionTypeProto)+ NumFunctionNP*sizeof(FunctionTypeNoProto)+ NumTypeName*sizeof(TypedefType)+NumTagged*sizeof(TagType)+ @@ -374,7 +379,19 @@ ASTContext::getTypeInfo(const Type *T) { // FIXME: This is wrong for struct layout: a reference in a struct has // pointer size. return getTypeInfo(cast(T)->getPointeeType()); - + case Type::MemberPointer: { + // Note that this is not only platform- but also ABI-dependent. We follow + // the GCC ABI, where pointers to data are one pointer large, pointers to + // functions two pointers. But if we want to support ABI compatibility with + // other compilers too, we need to delegate this completely to TargetInfo. + QualType Pointee = cast(T)->getPointeeType(); + unsigned AS = Pointee.getAddressSpace(); + Width = Target.getPointerWidth(AS); + if (Pointee->isFunctionType()) + Width *= 2; + Align = Target.getPointerAlign(AS); + // GCC aligns at single pointer width. + } case Type::Complex: { // Complex types have the same alignment as their elements, but twice the // size. @@ -808,6 +825,38 @@ QualType ASTContext::getReferenceType(QualType T) { return QualType(New, 0); } +/// getMemberPointerType - Return the uniqued reference to the type for a +/// member pointer to the specified type, in the specified class. +QualType ASTContext::getMemberPointerType(QualType T, const Type *Cls) +{ + // Unique pointers, to guarantee there is only one pointer of a particular + // structure. + llvm::FoldingSetNodeID ID; + MemberPointerType::Profile(ID, T, Cls); + + void *InsertPos = 0; + if (MemberPointerType *PT = + MemberPointerTypes.FindNodeOrInsertPos(ID, InsertPos)) + return QualType(PT, 0); + + // If the pointee or class type isn't canonical, this won't be a canonical + // type either, so fill in the canonical type field. + QualType Canonical; + if (!T->isCanonical()) { + Canonical = getMemberPointerType(getCanonicalType(T),getCanonicalType(Cls)); + + // Get the new insert position for the node we care about. + MemberPointerType *NewIP = + MemberPointerTypes.FindNodeOrInsertPos(ID, InsertPos); + assert(NewIP == 0 && "Shouldn't be in the map!"); NewIP = NewIP; + } + void *Mem = Allocator.Allocate(sizeof(MemberPointerType), 8); + MemberPointerType *New = new (Mem) MemberPointerType(T, Cls, Canonical); + Types.push_back(New); + MemberPointerTypes.InsertNode(New, InsertPos); + return QualType(New, 0); +} + /// getConstantArrayType - Return the unique reference to the type for an /// array of the specified element type. QualType ASTContext::getConstantArrayType(QualType EltTy, diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 94968920391d..ebaa01135555 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -288,6 +288,24 @@ const ReferenceType *Type::getAsReferenceType() const { return getDesugaredType()->getAsReferenceType(); } +const MemberPointerType *Type::getAsMemberPointerType() const { + // If this is directly a member pointer type, return it. + if (const MemberPointerType *MTy = dyn_cast(this)) + return MTy; + + // If the canonical form of this type isn't the right kind, reject it. + if (!isa(CanonicalType)) { + // Look through type qualifiers + if (isa(CanonicalType.getUnqualifiedType())) + return CanonicalType.getUnqualifiedType()->getAsMemberPointerType(); + return 0; + } + + // If this is a typedef for a member pointer type, strip the typedef off + // without losing all typedef information. + return getDesugaredType()->getAsMemberPointerType(); +} + /// isVariablyModifiedType (C99 6.7.5p3) - Return true for variable length /// array types and types that contain variable array types in their /// declarator @@ -300,8 +318,11 @@ bool Type::isVariablyModifiedType() const { if (const Type *T = getArrayElementTypeNoTypeQual()) return T->isVariablyModifiedType(); - // A pointer can point to a variably modified type - if (const PointerType *PT = getAsPointerType()) + // A pointer can point to a variably modified type. + // Also, C++ references and member pointers can point to a variably modified + // type, where VLAs appear as an extension to C++, and should be treated + // correctly. + if (const PointerLikeType *PT = getAsPointerLikeType()) return PT->getPointeeType()->isVariablyModifiedType(); // A function can return a variably modified type @@ -633,6 +654,7 @@ bool Type::isScalarType() const { return ASQT->getBaseType()->isScalarType(); return isa(CanonicalType) || isa(CanonicalType) || + isa(CanonicalType) || isa(CanonicalType) || isa(CanonicalType); } @@ -702,9 +724,9 @@ bool Type::isPODType() const { case Builtin: case Complex: case Pointer: + case MemberPointer: case Vector: case ExtVector: - // FIXME: pointer-to-member return true; case Tagged: @@ -951,6 +973,20 @@ void ReferenceType::getAsStringInternal(std::string &S) const { getPointeeType().getAsStringInternal(S); } +void MemberPointerType::getAsStringInternal(std::string &S) const { + std::string C; + Class->getAsStringInternal(C); + C += "::*"; + S = C + S; + + // Handle things like 'int (&A)[4];' correctly. + // FIXME: this should include vectors, but vectors use attributes I guess. + if (isa(getPointeeType())) + S = '(' + S + ')'; + + getPointeeType().getAsStringInternal(S); +} + void ConstantArrayType::getAsStringInternal(std::string &S) const { S += '['; S += llvm::utostr(getSize().getZExtValue()); diff --git a/clang/lib/AST/TypeSerialization.cpp b/clang/lib/AST/TypeSerialization.cpp index 4f3eeca28c69..12c3d03c1214 100644 --- a/clang/lib/AST/TypeSerialization.cpp +++ b/clang/lib/AST/TypeSerialization.cpp @@ -93,21 +93,29 @@ void Type::Create(ASTContext& Context, unsigned i, Deserializer& D) { case Type::IncompleteArray: D.RegisterPtr(PtrID,IncompleteArrayType::CreateImpl(Context,D)); break; - + + case Type::MemberPointer: + D.RegisterPtr(PtrID, MemberPointerType::CreateImpl(Context, D)); + break; + case Type::Pointer: - D.RegisterPtr(PtrID,PointerType::CreateImpl(Context,D)); + D.RegisterPtr(PtrID, PointerType::CreateImpl(Context, D)); break; case Type::BlockPointer: - D.RegisterPtr(PtrID,BlockPointerType::CreateImpl(Context,D)); + D.RegisterPtr(PtrID, BlockPointerType::CreateImpl(Context, D)); break; - + + case Type::Reference: + D.RegisterPtr(PtrID, ReferenceType::CreateImpl(Context, D)); + break; + case Type::Tagged: - D.RegisterPtr(PtrID,TagType::CreateImpl(Context,D)); + D.RegisterPtr(PtrID, TagType::CreateImpl(Context, D)); break; - + case Type::TypeName: - D.RegisterPtr(PtrID,TypedefType::CreateImpl(Context,D)); + D.RegisterPtr(PtrID, TypedefType::CreateImpl(Context, D)); break; case Type::TypeOfExp: @@ -123,7 +131,7 @@ void Type::Create(ASTContext& Context, unsigned i, Deserializer& D) { break; case Type::VariableArray: - D.RegisterPtr(PtrID,VariableArrayType::CreateImpl(Context,D)); + D.RegisterPtr(PtrID, VariableArrayType::CreateImpl(Context, D)); break; } } @@ -242,6 +250,33 @@ Type* PointerType::CreateImpl(ASTContext& Context, Deserializer& D) { return Context.getPointerType(QualType::ReadVal(D)).getTypePtr(); } +//===----------------------------------------------------------------------===// +// ReferenceType +//===----------------------------------------------------------------------===// + +void ReferenceType::EmitImpl(Serializer& S) const { + S.Emit(getPointeeType()); +} + +Type* ReferenceType::CreateImpl(ASTContext& Context, Deserializer& D) { + return Context.getReferenceType(QualType::ReadVal(D)).getTypePtr(); +} + +//===----------------------------------------------------------------------===// +// MemberPointerType +//===----------------------------------------------------------------------===// + +void MemberPointerType::EmitImpl(Serializer& S) const { + S.Emit(getPointeeType()); + S.Emit(QualType(Class, 0)); +} + +Type* MemberPointerType::CreateImpl(ASTContext& Context, Deserializer& D) { + QualType Pointee = QualType::ReadVal(D); + QualType Class = QualType::ReadVal(D); + return Context.getMemberPointerType(Pointee, Class.getTypePtr()).getTypePtr(); +} + //===----------------------------------------------------------------------===// // TagType //===----------------------------------------------------------------------===// diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 7f5c0988ed6b..0ac9e0898229 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -1550,10 +1550,10 @@ void Parser::ParseDeclarator(Declarator &D) { /// isn't parsed at all, making this function effectively parse the C++ /// ptr-operator production. /// -/// declarator: [C99 6.7.5] -/// pointer[opt] direct-declarator -/// [C++] '&' declarator [C++ 8p4, dcl.decl] -/// [GNU] '&' restrict[opt] attributes[opt] declarator +/// declarator: [C99 6.7.5] [C++ 8p4, dcl.decl] +/// [C] pointer[opt] direct-declarator +/// [C++] direct-declarator +/// [C++] ptr-operator declarator /// /// pointer: [C99 6.7.5] /// '*' type-qualifier-list[opt] @@ -1563,11 +1563,41 @@ void Parser::ParseDeclarator(Declarator &D) { /// '*' cv-qualifier-seq[opt] /// '&' /// [GNU] '&' restrict[opt] attributes[opt] -/// '::'[opt] nested-name-specifier '*' cv-qualifier-seq[opt] [TODO] +/// '::'[opt] nested-name-specifier '*' cv-qualifier-seq[opt] void Parser::ParseDeclaratorInternal(Declarator &D, DirectDeclParseFunction DirectDeclParser) { - tok::TokenKind Kind = Tok.getKind(); + // C++ member pointers start with a '::' or a nested-name. + // Member pointers get special handling, since there's no place for the + // scope spec in the generic path below. + if ((Tok.is(tok::coloncolon) || Tok.is(tok::identifier) || + Tok.is(tok::annot_cxxscope)) && getLang().CPlusPlus) { + CXXScopeSpec SS; + if (ParseOptionalCXXScopeSpecifier(SS)) { + if(Tok.isNot(tok::star)) { + // The scope spec really belongs to the direct-declarator. + D.getCXXScopeSpec() = SS; + if (DirectDeclParser) + (this->*DirectDeclParser)(D); + return; + } + + SourceLocation Loc = ConsumeToken(); + DeclSpec DS; + ParseTypeQualifierListOpt(DS); + + // Recurse to parse whatever is left. + ParseDeclaratorInternal(D, DirectDeclParser); + + // Sema will have to catch (syntactically invalid) pointers into global + // scope. It has to catch pointers into namespace scope anyway. + D.AddTypeInfo(DeclaratorChunk::getMemberPointer(SS,DS.getTypeQualifiers(), + Loc,DS.TakeAttributes())); + return; + } + } + + tok::TokenKind Kind = Tok.getKind(); // Not a pointer, C++ reference, or block. if (Kind != tok::star && (Kind != tok::amp || !getLang().CPlusPlus) && (Kind != tok::caret || !getLang().Blocks)) { @@ -1575,16 +1605,16 @@ void Parser::ParseDeclaratorInternal(Declarator &D, (this->*DirectDeclParser)(D); return; } - + // Otherwise, '*' -> pointer, '^' -> block, '&' -> reference. SourceLocation Loc = ConsumeToken(); // Eat the * or &. if (Kind == tok::star || (Kind == tok::caret && getLang().Blocks)) { // Is a pointer. DeclSpec DS; - + ParseTypeQualifierListOpt(DS); - + // Recursively parse the declarator. ParseDeclaratorInternal(D, DirectDeclParser); if (Kind == tok::star) @@ -1680,7 +1710,9 @@ void Parser::ParseDirectDeclarator(Declarator &D) { if (getLang().CPlusPlus) { if (D.mayHaveIdentifier()) { - bool afterCXXScope = ParseOptionalCXXScopeSpecifier(D.getCXXScopeSpec()); + // ParseDeclaratorInternal might already have parsed the scope. + bool afterCXXScope = D.getCXXScopeSpec().isSet() || + ParseOptionalCXXScopeSpecifier(D.getCXXScopeSpec()); if (afterCXXScope) { // Change the declaration context for name lookup, until this function // is exited (and the declarator has been parsed). diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp index 0c0fa35a5e76..699f8908079c 100644 --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -50,6 +50,7 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S, return cast(SD); } + // FIXME: Template parameters and dependent types. // FIXME: C++0x scoped enums // Fall through to produce an error: we found something that isn't diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index ecae79320dd4..21db14a2ae9b 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -287,9 +287,9 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip) { !T->isIncompleteOrObjectType()) { Diag(DeclType.Loc, diag::err_typecheck_invalid_restrict_invalid_pointee) << T; - DeclType.Ptr.TypeQuals &= QualType::Restrict; - } - + DeclType.Ptr.TypeQuals &= ~QualType::Restrict; + } + // Apply the pointer typequals to the pointer object. T = Context.getPointerType(T).getQualifiedType(DeclType.Ptr.TypeQuals); break; @@ -424,7 +424,7 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip) { } break; } - case DeclaratorChunk::Function: + case DeclaratorChunk::Function: { // If the function declarator has a prototype (i.e. it is not () and // does not have a K&R-style identifier list), then the arguments are part // of the type, otherwise the argument list is (). @@ -518,7 +518,54 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip) { } break; } - + case DeclaratorChunk::MemberPointer: + // The scope spec must refer to a class, or be dependent. + DeclContext *DC = static_cast( + DeclType.Mem.Scope().getScopeRep()); + QualType ClsType; + // FIXME: Extend for dependent types when it's actually supported. + // See ActOnCXXNestedNameSpecifier. + if (CXXRecordDecl *RD = dyn_cast_or_null(DC)) { + ClsType = Context.getTagDeclType(RD); + } else { + if (DC) { + Diag(DeclType.Mem.Scope().getBeginLoc(), + diag::err_illegal_decl_mempointer_in_nonclass) + << (D.getIdentifier() ? D.getIdentifier()->getName() : "type name") + << DeclType.Mem.Scope().getRange(); + } + D.setInvalidType(true); + ClsType = Context.IntTy; + } + + // C++ 8.3.3p3: A pointer to member shall not pointer to ... a member + // with reference type, or "cv void." + if (T->isReferenceType()) { + Diag(DeclType.Loc, diag::err_illegal_decl_pointer_to_reference) + << (D.getIdentifier() ? D.getIdentifier()->getName() : "type name"); + D.setInvalidType(true); + T = Context.IntTy; + } + if (T->isVoidType()) { + Diag(DeclType.Loc, diag::err_illegal_decl_mempointer_to_void) + << (D.getIdentifier() ? D.getIdentifier()->getName() : "type name"); + T = Context.IntTy; + } + + // Enforce C99 6.7.3p2: "Types other than pointer types derived from + // object or incomplete types shall not be restrict-qualified." + if ((DeclType.Mem.TypeQuals & QualType::Restrict) && + !T->isIncompleteOrObjectType()) { + Diag(DeclType.Loc, diag::err_typecheck_invalid_restrict_invalid_pointee) + << T; + DeclType.Mem.TypeQuals &= ~QualType::Restrict; + } + + T = Context.getMemberPointerType(T, ClsType.getTypePtr()); + + break; + } + // See if there are any attributes on this declarator chunk. if (const AttributeList *AL = DeclType.getAttrs()) ProcessTypeAttributeList(T, AL); diff --git a/clang/test/SemaCXX/member-pointer.cpp b/clang/test/SemaCXX/member-pointer.cpp new file mode 100644 index 000000000000..dcf70b2681e7 --- /dev/null +++ b/clang/test/SemaCXX/member-pointer.cpp @@ -0,0 +1,14 @@ +// RUN: clang -fsyntax-only -verify %s + +struct A {}; +enum B { Dummy }; +namespace C {} + +int A::*pdi1; +int (::A::*pdi2); +int (A::*pfi)(int); + +int B::*pbi; // expected-error {{expected a class or namespace}} +int C::*pci; // expected-error {{'pci' does not point into a class}} +void A::*pdv; // expected-error {{'pdv' declared as a member pointer to void}} +int& A::*pdr; // expected-error {{'pdr' declared as a pointer to a reference}}