Move most of the checking from ActOnCXXMemberDeclarator to other, more general routines. This is a step toward separating the checking logic from Declarators, which in turn is required for template instantiation.

llvm-svn: 66734
This commit is contained in:
Douglas Gregor 2009-03-11 23:00:04 +00:00
parent d9065c4e29
commit 0c88030abd
6 changed files with 158 additions and 73 deletions

View File

@ -344,6 +344,19 @@ public:
return false;
}
/// \brief Determines whether this is a static data member.
///
/// This will only be true in C++, and applies to, e.g., the
/// variable 'x' in:
/// \code
/// struct S {
/// static int x;
/// };
/// \endcode
bool isStaticDataMember() const {
return getDeclContext()->isRecord();
}
/// isFileVarDecl - Returns true for file scoped variable declaration.
bool isFileVarDecl() const {
if (getKind() != Decl::Var)

View File

@ -248,6 +248,8 @@ DIAG(err_mutable_nonmember, ERROR,
"'mutable' can only be applied to member variables")
DIAG(err_virtual_non_function, ERROR,
"'virtual' can only appear on non-static member functions")
DIAG(err_virtual_out_of_class, ERROR,
"'virtual' can only be specified inside the class definition")
DIAG(err_static_not_bitfield, ERROR,
"static member %0 cannot be a bit-field")
DIAG(err_typedef_not_bitfield, ERROR,
@ -1229,6 +1231,10 @@ DIAG(err_base_init_does_not_name_class, ERROR,
DIAG(err_base_init_direct_and_virtual, ERROR,
"base class initializer %0 names both a direct base class and an"
" inherited virtual base class")
DIAG(err_in_class_initializer_non_integral_type, ERROR,
"in-class initializer has non-integral, non-enumeration type %0")
DIAG(err_in_class_initializer_non_constant, ERROR,
"in-class initializer is not an integral constant expression")
// C++ anonymous unions and GNU anonymous structs/unions
DIAG(ext_anonymous_union, EXTENSION,

View File

@ -1431,10 +1431,15 @@ Sema::ActOnTypedefDeclarator(Scope* S, Declarator& D, DeclContext* DC,
DC = 0;
}
// Check that there are no default arguments (C++ only).
if (getLangOptions().CPlusPlus)
if (getLangOptions().CPlusPlus) {
// Check that there are no default arguments (C++ only).
CheckExtraCXXDefaultArguments(D);
if (D.getDeclSpec().isVirtualSpecified())
Diag(D.getDeclSpec().getVirtualSpecLoc(),
diag::err_virtual_non_function);
}
TypedefDecl *NewTD = ParseTypedefDecl(S, D, R, LastDeclarator);
if (!NewTD) return 0;
@ -1580,6 +1585,10 @@ Sema::ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC,
return 0;
}
if (D.getDeclSpec().isVirtualSpecified())
Diag(D.getDeclSpec().getVirtualSpecLoc(),
diag::err_virtual_non_function);
bool ThreadSpecified = D.getDeclSpec().isThreadSpecified();
if (!DC->isRecord() && S->getFnParent() == 0) {
// C99 6.9p2: The storage-class specifiers auto and register shall not
@ -1751,9 +1760,10 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
}
bool isInline = D.getDeclSpec().isInlineSpecified();
// bool isVirtual = D.getDeclSpec().isVirtualSpecified();
bool isVirtual = D.getDeclSpec().isVirtualSpecified();
bool isExplicit = D.getDeclSpec().isExplicitSpecified();
bool isVirtualOkay = false;
FunctionDecl *NewFD;
if (D.getKind() == Declarator::DK_Constructor) {
// This is a C++ constructor declaration.
@ -1784,6 +1794,8 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
if (InvalidDecl)
NewFD->setInvalidDecl();
isVirtualOkay = true;
} else {
Diag(D.getIdentifierLoc(), diag::err_destructor_not_member);
@ -1811,12 +1823,16 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
if (InvalidDecl)
NewFD->setInvalidDecl();
isVirtualOkay = true;
}
} else if (DC->isRecord()) {
// This is a C++ method declaration.
NewFD = CXXMethodDecl::Create(Context, cast<CXXRecordDecl>(DC),
D.getIdentifierLoc(), Name, R,
(SC == FunctionDecl::Static), isInline);
isVirtualOkay = (SC != FunctionDecl::Static);
} else {
NewFD = FunctionDecl::Create(Context, DC,
D.getIdentifierLoc(),
@ -1835,6 +1851,34 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
// from the semantic context.
NewFD->setLexicalDeclContext(CurContext);
// C++ [dcl.fct.spec]p5:
// The virtual specifier shall only be used in declarations of
// nonstatic class member functions that appear within a
// member-specification of a class declaration; see 10.3.
//
// FIXME: Checking the 'virtual' specifier is not sufficient. A
// function is also virtual if it overrides an already virtual
// function. This is important to do here because it's part of the
// declaration.
if (isVirtual && !InvalidDecl) {
if (!isVirtualOkay) {
Diag(D.getDeclSpec().getVirtualSpecLoc(),
diag::err_virtual_non_function);
} else if (!CurContext->isRecord()) {
// 'virtual' was specified outside of the class.
Diag(D.getDeclSpec().getVirtualSpecLoc(), diag::err_virtual_out_of_class)
<< CodeModificationHint::CreateRemoval(
SourceRange(D.getDeclSpec().getVirtualSpecLoc()));
} else {
// Okay: Add virtual to the method.
cast<CXXMethodDecl>(NewFD)->setVirtual();
CXXRecordDecl *CurClass = cast<CXXRecordDecl>(DC);
CurClass->setAggregate(false);
CurClass->setPOD(false);
CurClass->setPolymorphic(true);
}
}
// Handle GNU asm-label extension (encoded as an attribute).
if (Expr *E = (Expr*) D.getAsmLabel()) {
// The parser guarantees this is a string.
@ -2109,9 +2153,38 @@ void Sema::AddInitializerToDecl(DeclTy *dcl, ExprArg init, bool DirectInit) {
if (RealDecl == 0)
return;
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(RealDecl)) {
// With declarators parsed the way they are, the parser cannot
// distinguish between a normal initializer and a pure-specifier.
// Thus this grotesque test.
IntegerLiteral *IL;
Expr *Init = static_cast<Expr *>(init.get());
if ((IL = dyn_cast<IntegerLiteral>(Init)) && IL->getValue() == 0 &&
Context.getCanonicalType(IL->getType()) == Context.IntTy) {
if (Method->isVirtual())
Method->setPure();
else {
Diag(Method->getLocation(), diag::err_non_virtual_pure)
<< Method->getDeclName() << Init->getSourceRange();
Method->setInvalidDecl();
}
} else {
Diag(Method->getLocation(), diag::err_member_function_initialization)
<< Method->getDeclName() << Init->getSourceRange();
Method->setInvalidDecl();
}
return;
}
VarDecl *VDecl = dyn_cast<VarDecl>(RealDecl);
if (!VDecl) {
Diag(RealDecl->getLocation(), diag::err_illegal_initializer);
if (getLangOptions().CPlusPlus &&
RealDecl->getLexicalDeclContext()->isRecord() &&
isa<NamedDecl>(RealDecl))
Diag(RealDecl->getLocation(), diag::err_member_initialization)
<< cast<NamedDecl>(RealDecl)->getDeclName();
else
Diag(RealDecl->getLocation(), diag::err_illegal_initializer);
RealDecl->setInvalidDecl();
return;
}
@ -2150,6 +2223,52 @@ void Sema::AddInitializerToDecl(DeclTy *dcl, ExprArg init, bool DirectInit) {
CheckForConstantInitializer(Init, DclT);
}
}
} else if (VDecl->isStaticDataMember() &&
VDecl->getLexicalDeclContext()->isRecord()) {
// This is an in-class initialization for a static data member, e.g.,
//
// struct S {
// static const int value = 17;
// };
// Attach the initializer
VDecl->setInit(Init);
// C++ [class.mem]p4:
// A member-declarator can contain a constant-initializer only
// if it declares a static member (9.4) of const integral or
// const enumeration type, see 9.4.2.
QualType T = VDecl->getType();
if (!T->isDependentType() &&
(!Context.getCanonicalType(T).isConstQualified() ||
!T->isIntegralType())) {
Diag(VDecl->getLocation(), diag::err_member_initialization)
<< VDecl->getDeclName() << Init->getSourceRange();
VDecl->setInvalidDecl();
} else {
// C++ [class.static.data]p4:
// If a static data member is of const integral or const
// enumeration type, its declaration in the class definition
// can specify a constant-initializer which shall be an
// integral constant expression (5.19).
if (!Init->isTypeDependent() &&
!Init->getType()->isIntegralType()) {
// We have a non-dependent, non-integral or enumeration type.
Diag(Init->getSourceRange().getBegin(),
diag::err_in_class_initializer_non_integral_type)
<< Init->getType() << Init->getSourceRange();
VDecl->setInvalidDecl();
} else if (!Init->isTypeDependent() && !Init->isValueDependent()) {
// Check whether the expression is a constant expression.
llvm::APSInt Value;
SourceLocation Loc;
if (!Init->isIntegerConstantExpr(Value, Context, &Loc)) {
Diag(Loc, diag::err_in_class_initializer_non_constant)
<< Init->getSourceRange();
VDecl->setInvalidDecl();
}
}
}
} else if (VDecl->isFileVarDecl()) {
if (VDecl->getStorageClass() == VarDecl::Extern)
Diag(VDecl->getLocation(), diag::warn_extern_init);
@ -3252,6 +3371,7 @@ bool Sema::VerifyBitField(SourceLocation FieldLoc, IdentifierInfo *FieldName,
Sema::DeclTy *Sema::ActOnField(Scope *S, DeclTy *TagD,
SourceLocation DeclStart,
Declarator &D, ExprTy *BitfieldWidth) {
return HandleField(S, static_cast<RecordDecl*>(TagD), DeclStart, D,
static_cast<Expr*>(BitfieldWidth),
AS_public);
@ -3269,9 +3389,14 @@ FieldDecl *Sema::HandleField(Scope *S, RecordDecl *Record,
QualType T = GetTypeForDeclarator(D, S);
if (getLangOptions().CPlusPlus)
if (getLangOptions().CPlusPlus) {
CheckExtraCXXDefaultArguments(D);
if (D.getDeclSpec().isVirtualSpecified())
Diag(D.getDeclSpec().getVirtualSpecLoc(),
diag::err_virtual_non_function);
}
NamedDecl *PrevDecl = LookupName(S, II, LookupMemberName, true);
if (PrevDecl && !isDeclInScope(PrevDecl, Record, S))
PrevDecl = 0;

View File

@ -588,72 +588,8 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
assert((Name || isInstField) && "No identifier for non-field ?");
if (DS.isVirtualSpecified()) {
if (!isFunc || DS.getStorageClassSpec() == DeclSpec::SCS_static) {
Diag(DS.getVirtualSpecLoc(), diag::err_virtual_non_function);
Member->setInvalidDecl();
} else {
cast<CXXMethodDecl>(Member)->setVirtual();
CXXRecordDecl *CurClass = cast<CXXRecordDecl>(CurContext);
CurClass->setAggregate(false);
CurClass->setPOD(false);
CurClass->setPolymorphic(true);
}
}
// FIXME: The above definition of virtual is not sufficient. A function is
// also virtual if it overrides an already virtual function. This is important
// to do here because it decides the validity of a pure specifier.
if (Init) {
// C++ 9.2p4: A member-declarator can contain a constant-initializer only
// if it declares a static member of const integral or const enumeration
// type.
if (VarDecl *CVD = dyn_cast<VarDecl>(Member)) {
// ...static member of...
CVD->setInit(Init);
// ...const integral or const enumeration type.
if (Context.getCanonicalType(CVD->getType()).isConstQualified() &&
CVD->getType()->isIntegralType()) {
// constant-initializer
if (CheckForConstantInitializer(Init, CVD->getType()))
Member->setInvalidDecl();
} else {
// not const integral.
Diag(Loc, diag::err_member_initialization)
<< Name << Init->getSourceRange();
Member->setInvalidDecl();
}
} else {
// not static member. perhaps virtual function?
if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Member)) {
// With declarators parsed the way they are, the parser cannot
// distinguish between a normal initializer and a pure-specifier.
// Thus this grotesque test.
IntegerLiteral *IL;
if ((IL = dyn_cast<IntegerLiteral>(Init)) && IL->getValue() == 0 &&
Context.getCanonicalType(IL->getType()) == Context.IntTy) {
if (MD->isVirtual())
MD->setPure();
else {
Diag(Loc, diag::err_non_virtual_pure)
<< Name << Init->getSourceRange();
Member->setInvalidDecl();
}
} else {
Diag(Loc, diag::err_member_function_initialization)
<< Name << Init->getSourceRange();
Member->setInvalidDecl();
}
} else {
Diag(Loc, diag::err_member_initialization)
<< Name << Init->getSourceRange();
Member->setInvalidDecl();
}
}
}
if (Init)
AddInitializerToDecl(Member, ExprArg(*this, Init), false);
if (isInstField) {
FieldCollector->Add(cast<FieldDecl>(Member));

View File

@ -35,7 +35,7 @@ public:
int i = 0; // expected-error {{error: 'i' can only be initialized if it is a static const integral data member}}
static int si = 0; // expected-error {{error: 'si' can only be initialized if it is a static const integral data member}}
static const NestedC ci = 0; // expected-error {{error: 'ci' can only be initialized if it is a static const integral data member}}
static const int nci = vs; // expected-error {{error: initializer element is not a compile-time constant}}
static const int nci = vs; // expected-error {{in-class initializer is not an integral constant expression}}
static const int vi = 0;
static const E evi = 0;
@ -53,7 +53,7 @@ public:
typedef int A;
virtual int viv; // expected-error {{error: 'virtual' can only appear on non-static member functions}}
virtual int viv; // expected-error {{'virtual' can only appear on non-static member functions}}
virtual static int vsif(); // expected-error {{error: 'virtual' can only appear on non-static member functions}}
virtual int vif();

View File

@ -8,10 +8,15 @@ class A {
void i() = 1; // expected-error {{initializer on function does not look like a pure-specifier}}
void j() = 0u; // expected-error {{initializer on function does not look like a pure-specifier}}
void k();
public:
A(int);
};
virtual void A::k() { } // expected-error{{'virtual' can only be specified inside the class definition}}
class B : public A {
// Needs to recognize that overridden function is virtual.
//void g() = 0;