forked from OSchip/llvm-project
Implement template instantiation for static data members of class
templates, including in-class initializers. For example: template<typename T, T Divisor> class X { public: static const T value = 10 / Divisor; }; instantiated with, e.g., X<int, 5>::value to get the value '2'. llvm-svn: 67715
This commit is contained in:
parent
292ea55130
commit
ef1a09a336
|
@ -345,6 +345,8 @@ public:
|
|||
QualType R, Decl* LastDeclarator,
|
||||
NamedDecl* PrevDecl, bool& InvalidDecl,
|
||||
bool &Redeclaration);
|
||||
bool CheckVariableDeclaration(VarDecl *NewVD, NamedDecl *PrevDecl,
|
||||
bool &Redeclaration);
|
||||
NamedDecl* ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
|
||||
QualType R, Decl *LastDeclarator,
|
||||
NamedDecl* PrevDecl,
|
||||
|
|
|
@ -1587,11 +1587,6 @@ Sema::ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC,
|
|||
if (getLangOptions().CPlusPlus)
|
||||
CheckExtraCXXDefaultArguments(D);
|
||||
|
||||
if (R.getTypePtr()->isObjCInterfaceType()) {
|
||||
Diag(D.getIdentifierLoc(), diag::err_statically_allocated_object);
|
||||
InvalidDecl = true;
|
||||
}
|
||||
|
||||
VarDecl *NewVD;
|
||||
VarDecl::StorageClass SC;
|
||||
switch (D.getDeclSpec().getStorageClassSpec()) {
|
||||
|
@ -1641,12 +1636,6 @@ Sema::ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC,
|
|||
} else if (SC == VarDecl::None)
|
||||
SC = VarDecl::Static;
|
||||
}
|
||||
|
||||
// The variable can not have an abstract class type.
|
||||
if (RequireNonAbstractType(D.getIdentifierLoc(), R,
|
||||
diag::err_abstract_type_in_decl,
|
||||
AbstractVariableType))
|
||||
InvalidDecl = true;
|
||||
|
||||
// The variable can not
|
||||
NewVD = VarDecl::Create(Context, DC, D.getIdentifierLoc(),
|
||||
|
@ -1671,32 +1660,94 @@ Sema::ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC,
|
|||
SE->getByteLength())));
|
||||
}
|
||||
|
||||
// If name lookup finds a previous declaration that is not in the
|
||||
// same scope as the new declaration, this may still be an
|
||||
// acceptable redeclaration.
|
||||
if (PrevDecl && !isDeclInScope(PrevDecl, DC, S) &&
|
||||
!(NewVD->hasLinkage() &&
|
||||
isOutOfScopePreviousDeclaration(PrevDecl, DC, Context)))
|
||||
PrevDecl = 0;
|
||||
|
||||
// Merge the decl with the existing one if appropriate.
|
||||
if (PrevDecl) {
|
||||
if (isa<FieldDecl>(PrevDecl) && D.getCXXScopeSpec().isSet()) {
|
||||
// The user tried to define a non-static data member
|
||||
// out-of-line (C++ [dcl.meaning]p1).
|
||||
Diag(NewVD->getLocation(), diag::err_nonstatic_member_out_of_line)
|
||||
<< D.getCXXScopeSpec().getRange();
|
||||
PrevDecl = 0;
|
||||
InvalidDecl = true;
|
||||
}
|
||||
} else if (D.getCXXScopeSpec().isSet()) {
|
||||
// No previous declaration in the qualifying scope.
|
||||
Diag(D.getIdentifierLoc(), diag::err_typecheck_no_member)
|
||||
<< Name << D.getCXXScopeSpec().getRange();
|
||||
InvalidDecl = true;
|
||||
}
|
||||
|
||||
if (CheckVariableDeclaration(NewVD, PrevDecl, Redeclaration))
|
||||
InvalidDecl = true;
|
||||
|
||||
// If this is a locally-scoped extern C variable, update the map of
|
||||
// such variables.
|
||||
if (CurContext->isFunctionOrMethod() && NewVD->isExternC(Context) &&
|
||||
!InvalidDecl)
|
||||
RegisterLocallyScopedExternCDecl(NewVD, PrevDecl, S);
|
||||
|
||||
return NewVD;
|
||||
}
|
||||
|
||||
/// \brief Perform semantic checking on a newly-created variable
|
||||
/// declaration.
|
||||
///
|
||||
/// This routine performs all of the type-checking required for a
|
||||
/// variable declaration once it has been build. It is used both to
|
||||
/// check variables after they have been parsed and their declarators
|
||||
/// have been translated into a declaration, and to check
|
||||
///
|
||||
/// \returns true if an error was encountered, false otherwise.
|
||||
bool Sema::CheckVariableDeclaration(VarDecl *NewVD, NamedDecl *PrevDecl,
|
||||
bool &Redeclaration) {
|
||||
bool Invalid = false;
|
||||
|
||||
QualType T = NewVD->getType();
|
||||
|
||||
if (T->isObjCInterfaceType()) {
|
||||
Diag(NewVD->getLocation(), diag::err_statically_allocated_object);
|
||||
Invalid = true;
|
||||
}
|
||||
|
||||
// The variable can not have an abstract class type.
|
||||
if (RequireNonAbstractType(NewVD->getLocation(), T,
|
||||
diag::err_abstract_type_in_decl,
|
||||
AbstractVariableType))
|
||||
Invalid = true;
|
||||
|
||||
// Emit an error if an address space was applied to decl with local storage.
|
||||
// This includes arrays of objects with address space qualifiers, but not
|
||||
// automatic variables that point to other address spaces.
|
||||
// ISO/IEC TR 18037 S5.1.2
|
||||
if (NewVD->hasLocalStorage() && (NewVD->getType().getAddressSpace() != 0)) {
|
||||
Diag(D.getIdentifierLoc(), diag::err_as_qualified_auto_decl);
|
||||
InvalidDecl = true;
|
||||
if (NewVD->hasLocalStorage() && (T.getAddressSpace() != 0)) {
|
||||
Diag(NewVD->getLocation(), diag::err_as_qualified_auto_decl);
|
||||
Invalid = true;
|
||||
}
|
||||
|
||||
if (NewVD->hasLocalStorage() && NewVD->getType().isObjCGCWeak()) {
|
||||
Diag(D.getIdentifierLoc(), diag::warn_attribute_weak_on_local);
|
||||
}
|
||||
if (NewVD->hasLocalStorage() && T.isObjCGCWeak())
|
||||
Diag(NewVD->getLocation(), diag::warn_attribute_weak_on_local);
|
||||
|
||||
bool isIllegalVLA = R->isVariableArrayType() && NewVD->hasGlobalStorage();
|
||||
bool isIllegalVM = R->isVariablyModifiedType() && NewVD->hasLinkage();
|
||||
bool isIllegalVLA = T->isVariableArrayType() && NewVD->hasGlobalStorage();
|
||||
bool isIllegalVM = T->isVariablyModifiedType() && NewVD->hasLinkage();
|
||||
if (isIllegalVLA || isIllegalVM) {
|
||||
bool SizeIsNegative;
|
||||
QualType FixedTy =
|
||||
TryToFixInvalidVariablyModifiedType(R, Context, SizeIsNegative);
|
||||
TryToFixInvalidVariablyModifiedType(T, Context, SizeIsNegative);
|
||||
if (!FixedTy.isNull()) {
|
||||
Diag(NewVD->getLocation(), diag::warn_illegal_constant_array_size);
|
||||
NewVD->setType(FixedTy);
|
||||
} else if (R->isVariableArrayType()) {
|
||||
NewVD->setInvalidDecl();
|
||||
} else if (T->isVariableArrayType()) {
|
||||
Invalid = true;
|
||||
|
||||
const VariableArrayType *VAT = Context.getAsVariableArrayType(R);
|
||||
const VariableArrayType *VAT = Context.getAsVariableArrayType(T);
|
||||
// FIXME: This won't give the correct result for
|
||||
// int a[10][n];
|
||||
SourceRange SizeRange = VAT->getSizeExpr()->getSourceRange();
|
||||
|
@ -1711,7 +1762,7 @@ Sema::ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC,
|
|||
Diag(NewVD->getLocation(), diag::err_vla_decl_has_extern_linkage)
|
||||
<< SizeRange;
|
||||
} else {
|
||||
InvalidDecl = true;
|
||||
Invalid = true;
|
||||
|
||||
if (NewVD->isFileVarDecl())
|
||||
Diag(NewVD->getLocation(), diag::err_vm_decl_in_file_scope);
|
||||
|
@ -1720,59 +1771,29 @@ Sema::ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC,
|
|||
}
|
||||
}
|
||||
|
||||
// If name lookup finds a previous declaration that is not in the
|
||||
// same scope as the new declaration, this may still be an
|
||||
// acceptable redeclaration.
|
||||
if (PrevDecl && !isDeclInScope(PrevDecl, DC, S) &&
|
||||
!(NewVD->hasLinkage() &&
|
||||
isOutOfScopePreviousDeclaration(PrevDecl, DC, Context)))
|
||||
PrevDecl = 0;
|
||||
|
||||
if (!PrevDecl && NewVD->isExternC(Context)) {
|
||||
// Since we did not find anything by this name and we're declaring
|
||||
// an extern "C" variable, look for a non-visible extern "C"
|
||||
// declaration with the same name.
|
||||
llvm::DenseMap<DeclarationName, NamedDecl *>::iterator Pos
|
||||
= LocallyScopedExternalDecls.find(Name);
|
||||
= LocallyScopedExternalDecls.find(NewVD->getDeclName());
|
||||
if (Pos != LocallyScopedExternalDecls.end())
|
||||
PrevDecl = Pos->second;
|
||||
}
|
||||
|
||||
// Merge the decl with the existing one if appropriate.
|
||||
if (PrevDecl) {
|
||||
if (isa<FieldDecl>(PrevDecl) && D.getCXXScopeSpec().isSet()) {
|
||||
// The user tried to define a non-static data member
|
||||
// out-of-line (C++ [dcl.meaning]p1).
|
||||
Diag(NewVD->getLocation(), diag::err_nonstatic_member_out_of_line)
|
||||
<< D.getCXXScopeSpec().getRange();
|
||||
NewVD->Destroy(Context);
|
||||
return 0;
|
||||
}
|
||||
if (!Invalid && T->isVoidType() && !NewVD->hasExternalStorage()) {
|
||||
Diag(NewVD->getLocation(), diag::err_typecheck_decl_incomplete_type)
|
||||
<< T;
|
||||
Invalid = true;
|
||||
}
|
||||
|
||||
if (PrevDecl) {
|
||||
Redeclaration = true;
|
||||
if (MergeVarDecl(NewVD, PrevDecl))
|
||||
InvalidDecl = true;
|
||||
} else if (D.getCXXScopeSpec().isSet()) {
|
||||
// No previous declaration in the qualifying scope.
|
||||
Diag(D.getIdentifierLoc(), diag::err_typecheck_no_member)
|
||||
<< Name << D.getCXXScopeSpec().getRange();
|
||||
InvalidDecl = true;
|
||||
Invalid = true;
|
||||
}
|
||||
|
||||
if (!InvalidDecl && R->isVoidType() && !NewVD->hasExternalStorage()) {
|
||||
Diag(NewVD->getLocation(), diag::err_typecheck_decl_incomplete_type)
|
||||
<< R;
|
||||
InvalidDecl = true;
|
||||
}
|
||||
|
||||
|
||||
// If this is a locally-scoped extern C variable, update the map of
|
||||
// such variables.
|
||||
if (CurContext->isFunctionOrMethod() && NewVD->isExternC(Context) &&
|
||||
!InvalidDecl)
|
||||
RegisterLocallyScopedExternCDecl(NewVD, PrevDecl, S);
|
||||
|
||||
return NewVD;
|
||||
return NewVD->isInvalidDecl() || Invalid;
|
||||
}
|
||||
|
||||
NamedDecl*
|
||||
|
@ -2376,7 +2397,8 @@ void Sema::AddInitializerToDecl(DeclTy *dcl, ExprArg init, bool DirectInit) {
|
|||
Diag(Loc, diag::err_in_class_initializer_non_constant)
|
||||
<< Init->getSourceRange();
|
||||
VDecl->setInvalidDecl();
|
||||
}
|
||||
} else if (!VDecl->getType()->isDependentType())
|
||||
ImpCastExprToType(Init, VDecl->getType());
|
||||
}
|
||||
}
|
||||
} else if (VDecl->isFileVarDecl()) {
|
||||
|
|
|
@ -42,6 +42,7 @@ namespace {
|
|||
Decl *VisitTranslationUnitDecl(TranslationUnitDecl *D);
|
||||
Decl *VisitNamespaceDecl(NamespaceDecl *D);
|
||||
Decl *VisitTypedefDecl(TypedefDecl *D);
|
||||
Decl *VisitVarDecl(VarDecl *D);
|
||||
Decl *VisitFieldDecl(FieldDecl *D);
|
||||
Decl *VisitStaticAssertDecl(StaticAssertDecl *D);
|
||||
Decl *VisitEnumDecl(EnumDecl *D);
|
||||
|
@ -56,6 +57,7 @@ namespace {
|
|||
|
||||
// Base case. FIXME: Remove once we can instantiate everything.
|
||||
Decl *VisitDecl(Decl *) {
|
||||
assert(false && "Template instantiation of unknown declaration kind!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -102,6 +104,45 @@ Decl *TemplateDeclInstantiator::VisitTypedefDecl(TypedefDecl *D) {
|
|||
return Typedef;
|
||||
}
|
||||
|
||||
Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) {
|
||||
// Instantiate the type of the declaration
|
||||
QualType T = SemaRef.InstantiateType(D->getType(), TemplateArgs,
|
||||
NumTemplateArgs,
|
||||
D->getTypeSpecStartLoc(),
|
||||
D->getDeclName());
|
||||
if (T.isNull())
|
||||
return 0;
|
||||
|
||||
// Build the instantiataed declaration
|
||||
VarDecl *Var = VarDecl::Create(SemaRef.Context, Owner,
|
||||
D->getLocation(), D->getIdentifier(),
|
||||
T, D->getStorageClass(),
|
||||
D->getTypeSpecStartLoc());
|
||||
Var->setThreadSpecified(D->isThreadSpecified());
|
||||
Var->setCXXDirectInitializer(D->hasCXXDirectInitializer());
|
||||
Var->setDeclaredInCondition(D->isDeclaredInCondition());
|
||||
|
||||
// FIXME: In theory, we could have a previous declaration for
|
||||
// variables that are not static data members.
|
||||
bool Redeclaration = false;
|
||||
if (SemaRef.CheckVariableDeclaration(Var, 0, Redeclaration))
|
||||
Var->setInvalidDecl();
|
||||
|
||||
Owner->addDecl(Var);
|
||||
|
||||
if (D->getInit()) {
|
||||
OwningExprResult Init
|
||||
= SemaRef.InstantiateExpr(D->getInit(), TemplateArgs, NumTemplateArgs);
|
||||
if (Init.isInvalid())
|
||||
Var->setInvalidDecl();
|
||||
else
|
||||
SemaRef.AddInitializerToDecl(Var, move(Init),
|
||||
D->hasCXXDirectInitializer());
|
||||
}
|
||||
|
||||
return Var;
|
||||
}
|
||||
|
||||
Decl *TemplateDeclInstantiator::VisitFieldDecl(FieldDecl *D) {
|
||||
bool Invalid = false;
|
||||
QualType T = D->getType();
|
||||
|
|
|
@ -25,3 +25,28 @@ template<> struct Fibonacci<1> {
|
|||
|
||||
int array5[Fibonacci<5>::value == 5? 1 : -1];
|
||||
int array10[Fibonacci<10>::value == 55? 1 : -1];
|
||||
|
||||
template<unsigned I>
|
||||
struct FibonacciEval2;
|
||||
|
||||
template<unsigned I>
|
||||
struct Fibonacci2 {
|
||||
static const unsigned value
|
||||
= FibonacciEval2<I-1>::value + FibonacciEval2<I-2>::value;
|
||||
};
|
||||
|
||||
template<unsigned I>
|
||||
struct FibonacciEval2 {
|
||||
static const unsigned value = Fibonacci2<I>::value;
|
||||
};
|
||||
|
||||
template<> struct Fibonacci2<0> {
|
||||
static const unsigned value = 0;
|
||||
};
|
||||
|
||||
template<> struct Fibonacci2<1> {
|
||||
static const unsigned value = 1;
|
||||
};
|
||||
|
||||
int array5_2[Fibonacci2<5>::value == 5? 1 : -1];
|
||||
int array10_2[Fibonacci2<10>::value == 55? 1 : -1];
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
// RUN: clang-cc -fsyntax-only -verify %s
|
||||
|
||||
template<typename T, T Divisor>
|
||||
class X {
|
||||
public:
|
||||
static const T value = 10 / Divisor; // expected-error{{in-class initializer is not an integral constant expression}}
|
||||
};
|
||||
|
||||
int array1[X<int, 2>::value == 5? 1 : -1];
|
||||
X<int, 0> xi0; // expected-note{{in instantiation of template class 'class X<int, 0>' requested here}}
|
||||
|
||||
|
||||
template<typename T>
|
||||
class Y {
|
||||
static const T value = 0; // expected-error{{'value' can only be initialized if it is a static const integral data member}}
|
||||
};
|
||||
|
||||
Y<float> fy; // expected-note{{in instantiation of template class 'class Y<float>' requested here}}
|
Loading…
Reference in New Issue