зеркало из https://github.com/microsoft/clang-1.git
Template instantiation for static data members that are defined out-of-line.
Note that this also fixes a bug that affects non-template code, where we were not treating out-of-line static data members are "file-scope" variables, and therefore not checking their initializers. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@77002 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
0eb7cff82f
Коммит
7caa6825f4
|
@ -136,6 +136,32 @@ class ASTContext {
|
|||
/// wasting space in the Decl class.
|
||||
llvm::DenseMap<const Decl*, Attr*> DeclAttrs;
|
||||
|
||||
/// \brief Keeps track of the static data member templates from which
|
||||
/// static data members of class template specializations were instantiated.
|
||||
///
|
||||
/// This data structure stores the mapping from instantiations of static
|
||||
/// data members to the static data member representations within the
|
||||
/// class template from which they were instantiated.
|
||||
///
|
||||
/// Given the following example:
|
||||
///
|
||||
/// \code
|
||||
/// template<typename T>
|
||||
/// struct X {
|
||||
/// static T value;
|
||||
/// };
|
||||
///
|
||||
/// template<typename T>
|
||||
/// T X<T>::value = T(17);
|
||||
///
|
||||
/// int *x = &X<int>::value;
|
||||
/// \endcode
|
||||
///
|
||||
/// This mapping will contain an entry that maps from the VarDecl for
|
||||
/// X<int>::value to the corresponding VarDecl for X<T>::value (within the
|
||||
/// class template X).
|
||||
llvm::DenseMap<VarDecl *, VarDecl *> InstantiatedFromStaticDataMember;
|
||||
|
||||
TranslationUnitDecl *TUDecl;
|
||||
|
||||
/// SourceMgr - The associated SourceManager object.
|
||||
|
@ -193,6 +219,15 @@ public:
|
|||
/// \brief Erase the attributes corresponding to the given declaration.
|
||||
void eraseDeclAttrs(const Decl *D) { DeclAttrs.erase(D); }
|
||||
|
||||
/// \brief If this variable is an instantiated static data member of a
|
||||
/// class template specialization, returns the templated static data member
|
||||
/// from which it was instantiated.
|
||||
VarDecl *getInstantiatedFromStaticDataMember(VarDecl *Var);
|
||||
|
||||
/// \brief Note that the static data member \p Inst is an instantiation of
|
||||
/// the static data member template \p Tmpl of a class template.
|
||||
void setInstantiatedFromStaticDataMember(VarDecl *Inst, VarDecl *Tmpl);
|
||||
|
||||
TranslationUnitDecl *getTranslationUnitDecl() const { return TUDecl; }
|
||||
|
||||
const char *getCommentForDecl(const Decl *D);
|
||||
|
|
|
@ -477,6 +477,11 @@ public:
|
|||
return getDeclContext()->isRecord();
|
||||
}
|
||||
|
||||
/// \brief If this variable is an instantiated static data member of a
|
||||
/// class template specialization, returns the templated static data member
|
||||
/// from which it was instantiated.
|
||||
VarDecl *getInstantiatedFromStaticDataMember();
|
||||
|
||||
/// isFileVarDecl - Returns true for file scoped variable declaration.
|
||||
bool isFileVarDecl() const {
|
||||
if (getKind() != Decl::Var)
|
||||
|
@ -486,6 +491,9 @@ public:
|
|||
if (isa<TranslationUnitDecl>(Ctx) || isa<NamespaceDecl>(Ctx) )
|
||||
return true;
|
||||
}
|
||||
if (isStaticDataMember() && isOutOfLine())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -888,6 +888,8 @@ def note_template_member_function_here : Note<
|
|||
"in instantiation of member function %q0 requested here">;
|
||||
def note_function_template_spec_here : Note<
|
||||
"in instantiation of function template specialization %q0 requested here">;
|
||||
def note_template_static_data_member_def_here : Note<
|
||||
"in instantiation of static data member %q0 requested here">;
|
||||
|
||||
def note_default_arg_instantiation_here : Note<
|
||||
"in instantiation of default argument for '%0' required here">;
|
||||
|
|
|
@ -220,6 +220,25 @@ void ASTContext::InitBuiltinTypes() {
|
|||
InitBuiltinType(NullPtrTy, BuiltinType::NullPtr);
|
||||
}
|
||||
|
||||
VarDecl *ASTContext::getInstantiatedFromStaticDataMember(VarDecl *Var) {
|
||||
assert(Var->isStaticDataMember() && "Not a static data member");
|
||||
llvm::DenseMap<VarDecl *, VarDecl *>::iterator Pos
|
||||
= InstantiatedFromStaticDataMember.find(Var);
|
||||
if (Pos == InstantiatedFromStaticDataMember.end())
|
||||
return 0;
|
||||
|
||||
return Pos->second;
|
||||
}
|
||||
|
||||
void
|
||||
ASTContext::setInstantiatedFromStaticDataMember(VarDecl *Inst, VarDecl *Tmpl) {
|
||||
assert(Inst->isStaticDataMember() && "Not a static data member");
|
||||
assert(Tmpl->isStaticDataMember() && "Not a static data member");
|
||||
assert(!InstantiatedFromStaticDataMember[Inst] &&
|
||||
"Already noted what static data member was instantiated from");
|
||||
InstantiatedFromStaticDataMember[Inst] = Tmpl;
|
||||
}
|
||||
|
||||
namespace {
|
||||
class BeforeInTranslationUnit
|
||||
: std::binary_function<SourceRange, SourceRange, bool> {
|
||||
|
|
|
@ -341,6 +341,10 @@ SourceRange VarDecl::getSourceRange() const {
|
|||
return SourceRange(getLocation(), getLocation());
|
||||
}
|
||||
|
||||
VarDecl *VarDecl::getInstantiatedFromStaticDataMember() {
|
||||
return getASTContext().getInstantiatedFromStaticDataMember(this);
|
||||
}
|
||||
|
||||
bool VarDecl::isTentativeDefinition(ASTContext &Context) const {
|
||||
if (!isFileVarDecl() || Context.getLangOptions().CPlusPlus)
|
||||
return false;
|
||||
|
|
|
@ -2732,7 +2732,10 @@ public:
|
|||
void InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
|
||||
FunctionDecl *Function,
|
||||
bool Recursive = false);
|
||||
void InstantiateVariableDefinition(VarDecl *Var);
|
||||
void InstantiateStaticDataMemberDefinition(
|
||||
SourceLocation PointOfInstantiation,
|
||||
VarDecl *Var,
|
||||
bool Recursive = false);
|
||||
|
||||
NamedDecl *InstantiateCurrentDeclRef(NamedDecl *D);
|
||||
|
||||
|
|
|
@ -5780,10 +5780,17 @@ void Sema::MarkDeclarationReferenced(SourceLocation Loc, Decl *D) {
|
|||
}
|
||||
|
||||
if (VarDecl *Var = dyn_cast<VarDecl>(D)) {
|
||||
(void)Var;
|
||||
// FIXME: implicit template instantiation
|
||||
// Implicit instantiation of static data members of class templates.
|
||||
// FIXME: distinguish between implicit instantiations (which we need to
|
||||
// actually instantiate) and explicit specializations.
|
||||
if (Var->isStaticDataMember() &&
|
||||
Var->getInstantiatedFromStaticDataMember())
|
||||
PendingImplicitInstantiations.push_back(std::make_pair(Var, Loc));
|
||||
|
||||
// FIXME: keep track of references to static data?
|
||||
|
||||
D->setUsed(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -189,8 +189,7 @@ void Sema::PrintInstantiationStack() {
|
|||
DiagID)
|
||||
<< Context.getTypeDeclType(Record)
|
||||
<< Active->InstantiationRange;
|
||||
} else {
|
||||
FunctionDecl *Function = cast<FunctionDecl>(D);
|
||||
} else if (FunctionDecl *Function = dyn_cast<FunctionDecl>(D)) {
|
||||
unsigned DiagID;
|
||||
if (Function->getPrimaryTemplate())
|
||||
DiagID = diag::note_function_template_spec_here;
|
||||
|
@ -200,6 +199,11 @@ void Sema::PrintInstantiationStack() {
|
|||
DiagID)
|
||||
<< Function
|
||||
<< Active->InstantiationRange;
|
||||
} else {
|
||||
Diags.Report(FullSourceLoc(Active->PointOfInstantiation, SourceMgr),
|
||||
diag::note_template_static_data_member_def_here)
|
||||
<< cast<VarDecl>(D)
|
||||
<< Active->InstantiationRange;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -1059,9 +1063,8 @@ Sema::InstantiateClassMembers(SourceLocation PointOfInstantiation,
|
|||
if (!Function->getBody())
|
||||
InstantiateFunctionDefinition(PointOfInstantiation, Function);
|
||||
} else if (VarDecl *Var = dyn_cast<VarDecl>(*D)) {
|
||||
const VarDecl *Def = 0;
|
||||
if (!Var->getDefinition(Def))
|
||||
InstantiateVariableDefinition(Var);
|
||||
if (Var->isStaticDataMember())
|
||||
InstantiateStaticDataMemberDefinition(PointOfInstantiation, Var);
|
||||
} else if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(*D)) {
|
||||
if (!Record->isInjectedClassName() && !Record->getDefinition(Context)) {
|
||||
assert(Record->getInstantiatedFromMemberClass() &&
|
||||
|
|
|
@ -120,12 +120,24 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) {
|
|||
Var->setCXXDirectInitializer(D->hasCXXDirectInitializer());
|
||||
Var->setDeclaredInCondition(D->isDeclaredInCondition());
|
||||
|
||||
// If we are instantiating a static data member defined
|
||||
// out-of-line, the instantiation will have the same lexical
|
||||
// context (which will be a namespace scope) as the template.
|
||||
if (D->isOutOfLine())
|
||||
Var->setLexicalDeclContext(D->getLexicalDeclContext());
|
||||
|
||||
// FIXME: In theory, we could have a previous declaration for variables that
|
||||
// are not static data members.
|
||||
bool Redeclaration = false;
|
||||
SemaRef.CheckVariableDeclaration(Var, 0, Redeclaration);
|
||||
Owner->addDecl(Var);
|
||||
|
||||
|
||||
if (D->isOutOfLine()) {
|
||||
D->getLexicalDeclContext()->addDecl(Var);
|
||||
Owner->makeDeclVisibleInContext(Var);
|
||||
} else {
|
||||
Owner->addDecl(Var);
|
||||
}
|
||||
|
||||
if (D->getInit()) {
|
||||
OwningExprResult Init
|
||||
= SemaRef.InstantiateExpr(D->getInit(), TemplateArgs);
|
||||
|
@ -138,6 +150,11 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) {
|
|||
// FIXME: Call ActOnUninitializedDecl? (Not always)
|
||||
}
|
||||
|
||||
// Link instantiations of static data members back to the template from
|
||||
// which they were instantiated.
|
||||
if (Var->isStaticDataMember())
|
||||
SemaRef.Context.setInstantiatedFromStaticDataMember(Var, D);
|
||||
|
||||
return Var;
|
||||
}
|
||||
|
||||
|
@ -374,6 +391,12 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D) {
|
|||
D->isInline());
|
||||
Method->setInstantiationOfMemberFunction(D);
|
||||
|
||||
// If we are instantiating a member function defined
|
||||
// out-of-line, the instantiation will have the same lexical
|
||||
// context (which will be a namespace scope) as the template.
|
||||
if (D->isOutOfLine())
|
||||
Method->setLexicalDeclContext(D->getLexicalDeclContext());
|
||||
|
||||
// Attach the parameters
|
||||
for (unsigned P = 0; P < Params.size(); ++P)
|
||||
Params[P]->setOwningFunction(Method);
|
||||
|
@ -773,9 +796,103 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
|
|||
/// \brief Instantiate the definition of the given variable from its
|
||||
/// template.
|
||||
///
|
||||
/// \param Var the already-instantiated declaration of a variable.
|
||||
void Sema::InstantiateVariableDefinition(VarDecl *Var) {
|
||||
// FIXME: Implement this!
|
||||
/// \param PointOfInstantiation the point at which the instantiation was
|
||||
/// required. Note that this is not precisely a "point of instantiation"
|
||||
/// for the function, but it's close.
|
||||
///
|
||||
/// \param Var the already-instantiated declaration of a static member
|
||||
/// variable of a class template specialization.
|
||||
///
|
||||
/// \param Recursive if true, recursively instantiates any functions that
|
||||
/// are required by this instantiation.
|
||||
void Sema::InstantiateStaticDataMemberDefinition(
|
||||
SourceLocation PointOfInstantiation,
|
||||
VarDecl *Var,
|
||||
bool Recursive) {
|
||||
if (Var->isInvalidDecl())
|
||||
return;
|
||||
|
||||
// Find the out-of-line definition of this static data member.
|
||||
// FIXME: Do we have to look for specializations separately?
|
||||
VarDecl *Def = Var->getInstantiatedFromStaticDataMember();
|
||||
bool FoundOutOfLineDef = false;
|
||||
assert(Def && "This data member was not instantiated from a template?");
|
||||
assert(Def->isStaticDataMember() && "Not a static data member?");
|
||||
for (VarDecl::redecl_iterator RD = Def->redecls_begin(),
|
||||
RDEnd = Def->redecls_end();
|
||||
RD != RDEnd; ++RD) {
|
||||
if (RD->getLexicalDeclContext()->isFileContext()) {
|
||||
Def = *RD;
|
||||
FoundOutOfLineDef = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!FoundOutOfLineDef) {
|
||||
// We did not find an out-of-line definition of this static data member,
|
||||
// so we won't perform any instantiation. Rather, we rely on the user to
|
||||
// instantiate this definition (or provide a specialization for it) in
|
||||
// another translation unit.
|
||||
return;
|
||||
}
|
||||
|
||||
InstantiatingTemplate Inst(*this, PointOfInstantiation, Var);
|
||||
if (Inst)
|
||||
return;
|
||||
|
||||
// If we're performing recursive template instantiation, create our own
|
||||
// queue of pending implicit instantiations that we will instantiate later,
|
||||
// while we're still within our own instantiation context.
|
||||
std::deque<PendingImplicitInstantiation> SavedPendingImplicitInstantiations;
|
||||
if (Recursive)
|
||||
PendingImplicitInstantiations.swap(SavedPendingImplicitInstantiations);
|
||||
|
||||
// Enter the scope of this instantiation. We don't use
|
||||
// PushDeclContext because we don't have a scope.
|
||||
DeclContext *PreviousContext = CurContext;
|
||||
CurContext = Var->getDeclContext();
|
||||
|
||||
#if 0
|
||||
// Instantiate the initializer of this static data member.
|
||||
OwningExprResult Init
|
||||
= InstantiateExpr(Def->getInit(), getTemplateInstantiationArgs(Var));
|
||||
if (Init.isInvalid()) {
|
||||
// If instantiation of the initializer failed, mark the declaration invalid
|
||||
// and don't instantiate anything else that was triggered by this
|
||||
// instantiation.
|
||||
Var->setInvalidDecl();
|
||||
|
||||
// Restore the set of pending implicit instantiations.
|
||||
PendingImplicitInstantiations.swap(SavedPendingImplicitInstantiations);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Type-check the initializer.
|
||||
if (Init.get())
|
||||
AddInitializerToDecl(DeclPtrTy::make(Var), move(Init),
|
||||
Def->hasCXXDirectInitializer());
|
||||
else
|
||||
ActOnUninitializedDecl(DeclPtrTy::make(Var), false);
|
||||
#else
|
||||
Var = cast_or_null<VarDecl>(InstantiateDecl(Def, Var->getDeclContext(),
|
||||
getTemplateInstantiationArgs(Var)));
|
||||
#endif
|
||||
|
||||
CurContext = PreviousContext;
|
||||
|
||||
if (Var) {
|
||||
DeclGroupRef DG(Var);
|
||||
Consumer.HandleTopLevelDecl(DG);
|
||||
}
|
||||
|
||||
if (Recursive) {
|
||||
// Instantiate any pending implicit instantiations found during the
|
||||
// instantiation of this template.
|
||||
PerformPendingImplicitInstantiations();
|
||||
|
||||
// Restore the set of pending implicit instantiations.
|
||||
PendingImplicitInstantiations.swap(SavedPendingImplicitInstantiations);
|
||||
}
|
||||
}
|
||||
|
||||
static bool isInstantiationOf(ASTContext &Ctx, NamedDecl *D, Decl *Other) {
|
||||
|
@ -794,6 +911,11 @@ static bool isInstantiationOf(ASTContext &Ctx, NamedDecl *D, Decl *Other) {
|
|||
return Enum->getInstantiatedFromMemberEnum()->getCanonicalDecl()
|
||||
== D->getCanonicalDecl();
|
||||
|
||||
if (VarDecl *Var = dyn_cast<VarDecl>(Other))
|
||||
if (Var->isStaticDataMember())
|
||||
return Var->getInstantiatedFromStaticDataMember()->getCanonicalDecl()
|
||||
== D->getCanonicalDecl();
|
||||
|
||||
// FIXME: How can we find instantiations of anonymous unions?
|
||||
|
||||
return D->getDeclName() && isa<NamedDecl>(Other) &&
|
||||
|
@ -912,10 +1034,16 @@ void Sema::PerformPendingImplicitInstantiations() {
|
|||
PendingImplicitInstantiation Inst = PendingImplicitInstantiations.front();
|
||||
PendingImplicitInstantiations.pop_front();
|
||||
|
||||
if (FunctionDecl *Function = dyn_cast<FunctionDecl>(Inst.first))
|
||||
// Instantiate function definitions
|
||||
if (FunctionDecl *Function = dyn_cast<FunctionDecl>(Inst.first)) {
|
||||
if (!Function->getBody())
|
||||
InstantiateFunctionDefinition(/*FIXME:*/Inst.second, Function, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
// FIXME: instantiate static member variables
|
||||
// Instantiate static data member definitions.
|
||||
VarDecl *Var = cast<VarDecl>(Inst.first);
|
||||
assert(Var->isStaticDataMember() && "Not a static data member?");
|
||||
InstantiateStaticDataMemberDefinition(/*FIXME:*/Inst.second, Var, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
// RUN: clang-cc -fsyntax-only -verify %s
|
||||
|
||||
// Test instantiation of static data members declared out-of-line.
|
||||
|
||||
template<typename T>
|
||||
struct X {
|
||||
static T value;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
T X<T>::value = 17; // expected-error{{initialize}}
|
||||
|
||||
struct InitOkay {
|
||||
InitOkay(int) { }
|
||||
};
|
||||
|
||||
struct CannotInit { };
|
||||
|
||||
int &returnInt() { return X<int>::value; }
|
||||
float &returnFloat() { return X<float>::value; }
|
||||
|
||||
InitOkay &returnInitOkay() { return X<InitOkay>::value; }
|
||||
|
||||
unsigned long sizeOkay() { return sizeof(X<CannotInit>::value); }
|
||||
|
||||
CannotInit &returnError() {
|
||||
return X<CannotInit>::value; // expected-note{{instantiation}}
|
||||
}
|
|
@ -6,7 +6,7 @@ struct X0 {
|
|||
};
|
||||
|
||||
template<typename T>
|
||||
T X0<T>::value = 0;
|
||||
T X0<T>::value = 0; // expected-error{{initialize}}
|
||||
|
||||
struct X1 {
|
||||
X1(int);
|
||||
|
@ -20,7 +20,7 @@ X1& get_X1() { return X0<X1>::value; }
|
|||
double*& get_double_ptr() { return X0<int*>::value; } // expected-error{{initialized}}
|
||||
|
||||
X2& get_X2() {
|
||||
return X0<X2>::value; // FIXME: instantiation should fail!
|
||||
return X0<X2>::value; // expected-note{{instantiation}}
|
||||
}
|
||||
|
||||
template<typename T> T x; // expected-error{{variable 'x' declared as a template}}
|
Загрузка…
Ссылка в новой задаче