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.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@58860 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp
index de7a1d3..7e51ef5 100644
--- a/lib/AST/DeclBase.cpp
+++ b/lib/AST/DeclBase.cpp
@@ -239,6 +239,7 @@
   case CXXMethod:
   case CXXConstructor:
   case CXXDestructor:
+  case CXXConversion:
   case CXXClassVar:
     break;
   }
diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp
index b0df75b..b2878b6 100644
--- a/lib/AST/DeclCXX.cpp
+++ b/lib/AST/DeclCXX.cpp
@@ -48,6 +48,11 @@
   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 @@
   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 @@
                                      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/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index d826588..58cc9e2 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -1229,7 +1229,11 @@
   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 @@
 ///         '*' 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 @@
     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 @@
     }
 
     // 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 @@
     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/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp
index 3134ff8..5dc43ed 100644
--- a/lib/Parse/ParseExprCXX.cpp
+++ b/lib/Parse/ParseExprCXX.cpp
@@ -292,6 +292,32 @@
   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 @@
     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/lib/Sema/Sema.h b/lib/Sema/Sema.h
index f9dd257..d1da9e2 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -271,6 +271,7 @@
   // 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 @@
                                   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/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index fd94c80..12a4b08 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -38,6 +38,11 @@
   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 @@
 
       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 @@
       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/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index 09f4cbd..fe7efba 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -1004,6 +1004,82 @@
   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 @@
   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
 //===----------------------------------------------------------------------===//