#include "llvm/ADT/SmallVector.h"
namespace clang {
+
+class ClassTemplateDecl;
class CXXRecordDecl;
class CXXConstructorDecl;
class CXXDestructorDecl;
/// CXXConversionDecl.
OverloadedFunctionDecl Conversions;
+ /// \brief The template or declaration that is declaration is
+ /// instantiated from.
+ ///
+ /// For non-templates, this value will be NULL. For record
+ /// declarations that describe a class template, this will be a
+ /// pointer to a ClassTemplateDecl (the bit is 0). For member
+ /// classes of class template specializations, this will be the
+ /// RecordDecl from which the member class was instantiated (the bit
+ /// is 1).
+ llvm::PointerIntPair<Decl*, 1> TemplateOrInstantiation;
+
protected:
CXXRecordDecl(Kind K, TagKind TK, DeclContext *DC,
SourceLocation L, IdentifierInfo *Id);
/// addedAssignmentOperator - Notify the class that another assignment
/// operator has been added. This routine helps maintain information about the
- /// class based on which operators have been added.
+ /// class based on which operators have been added.
void addedAssignmentOperator(ASTContext &Context, CXXMethodDecl *OpDecl);
/// hasUserDeclaredCopyAssignment - Whether this class has a
/// setAbstract - Set whether this class is abstract (C++ [class.abstract])
void setAbstract(bool Abs) { Abstract = Abs; }
+ /// \brief If this record is an instantiation of a member class,
+ /// retrieves the member class from which it was instantiated.
+ ///
+ /// This routine will return non-NULL for (non-templated) member
+ /// classes of class templates. For example, given:
+ ///
+ /// \code
+ /// template<typename T>
+ /// struct X {
+ /// struct A { };
+ /// };
+ /// \endcode
+ ///
+ /// The declaration for X<int>::A is a (non-templated) CXXRecordDecl
+ /// whose parent is the class template specialization X<int>. For
+ /// this declaration, getInstantiatedFromMemberClass() will return
+ /// the CXXRecordDecl X<T>::A. When a complete definition of
+ /// X<int>::A is required, it will be instantiated from the
+ /// declaration returned by getInstantiatedFromMemberClass().
+ CXXRecordDecl *getInstantiatedFromMemberClass();
+
+ /// \brief Specify that this record is an instantiation of the
+ /// member class RD.
+ void setInstantiationOfMemberClass(CXXRecordDecl *RD) {
+ TemplateOrInstantiation.setInt(1);
+ TemplateOrInstantiation.setPointer(RD);
+ }
+
+ /// \brief Retrieves the class template that is described by this
+ /// class declaration.
+ ///
+ /// Every class template is represented as a ClassTemplateDecl and a
+ /// CXXRecordDecl. The former contains template properties (such as
+ /// the template parameter lists) while the latter contains the
+ /// actual description of the template's
+ /// contents. ClassTemplateDecl::getTemplatedDecl() retrieves the
+ /// CXXRecordDecl that from a ClassTemplateDecl, while
+ /// getDescribedClassTemplate() retrieves the ClassTemplateDecl from
+ /// a CXXRecordDecl.
+ ClassTemplateDecl *getDescribedClassTemplate();
+
+ void setDescribedClassTemplate(ClassTemplateDecl *Template);
+
/// viewInheritance - Renders and displays an inheritance diagram
/// for this C++ class and all of its base classes (transitively) using
/// GraphViz.
def note_template_decl_here : Note<"template is declared here">;
+def note_member_of_template_here : Note<"member is declared here">;
def err_template_arg_must_be_type : Error<
"template argument for template type parameter must be a type">;
def err_template_arg_must_be_expr : Error<
def err_template_implicit_instantiate_undefined : Error<
"implicit instantiation of undefined template %0">;
+def err_implicit_instantiate_member_undefined : Error<
+ "implicit instantiation of undefined member %0">;
def note_template_class_instantiation_here : Note<
"in instantiation of template class %0 requested here">;
+def note_template_member_class_here : Note<
+ "in instantiation of member class %0 requested here">;
def note_default_arg_instantiation_here : Note<
"in instantiation of default argument for '%0' required here">;
def err_field_instantiates_to_function : Error<
//===----------------------------------------------------------------------===//
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclTemplate.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Expr.h"
#include "clang/Basic/IdentifierTable.h"
UserDeclaredConstructor(false), UserDeclaredCopyConstructor(false),
UserDeclaredCopyAssignment(false), UserDeclaredDestructor(false),
Aggregate(true), PlainOldData(true), Polymorphic(false), Abstract(false),
- Bases(0), NumBases(0), Conversions(DC, DeclarationName()) { }
+ Bases(0), NumBases(0), Conversions(DC, DeclarationName()),
+ TemplateOrInstantiation() { }
CXXRecordDecl *CXXRecordDecl::Create(ASTContext &C, TagKind TK, DeclContext *DC,
SourceLocation L, IdentifierInfo *Id,
Conversions.addOverload(ConvDecl);
}
+CXXRecordDecl *CXXRecordDecl::getInstantiatedFromMemberClass() {
+ if (TemplateOrInstantiation.getInt() == 1)
+ return cast_or_null<CXXRecordDecl>(TemplateOrInstantiation.getPointer());
+ return 0;
+}
+
+void CXXRecordDecl::setDescribedClassTemplate(ClassTemplateDecl *Template) {
+ TemplateOrInstantiation.setInt(0);
+ TemplateOrInstantiation.setPointer(Template);
+}
+
+ClassTemplateDecl *CXXRecordDecl::getDescribedClassTemplate() {
+ if (TemplateOrInstantiation.getInt() == 0)
+ return cast_or_null<ClassTemplateDecl>(
+ TemplateOrInstantiation.getPointer());
+ return 0;
+}
+
CXXMethodDecl *
CXXMethodDecl::Create(ASTContext &C, CXXRecordDecl *RD,
SourceLocation L, DeclarationName N,
/// \brief The kind of template instantiation we are performing
enum {
/// We are instantiating a template declaration. The entity is
- /// the declaration we're instantiation (e.g., a
- /// ClassTemplateSpecializationDecl).
+ /// the declaration we're instantiation (e.g., a CXXRecordDecl).
TemplateInstantiation,
/// We are instantiating a default argument for a template
struct InstantiatingTemplate {
/// \brief Note that we are instantiating a class template.
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
- ClassTemplateSpecializationDecl *Entity,
+ CXXRecordDecl *Entity,
SourceRange InstantiationRange = SourceRange());
/// \brief Note that we are instantiating a default argument in a
unsigned NumTemplateArgs);
bool
- InstantiateBaseSpecifiers(ClassTemplateSpecializationDecl *ClassTemplateSpec,
- ClassTemplateDecl *ClassTemplate);
+ InstantiateBaseSpecifiers(CXXRecordDecl *Instantiation,
+ CXXRecordDecl *Pattern,
+ const TemplateArgument *TemplateArgs,
+ unsigned NumTemplateArgs);
+
+ bool
+ InstantiateClass(SourceLocation PointOfInstantiation,
+ CXXRecordDecl *Instantiation, CXXRecordDecl *Pattern,
+ const TemplateArgument *TemplateArgs,
+ unsigned NumTemplateArgs);
+
bool
InstantiateClassTemplateSpecialization(
ClassTemplateSpecializationDecl *ClassTemplateSpec,
// class itself; this is known as the injected-class-name. For
// purposes of access checking, the injected-class-name is treated
// as if it were a public member name.
- RecordDecl *InjectedClassName
+ CXXRecordDecl *InjectedClassName
= CXXRecordDecl::Create(Context, Record->getTagKind(),
CurContext, Record->getLocation(),
Record->getIdentifier(), Record);
InjectedClassName->setImplicit();
+ InjectedClassName->setAccess(AS_public);
PushOnScopeChains(InjectedClassName, S);
assert(InjectedClassName->isInjectedClassName() &&
"Broken injected-class-name");
Sema::InstantiatingTemplate::
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
- ClassTemplateSpecializationDecl *Entity,
+ CXXRecordDecl *Entity,
SourceRange InstantiationRange)
: SemaRef(SemaRef) {
++Active) {
switch (Active->Kind) {
case ActiveTemplateInstantiation::TemplateInstantiation: {
- ClassTemplateSpecializationDecl *Spec
- = cast<ClassTemplateSpecializationDecl>((Decl*)Active->Entity);
+ unsigned DiagID = diag::note_template_member_class_here;
+ CXXRecordDecl *Record = (CXXRecordDecl *)Active->Entity;
+ if (isa<ClassTemplateSpecializationDecl>(Record))
+ DiagID = diag::note_template_class_instantiation_here;
Diags.Report(FullSourceLoc(Active->PointOfInstantiation, SourceMgr),
- diag::note_template_class_instantiation_here)
- << Context.getTypeDeclType(Spec)
+ DiagID)
+ << Context.getTypeDeclType(Record)
<< Active->InstantiationRange;
break;
}
/// attaches the instantiated base classes to the class template
/// specialization if successful.
bool
-Sema::InstantiateBaseSpecifiers(
- ClassTemplateSpecializationDecl *ClassTemplateSpec,
- ClassTemplateDecl *ClassTemplate) {
+Sema::InstantiateBaseSpecifiers(CXXRecordDecl *Instantiation,
+ CXXRecordDecl *Pattern,
+ const TemplateArgument *TemplateArgs,
+ unsigned NumTemplateArgs) {
bool Invalid = false;
llvm::SmallVector<CXXBaseSpecifier*, 8> InstantiatedBases;
- for (ClassTemplateSpecializationDecl::base_class_iterator
- Base = ClassTemplate->getTemplatedDecl()->bases_begin(),
- BaseEnd = ClassTemplate->getTemplatedDecl()->bases_end();
+ for (ClassTemplateSpecializationDecl::base_class_iterator
+ Base = Pattern->bases_begin(), BaseEnd = Pattern->bases_end();
Base != BaseEnd; ++Base) {
if (!Base->getType()->isDependentType()) {
// FIXME: Allocate via ASTContext
}
QualType BaseType = InstantiateType(Base->getType(),
- ClassTemplateSpec->getTemplateArgs(),
- ClassTemplateSpec->getNumTemplateArgs(),
+ TemplateArgs, NumTemplateArgs,
Base->getSourceRange().getBegin(),
DeclarationName());
if (BaseType.isNull()) {
}
if (CXXBaseSpecifier *InstantiatedBase
- = CheckBaseSpecifier(ClassTemplateSpec,
+ = CheckBaseSpecifier(Instantiation,
Base->getSourceRange(),
Base->isVirtual(),
Base->getAccessSpecifierAsWritten(),
}
if (!Invalid &&
- AttachBaseSpecifiers(ClassTemplateSpec, &InstantiatedBases[0],
+ AttachBaseSpecifiers(Instantiation, &InstantiatedBases[0],
InstantiatedBases.size()))
Invalid = true;
return Invalid;
}
-bool
-Sema::InstantiateClassTemplateSpecialization(
- ClassTemplateSpecializationDecl *ClassTemplateSpec,
- bool ExplicitInstantiation) {
- // Perform the actual instantiation on the canonical declaration.
- ClassTemplateSpec = cast<ClassTemplateSpecializationDecl>(
- Context.getCanonicalDecl(ClassTemplateSpec));
-
- // We can only instantiate something that hasn't already been
- // instantiated or specialized. Fail without any diagnostics: our
- // caller will provide an error message.
- if (ClassTemplateSpec->getSpecializationKind() != TSK_Undeclared)
- return true;
-
- // FIXME: Push this class template instantiation onto the
- // instantiation stack, checking for recursion that exceeds a
- // certain depth.
-
- // FIXME: Perform class template partial specialization to select
- // the best template.
- ClassTemplateDecl *Template = ClassTemplateSpec->getSpecializedTemplate();
-
- RecordDecl *Pattern = cast_or_null<RecordDecl>(
- Template->getTemplatedDecl()->getDefinition(Context));
- if (!Pattern) {
- Diag(ClassTemplateSpec->getLocation(),
- diag::err_template_implicit_instantiate_undefined)
- << Context.getTypeDeclType(ClassTemplateSpec);
- Diag(Template->getTemplatedDecl()->getLocation(),
- diag::note_template_decl_here);
+/// \brief Instantiate the definition of a class from a given pattern.
+///
+/// \param PointOfInstantiation The point of instantiation within the
+/// source code.
+///
+/// \param Instantiation is the declaration whose definition is being
+/// instantiated. This will be either a class template specialization
+/// or a member class of a class template specialization.
+///
+/// \param Pattern is the pattern from which the instantiation
+/// occurs. This will be either the declaration of a class template or
+/// the declaration of a member class of a class template.
+///
+/// \param TemplateArgs The template arguments to be substituted into
+/// the pattern.
+///
+/// \param NumTemplateArgs The number of templates arguments in
+/// TemplateArgs.
+///
+/// \returns true if an error occurred, false otherwise.
+bool
+Sema::InstantiateClass(SourceLocation PointOfInstantiation,
+ CXXRecordDecl *Instantiation, CXXRecordDecl *Pattern,
+ const TemplateArgument *TemplateArgs,
+ unsigned NumTemplateArgs) {
+ bool Invalid = false;
+
+ CXXRecordDecl *PatternDef
+ = cast_or_null<CXXRecordDecl>(Pattern->getDefinition(Context));
+ if (!PatternDef) {
+ if (Pattern == Instantiation->getInstantiatedFromMemberClass()) {
+ Diag(PointOfInstantiation,
+ diag::err_implicit_instantiate_member_undefined)
+ << Context.getTypeDeclType(Instantiation);
+ Diag(Pattern->getLocation(), diag::note_member_of_template_here);
+ } else {
+ Diag(PointOfInstantiation,
+ diag::err_template_implicit_instantiate_undefined)
+ << Context.getTypeDeclType(Instantiation);
+ Diag(Pattern->getLocation(), diag::note_template_decl_here);
+ }
return true;
}
+ Pattern = PatternDef;
- // Note that this is an instantiation.
- ClassTemplateSpec->setSpecializationKind(
- ExplicitInstantiation? TSK_ExplicitInstantiation
- : TSK_ImplicitInstantiation);
-
-
- bool Invalid = false;
-
- InstantiatingTemplate Inst(*this, ClassTemplateSpec->getLocation(),
- ClassTemplateSpec);
+ InstantiatingTemplate Inst(*this, Instantiation->getLocation(),
+ Instantiation);
if (Inst)
return true;
// Enter the scope of this instantiation. We don't use
// PushDeclContext because we don't have a scope.
DeclContext *PreviousContext = CurContext;
- CurContext = ClassTemplateSpec;
+ CurContext = Instantiation;
// Start the definition of this instantiation.
- ClassTemplateSpec->startDefinition();
+ Instantiation->startDefinition();
// Instantiate the base class specifiers.
- if (InstantiateBaseSpecifiers(ClassTemplateSpec, Template))
+ if (InstantiateBaseSpecifiers(Instantiation, Pattern, TemplateArgs,
+ NumTemplateArgs))
Invalid = true;
- // FIXME: Create the injected-class-name for the
- // instantiation. Should this be a typedef or something like it?
-
llvm::SmallVector<DeclTy *, 32> Fields;
for (RecordDecl::decl_iterator Member = Pattern->decls_begin(),
MemberEnd = Pattern->decls_end();
Member != MemberEnd; ++Member) {
- Decl *NewMember = InstantiateDecl(*Member, ClassTemplateSpec,
- ClassTemplateSpec->getTemplateArgs(),
- ClassTemplateSpec->getNumTemplateArgs());
+ Decl *NewMember = InstantiateDecl(*Member, Instantiation,
+ TemplateArgs, NumTemplateArgs);
if (NewMember) {
if (NewMember->isInvalidDecl())
Invalid = true;
}
// Finish checking fields.
- ActOnFields(0, ClassTemplateSpec->getLocation(), ClassTemplateSpec,
+ ActOnFields(0, Instantiation->getLocation(), Instantiation,
&Fields[0], Fields.size(), SourceLocation(), SourceLocation(),
0);
// Add any implicitly-declared members that we might need.
- AddImplicitlyDeclaredMembersToClass(ClassTemplateSpec);
+ AddImplicitlyDeclaredMembersToClass(Instantiation);
// Exit the scope of this instantiation.
CurContext = PreviousContext;
return Invalid;
}
+bool
+Sema::InstantiateClassTemplateSpecialization(
+ ClassTemplateSpecializationDecl *ClassTemplateSpec,
+ bool ExplicitInstantiation) {
+ // Perform the actual instantiation on the canonical declaration.
+ ClassTemplateSpec = cast<ClassTemplateSpecializationDecl>(
+ Context.getCanonicalDecl(ClassTemplateSpec));
+
+ // We can only instantiate something that hasn't already been
+ // instantiated or specialized. Fail without any diagnostics: our
+ // caller will provide an error message.
+ if (ClassTemplateSpec->getSpecializationKind() != TSK_Undeclared)
+ return true;
+
+ // FIXME: Push this class template instantiation onto the
+ // instantiation stack, checking for recursion that exceeds a
+ // certain depth.
+
+ // FIXME: Perform class template partial specialization to select
+ // the best template.
+ ClassTemplateDecl *Template = ClassTemplateSpec->getSpecializedTemplate();
+
+ CXXRecordDecl *Pattern = Template->getTemplatedDecl();
+
+ // Note that this is an instantiation.
+ ClassTemplateSpec->setSpecializationKind(
+ ExplicitInstantiation? TSK_ExplicitInstantiation
+ : TSK_ImplicitInstantiation);
+
+ return InstantiateClass(ClassTemplateSpec->getLocation(),
+ ClassTemplateSpec, Pattern,
+ ClassTemplateSpec->getTemplateArgs(),
+ ClassTemplateSpec->getNumTemplateArgs());
+}
+
/// \brief Instantiate a sequence of nested-name-specifiers into a
/// scope specifier.
CXXScopeSpec
Decl *VisitStaticAssertDecl(StaticAssertDecl *D);
Decl *VisitEnumDecl(EnumDecl *D);
Decl *VisitEnumConstantDecl(EnumConstantDecl *D);
+ Decl *VisitCXXRecordDecl(CXXRecordDecl *D);
Decl *VisitCXXMethodDecl(CXXMethodDecl *D);
Decl *VisitCXXConstructorDecl(CXXConstructorDecl *D);
Decl *VisitCXXDestructorDecl(CXXDestructorDecl *D);
return 0;
}
+Decl *TemplateDeclInstantiator::VisitCXXRecordDecl(CXXRecordDecl *D) {
+ CXXRecordDecl *PrevDecl = 0;
+ if (D->isInjectedClassName())
+ PrevDecl = cast<CXXRecordDecl>(Owner);
+
+ CXXRecordDecl *Record
+ = CXXRecordDecl::Create(SemaRef.Context, D->getTagKind(), Owner,
+ D->getLocation(), D->getIdentifier(), PrevDecl);
+ Record->setImplicit(D->isImplicit());
+ Record->setAccess(D->getAccess());
+
+ if (!D->isInjectedClassName())
+ Record->setInstantiationOfMemberClass(D);
+
+ Owner->addDecl(Record);
+ return Record;
+}
+
Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D) {
// Only handle actual methods; we'll deal with constructors,
// destructors, etc. separately.
if (!T->isIncompleteType())
return false;
- // If we have a class template specialization, try to instantiate
- // it.
- if (const RecordType *Record = T->getAsRecordType())
+ // If we have a class template specialization or a class member of a
+ // class template specialization, try to instantiate it.
+ if (const RecordType *Record = T->getAsRecordType()) {
if (ClassTemplateSpecializationDecl *ClassTemplateSpec
- = dyn_cast<ClassTemplateSpecializationDecl>(Record->getDecl()))
+ = dyn_cast<ClassTemplateSpecializationDecl>(Record->getDecl())) {
if (ClassTemplateSpec->getSpecializationKind() == TSK_Undeclared) {
// Update the class template specialization's location to
// refer to the point of instantiation.
return InstantiateClassTemplateSpecialization(ClassTemplateSpec,
/*ExplicitInstantiation=*/false);
}
-
+ } else if (CXXRecordDecl *Rec
+ = dyn_cast<CXXRecordDecl>(Record->getDecl())) {
+ if (CXXRecordDecl *Pattern = Rec->getInstantiatedFromMemberClass()) {
+ // Find the class template specialization that surrounds this
+ // member class.
+ ClassTemplateSpecializationDecl *Spec = 0;
+ for (DeclContext *Parent = Rec->getDeclContext();
+ Parent && !Spec; Parent = Parent->getParent())
+ Spec = dyn_cast<ClassTemplateSpecializationDecl>(Parent);
+ assert(Spec && "Not a member of a class template specialization?");
+ return InstantiateClass(Loc, Rec, Pattern,
+ Spec->getTemplateArgs(),
+ Spec->getNumTemplateArgs());
+ }
+ }
+ }
if (PrintType.isNull())
PrintType = T;
--- /dev/null
+// RUN: clang-cc -fsyntax-only -verify %s
+
+template<typename T>
+class X {
+public:
+ struct C { T &foo(); };
+
+ struct D {
+ struct E { T &bar(); };
+ struct F; // expected-note{{member is declared here}}
+ };
+};
+
+X<int>::C *c1;
+X<float>::C *c2;
+
+X<int>::X *xi;
+X<float>::X *xf;
+
+void test_naming() {
+ c1 = c2; // expected-error{{incompatible type assigning 'X<float>::C *', expected 'X<int>::C *'}}
+ xi = xf; // expected-error{{incompatible type assigning}}
+ // FIXME: error above doesn't print the type X<int>::X cleanly!
+}
+
+void test_instantiation(X<double>::C *x,
+ X<float>::D::E *e,
+ X<float>::D::F *f) {
+ double &dr = x->foo();
+ float &fr = e->bar();
+ f->foo(); // expected-error{{implicit instantiation of undefined member 'struct X<float>::D::F'}}
+
+}