diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 5a07c48d4ab7..c44515eaa46e 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -68,6 +68,7 @@ public: CXXMethod, CXXConstructor, CXXDestructor, + CXXConversion, Var, ImplicitParam, CXXClassVar, @@ -91,7 +92,7 @@ public: TagFirst = Enum , TagLast = CXXRecord, RecordFirst = Record , RecordLast = CXXRecord, ValueFirst = EnumConstant , ValueLast = ParmVar, - FunctionFirst = Function , FunctionLast = CXXDestructor, + FunctionFirst = Function , FunctionLast = CXXConversion, VarFirst = Var , VarLast = ParmVar }; diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index 7be7197080cd..0c230e767548 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -21,6 +21,7 @@ namespace clang { class CXXRecordDecl; class CXXConstructorDecl; class CXXDestructorDecl; +class CXXConversionDecl; /// OverloadedFunctionDecl - An instance of this class represents a /// set of overloaded functions. All of the functions have the same @@ -53,8 +54,9 @@ public: void addOverload(FunctionDecl *FD) { assert((!getNumFunctions() || (FD->getDeclContext() == getDeclContext())) && "Overloaded functions must all be in the same context"); - assert(FD->getIdentifier() == getIdentifier() && - "Overloaded functions must have the same name."); + assert((FD->getIdentifier() == getIdentifier() || + isa(FD)) && + "Overloaded functions must have the same name or be conversions."); Functions.push_back(FD); } @@ -238,12 +240,18 @@ class CXXRecordDecl : public RecordDecl, public DeclContext { // Destructor - The destructor of this C++ class. CXXDestructorDecl *Destructor; + /// Conversions - Overload set containing the conversion functions + /// of this C++ class (but not its inherited conversion + /// functions). Each of the entries in this overload set is a + /// CXXConversionDecl. + OverloadedFunctionDecl Conversions; + CXXRecordDecl(TagKind TK, DeclContext *DC, SourceLocation L, IdentifierInfo *Id) : RecordDecl(CXXRecord, TK, DC, L, Id), DeclContext(CXXRecord), UserDeclaredConstructor(false), UserDeclaredCopyConstructor(false), Aggregate(true), Polymorphic(false), Bases(0), NumBases(0), - Constructors(DC, Id), Destructor(0) { } + Constructors(DC, Id), Destructor(0), Conversions(DC, Id) { } ~CXXRecordDecl(); @@ -321,6 +329,19 @@ public: this->Destructor = Destructor; } + /// getConversions - Retrieve the overload set containing all of the + /// conversion functions in this class. + OverloadedFunctionDecl *getConversionFunctions() { + return &Conversions; + } + const OverloadedFunctionDecl *getConversionFunctions() const { + return &Conversions; + } + + /// addConversionFunction - Add a new conversion function to the + /// list of conversion functions. + void addConversionFunction(ASTContext &Context, CXXConversionDecl *ConvDecl); + /// 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, @@ -418,7 +439,7 @@ public: // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { - return D->getKind() >= CXXMethod && D->getKind() <= CXXConstructor; + return D->getKind() >= CXXMethod && D->getKind() <= CXXConversion; } static bool classof(const CXXMethodDecl *D) { return true; } @@ -720,6 +741,60 @@ public: static CXXDestructorDecl* CreateImpl(llvm::Deserializer& D, ASTContext& C); }; +/// CXXConversionDecl - Represents a C++ conversion function within a +/// class. For example: +/// +/// @code +/// class X { +/// public: +/// operator bool(); +/// }; +/// @endcode +class CXXConversionDecl : public CXXMethodDecl { + /// Explicit - Whether this conversion function is marked + /// "explicit", meaning that it can only be applied when the user + /// explicitly wrote a cast. This is a C++0x feature. + bool Explicit : 1; + + CXXConversionDecl(CXXRecordDecl *RD, SourceLocation L, + IdentifierInfo *Id, QualType T, + bool isInline, bool isExplicit) + : CXXMethodDecl(CXXConversion, RD, L, Id, T, false, isInline, + /*PrevDecl=*/0), + Explicit(isExplicit) { } + +public: + static CXXConversionDecl *Create(ASTContext &C, CXXRecordDecl *RD, + SourceLocation L, IdentifierInfo *Id, + QualType T, bool isInline, + bool isExplicit); + + /// isExplicit - Whether this is an explicit conversion operator + /// (C++0x only). Explicit conversion operators are only considered + /// when the user has explicitly written a cast. + bool isExplicit() const { return Explicit; } + + /// getConversionType - Returns the type that this conversion + /// function is converting to. + QualType getConversionType() const { + return getType()->getAsFunctionType()->getResultType(); + } + + // Implement isa/cast/dyncast/etc. + static bool classof(const Decl *D) { + return D->getKind() == CXXConversion; + } + static bool classof(const CXXConversionDecl *D) { return true; } + + /// EmitImpl - Serialize this CXXConversionDecl. Called by Decl::Emit. + // FIXME: Implement this. + //virtual void EmitImpl(llvm::Serializer& S) const; + + /// CreateImpl - Deserialize a CXXConversionDecl. Called by Decl::Create. + // FIXME: Implement this. + static CXXConversionDecl* CreateImpl(llvm::Deserializer& D, ASTContext& C); +}; + /// CXXClassVarDecl - Represents a static data member of a struct/union/class. class CXXClassVarDecl : public VarDecl { diff --git a/clang/include/clang/Basic/DiagnosticKinds.def b/clang/include/clang/Basic/DiagnosticKinds.def index 64afc4683f6d..b3b6e81f9f1f 100644 --- a/clang/include/clang/Basic/DiagnosticKinds.def +++ b/clang/include/clang/Basic/DiagnosticKinds.def @@ -1309,6 +1309,31 @@ DIAG(err_operator_overload_must_be_member, ERROR, "overloaded operator '%0' must be a non-static member function") DIAG(err_operator_overload_post_incdec_must_be_int, ERROR, "%0parameter of overloaded post-%1 operator must have type 'int' (not '%2')") +DIAG(err_operator_missing_type_specifier, ERROR, + "missing type specifier after 'operator'") + +// C++ conversion functions +DIAG(err_conv_function_not_member, ERROR, + "conversion function must be a non-static member function") +DIAG(err_conv_function_return_type, ERROR, + "conversion function cannot have a return type") +DIAG(err_conv_function_with_params, ERROR, + "conversion function cannot have any parameters") +DIAG(err_conv_function_variadic, ERROR, + "conversion function cannot be variadic") +DIAG(err_conv_function_to_array, ERROR, + "conversion function cannot convert to an array type") +DIAG(err_conv_function_to_function, ERROR, + "conversion function cannot convert to a function type") +DIAG(err_conv_function_redeclared, ERROR, + "conversion function cannot be redeclared") +DIAG(warn_conv_to_self_not_used, WARNING, + "conversion function converting '%0' to itself will never be used") +DIAG(warn_conv_to_base_not_used, WARNING, + "conversion function converting '%0' to its base class '%1' will never be used") +DIAG(warn_conv_to_void_not_used, WARNING, + "conversion function converting '%0' to '%1' will never be used") + DIAG(warn_not_compound_assign, WARNING, "use of unary operator that may be intended as compound assignment (%0=)") diff --git a/clang/include/clang/Parse/Action.h b/clang/include/clang/Parse/Action.h index 423150ff76cb..698feff0b946 100644 --- a/clang/include/clang/Parse/Action.h +++ b/clang/include/clang/Parse/Action.h @@ -42,9 +42,10 @@ namespace clang { /// the parser has just done or is about to do when the method is called. They /// are not requests that the actions module do the specified action. /// -/// All of the methods here are optional except isTypeName(), which must be -/// specified in order for the parse to complete accurately. The MinimalAction -/// class does this bare-minimum of tracking to implement this functionality. +/// All of the methods here are optional except isTypeName() and +/// isCurrentClassName(), which must be specified in order for the +/// parse to complete accurately. The MinimalAction class does this +/// bare-minimum of tracking to implement this functionality. class Action { public: /// Out-of-line virtual destructor to provide home for this class. @@ -108,6 +109,14 @@ public: /// name of the innermost C++ class type currently being defined. virtual bool isCurrentClassName(const IdentifierInfo &II, Scope *S) = 0; + /// getTypeAsString - Returns a string that describes the given + /// type. This callback is used in C++ to form identifiers for + /// special declarations that otherwise don't have simple names, + /// such as constructors, destructors, and conversion functions. + virtual std::string getTypeAsString(TypeTy *Type) { + return ""; + } + /// ActOnDeclarator - This callback is invoked when a declarator is parsed and /// 'Init' specifies the initializer if any. This is for things like: /// "int X = 4" or "typedef int foo". diff --git a/clang/include/clang/Parse/DeclSpec.h b/clang/include/clang/Parse/DeclSpec.h index fab6ec3c34f0..76e3e7ba892b 100644 --- a/clang/include/clang/Parse/DeclSpec.h +++ b/clang/include/clang/Parse/DeclSpec.h @@ -610,7 +610,9 @@ public: 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) + DK_Destructor, // A C++ destructor (identifier is ~class name) + DK_Conversion // A C++ conversion function (identifier is + // "operator " then the type name) }; private: @@ -639,8 +641,9 @@ 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. + // When Kind is DK_Constructor, DK_Destructor, or DK_Conversion, the + // type associated with the constructor, destructor, or conversion + // operator. Action::TypeTy *Type; public: @@ -755,6 +758,16 @@ public: Type = Ty; } + // SetConversionFunction - Set this declarator to be a C++ + // conversion function declarator. + void SetConversionFunction(Action::TypeTy *Ty, IdentifierInfo *ID, + SourceLocation Loc) { + Identifier = ID; + IdentifierLoc = Loc; + Kind = DK_Conversion; + Type = Ty; + } + void AddTypeInfo(const DeclaratorChunk &TI) { DeclTypeInfo.push_back(TI); } diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index f46d901cdb48..f9b233ca0eb1 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -475,6 +475,8 @@ private: /// simple-type-specifier. void ParseCXXSimpleTypeSpecifier(DeclSpec &DS); + bool ParseCXXTypeSpecifierSeq(DeclSpec &DS); + //===--------------------------------------------------------------------===// // C++ if/switch/while/for condition expression. ExprResult ParseCXXCondition(); @@ -701,7 +703,7 @@ private: /// ParseDeclarator - Parse and verify a newly-initialized declarator. void ParseDeclarator(Declarator &D); - void ParseDeclaratorInternal(Declarator &D); + void ParseDeclaratorInternal(Declarator &D, bool PtrOperator = false); void ParseTypeQualifierListOpt(DeclSpec &DS); void ParseDirectDeclarator(Declarator &D); void ParseParenDeclarator(Declarator &D); @@ -737,6 +739,7 @@ private: //===--------------------------------------------------------------------===// // C++ 13.5: Overloaded operators [over.oper] IdentifierInfo *MaybeParseOperatorFunctionId(); + TypeTy *ParseConversionFunctionId(); }; } // end namespace clang diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index de7a1d3ca430..7e51ef501007 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -239,6 +239,7 @@ void Decl::addDeclKind(Kind k) { case CXXMethod: case CXXConstructor: case CXXDestructor: + case CXXConversion: case CXXClassVar: break; } diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index b0df75b404d1..b2878b667aa7 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -48,6 +48,11 @@ void CXXRecordDecl::Destroy(ASTContext &C) { if (isDefinition()) Destructor->Destroy(C); + for (OverloadedFunctionDecl::function_iterator func + = Conversions.function_begin(); + func != Conversions.function_end(); ++func) + (*func)->Destroy(C); + RecordDecl::Destroy(C); } @@ -101,6 +106,11 @@ CXXRecordDecl::addConstructor(ASTContext &Context, Constructors.addOverload(ConDecl); } +void CXXRecordDecl::addConversionFunction(ASTContext &Context, + CXXConversionDecl *ConvDecl) { + Conversions.addOverload(ConvDecl); +} + CXXMethodDecl * CXXMethodDecl::Create(ASTContext &C, CXXRecordDecl *RD, SourceLocation L, IdentifierInfo *Id, @@ -232,6 +242,14 @@ CXXDestructorDecl::Create(ASTContext &C, CXXRecordDecl *RD, isImplicitlyDeclared); } +CXXConversionDecl * +CXXConversionDecl::Create(ASTContext &C, CXXRecordDecl *RD, + SourceLocation L, IdentifierInfo *Id, + QualType T, bool isInline, bool isExplicit) { + void *Mem = C.getAllocator().Allocate(); + return new (Mem) CXXConversionDecl(RD, L, Id, T, isInline, isExplicit); +} + CXXClassVarDecl *CXXClassVarDecl::Create(ASTContext &C, CXXRecordDecl *RD, SourceLocation L, IdentifierInfo *Id, QualType T, ScopedDecl *PrevDecl) { diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index d82658825a45..58cc9e2c6b3a 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -1229,7 +1229,11 @@ void Parser::ParseDeclarator(Declarator &D) { ParseDeclaratorInternal(D); } -/// ParseDeclaratorInternal +/// ParseDeclaratorInternal - Parse a C or C++ declarator. If +/// PtrOperator is true, then this routine won't parse the final +/// direct-declarator; therefore, it effectively parses the C++ +/// ptr-operator production. +/// /// declarator: [C99 6.7.5] /// pointer[opt] direct-declarator /// [C++] '&' declarator [C++ 8p4, dcl.decl] @@ -1239,13 +1243,21 @@ void Parser::ParseDeclarator(Declarator &D) { /// '*' type-qualifier-list[opt] /// '*' type-qualifier-list[opt] pointer /// -void Parser::ParseDeclaratorInternal(Declarator &D) { +/// ptr-operator: +/// '*' cv-qualifier-seq[opt] +/// '&' +/// [GNU] '&' restrict[opt] attributes[opt] +/// '::'[opt] nested-name-specifier '*' cv-qualifier-seq[opt] [TODO] +void Parser::ParseDeclaratorInternal(Declarator &D, bool PtrOperator) { 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)) - return ParseDirectDeclarator(D); + (Kind != tok::caret || !getLang().Blocks)) { + if (!PtrOperator) + ParseDirectDeclarator(D); + return; + } // Otherwise, '*' -> pointer, '^' -> block, '&' -> reference. SourceLocation Loc = ConsumeToken(); // Eat the * or &. @@ -1257,7 +1269,7 @@ void Parser::ParseDeclaratorInternal(Declarator &D) { ParseTypeQualifierListOpt(DS); // Recursively parse the declarator. - ParseDeclaratorInternal(D); + ParseDeclaratorInternal(D, PtrOperator); if (Kind == tok::star) // Remember that we parsed a pointer type, and remember the type-quals. D.AddTypeInfo(DeclaratorChunk::getPointer(DS.getTypeQualifiers(), Loc, @@ -1290,7 +1302,7 @@ void Parser::ParseDeclaratorInternal(Declarator &D) { } // Recursively parse the declarator. - ParseDeclaratorInternal(D); + ParseDeclaratorInternal(D, PtrOperator); if (D.getNumTypeObjects() > 0) { // C++ [dcl.ref]p4: There shall be no references to references. @@ -1382,7 +1394,13 @@ void Parser::ParseDirectDeclarator(Declarator &D) { if (IdentifierInfo *II = MaybeParseOperatorFunctionId()) { D.SetIdentifier(II, OperatorLoc); } else { - // This must be a user-defined conversion. + // This must be a conversion function (C++ [class.conv.fct]). + if (TypeTy *ConvType = ParseConversionFunctionId()) { + IdentifierInfo *II + = &PP.getIdentifierTable().get(std::string("operator ") + + Actions.getTypeAsString(ConvType)); + D.SetConversionFunction(ConvType, II, OperatorLoc); + } } } else if (Tok.is(tok::l_paren)) { // direct-declarator: '(' declarator ')' diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 3134ff87b60b..5dc43ed34d27 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -292,6 +292,32 @@ void Parser::ParseCXXSimpleTypeSpecifier(DeclSpec &DS) { DS.Finish(Diags, PP.getSourceManager(), getLang()); } +/// ParseCXXTypeSpecifierSeq - Parse a C++ type-specifier-seq (C++ +/// [dcl.name]), which is a non-empty sequence of type-specifiers, +/// e.g., "const short int". Note that the DeclSpec is *not* finished +/// by parsing the type-specifier-seq, because these sequences are +/// typically followed by some form of declarator. Returns true and +/// emits diagnostics if this is not a type-specifier-seq, false +/// otherwise. +/// +/// type-specifier-seq: [C++ 8.1] +/// type-specifier type-specifier-seq[opt] +/// +bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS) { + DS.SetRangeStart(Tok.getLocation()); + const char *PrevSpec = 0; + int isInvalid = 0; + + // Parse one or more of the type specifiers. + if (!MaybeParseTypeSpecifier(DS, isInvalid, PrevSpec)) { + Diag(Tok.getLocation(), diag::err_operator_missing_type_specifier); + return true; + } + while (MaybeParseTypeSpecifier(DS, isInvalid, PrevSpec)); + + return false; +} + /// MaybeParseOperatorFunctionId - Attempts to parse a C++ overloaded /// operator name (C++ [over.oper]). If successful, returns the /// predefined identifier that corresponds to that overloaded @@ -365,3 +391,38 @@ IdentifierInfo *Parser::MaybeParseOperatorFunctionId() { return &PP.getIdentifierTable().getOverloadedOperator(Op); } } + +/// ParseConversionFunctionId - Parse a C++ conversion-function-id, +/// which expresses the name of a user-defined conversion operator +/// (C++ [class.conv.fct]p1). Returns the type that this operator is +/// specifying a conversion for, or NULL if there was an error. +/// +/// conversion-function-id: [C++ 12.3.2] +/// operator conversion-type-id +/// +/// conversion-type-id: +/// type-specifier-seq conversion-declarator[opt] +/// +/// conversion-declarator: +/// ptr-operator conversion-declarator[opt] +Parser::TypeTy *Parser::ParseConversionFunctionId() { + assert(Tok.is(tok::kw_operator) && "Expected 'operator' keyword"); + ConsumeToken(); // 'operator' + + // Parse the type-specifier-seq. + DeclSpec DS; + if (ParseCXXTypeSpecifierSeq(DS)) + return 0; + + // Parse the conversion-declarator, which is merely a sequence of + // ptr-operators. + Declarator D(DS, Declarator::TypeNameContext); + ParseDeclaratorInternal(D, /*PtrOperator=*/true); + + // Finish up the type. + Action::TypeResult Result = Actions.ActOnTypeName(CurScope, D); + if (Result.isInvalid) + return 0; + else + return Result.Val; +} diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index f9dd25788cdf..d1da9e2944a8 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -271,6 +271,7 @@ private: // Symbol table / Decl tracking callbacks: SemaDecl.cpp. // virtual TypeTy *isTypeName(const IdentifierInfo &II, Scope *S); + virtual std::string getTypeAsString(TypeTy *Type); virtual DeclTy *ActOnDeclarator(Scope *S, Declarator &D, DeclTy *LastInGroup); virtual DeclTy *ActOnParamDeclarator(Scope *S, Declarator &D); virtual void ActOnParamDefaultArgument(DeclTy *param, @@ -845,8 +846,11 @@ public: FunctionDecl::StorageClass& SC); bool CheckDestructorDeclarator(Declarator &D, QualType &R, FunctionDecl::StorageClass& SC); + bool CheckConversionDeclarator(Declarator &D, QualType &R, + FunctionDecl::StorageClass& SC); DeclTy *ActOnConstructorDeclarator(CXXConstructorDecl *Constructor); DeclTy *ActOnDestructorDeclarator(CXXDestructorDecl *Destructor); + DeclTy *ActOnConversionDeclarator(CXXConversionDecl *Conversion); //===--------------------------------------------------------------------===// // C++ Derived Classes diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index fd94c80d8b99..12a4b08e919c 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -38,6 +38,11 @@ Sema::TypeTy *Sema::isTypeName(const IdentifierInfo &II, Scope *S) { return 0; } +std::string Sema::getTypeAsString(TypeTy *Type) { + QualType Ty = QualType::getFromOpaquePtr(Type); + return Ty.getAsString(); +} + DeclContext *Sema::getDCParent(DeclContext *DC) { // If CurContext is a ObjC method, getParent() will return NULL. if (isa(DC)) @@ -835,6 +840,22 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { if (isInvalidDecl) NewFD->setInvalidDecl(); + } else if (D.getKind() == Declarator::DK_Conversion) { + if (D.getContext() != Declarator::MemberContext) { + Diag(D.getIdentifierLoc(), + diag::err_conv_function_not_member); + return 0; + } else { + bool isInvalidDecl = CheckConversionDeclarator(D, R, SC); + + NewFD = CXXConversionDecl::Create(Context, + cast(CurContext), + D.getIdentifierLoc(), II, R, + isInline, isExplicit); + + if (isInvalidDecl) + NewFD->setInvalidDecl(); + } } else if (D.getContext() == Declarator::MemberContext) { // This is a C++ method declaration. NewFD = CXXMethodDecl::Create(Context, cast(CurContext), @@ -931,6 +952,8 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { return ActOnConstructorDeclarator(Constructor); else if (CXXDestructorDecl *Destructor = dyn_cast(NewFD)) return ActOnDestructorDeclarator(Destructor); + else if (CXXConversionDecl *Conversion = dyn_cast(NewFD)) + return ActOnConversionDeclarator(Conversion); // Extra checking for C++ overloaded operators (C++ [over.oper]). if (NewFD->isOverloadedOperator() && diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 09f4cbd2a6a7..fe7efbae5f8b 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -1004,6 +1004,82 @@ bool Sema::CheckDestructorDeclarator(Declarator &D, QualType &R, return isInvalid; } +/// CheckConversionDeclarator - Called by ActOnDeclarator to check the +/// well-formednes of the conversion function 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 conversion operator. +bool Sema::CheckConversionDeclarator(Declarator &D, QualType &R, + FunctionDecl::StorageClass& SC) { + bool isInvalid = false; + + // C++ [class.conv.fct]p1: + // Neither parameter types nor return type can be specified. The + // type of a conversion function (8.3.5) is “function taking no + // parameter returning conversion-type-id.” + if (SC == FunctionDecl::Static) { + Diag(D.getIdentifierLoc(), + diag::err_conv_function_not_member, + "static", + SourceRange(D.getDeclSpec().getStorageClassSpecLoc()), + SourceRange(D.getIdentifierLoc())); + isInvalid = true; + SC = FunctionDecl::None; + } + if (D.getDeclSpec().hasTypeSpecifier()) { + // Conversion functions don't have return types, but the parser will + // happily parse something like: + // + // class X { + // float operator bool(); + // }; + // + // The return type will be changed later anyway. + Diag(D.getIdentifierLoc(), + diag::err_conv_function_return_type, + SourceRange(D.getDeclSpec().getTypeSpecTypeLoc()), + SourceRange(D.getIdentifierLoc())); + } + + // Make sure we don't have any parameters. + if (R->getAsFunctionTypeProto()->getNumArgs() > 0) { + Diag(D.getIdentifierLoc(), diag::err_conv_function_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 conversion function isn't variadic. + if (R->getAsFunctionTypeProto()->isVariadic()) + Diag(D.getIdentifierLoc(), diag::err_conv_function_variadic); + + // C++ [class.conv.fct]p4: + // The conversion-type-id shall not represent a function type nor + // an array type. + QualType ConvType = QualType::getFromOpaquePtr(D.getDeclaratorIdType()); + if (ConvType->isArrayType()) { + Diag(D.getIdentifierLoc(), diag::err_conv_function_to_array); + ConvType = Context.getPointerType(ConvType); + } else if (ConvType->isFunctionType()) { + Diag(D.getIdentifierLoc(), diag::err_conv_function_to_function); + ConvType = Context.getPointerType(ConvType); + } + + // Rebuild the function type "R" without any parameters (in case any + // of the errors above fired) and with the conversion type as the + // return type. + R = Context.getFunctionType(ConvType, 0, 0, false, + R->getAsFunctionTypeProto()->getTypeQuals()); + + 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 @@ -1092,6 +1168,65 @@ Sema::DeclTy *Sema::ActOnDestructorDeclarator(CXXDestructorDecl *Destructor) { return (DeclTy *)Destructor; } +/// ActOnConversionDeclarator - Called by ActOnDeclarator to complete +/// the declaration of the given C++ conversion function. This routine +/// is responsible for recording the conversion function in the C++ +/// class, if possible. +Sema::DeclTy *Sema::ActOnConversionDeclarator(CXXConversionDecl *Conversion) { + assert(Conversion && "Expected to receive a conversion function declaration"); + + CXXRecordDecl *ClassDecl = cast(CurContext); + + // Make sure we aren't redeclaring the conversion function. + QualType ConvType = Context.getCanonicalType(Conversion->getConversionType()); + OverloadedFunctionDecl *Conversions = ClassDecl->getConversionFunctions(); + for (OverloadedFunctionDecl::function_iterator Func + = Conversions->function_begin(); + Func != Conversions->function_end(); ++Func) { + CXXConversionDecl *OtherConv = cast(*Func); + if (ConvType == Context.getCanonicalType(OtherConv->getConversionType())) { + Diag(Conversion->getLocation(), diag::err_conv_function_redeclared); + Diag(OtherConv->getLocation(), + OtherConv->isThisDeclarationADefinition()? + diag::err_previous_definition + : diag::err_previous_declaration); + Conversion->setInvalidDecl(); + return (DeclTy *)Conversion; + } + } + + // C++ [class.conv.fct]p1: + // [...] A conversion function is never used to convert a + // (possibly cv-qualified) object to the (possibly cv-qualified) + // same object type (or a reference to it), to a (possibly + // cv-qualified) base class of that type (or a reference to it), + // or to (possibly cv-qualified) void. + // FIXME: Suppress this warning if the conversion function ends up + // being a virtual function that overrides a virtual function in a + // base class. + QualType ClassType + = Context.getCanonicalType(Context.getTypeDeclType(ClassDecl)); + if (const ReferenceType *ConvTypeRef = ConvType->getAsReferenceType()) + ConvType = ConvTypeRef->getPointeeType(); + if (ConvType->isRecordType()) { + ConvType = Context.getCanonicalType(ConvType).getUnqualifiedType(); + if (ConvType == ClassType) + Diag(Conversion->getLocation(), diag::warn_conv_to_self_not_used, + ClassType.getAsString()); + else if (IsDerivedFrom(ClassType, ConvType)) + Diag(Conversion->getLocation(), diag::warn_conv_to_base_not_used, + ClassType.getAsString(), + ConvType.getAsString()); + } else if (ConvType->isVoidType()) { + Diag(Conversion->getLocation(), diag::warn_conv_to_void_not_used, + ClassType.getAsString(), ConvType.getAsString()); + } + + ClassDecl->addConversionFunction(Context, Conversion); + + return (DeclTy *)Conversion; +} + //===----------------------------------------------------------------------===// // Namespace Handling //===----------------------------------------------------------------------===// diff --git a/clang/test/SemaCXX/conversion-function.cpp b/clang/test/SemaCXX/conversion-function.cpp new file mode 100644 index 000000000000..3c96e54c5317 --- /dev/null +++ b/clang/test/SemaCXX/conversion-function.cpp @@ -0,0 +1,42 @@ +// RUN: clang -fsyntax-only -verify %s +class X { +public: + operator bool(); + operator int() const; +}; + +operator int(); // expected-error{{conversion function must be a non-static member function}} + +typedef int func_type(int); +typedef int array_type[10]; + +class Y { +public: + void operator bool(int, ...) const; // expected-error{{conversion function cannot have a return type}} \ + // expected-error{{conversion function cannot have any parameters}} \ + // expected-error{{conversion function cannot be variadic}} + operator func_type(); // expected-error{{conversion function cannot convert to a function type}} + operator array_type(); // expected-error{{conversion function cannot convert to an array type}} +}; + + +typedef int INT; +typedef INT* INT_PTR; + +class Z { + operator int(); // expected-error{{previous declaration is here}} + operator int**(); // expected-error{{previous declaration is here}} + + operator INT(); // expected-error{{conversion function cannot be redeclared}} + operator INT_PTR*(); // expected-error{{conversion function cannot be redeclared}} +}; + + +class A { }; + +class B : public A { +public: + operator A&() const; // expected-warning{{conversion function converting 'class B' to its base class 'class A' will never be used}} + operator const void() const; // expected-warning{{conversion function converting 'class B' to 'void const' will never be used}} + operator const B(); // expected-warning{{conversion function converting 'class B' to itself will never be used}} +};