[cfe-commits] r67875 - in /cfe/trunk: include/clang/AST/ include/clang/Basic/ include/clang/Parse/ lib/AST/ lib/Parse/ lib/Sema/ test/SemaTemplate/

Douglas Gregor dgregor at apple.com
Fri Mar 27 16:10:49 PDT 2009


Author: dgregor
Date: Fri Mar 27 18:10:48 2009
New Revision: 67875

URL: http://llvm.org/viewvc/llvm-project?rev=67875&view=rev
Log:
Initial implementation of parsing, semantic analysis, and template
instantiation for C++ typename-specifiers such as

  typename T::type

The parsing of typename-specifiers is relatively easy thanks to
annotation tokens. When we see the "typename", we parse the
typename-specifier and produce a typename annotation token. There are
only a few places where we need to handle this. We currently parse the
typename-specifier form that terminates in an identifier, but not the
simple-template-id form, e.g.,

  typename T::template apply<U, V>

Parsing of nested-name-specifiers has a similar problem, since at this
point we don't have any representation of a class template
specialization whose template-name is unknown.

Semantic analysis is only partially complete, with some support for
template instantiation that works for simple examples. 


Added:
    cfe/trunk/test/SemaTemplate/typename-specifier.cpp
Modified:
    cfe/trunk/include/clang/AST/ASTContext.h
    cfe/trunk/include/clang/AST/DeclarationName.h
    cfe/trunk/include/clang/AST/NestedNameSpecifier.h
    cfe/trunk/include/clang/AST/Type.h
    cfe/trunk/include/clang/AST/TypeNodes.def
    cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Parse/Action.h
    cfe/trunk/lib/AST/ASTContext.cpp
    cfe/trunk/lib/AST/NestedNameSpecifier.cpp
    cfe/trunk/lib/AST/Type.cpp
    cfe/trunk/lib/AST/TypeSerialization.cpp
    cfe/trunk/lib/Parse/ParseDecl.cpp
    cfe/trunk/lib/Parse/ParseExpr.cpp
    cfe/trunk/lib/Parse/ParseTentative.cpp
    cfe/trunk/lib/Parse/Parser.cpp
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp
    cfe/trunk/lib/Sema/SemaTemplate.cpp
    cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp

Modified: cfe/trunk/include/clang/AST/ASTContext.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ASTContext.h?rev=67875&r1=67874&r2=67875&view=diff

==============================================================================
--- cfe/trunk/include/clang/AST/ASTContext.h (original)
+++ cfe/trunk/include/clang/AST/ASTContext.h Fri Mar 27 18:10:48 2009
@@ -73,6 +73,7 @@
   llvm::FoldingSet<ClassTemplateSpecializationType> 
     ClassTemplateSpecializationTypes;
   llvm::FoldingSet<QualifiedNameType> QualifiedNameTypes;
+  llvm::FoldingSet<TypenameType> TypenameTypes;
   llvm::FoldingSet<ObjCQualifiedInterfaceType> ObjCQualifiedInterfaceTypes;
   llvm::FoldingSet<ObjCQualifiedIdType> ObjCQualifiedIdTypes;
 
@@ -295,6 +296,9 @@
 
   QualType getQualifiedNameType(NestedNameSpecifier *NNS,
                                 QualType NamedType);
+  QualType getTypenameType(NestedNameSpecifier *NNS, 
+                           const IdentifierInfo *Name,
+                           QualType Canon = QualType());
 
   /// getObjCQualifiedInterfaceType - Return a 
   /// ObjCQualifiedInterfaceType type for the given interface decl and
@@ -526,6 +530,32 @@
                            ->getDecl());
   }
 
+  /// \brief Retrieves the "canonical" nested name specifier for a
+  /// given nested name specifier.
+  ///
+  /// The canonical nested name specifier is a nested name specifier
+  /// that uniquely identifies a type or namespace within the type
+  /// system. For example, given:
+  ///
+  /// \code
+  /// namespace N {
+  ///   struct S {
+  ///     template<typename T> struct X { typename T* type; };
+  ///   };
+  /// }
+  ///
+  /// template<typename T> struct Y {
+  ///   typename N::S::X<T>::type member;
+  /// };
+  /// \endcode
+  ///
+  /// Here, the nested-name-specifier for N::S::X<T>:: will be
+  /// S::X<template-param-0-0>, since 'S' and 'X' are uniquely defined
+  /// by declarations in the type system and the canonical type for
+  /// the template type parameter 'T' is template-param-0-0.
+  NestedNameSpecifier *
+  getCanonicalNestedNameSpecifier(NestedNameSpecifier *NNS);
+
   /// Type Query functions.  If the type is an instance of the specified class,
   /// return the Type pointer for the underlying maximally pretty type.  This
   /// is a member of ASTContext because this may need to do some amount of

