// CPPSymTableSpecializeClass.cpp: Templated class specialization
#include "CPPSymTable.h"
#include "CPPScope.h"
#include "CPPTypeTemplated.h"
#include "CPPTypeClass.h"
#include "CPPDecl.h"
#include "JLCast.h"
#include "CPPParserSym.h"
#include "TokenStream.h"
#include "JLTokenBuffer.h"
#include "TemplateArgumentList.h"
#include "TemplateParameterList.h"
#include "CPPScopeContainers.h"
////////////////////////////////////////////////////////////////////
// LookupClassSpecialization: Given the name of a class template
// and a list of template arguments, create a specialization
// if one does not exist.
//
// Inputs:
// const JLStr& className name of the class template
// CPPScopeRefI containingScope
// The scope in which the class
// template is declared
// TemplateArgumentListRef argList list of actual template
// arguments (types or values)
// const ANTLRTokenPtr& errTok Token for error reporting
// Outputs:
// JLStr& specializationNameReturn
// If successful, filled with the
// decorated template class name
// CPPTypeRefI& specializationTypeReturn
// If successful, points to the
// type entry for the class specialization.
// Return:
// bool true on success, false if something was wrong with
// the specialization, in which case an error has
// already been reported.
//
// Note: A class specialization name contains the types of
// the arguments, for example:
// template declaration invocation name
// -------------------- ---------- ----
// template <class T> class A{} A<int> A<int>
// template <class T> class A{} A<char***> A<char***>
//
// Note: If one or more of the template arguments are themselves
// template placeholders (unspecified values or types), then return
// type, if successful, will be unspecialized or partially-specialized.
// For example:
// template <class T> class A { };
// template <class T> class A<T*> { };
// template <class X> class B {
// A<X> ax; // type of ax is unspecialized template "A"
// A<X*> ax; // type of ax is partially-specialized template "A<$0*>"
// };
// When template "B" is fully-specialized, then it will be reparsed
// and ax will be given a fully-specialized type.
////////////////////////////////////////////////////////////////////
bool
CPPSymTable::LookupClassSpecialization(
const JLStr& className,
CPPScopeRefI containingScope,
TemplateArgumentListRefI templateArgumentList,
const ANTLRTokenPtr& errTok,
JLStr& specializationNameReturn,
CPPTypeRefI& specializationTypeReturn
)
{
// TODO: if the containing scope is a template placeholder scope,
// then don't even bother -- return another template placeholder.
// Look up the primary template declaration
CPPDeclRefI primaryDecl = GetClassPrimaryTemplate(className, containingScope);
assert(primaryDecl->GetType()->GetFlavor() == CPPType::Templated);
// Get the primary template parameter list
TemplateParameterListRefI primaryTemplateParameterList =
STATIC_CAST(CPPTypeTemplated*, primaryDecl->GetType().get())->GetParameterList();
// TODO: Make sure that the given template argument list matches the
// primary template parameter list, other than those that
// have default arguments.
// TODO: Using the primary template, fill in the default
// template arguments for any missing arguments.
// This may require reparsing of the default argument values.
// Given the completed argument list, create the
// specialization name for the class and scope
specializationNameReturn = FormatClassSpecializationName(
className,
templateArgumentList
);
// Given this name, if there already exists a
// specialization for the template under that name,
// then return it. Note that this may return types that are
// themselves unspecialized templates, for example:
// template <class T> class A { };
// template <class X> class B {
// A<X> ax; // ax is still unspecialized
// };
//
// Note that this also prevents recursive
// instantiation of template classes in cases like:
// template <class T> class A { A<T>* foo(); };
{
CPPScope::DeclIterator iter;
CPPDeclRefI tmpDecl =
containingScope->FindDeclByName(specializationNameReturn, iter);
if (tmpDecl.get() != 0)
{
specializationTypeReturn = tmpDecl->GetType();
return true;
}
}
// Perform type deduction to choose the correct
// class template partial specialization.
// TODO: This should also get the results of the type deduction.
// TODO: This should report an error for ambiguous specializations.
CPPTypeRefI partialSpecializationType;
CPPDeclRefI partialSpecializationDecl;
TemplateParameterListRefI partialSpecializationParameterList;
if (!ChooseClassPartialSpecialization(
className,
containingScope,
templateArgumentList,
partialSpecializationType,
partialSpecializationDecl,
partialSpecializationParameterList
)
)
{
return false;
}
assert(partialSpecializationType->GetFlavor() == CPPType::Templated);
//////////////////////////////////////////////////////////////////////
// Save symbol table state
//////////////////////////////////////////////////////////////////////
CPPScopeState scopeState = SaveScope();
//////////////////////////////////////////////////////////////////////
// Create the right symbol table state for the template
//////////////////////////////////////////////////////////////////////
// Push a resolved global scope on the stack to avoid searching
// any scopes already on the stack.
EnterResolvedGlobalScope();
// TODO: Push the full scope context of the template declaration
// Push a local scope for the template arguments
EnterLocalScope();
// Enter the actual template arguments into the local scope
// TODO: use the deduced arguments instead of the actual arguments
assert(templateArgumentList->size() == partialSpecializationParameterList->size());
TemplateParameterList::iterator parmListIter = partialSpecializationParameterList->begin();
for (
TemplateArgumentList::iterator argListIter = templateArgumentList->begin();
argListIter != templateArgumentList->end();
argListIter++, parmListIter++
)
{
CPPDecl* newDecl;
if ((*argListIter)->GetFlavor() == TemplateArgument::Type)
{
newDecl = new CPPDeclType(
(*parmListIter)->GetName(),
CPPConst::asPUBLIC,
(*argListIter)->GetType(),
false // type, not typedef
);
} else {
newDecl = new CPPDeclObject(
(*parmListIter)->GetName(),
CPPConst::asPUBLIC,
(*parmListIter)->GetType(),
CPPConst::scNONE,
CPPConst::dmNONE,
(*argListIter)->GetValue(),
StrListRefI()
);
}
GetCurrentScope()->AddDecl(newDecl);
}
// Set up the current access of the containing scope to be equal
// to the access of the template declaration
CPPConst::AccessSpecifier saveAccess = containingScope->GetCurrentAccess();
containingScope->SetCurrentAccess(partialSpecializationDecl->GetAccessSpecifier());
// Push the containing scope to get results of the
// template specialization re-parsing
PushScope(containingScope);
// Set up a declaration-name-substituation, so that when the
// specialized class declaration is entered, it shows up
// under the specialization name instead of the raw class name.
DeclNameSubst saveDeclNameSubst = declNameSubst;
declNameSubst.first = className;
declNameSubst.second = specializationNameReturn;
//////////////////////////////////////////////////////////////////////
// Create a new parser using the current symbol table
//////////////////////////////////////////////////////////////////////
// Create a token stream/buffer from the template tokens
TokenListStream templateTokenStream(
*STATIC_CAST(CPPTypeTemplated*, partialSpecializationType.get())->GetTokens()
);
JLTokenBuffer templateTokenBuffer(
&templateTokenStream,
CPPParser::ConstLLK
);
CPPParserSym subParser(
&templateTokenBuffer,
this,
parser->GetOptions()
);
subParser.init();
//////////////////////////////////////////////////////////////////////
// Do the processing of the specialized template
//////////////////////////////////////////////////////////////////////
// Parse the class body.
// Use a special rule that catches errors with exceptions
subParser.template_specialization_reparse();
//////////////////////////////////////////////////////////////////////
// Restore the parser state
//////////////////////////////////////////////////////////////////////
// Pop the scope to the previous level
RestoreScope(scopeState);
// Find the specialization scope and type
{
CPPScope::DeclIterator declIter;
CPPDeclRefI tmpDecl;
tmpDecl = containingScope->FindDeclByName(specializationNameReturn, declIter);
if (tmpDecl.get() == 0)
{
// something wrong with the reparse
} else {
specializationTypeReturn = tmpDecl->GetType();
assert(specializationTypeReturn.get() != 0);
}
}
// Restore the access of the containing scope
containingScope->SetCurrentAccess(saveAccess);
// Restore the declaration-name-substituation to its previous state
declNameSubst = saveDeclNameSubst;
return true;
}
////////////////////////////////////////////////////////////////////
// ChooseClassPartialSpecialization: Given the name of a class template
// and a list of template arguments, choose the partial specialization
// that most closely matches the arguments (or choose the primary template
// declaration if that is closest).
//
// Inputs:
// const JLStr& className name of the class template
// CPPScopeRefI containingScope
// The scope in which the class
// template is declared
// TemplateParameterListRefI primaryTemplateParameterList
// The template parameter list of
// the primary class template declaration.
// TemplateArgumentListRef templateArgumentList
// list of actual template
// arguments (types or values)
// Outputs:
// CPPTypeRefI& partialSpecializationTypeReturn
// The type of the selected partial
// specialization.
// CPPTypeRefI& partialSpecializationDeclReturn
// The partial specialization declaration
// Return:
// bool true on success, false if something was wrong with
// the specialization, in which case an error has
// already been reported.
////////////////////////////////////////////////////////////////////
bool
CPPSymTable::ChooseClassPartialSpecialization(
const JLStr& className,
CPPScopeRefI containingScope,
TemplateArgumentListRefI templateArgumentList,
CPPTypeRefI& partialSpecializationTypeReturn,
CPPDeclRefI& partialSpecializationDeclReturn,
TemplateParameterListRefI& partialSpecializationParameterListReturn
)
{
// TODO: almost everything
// For now, just choose the primary template and ignore all else
CPPScope::DeclIterator declIter;
for (
partialSpecializationDeclReturn = containingScope->FindDeclByName(className, declIter);
partialSpecializationDeclReturn.get() != 0;
partialSpecializationDeclReturn = containingScope->NextDecl(declIter)
)
{
// If there is a template under a name, then there should be no
// non-templated decl under that name.
assert(partialSpecializationDeclReturn->GetType()->GetFlavor() == CPPType::Templated);
// For now, just return primary template decl
if (STATIC_CAST(CPPTypeTemplated*, partialSpecializationDeclReturn->GetType().get())->IsPrimary())
{
partialSpecializationTypeReturn = partialSpecializationDeclReturn->GetType();
partialSpecializationParameterListReturn =
STATIC_CAST(CPPTypeTemplated*, partialSpecializationTypeReturn.get())->
GetParameterList();
return true;
}
}
assert(0);
return false;
// Given the completed argument list, select a list of candidates
// from the primary template and any partial specializations.
// Given the list of candidates, determine the partial ordering
// and find the "most specialized" candidate. If no candidate is
// most specialized then report an ambiguity error.
// Given the template argument list, construct a name for the
// (perhaps partially) specialized template class type.
// If the specialization name exists already in the containing
// scope, then return the associated scope and type.
// If the specialization name does not already exist in the containing
// scope, then create a (perhaps partial) specialization type and scope
// by filling in the template placeholders and reparsing the template class
// definition.
}
////////////////////////////////////////////////////////////////////
// GetClassPrimaryTemplate: Given the name of a class template,
// lookup the primary template declaration.
//
// Inputs:
// const JLStr& className name of the class template
// CPPScopeRefI containingScope
// The scope in which the class
// primary template is declared
// Return:
// CPPDeclRefI declaration of the primary template
// Note: asserts if no declaration is found
////////////////////////////////////////////////////////////////////
CPPDeclRefI
CPPSymTable::GetClassPrimaryTemplate(
const JLStr& className,
CPPScopeRefI containingScope
)
{
// Find the templated declaration under the given name that is
// a primary template declaration.
CPPScope::DeclIterator iter;
for (
CPPDeclRefI tmpDecl = containingScope->FindDeclByName(className, iter);
tmpDecl.get() != 0;
tmpDecl = containingScope->NextDecl(iter)
)
{
if (
tmpDecl->GetType()->GetFlavor() == CPPType::Templated &&
STATIC_CAST(CPPTypeTemplated*, tmpDecl->GetType().get())->IsPrimary()
)
{
return tmpDecl;
}
if (!NameIsDecoratedName(className, containingScope->GetName()))
{
// Found a hiding declaration
break;
}
}
// TODO: assert here is bad
assert(0);
return CPPDeclRefI();
}
///////////////////////////////////////////////////////////////////
// FormatClassSpecializationName: Given the name of a templated
// class, and a non-empty list of specialization arguments,
// sythesize a name for the decl/scope that is unique compared with
// other possible specializations.
//
// This overload of the method is for template partial specializations.
//
// This constructs a name composed of the raw name combined
// with the formatted type names and values of the template arguments, with
// the exception that the template placeholder types are formatted to
// indicate their position in the template parameter list. Some examples:
//
// name template-parm-list specialization result
// ==== ================== ============== =====================
// A <class M, class N> <M,N*> A<$0,$1*>
// A <class M> <M,M*> A<$0*,$1*>
// A <> <int,char**> A<int,char**>
//
// For nested template declarations, the index variables have an additional
// number indicating their template nesting level in addition to parameter
// position. For example:
// template <class T1> class A {
// template <class T2> class B {};
// };
// Yields A<$0>::B<$0_1> for the name of the inner class template.
//
// Inputs:
// const JLStr& name
// The name of the declaration
// TemplateArgumentListRefI specializationArgList
// List of template specialization
// arguments. May be empty
///////////////////////////////////////////////////////////////////
JLStr
CPPSymTable::FormatClassSpecializationName(
const JLStr& name,
TemplateArgumentListRefI specializationArgList
)
{
JLStr specializedName = name;
specializedName += "<";
assert(specializationArgList->size() != 0);
for (
TemplateArgumentList::iterator iter = specializationArgList->begin();
iter != specializationArgList->end();
iter++
)
{
if (iter != specializationArgList->begin())
{
specializedName += ",";
}
specializedName += (*iter)->Format();
}
specializedName += ">";
return specializedName;
}
///////////////////////////////////////////////////////////////////
// FormatClassSpecializationName: Given the name of a primary templated
// class, and the formal template parameters,
// sythesize a name for the decl/scope that is unique compared with
// other possible specializations.
//
// This constructs a name composed of the raw name combined
// with the template placeholder types formatted to
// indicate their position in the template parameter list. Some examples:
//
// name template-parm-list result
// ==== ================== ===========
// A <class M, class N> A<$0,$1>
// A <int I, class N> A<#0,$1>
//
// Inputs:
// const JLStr& name
// The name of the declaration
// TemplateParameterListRefI templateParameterList
// List of formal template parameters
///////////////////////////////////////////////////////////////////
JLStr
CPPSymTable::FormatClassSpecializationName(
const JLStr& name,
TemplateParameterListRefI templateParameterList
)
{
JLStr specializedName = name;
specializedName += "<";
for (
TemplateParameterList::iterator iter = templateParameterList->begin();
iter != templateParameterList->end();
iter++
)
{
if (iter != templateParameterList->begin())
{
specializedName += ",";
}
specializedName += (*iter)->Format();
}
specializedName += ">";
return specializedName;
}