From 656de633acefb7ced01a4b573dbd4f70b4300097 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 11 Mar 2009 23:52:16 +0000 Subject: [PATCH] Fix various problems with matching out-of-line definitions of static class members to the corresponding in-class declaration. Diagnose the erroneous use of 'static' on out-of-line definitions of class members. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@66740 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.def | 2 + lib/Sema/SemaDecl.cpp | 84 ++++++++++++------- test/SemaCXX/nested-name-spec.cpp | 14 ++++ .../nested-name-spec-template.cpp | 3 +- 4 files changed, 72 insertions(+), 31 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.def b/include/clang/Basic/DiagnosticSemaKinds.def index ddf70e6a19..8408565ce0 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.def +++ b/include/clang/Basic/DiagnosticSemaKinds.def @@ -252,6 +252,8 @@ 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_static_out_of_line, ERROR, + "'static' can not be specified on an out-of-line static member definition") DIAG(err_typedef_not_bitfield, ERROR, "typedef member %0 cannot be a bit-field") DIAG(err_not_integral_type_bitfield, ERROR, diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 629e4237e5..7d3d1c68f2 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -581,7 +581,9 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD) { const CXXMethodDecl* OldMethod = dyn_cast(Old); const CXXMethodDecl* NewMethod = dyn_cast(New); - if (OldMethod && NewMethod) { + if (OldMethod && NewMethod && + OldMethod->getLexicalDeclContext() == + NewMethod->getLexicalDeclContext()) { // -- Member function declarations with the same name and the // same parameter types cannot be overloaded if any of them // is a static member function declaration. @@ -595,21 +597,18 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD) { // [...] A member shall not be declared twice in the // member-specification, except that a nested class or member // class template can be declared and then later defined. - if (OldMethod->getLexicalDeclContext() == - NewMethod->getLexicalDeclContext()) { - unsigned NewDiag; - if (isa(OldMethod)) - NewDiag = diag::err_constructor_redeclared; - else if (isa(NewMethod)) - NewDiag = diag::err_destructor_redeclared; - else if (isa(NewMethod)) - NewDiag = diag::err_conv_function_redeclared; - else - NewDiag = diag::err_member_redeclared; - - Diag(New->getLocation(), NewDiag); - Diag(Old->getLocation(), PrevDiag) << Old << Old->getType(); - } + unsigned NewDiag; + if (isa(OldMethod)) + NewDiag = diag::err_constructor_redeclared; + else if (isa(NewMethod)) + NewDiag = diag::err_destructor_redeclared; + else if (isa(NewMethod)) + NewDiag = diag::err_conv_function_redeclared; + else + NewDiag = diag::err_member_redeclared; + + Diag(New->getLocation(), NewDiag); + Diag(Old->getLocation(), PrevDiag) << Old << Old->getType(); } // (C++98 8.3.5p3): @@ -805,6 +804,7 @@ bool Sema::MergeVarDecl(VarDecl *New, Decl *OldD) { return true; } New->setType(MergedT); + // C99 6.2.2p4: Check if we have a static decl followed by a non-static. if (New->getStorageClass() == VarDecl::Static && (Old->getStorageClass() == VarDecl::None || @@ -821,7 +821,10 @@ bool Sema::MergeVarDecl(VarDecl *New, Decl *OldD) { return true; } // Variables with external linkage are analyzed in FinalizeDeclaratorGroup. - if (New->getStorageClass() != VarDecl::Extern && !New->isFileVarDecl()) { + if (New->getStorageClass() != VarDecl::Extern && !New->isFileVarDecl() && + // Don't complain about out-of-line definitions of static members. + !(Old->getLexicalDeclContext()->isRecord() && + !New->getLexicalDeclContext()->isRecord())) { Diag(New->getLocation(), diag::err_redefinition) << New->getDeclName(); Diag(Old->getLocation(), diag::note_previous_definition); return true; @@ -1331,10 +1334,6 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl, if (New == 0) return 0; - // Set the lexical context. If the declarator has a C++ scope specifier, the - // lexical context will be different from the semantic context. - New->setLexicalDeclContext(CurContext); - // If this has an identifier and is not an invalid redeclaration, // add it to the scope stack. if (Name && !(Redeclaration && InvalidDecl)) @@ -1598,6 +1597,16 @@ Sema::ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC, InvalidDecl = true; } } + if (DC->isRecord() && !CurContext->isRecord()) { + // This is an out-of-line definition of a static data member. + if (SC == VarDecl::Static) { + Diag(D.getDeclSpec().getStorageClassSpecLoc(), + diag::err_static_out_of_line) + << CodeModificationHint::CreateRemoval( + SourceRange(D.getDeclSpec().getStorageClassSpecLoc())); + } else if (SC == VarDecl::None) + SC = VarDecl::Static; + } NewVD = VarDecl::Create(Context, DC, D.getIdentifierLoc(), II, R, SC, // FIXME: Move to DeclGroup... @@ -1605,6 +1614,10 @@ Sema::ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC, NewVD->setThreadSpecified(ThreadSpecified); NewVD->setNextDeclarator(LastDeclarator); + // Set the lexical context. If the declarator has a C++ scope specifier, the + // lexical context will be different from the semantic context. + NewVD->setLexicalDeclContext(CurContext); + // Handle attributes prior to checking for duplicates in MergeVarDecl ProcessDeclAttributes(NewVD, D); @@ -1697,13 +1710,11 @@ Sema::ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC, Redeclaration = true; if (MergeVarDecl(NewVD, PrevDecl)) InvalidDecl = true; - - 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; - } + } 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 (!InvalidDecl && R->isVoidType() && !NewVD->hasExternalStorage()) { @@ -1743,7 +1754,7 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, case DeclSpec::SCS_unspecified: SC = FunctionDecl::None; break; case DeclSpec::SCS_extern: SC = FunctionDecl::Extern; break; case DeclSpec::SCS_static: { - if (DC->getLookupContext()->isFunctionOrMethod()) { + if (CurContext->getLookupContext()->isFunctionOrMethod()) { // C99 6.7.1p5: // The declaration of an identifier for a function that has // block scope shall have no explicit storage-class specifier @@ -1879,6 +1890,21 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, } } + if (SC == FunctionDecl::Static && isa(NewFD) && + !CurContext->isRecord()) { + // C++ [class.static]p1: + // A data or function member of a class may be declared static + // in a class definition, in which case it is a static member of + // the class. + + // Complain about the 'static' specifier if it's on an out-of-line + // member function definition. + Diag(D.getDeclSpec().getStorageClassSpecLoc(), + diag::err_static_out_of_line) + << CodeModificationHint::CreateRemoval( + SourceRange(D.getDeclSpec().getStorageClassSpecLoc())); + } + // Handle GNU asm-label extension (encoded as an attribute). if (Expr *E = (Expr*) D.getAsmLabel()) { // The parser guarantees this is a string. diff --git a/test/SemaCXX/nested-name-spec.cpp b/test/SemaCXX/nested-name-spec.cpp index f75b279a44..eaca56c725 100644 --- a/test/SemaCXX/nested-name-spec.cpp +++ b/test/SemaCXX/nested-name-spec.cpp @@ -2,6 +2,11 @@ namespace A { struct C { static int cx; + + static int cx2; + + static int Ag1(); + static int Ag2(); }; int ax; void Af(); @@ -11,6 +16,15 @@ A:: ; // expected-error {{expected unqualified-id}} ::A::ax::undef ex3; // expected-error {{expected a class or namespace}} expected-error {{invalid token after top level declarator}} A::undef1::undef2 ex4; // expected-error {{no member named 'undef1'}} expected-error {{invalid token after top level declarator}} +int A::C::Ag1() { return 0; } + +static int A::C::Ag2() { return 0; } // expected-error{{'static' can not be specified on an out-of-line static member}} + +int A::C::cx = 17; + + +static int A::C::cx2 = 17; // expected-error{{'static' can not be specified on an out-of-line static member}} + class C2 { void m(); // expected-note{{member declaration nearly matches}} diff --git a/test/SemaTemplate/nested-name-spec-template.cpp b/test/SemaTemplate/nested-name-spec-template.cpp index 4007f232c4..8db2bc3429 100644 --- a/test/SemaTemplate/nested-name-spec-template.cpp +++ b/test/SemaTemplate/nested-name-spec-template.cpp @@ -33,8 +33,7 @@ N::M::template; // expected-error{{expected template name after 'template' keywo N::M::template Promote; // expected-error{{expected '<' after 'template Promote' in nested name specifier}} \ // expected-error{{C++ requires a type specifier for all declarations}} \ -// expected-error{{redefinition of 'Promote' as different kind of symbol}} \ -// expected-error{{no member named 'Promote'}} +// expected-error{{redefinition of 'Promote' as different kind of symbol}} namespace N { template struct A; -- 2.40.0