diff options
| author | Douglas Gregor <dgregor@apple.com> | 2008-11-07 20:08:42 +0000 | 
|---|---|---|
| committer | Douglas Gregor <dgregor@apple.com> | 2008-11-07 20:08:42 +0000 | 
| commit | dbc5daf05882c1c1934b35d5899b6ca53ce4cff9 (patch) | |
| tree | af766082edf0c17460eb8950a94a9b87000fec02 | |
| parent | cb0df597e0f7f43b14cbed1f9dd1f39b22fe08be (diff) | |
| download | bcm5719-llvm-dbc5daf05882c1c1934b35d5899b6ca53ce4cff9.tar.gz bcm5719-llvm-dbc5daf05882c1c1934b35d5899b6ca53ce4cff9.zip | |
Parsing, ASTs, and semantic analysis for the declaration of conversion
functions in C++, e.g.,
  struct X {
    operator bool() const;
  };
Note that these conversions don't actually do anything, since we don't
yet have the ability to use them for implicit or explicit conversions.
llvm-svn: 58860
| -rw-r--r-- | clang/include/clang/AST/DeclBase.h | 3 | ||||
| -rw-r--r-- | clang/include/clang/AST/DeclCXX.h | 83 | ||||
| -rw-r--r-- | clang/include/clang/Basic/DiagnosticKinds.def | 25 | ||||
| -rw-r--r-- | clang/include/clang/Parse/Action.h | 15 | ||||
| -rw-r--r-- | clang/include/clang/Parse/DeclSpec.h | 19 | ||||
| -rw-r--r-- | clang/include/clang/Parse/Parser.h | 5 | ||||
| -rw-r--r-- | clang/lib/AST/DeclBase.cpp | 1 | ||||
| -rw-r--r-- | clang/lib/AST/DeclCXX.cpp | 18 | ||||
| -rw-r--r-- | clang/lib/Parse/ParseDecl.cpp | 32 | ||||
| -rw-r--r-- | clang/lib/Parse/ParseExprCXX.cpp | 61 | ||||
| -rw-r--r-- | clang/lib/Sema/Sema.h | 4 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 23 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 135 | ||||
| -rw-r--r-- | clang/test/SemaCXX/conversion-function.cpp | 42 | 
14 files changed, 447 insertions, 19 deletions
| diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 5a07c48d4ab..c44515eaa46 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 7be7197080c..0c230e76754 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<CXXConversionDecl>(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 64afc4683f6..b3b6e81f9f1 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 423150ff76c..698feff0b94 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 "<unknown type>"; +  } +    /// 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 fab6ec3c34f..76e3e7ba892 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 f46d901cdb4..f9b233ca0eb 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 de7a1d3ca43..7e51ef50100 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 b0df75b404d..b2878b667aa 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<CXXConversionDecl>(); +  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 d82658825a4..58cc9e2c6b3 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 3134ff87b60..5dc43ed34d2 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 f9dd25788cd..d1da9e2944a 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 fd94c80d8b9..12a4b08e919 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<ObjCMethodDecl>(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<CXXRecordDecl>(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<CXXRecordDecl>(CurContext), @@ -931,6 +952,8 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) {        return ActOnConstructorDeclarator(Constructor);      else if (CXXDestructorDecl *Destructor = dyn_cast<CXXDestructorDecl>(NewFD))        return ActOnDestructorDeclarator(Destructor); +    else if (CXXConversionDecl *Conversion = dyn_cast<CXXConversionDecl>(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 09f4cbd2a6a..fe7efbae5f8 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<CXXRecordDecl>(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<CXXConversionDecl>(*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 00000000000..3c96e54c531 --- /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}} +}; | 