Modified: cfe/trunk/include/clang/AST/DeclarationName.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclarationName.h?rev=67875&r1=67874&r2=67875&view=diff

==============================================================================
--- cfe/trunk/include/clang/AST/DeclarationName.h (original)
+++ cfe/trunk/include/clang/AST/DeclarationName.h Fri Mar 27 18:10:48 2009
@@ -149,7 +149,8 @@
   DeclarationName() : Ptr(0) { }
 
   // Construct a declaration name from an IdentifierInfo *.
-  DeclarationName(IdentifierInfo *II) : Ptr(reinterpret_cast<uintptr_t>(II)) { 
+  DeclarationName(const IdentifierInfo *II) 
+    : Ptr(reinterpret_cast<uintptr_t>(II)) { 
     assert((Ptr & PtrMask) == 0 && "Improperly aligned IdentifierInfo");
   }
 

Modified: cfe/trunk/include/clang/AST/NestedNameSpecifier.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/NestedNameSpecifier.h?rev=67875&r1=67874&r2=67875&view=diff

==============================================================================
--- cfe/trunk/include/clang/AST/NestedNameSpecifier.h (original)
+++ cfe/trunk/include/clang/AST/NestedNameSpecifier.h Fri Mar 27 18:10:48 2009
@@ -169,6 +169,10 @@
   }
 
   void Destroy(ASTContext &Context);
+
+  /// \brief Dump the nested name specifier to standard output to aid
+  /// in debugging.
+  void Dump();
 };
 
 }

Modified: cfe/trunk/include/clang/AST/Type.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Type.h?rev=67875&r1=67874&r2=67875&view=diff

==============================================================================
--- cfe/trunk/include/clang/AST/Type.h (original)
+++ cfe/trunk/include/clang/AST/Type.h Fri Mar 27 18:10:48 2009
@@ -1609,6 +1609,65 @@
   friend class Type;
 };
 
+/// \brief Represents a 'typename' specifier that names a type within
+/// a dependent type, e.g., "typename T::type".
+///
+/// TypenameType has a very similar structure to QualifiedNameType,
+/// which also involves a nested-name-specifier following by a type,
+/// and (FIXME!) both can even be prefixed by the 'typename'
+/// keyword. However, the two types serve very different roles:
+/// QualifiedNameType is a non-semantic type that serves only as sugar
+/// to show how a particular type was written in the source
+/// code. TypenameType, on the other hand, only occurs when the
+/// nested-name-specifier is dependent, such that we cannot resolve
+/// the actual type until after instantiation.
+class TypenameType : public Type, public llvm::FoldingSetNode {
+  /// \brief The nested name specifier containing the qualifier.
+  NestedNameSpecifier *NNS;
+
+  /// \brief The type that this typename specifier refers to.
+  /// FIXME: Also need to represent the "template simple-template-id" case.
+  const IdentifierInfo *Name;
+
+  TypenameType(NestedNameSpecifier *NNS, const IdentifierInfo *Name,
+               QualType CanonType)
+    : Type(Typename, CanonType, true), NNS(NNS), Name(Name) { 
+    assert(NNS->isDependent() && 
+           "TypenameType requires a dependent nested-name-specifier");
+  }
+
+  friend class ASTContext;  // ASTContext creates these
+
+public:
+  /// \brief Retrieve the qualification on this type.
+  NestedNameSpecifier *getQualifier() const { return NNS; }
+
+  /// \brief Retrieve the type named by the typename specifier.
+  const IdentifierInfo *getName() const { return Name; }
+
+  virtual void getAsStringInternal(std::string &InnerString) const;
+
+  void Profile(llvm::FoldingSetNodeID &ID) {
+    Profile(ID, NNS, Name);
+  }
+
+  static void Profile(llvm::FoldingSetNodeID &ID, NestedNameSpecifier *NNS,
+                      const IdentifierInfo *Name) {
+    ID.AddPointer(NNS);
+    ID.AddPointer(Name);
+  }
+
+  static bool classof(const Type *T) { 
+    return T->getTypeClass() == Typename; 
+  }
+  static bool classof(const TypenameType *T) { return true; }
+
+protected:
+  virtual void EmitImpl(llvm::Serializer& S) const;
+  static Type* CreateImpl(ASTContext& Context, llvm::Deserializer& D);
+  friend class Type;
+};
+
 /// ObjCInterfaceType - Interfaces are the core concept in Objective-C for
 /// object oriented design.  They basically correspond to C++ classes.  There
 /// are two kinds of interface types, normal interfaces like "NSString" and

