forked from OSchip/llvm-project
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:
parent
669f1d0b0b
commit
333489bba3
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -169,6 +169,10 @@ public:
|
|||
}
|
||||
|
||||
void Destroy(ASTContext &Context);
|
||||
|
||||
/// \brief Dump the nested name specifier to standard output to aid
|
||||
/// in debugging.
|
||||
void Dump();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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">;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
//
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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}}
|
Loading…
Reference in New Issue