From 021c3b372c58f5423b4fa2a5be6933d1c7ecc663 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 11 Mar 2009 23:00:04 +0000 Subject: [PATCH] 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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@66734 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Decl.h | 13 ++ include/clang/Basic/DiagnosticSemaKinds.def | 6 + lib/Sema/SemaDecl.cpp | 135 +++++++++++++++++++- lib/Sema/SemaDeclCXX.cpp | 68 +--------- test/SemaCXX/class.cpp | 4 +- test/SemaCXX/virtuals.cpp | 5 + 6 files changed, 158 insertions(+), 73 deletions(-) diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 495471ef51..8037d84c30 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -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) diff --git a/include/clang/Basic/DiagnosticSemaKinds.def b/include/clang/Basic/DiagnosticSemaKinds.def index 96dc3ab283..ddf70e6a19 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.def +++ b/include/clang/Basic/DiagnosticSemaKinds.def @@ -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, diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 2e033bf907..629e4237e5 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -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(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(NewFD)->setVirtual(); + CXXRecordDecl *CurClass = cast(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(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(init.get()); + if ((IL = dyn_cast(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(RealDecl); if (!VDecl) { - Diag(RealDecl->getLocation(), diag::err_illegal_initializer); + if (getLangOptions().CPlusPlus && + RealDecl->getLexicalDeclContext()->isRecord() && + isa(RealDecl)) + Diag(RealDecl->getLocation(), diag::err_member_initialization) + << cast(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(TagD), DeclStart, D, static_cast(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; diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 0ea501d993..daaedae103 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -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(Member)->setVirtual(); - CXXRecordDecl *CurClass = cast(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(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(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(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(Member)); diff --git a/test/SemaCXX/class.cpp b/test/SemaCXX/class.cpp index 776fb091da..628253b495 100644 --- a/test/SemaCXX/class.cpp +++ b/test/SemaCXX/class.cpp @@ -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(); diff --git a/test/SemaCXX/virtuals.cpp b/test/SemaCXX/virtuals.cpp index 6cbf3ef15f..ad82edd5d8 100644 --- a/test/SemaCXX/virtuals.cpp +++ b/test/SemaCXX/virtuals.cpp @@ -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; -- 2.40.0