Modified: cfe/trunk/include/clang/AST/TypeNodes.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/TypeNodes.def?rev=67875&r1=67874&r2=67875&view=diff

==============================================================================
--- cfe/trunk/include/clang/AST/TypeNodes.def (original)
+++ cfe/trunk/include/clang/AST/TypeNodes.def Fri Mar 27 18:10:48 2009
@@ -74,6 +74,7 @@
 DEPENDENT_TYPE(TemplateTypeParm, Type)
 NON_CANONICAL_TYPE(ClassTemplateSpecialization, Type)
 NON_CANONICAL_TYPE(QualifiedName, Type)
+DEPENDENT_TYPE(Typename, Type)
 TYPE(ObjCInterface, Type)
 TYPE(ObjCQualifiedInterface, ObjCInterfaceType)
 TYPE(ObjCQualifiedId, Type)

Modified: cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td?rev=67875&r1=67874&r2=67875&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td Fri Mar 27 18:10:48 2009
@@ -232,6 +232,8 @@
   "use of right-shift operator ('>>') in template argument will require "
   "parentheses in C++0x">;
 
+def err_expected_qualified_after_typename : Error<
+  "expected a qualified name after 'typename'">;
 
 // Language specific pragmas
 // - Generic warnings

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=67875&r1=67874&r2=67875&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Fri Mar 27 18:10:48 2009
@@ -687,6 +687,17 @@
   "in instantiation of default argument for '%0' required here">;
 def err_field_instantiates_to_function : Error<
   "data member instantiated with function type %0">;
+def err_nested_name_spec_non_tag : Error<
+  "type %0 cannot be used prior to '::' because it has no members">;
+
+// C++ typename-specifiers
+def err_typename_nested_not_found : Error<"no type named %0 in %1">;
+def err_typename_nested_not_found_global : Error<
+    "no type named %0 in the global namespace">;
+def err_typename_nested_not_type : Error<
+    "typename specifier refers to non-type member %0">;
+def note_typename_refers_here : Note<
+    "referenced member %0 is declared here">;
 
 def err_unexpected_typedef : Error<
   "unexpected type name %0: expected expression">;

Modified: cfe/trunk/include/clang/Parse/Action.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Action.h?rev=67875&r1=67874&r2=67875&view=diff

==============================================================================
--- cfe/trunk/include/clang/Parse/Action.h (original)
+++ cfe/trunk/include/clang/Parse/Action.h Fri Mar 27 18:10:48 2009
@@ -1264,6 +1264,19 @@
     return 0;
   }
 
+  /// \brief Called when the parser has parsed a C++ typename
+  /// specifier, e.g., "typename T::type".
+  ///
+  /// \param TypenameLoc the location of the 'typename' keyword
+  /// \param SS the nested-name-specifier following the typename (e.g., 'T::').
+  /// \param II the identifier we're retrieving (e.g., 'type' in the example).
+  /// \param IdLoc the location of the identifier.
+  virtual TypeResult
+  ActOnTypenameType(SourceLocation TypenameLoc, const CXXScopeSpec &SS,
+                    const IdentifierInfo &II, SourceLocation IdLoc) {
+    return 0;
+  }
+
   //===----------------------- Obj-C Declarations -------------------------===//
   
   // ActOnStartClassInterface - this action is called immediately after parsing

Modified: cfe/trunk/lib/AST/ASTContext.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=67875&r1=67874&r2=67875&view=diff

==============================================================================
--- cfe/trunk/lib/AST/ASTContext.cpp (original)
+++ cfe/trunk/lib/AST/ASTContext.cpp Fri Mar 27 18:10:48 2009
@@ -1411,6 +1411,32 @@
   return QualType(T, 0);
 }
 
+QualType ASTContext::getTypenameType(NestedNameSpecifier *NNS, 
+                                     const IdentifierInfo *Name,
+                                     QualType Canon) {
+  assert(NNS->isDependent() && "nested-name-specifier must be dependent");
+
+  if (Canon.isNull()) {
+    NestedNameSpecifier *CanonNNS = getCanonicalNestedNameSpecifier(NNS);
+    if (CanonNNS != NNS)
+      Canon = getTypenameType(CanonNNS, Name);
+  }
+
+  llvm::FoldingSetNodeID ID;
+  TypenameType::Profile(ID, NNS, Name);
+
+  void *InsertPos = 0;
+  TypenameType *T 
+    = TypenameTypes.FindNodeOrInsertPos(ID, InsertPos);
+  if (T)
+    return QualType(T, 0);
+
+  T = new (*this) TypenameType(NNS, Name, Canon);
+  Types.push_back(T);
+  TypenameTypes.InsertNode(T, InsertPos);
+  return QualType(T, 0);  
+}
+
 /// CmpProtocolNames - Comparison predicate for sorting protocols
 /// alphabetically.
 static bool CmpProtocolNames(const ObjCProtocolDecl *LHS,
@@ -1582,6 +1608,46 @@
                               VAT->getIndexTypeQualifier());
 }
 
