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/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;
}