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. 

llvm-svn: 67875
This commit is contained in:
Douglas Gregor 2009-03-27 23:10:48 +00:00
parent 669f1d0b0b
commit 333489bba3
21 changed files with 486 additions and 20 deletions

View File

@ -73,6 +73,7 @@ class ASTContext {
llvm::FoldingSet<ClassTemplateSpecializationType>
ClassTemplateSpecializationTypes;
llvm::FoldingSet<QualifiedNameType> QualifiedNameTypes;
llvm::FoldingSet<TypenameType> TypenameTypes;
llvm::FoldingSet<ObjCQualifiedInterfaceType> ObjCQualifiedInterfaceTypes;
llvm::FoldingSet<ObjCQualifiedIdType> ObjCQualifiedIdTypes;
@ -295,6 +296,9 @@ public:
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 @@ public:
->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

View File

@ -149,7 +149,8 @@ public:
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");
}

View File

@ -169,6 +169,10 @@ public:
}
void Destroy(ASTContext &Context);
/// \brief Dump the nested name specifier to standard output to aid
/// in debugging.
void Dump();
};
}

View File

@ -1609,6 +1609,65 @@ protected:
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

View File

@ -74,6 +74,7 @@ TYPE(Enum, TagType)
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)

View File

@ -232,6 +232,8 @@ def warn_cxx0x_right_shift_in_template_arg : Warning<
"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

View File

@ -687,6 +687,17 @@ def note_default_arg_instantiation_here : Note<
"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">;

View File

@ -1264,6 +1264,19 @@ public:
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

View File

@ -1411,6 +1411,32 @@ ASTContext::getQualifiedNameType(NestedNameSpecifier *NNS,
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 @@ QualType ASTContext::getCanonicalType(QualType T) {
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.

View File

@ -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 @@ void NestedNameSpecifier::Destroy(ASTContext &Context) {
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());
}

View File

@ -1435,6 +1435,22 @@ void QualifiedNameType::getAsStringInternal(std::string &InnerString) const {
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;

View File

@ -430,6 +430,19 @@ QualifiedNameType::CreateImpl(ASTContext& Context, llvm::Deserializer& D) {
return 0;
}
//===----------------------------------------------------------------------===//
// 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
//===----------------------------------------------------------------------===//

View File

@ -788,6 +788,12 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
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 @@ bool Parser::ParseOptionalTypeSpecifier(DeclSpec &DS, int& isInvalid,
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 @@ bool Parser::isTypeSpecifierQualifier() {
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 @@ bool Parser::isDeclarationSpecifier() {
// 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())

View File

@ -712,6 +712,7 @@ Parser::OwningExprResult Parser::ParseCastExpression(bool isUnaryExpression,
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) {

View File

@ -516,11 +516,11 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract,
/// 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::TryParseDeclarator(bool mayBeAbstract,
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())

View File

@ -795,10 +795,42 @@ Parser::OwningExprResult Parser::ParseSimpleAsm(SourceLocation *EndLoc) {
/// 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 @@ bool Parser::TryAnnotateTypeOrScopeToken() {
// 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

View File

@ -1790,6 +1790,20 @@ public:
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
//

View File

@ -127,17 +127,20 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S,
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());
}

View File

@ -1964,3 +1964,84 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK,
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();
}

View File

@ -485,8 +485,23 @@ QualType
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 @@ Sema::InstantiateNestedNameSpecifier(NestedNameSpecifier *NNS,
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;
}

View File

@ -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}}