+NestedNameSpecifier *
+ASTContext::getCanonicalNestedNameSpecifier(NestedNameSpecifier *NNS) {
+  if (!NNS) 
+    return 0;
+
+  switch (NNS->getKind()) {
+  case NestedNameSpecifier::Identifier:
+    // Canonicalize the prefix but keep the identifier the same.
+    return NestedNameSpecifier::Create(*this, 
+                         getCanonicalNestedNameSpecifier(NNS->getPrefix()),
+                                       NNS->getAsIdentifier());
+
+  case NestedNameSpecifier::Namespace:
+    // A namespace is canonical; build a nested-name-specifier with
+    // this namespace and no prefix.
+    return NestedNameSpecifier::Create(*this, 0, NNS->getAsNamespace());
+
+  case NestedNameSpecifier::TypeSpec:
+  case NestedNameSpecifier::TypeSpecWithTemplate: {
+    QualType T = getCanonicalType(QualType(NNS->getAsType(), 0));
+    NestedNameSpecifier *Prefix = 0;
+
+    // FIXME: This isn't the right check!
+    if (T->isDependentType())
+      Prefix = getCanonicalNestedNameSpecifier(NNS->getPrefix());
+
+    return NestedNameSpecifier::Create(*this, Prefix, 
+                 NNS->getKind() == NestedNameSpecifier::TypeSpecWithTemplate, 
+                                       T.getTypePtr());
+  }
+
+  case NestedNameSpecifier::Global:
+    // The global specifier is canonical and unique.
+    return NNS;
+  }
+
+  // Required to silence a GCC warning
+  return 0;
+}
+
 
 const ArrayType *ASTContext::getAsArrayType(QualType T) {
   // Handle the non-qualified case efficiently.

Modified: cfe/trunk/lib/AST/NestedNameSpecifier.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/NestedNameSpecifier.cpp?rev=67875&r1=67874&r2=67875&view=diff

==============================================================================
--- cfe/trunk/lib/AST/NestedNameSpecifier.cpp (original)
+++ cfe/trunk/lib/AST/NestedNameSpecifier.cpp Fri Mar 27 18:10:48 2009
@@ -17,6 +17,7 @@
 #include "clang/AST/Type.h"
 #include "llvm/Support/raw_ostream.h"
 #include <cassert>
+#include <stdio.h>
 
 using namespace clang;
 
@@ -150,3 +151,12 @@
   this->~NestedNameSpecifier();
   Context.Deallocate((void *)this);
 }
+
+void NestedNameSpecifier::Dump() {
+  std::string Result;
+  {
+    llvm::raw_string_ostream OS(Result);
+    Print(OS);
+  }
+  fprintf(stderr, "%s", Result.c_str());
+}

Modified: cfe/trunk/lib/AST/Type.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Type.cpp?rev=67875&r1=67874&r2=67875&view=diff

==============================================================================
--- cfe/trunk/lib/AST/Type.cpp (original)
+++ cfe/trunk/lib/AST/Type.cpp Fri Mar 27 18:10:48 2009
@@ -1435,6 +1435,22 @@
     InnerString = MyString + ' ' + InnerString;
 }
 
+void TypenameType::getAsStringInternal(std::string &InnerString) const {
+  std::string MyString;
+
+  {
+    llvm::raw_string_ostream OS(MyString);
+    OS << "typename ";
+    NNS->Print(OS);
+    OS << Name->getName();
+  }
+  
+  if (InnerString.empty())
+    InnerString.swap(MyString);
+  else
+    InnerString = MyString + ' ' + InnerString;
+}
+
 void ObjCInterfaceType::getAsStringInternal(std::string &InnerString) const {
   if (!InnerString.empty())    // Prefix the basic type, e.g. 'typedefname X'.
     InnerString = ' ' + InnerString;

Modified: cfe/trunk/lib/AST/TypeSerialization.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/TypeSerialization.cpp?rev=67875&r1=67874&r2=67875&view=diff

==============================================================================
--- cfe/trunk/lib/AST/TypeSerialization.cpp (original)
+++ cfe/trunk/lib/AST/TypeSerialization.cpp Fri Mar 27 18:10:48 2009
@@ -431,6 +431,19 @@
 }
 
 //===----------------------------------------------------------------------===//
