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.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@67875 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index e433769..2956460 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -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.
diff --git a/lib/AST/NestedNameSpecifier.cpp b/lib/AST/NestedNameSpecifier.cpp
index 62e972e..40efe2a 100644
--- a/lib/AST/NestedNameSpecifier.cpp
+++ b/lib/AST/NestedNameSpecifier.cpp
@@ -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());
+}
diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp
index 103c0a9..0a3e8f9 100644
--- a/lib/AST/Type.cpp
+++ b/lib/AST/Type.cpp
@@ -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;
diff --git a/lib/AST/TypeSerialization.cpp b/lib/AST/TypeSerialization.cpp
index 4490847..4f0334f 100644
--- a/lib/AST/TypeSerialization.cpp
+++ b/lib/AST/TypeSerialization.cpp
@@ -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
//===----------------------------------------------------------------------===//
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index 2c26b13..b1cbc3c 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -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())
diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp
index 913f9ba..f10cb38 100644
--- a/lib/Parse/ParseExpr.cpp
+++ b/lib/Parse/ParseExpr.cpp
@@ -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) {
diff --git a/lib/Parse/ParseTentative.cpp b/lib/Parse/ParseTentative.cpp
index 2cca2cd..335a455 100644
--- a/lib/Parse/ParseTentative.cpp
+++ b/lib/Parse/ParseTentative.cpp
@@ -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())
diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp
index 5ce8b3d..7f2c5d3 100644
--- a/lib/Parse/Parser.cpp
+++ b/lib/Parse/Parser.cpp
@@ -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
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index ac260c2..8dee4f1 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -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
//
diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp
index 8fb2811..a879989 100644
--- a/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/lib/Sema/SemaCXXScopeSpec.cpp
@@ -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());
}
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp
index 697582c..6994697 100644
--- a/lib/Sema/SemaTemplate.cpp
+++ b/lib/Sema/SemaTemplate.cpp
@@ -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();
+}
diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp
index 52f87b9..b274f87 100644
--- a/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/lib/Sema/SemaTemplateInstantiate.cpp
@@ -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;
}