+// TypenameType
+//===----------------------------------------------------------------------===//
+void TypenameType::EmitImpl(llvm::Serializer& S) const {
+  // FIXME: Serialize the actual components
+}
+
+Type* 
+TypenameType::CreateImpl(ASTContext& Context, llvm::Deserializer& D) {
+  // FIXME: Implement de-serialization
+  return 0;
+}
+
+//===----------------------------------------------------------------------===//
 // VariableArrayType
 //===----------------------------------------------------------------------===//
 

Modified: cfe/trunk/lib/Parse/ParseDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDecl.cpp?rev=67875&r1=67874&r2=67875&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseDecl.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDecl.cpp Fri Mar 27 18:10:48 2009
@@ -788,6 +788,12 @@
                                  getLang())*2;
       break;
 
+    // C++ typename-specifier:
+    case tok::kw_typename:
+      if (TryAnnotateTypeOrScopeToken())
+        continue;
+      break;
+
     // GNU typeof support.
     case tok::kw_typeof:
       ParseTypeofSpecifier(DS);
@@ -876,6 +882,7 @@
 
   switch (Tok.getKind()) {
   case tok::identifier:   // foo::bar
+  case tok::kw_typename:  // typename foo::bar
     // Annotate typenames and C++ scope specifiers.  If we get one, just
     // recurse to handle whatever we get.
     if (TryAnnotateTypeOrScopeToken())
@@ -1387,12 +1394,14 @@
   default: return false;
       
   case tok::identifier:   // foo::bar
+  case tok::kw_typename:  // typename T::type
     // Annotate typenames and C++ scope specifiers.  If we get one, just
     // recurse to handle whatever we get.
     if (TryAnnotateTypeOrScopeToken())
       return isTypeSpecifierQualifier();
     // Otherwise, not a type specifier.
     return false;
+
   case tok::coloncolon:   // ::foo::bar
     if (NextToken().is(tok::kw_new) ||    // ::new
         NextToken().is(tok::kw_delete))   // ::delete
@@ -1466,7 +1475,9 @@
     // Unfortunate hack to support "Class.factoryMethod" notation.
     if (getLang().ObjC1 && NextToken().is(tok::period))
       return false;
+    // Fall through
 
+  case tok::kw_typename: // typename T::type
     // Annotate typenames and C++ scope specifiers.  If we get one, just
     // recurse to handle whatever we get.
     if (TryAnnotateTypeOrScopeToken())

Modified: cfe/trunk/lib/Parse/ParseExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseExpr.cpp?rev=67875&r1=67874&r2=67875&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseExpr.cpp (original)
+++ cfe/trunk/lib/Parse/ParseExpr.cpp Fri Mar 27 18:10:48 2009
@@ -712,6 +712,7 @@
   case tok::kw_float:
   case tok::kw_double:
   case tok::kw_void:
+  case tok::kw_typename:
   case tok::kw_typeof:
   case tok::annot_typename: {
     if (!getLang().CPlusPlus) {

Modified: cfe/trunk/lib/Parse/ParseTentative.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseTentative.cpp?rev=67875&r1=67874&r2=67875&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseTentative.cpp (original)
+++ cfe/trunk/lib/Parse/ParseTentative.cpp Fri Mar 27 18:10:48 2009
@@ -516,11 +516,11 @@
 ///           class-specifier
 ///           enum-specifier
 ///           elaborated-type-specifier
-///           typename-specifier                                    [TODO]
+///           typename-specifier
 ///           cv-qualifier
 ///
 ///         simple-type-specifier:
-///           '::'[opt] nested-name-specifier[opt] type-name        [TODO]
+///           '::'[opt] nested-name-specifier[opt] type-name
 ///           '::'[opt] nested-name-specifier 'template'
 ///                 simple-template-id                              [TODO]
 ///           'char'
@@ -578,6 +578,7 @@
 Parser::TPResult Parser::isCXXDeclarationSpecifier() {
   switch (Tok.getKind()) {
   case tok::identifier:   // foo::bar
+  case tok::kw_typename:  // typename T::type
     // Annotate typenames and C++ scope specifiers.  If we get one, just
     // recurse to handle whatever we get.
     if (TryAnnotateTypeOrScopeToken())

Modified: cfe/trunk/lib/Parse/Parser.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/Parser.cpp?rev=67875&r1=67874&r2=67875&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/Parser.cpp (original)
+++ cfe/trunk/lib/Parse/Parser.cpp Fri Mar 27 18:10:48 2009
@@ -795,10 +795,42 @@
 /// Note that this routine emits an error if you call it with ::new or ::delete
 /// as the current tokens, so only call it in contexts where these are invalid.
 bool Parser::TryAnnotateTypeOrScopeToken() {
-  assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon)) &&
+  assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon) 
+          || Tok.is(tok::kw_typename)) &&
          "Cannot be a type or scope token!");
   
-  // FIXME: Implement template-ids
+  if (Tok.is(tok::kw_typename)) {
+    // Parse a C++ typename-specifier, e.g., "typename T::type".
+    //
+    //   typename-specifier:
+    //     'typename' '::' [opt] nested-name-specifier identifier
+    //     'typename' '::' [opt] nested-name-specifier template [opt] 
+    //            simple-template-id  [TODO]
+    SourceLocation TypenameLoc = ConsumeToken();
+    CXXScopeSpec SS;
+    bool HadNestedNameSpecifier = ParseOptionalCXXScopeSpecifier(SS);
+    if (!HadNestedNameSpecifier) {
+      Diag(Tok.getLocation(), diag::err_expected_qualified_after_typename);
+      return false;
+    }
+
+    TypeResult Ty;
+    if (Tok.is(tok::identifier)) {
+      // FIXME: check whether the next token is '<', first!
+      Ty = Actions.ActOnTypenameType(TypenameLoc, SS, *Tok.getIdentifierInfo(), 
+                                     Tok.getLocation());
+      // FIXME: better error recovery!
+      Tok.setKind(tok::annot_typename);
+      Tok.setAnnotationValue(Ty.get());
+      Tok.setAnnotationEndLoc(Tok.getLocation());
+      Tok.setLocation(TypenameLoc);
+      PP.AnnotateCachedTokens(Tok);
+      return true;
+    } 
+
+    return false;
+  }
+
   CXXScopeSpec SS;
   if (getLang().CPlusPlus)
     ParseOptionalCXXScopeSpecifier(SS);
@@ -841,7 +873,7 @@
     // template-id, is not part of the annotation. Fall through to
     // push that token back into the stream and complete the C++ scope
     // specifier annotation.
-  }
+  } 
 
   if (Tok.is(tok::annot_template_id)) {
     TemplateIdAnnotation *TemplateId 

Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=67875&r1=67874&r2=67875&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Fri Mar 27 18:10:48 2009
@@ -1790,6 +1790,20 @@
   bool CheckTemplateDeclScope(Scope *S, 
                               MultiTemplateParamsArg &TemplateParameterLists);
 
+  /// \brief Called when the parser has parsed a C++ typename
+  /// specifier, e.g., "typename T::type".
+  ///
+  /// \param TypenameLoc the location of the 'typename' keyword
+  /// \param SS the nested-name-specifier following the typename (e.g., 'T::').
+  /// \param II the identifier we're retrieving (e.g., 'type' in the example).
+  /// \param IdLoc the location of the identifier.
+  virtual TypeResult
+  ActOnTypenameType(SourceLocation TypenameLoc, const CXXScopeSpec &SS,
+                    const IdentifierInfo &II, SourceLocation IdLoc);
+  QualType CheckTypenameType(NestedNameSpecifier *NNS,
+                             const IdentifierInfo &II,
+                             SourceRange Range);
+                             
   //===--------------------------------------------------------------------===//
   // C++ Template Instantiation
   //

Modified: cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp?rev=67875&r1=67874&r2=67875&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp Fri Mar 27 18:10:48 2009
@@ -127,17 +127,20 @@
     if (TypeDecl *Type = dyn_cast<TypeDecl>(SD)) {
       // Determine whether we have a class (or, in C++0x, an enum) or
       // a typedef thereof. If so, build the nested-name-specifier.
-      QualType T;
-      if (TypedefDecl *TD = dyn_cast<TypedefDecl>(SD)) {
+      QualType T = Context.getTypeDeclType(Type);
+      bool AcceptableType = false;
+      if (T->isDependentType())
+        AcceptableType = true;
+      else if (TypedefDecl *TD = dyn_cast<TypedefDecl>(SD)) {
         if (TD->getUnderlyingType()->isRecordType() ||
             (getLangOptions().CPlusPlus0x && 
              TD->getUnderlyingType()->isEnumeralType()))
-          T = Context.getTypeDeclType(TD);
+          AcceptableType = true;
       } else if (isa<RecordDecl>(Type) || 
                  (getLangOptions().CPlusPlus0x && isa<EnumDecl>(Type)))
-        T = Context.getTypeDeclType(Type);
+        AcceptableType = true;
 
-      if (!T.isNull())
+      if (AcceptableType)
         return NestedNameSpecifier::Create(Context, Prefix, false, 
                                            T.getTypePtr());
     }

Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=67875&r1=67874&r2=67875&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Fri Mar 27 18:10:48 2009
@@ -1964,3 +1964,84 @@
   CurContext->addDecl(Specialization);
   return Specialization;
 }
+
+Sema::TypeResult
+Sema::ActOnTypenameType(SourceLocation TypenameLoc, const CXXScopeSpec &SS,
+                        const IdentifierInfo &II, SourceLocation IdLoc) {
+  NestedNameSpecifier *NNS 
+    = static_cast<NestedNameSpecifier *>(SS.getScopeRep());
+  if (!NNS)
+    return true;
+
+  QualType T = CheckTypenameType(NNS, II, SourceRange(TypenameLoc, IdLoc));
+  if (T.isNull())
+    return 0;
+
+  return T.getAsOpaquePtr();
+}
+
+/// \brief Build the type that describes a C++ typename specifier,
+/// e.g., "typename T::type".
+QualType
+Sema::CheckTypenameType(NestedNameSpecifier *NNS, const IdentifierInfo &II,
+                        SourceRange Range) {
+  if (NNS->isDependent()) // FIXME: member of the current instantiation!
+    return Context.getTypenameType(NNS, &II);
+
+  CXXScopeSpec SS;
+  SS.setScopeRep(NNS);
+  SS.setRange(Range);
+  if (RequireCompleteDeclContext(SS))
+    return QualType();
+
+  DeclContext *Ctx = computeDeclContext(SS);
+  assert(Ctx && "No declaration context?");
+
+  DeclarationName Name(&II);
+  LookupResult Result = LookupQualifiedName(Ctx, Name, LookupOrdinaryName, 
+                                            false);
+  unsigned DiagID = 0;
+  Decl *Referenced = 0;
+  switch (Result.getKind()) {
+  case LookupResult::NotFound:
+    if (Ctx->isTranslationUnit())
+      DiagID = diag::err_typename_nested_not_found_global;
+    else
+      DiagID = diag::err_typename_nested_not_found;
+    break;
+
+  case LookupResult::Found:
+    if (TypeDecl *Type = dyn_cast<TypeDecl>(Result.getAsDecl())) {
+      // We found a type. Build a QualifiedNameType, since the
+      // typename-specifier was just sugar. FIXME: Tell
+      // QualifiedNameType that it has a "typename" prefix.
+      return Context.getQualifiedNameType(NNS, Context.getTypeDeclType(Type));
+    }
+
+    DiagID = diag::err_typename_nested_not_type;
+    Referenced = Result.getAsDecl();
+    break;
+
+  case LookupResult::FoundOverloaded:
+    DiagID = diag::err_typename_nested_not_type;
+    Referenced = *Result.begin();
+    break;
+
+  case LookupResult::AmbiguousBaseSubobjectTypes:
+  case LookupResult::AmbiguousBaseSubobjects:
+  case LookupResult::AmbiguousReference:
+    DiagnoseAmbiguousLookup(Result, Name, Range.getEnd(), Range);
+    return QualType();
+  }
+
+  // If we get here, it's because name lookup did not find a
+  // type. Emit an appropriate diagnostic and return an error.
+  if (NamedDecl *NamedCtx = dyn_cast<NamedDecl>(Ctx))
+    Diag(Range.getEnd(), DiagID) << Range << Name << NamedCtx;
+  else
+    Diag(Range.getEnd(), DiagID) << Range << Name;
+  if (Referenced)
+    Diag(Referenced->getLocation(), diag::note_typename_refers_here)
+      << Name;
+  return QualType();
+}

Modified: cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp?rev=67875&r1=67874&r2=67875&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp Fri Mar 27 18:10:48 2009
@@ -485,8 +485,23 @@
 TemplateTypeInstantiator::
 InstantiateQualifiedNameType(const QualifiedNameType *T, 
                              unsigned Quals) const {
-  assert(false && "Cannot have dependent qualified name types (yet)");
-  return QualType();
+  // When we instantiated a qualified name type, there's no point in
+  // keeping the qualification around in the instantiated result. So,
+  // just instantiate the named type.
+  return (*this)(T->getNamedType());
+}
+
+QualType 
+TemplateTypeInstantiator::
+InstantiateTypenameType(const TypenameType *T, unsigned Quals) const {
+  NestedNameSpecifier *NNS 
+    = SemaRef.InstantiateNestedNameSpecifier(T->getQualifier(), 
+                                             SourceRange(Loc),
+                                             TemplateArgs, NumTemplateArgs);
+  if (!NNS)
+    return QualType();
+
+  return SemaRef.CheckTypenameType(NNS, *T->getName(), SourceRange(Loc));
 }
 
 QualType 
@@ -799,21 +814,29 @@
     if (!T->isDependentType())
       return NNS;
 
+    // FIXME: We won't be able to perform the instantiation here when
+    // the template-name is dependent, e.g., we have something like
+    // "T::template apply<U>::type".
     T = InstantiateType(T, TemplateArgs, NumTemplateArgs, Range.getBegin(),
                         DeclarationName());
     if (T.isNull())
       return 0;
 
-    // Note that T.getTypePtr(), below, strips cv-qualifiers. This is
-    // perfectly reasonable, since cv-qualified types in
-    // nested-name-specifiers don't matter.
-    // FIXME: we need to perform more checking on this type.
-    return NestedNameSpecifier::Create(Context, Prefix, 
+    if (T->isRecordType() ||
+        (getLangOptions().CPlusPlus0x && T->isEnumeralType())) {
+      // Note that T.getTypePtr(), below, strips cv-qualifiers. This is
+      // perfectly reasonable, since cv-qualified types in
+      // nested-name-specifiers don't matter.
+      return NestedNameSpecifier::Create(Context, Prefix, 
                  NNS->getKind() == NestedNameSpecifier::TypeSpecWithTemplate,
-                                       T.getTypePtr());
+                                         T.getTypePtr());
+    }
+
+    Diag(Range.getBegin(), diag::err_nested_name_spec_non_tag) << T;
+    return 0;
   }
   }
 
-  // Required to silence GCC warning.
+  // Required to silence a GCC warning
   return 0;
 }

Added: cfe/trunk/test/SemaTemplate/typename-specifier.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/typename-specifier.cpp?rev=67875&view=auto

==============================================================================
--- cfe/trunk/test/SemaTemplate/typename-specifier.cpp (added)
+++ cfe/trunk/test/SemaTemplate/typename-specifier.cpp Fri Mar 27 18:10:48 2009
@@ -0,0 +1,74 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+namespace N {
+  struct A {
+    typedef int type;
+  };
+
+  struct B {
+  };
+
+  struct C {
+    struct type { };
+    int type; // expected-note 2{{referenced member 'type' is declared here}}
+  };
+}
+
+int i;
+
+typename N::A::type *ip1 = &i;
+typename N::B::type *ip2 = &i; // expected-error{{ no type named 'type' in 'B'}}
+typename N::C::type *ip3 = &i; // expected-error{{typename specifier refers to non-type member 'type'}}
+
+void test(double d) {
+  typename N::A::type f(typename N::A::type(a)); // expected-warning{{parentheses were disambiguated as a function declarator}}
+  int five = f(5);
+  
+  using namespace N;
+  for (typename A::type i = 0; i < 10; ++i)
+    five += 1;
+
+  const typename N::A::type f2(d);
+}
+
+namespace N {
+  template<typename T>
+  struct X {
+    typedef typename T::type type; // expected-error 2{{no type named 'type' in 'B'}} \
+    // FIXME: location info for error above isn't very good \
+    // expected-error 2{{typename specifier refers to non-type member 'type'}} \
+    // expected-error{{type 'int' cannot be used prior to '::' because it has no members}}
+  };
+}
+
+N::X<N::A>::type *ip4 = &i;
+N::X<N::B>::type *ip5 = &i; // expected-note{{in instantiation of template class 'struct N::X<struct N::B>' requested here}} \
+// FIXME: expected-error{{invalid token after top level declarator}}
+N::X<N::C>::type *ip6 = &i; // expected-note{{in instantiation of template class 'struct N::X<struct N::C>' requested here}} \
+// FIXME: expected-error{{invalid token after top level declarator}}
+
+N::X<int>::type fail1; // expected-note{{in instantiation of template class 'struct N::X<int>' requested here}} \
+// FIXME: expected-error{{invalid token after top level declarator}}
+
+template<typename T>
+struct Y {
+  typedef typename N::X<T>::type *type; // expected-note{{in instantiation of template class 'struct N::X<struct B>' requested here}} \
+  // expected-note{{in instantiation of template class 'struct N::X<struct C>' requested here}}
+};
+
+struct A {
+  typedef int type;
+};
+
+struct B {
+};
+
+struct C {
+  struct type { };
+  int type; // expected-note{{referenced member 'type' is declared here}}
+};
+
+::Y<A>::type ip7 = &i;
+::Y<B>::type ip8 = &i; // expected-note{{in instantiation of template class 'struct Y<struct B>' requested here}} \
+// FIXME: expected-error{{invalid token after top level declarator}}
+::Y<C>::type ip9 = &i; // expected-note{{in instantiation of template class 'struct Y<struct C>' requested here}} \
+// FIXME: expected-error{{invalid token after top level declarator}}





More information about the cfe-commits mailing list