diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h index 7670798d13..f0ac1a5335 100644 --- a/include/clang/AST/DeclBase.h +++ b/include/clang/AST/DeclBase.h @@ -310,10 +310,21 @@ public: } } + bool isFileContext() const { + return DeclKind == Decl::TranslationUnit || DeclKind == Decl::Namespace; + } + bool isCXXRecord() const { return DeclKind == Decl::CXXRecord; } + bool Encloses(DeclContext *DC) const { + for (; DC; DC = DC->getParent()) + if (DC == this) + return true; + return false; + } + const ScopedDecl *getDeclChain() const { return DeclChain; } ScopedDecl *getDeclChain() { return DeclChain; } void setDeclChain(ScopedDecl *D) { DeclChain = D; } diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 51d1a935f0..d8e7444be1 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -391,12 +391,14 @@ protected: /// CXXMethodDecl - Represents a static or instance method of a /// struct/union/class. class CXXMethodDecl : public FunctionDecl { + bool IsInlineDef : 1; + protected: CXXMethodDecl(Kind DK, CXXRecordDecl *RD, SourceLocation L, IdentifierInfo *Id, QualType T, bool isStatic, bool isInline, ScopedDecl *PrevDecl) : FunctionDecl(DK, RD, L, Id, T, (isStatic ? Static : None), - isInline, PrevDecl) {} + isInline, PrevDecl), IsInlineDef(false) {} public: static CXXMethodDecl *Create(ASTContext &C, CXXRecordDecl *RD, @@ -407,6 +409,10 @@ public: bool isStatic() const { return getStorageClass() == Static; } bool isInstance() const { return !isStatic(); } + bool isOutOfLineDefinition() const { return !isInlineDefinition(); } + bool isInlineDefinition() const { return IsInlineDef; } + void setInlineDefinition(bool flag) { IsInlineDef = flag; } + void setAccess(AccessSpecifier AS) { Access = AS; } AccessSpecifier getAccess() const { return AccessSpecifier(Access); } diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index fa3918205c..508f15633e 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -1165,6 +1165,14 @@ DIAG(err_type_defined_in_condition, ERROR, "types may not be defined in conditions") DIAG(err_typecheck_bool_condition, ERROR, "expression must have bool type (or be convertible to bool) ('%0' invalid)") +DIAG(err_expected_class_or_namespace, ERROR, + "expected a class or namespace") +DIAG(err_invalid_declarator_scope, ERROR, + "definition or redeclaration for '%0' not in a namespace enclosing '%1'") +DIAG(err_invalid_declarator_in_function, ERROR, + "definition or redeclaration for '%0' not allowed inside a function") +DIAG(err_not_tag_in_scope, ERROR, + "'%0' does not name a tag member in the specified scope") // assignment related diagnostics (also for argument passing, returning, etc). diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index 2113417753..532be59cd4 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -35,7 +35,7 @@ static inline RecordDecl *CreateStructDecl(ASTContext &C, const char *Name) void Sema::ActOnTranslationUnitScope(SourceLocation Loc, Scope *S) { TUScope = S; - CurContext = Context.getTranslationUnitDecl(); + PushDeclContext(Context.getTranslationUnitDecl()); if (!PP.getLangOptions().ObjC1) return; // Synthesize "typedef struct objc_selector *SEL;" @@ -81,8 +81,8 @@ void Sema::ActOnTranslationUnitScope(SourceLocation Loc, Scope *S) { } Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer) - : PP(pp), Context(ctxt), Consumer(consumer), CurContext(0), CurBlock(0), - PackContext(0), IdResolver(pp.getLangOptions()) { + : PP(pp), Context(ctxt), Consumer(consumer), CurContext(0),PreDeclaratorDC(0), + CurBlock(0), PackContext(0), IdResolver(pp.getLangOptions()) { // Get IdentifierInfo objects for known functions for which we // do extra checking. diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index c04f80d0ee..99d9181b6c 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -109,6 +109,14 @@ public: /// CurContext - This is the current declaration context of parsing. DeclContext *CurContext; + /// LexicalFileContext - The current lexical file declaration context, + /// the translation unit or a namespace. + DeclContext *LexicalFileContext; + + /// PreDeclaratorDC - Keeps the declaration context before switching to the + /// context of a declarator's nested-name-specifier. + DeclContext *PreDeclaratorDC; + /// CurBlock - If inside of a block definition, this contains a pointer to /// the active block object that represents it. BlockSemaInfo *CurBlock; @@ -332,7 +340,7 @@ public: virtual void ActOnEnumBody(SourceLocation EnumLoc, DeclTy *EnumDecl, DeclTy **Elements, unsigned NumElements); - DeclContext *getDCParent(DeclContext *DC); + DeclContext *getContainingDC(DeclContext *DC); /// Set the current declaration context until it gets popped. void PushDeclContext(DeclContext *DC); @@ -441,6 +449,7 @@ public: /// More parsing and symbol table subroutines... Decl *LookupDecl(const IdentifierInfo *II, unsigned NSI, Scope *S, + DeclContext *LookupCtx = 0, bool enableLazyBuiltinCreation = true); ObjCInterfaceDecl *getObjCInterfaceDecl(IdentifierInfo *Id); ScopedDecl *LazilyCreateBuiltin(IdentifierInfo *II, unsigned ID, @@ -773,6 +782,38 @@ public: SourceLocation EqualLoc, ExprTy *AssignExprVal); + /// ActOnCXXGlobalScopeSpecifier - Return the object that represents the + /// global scope ('::'). + virtual CXXScopeTy *ActOnCXXGlobalScopeSpecifier(Scope *S, + SourceLocation CCLoc); + + /// ActOnCXXNestedNameSpecifier - Called during parsing of a + /// nested-name-specifier. e.g. for "foo::bar::" we parsed "foo::" and now + /// we want to resolve "bar::". 'SS' is empty or the previously parsed + /// nested-name part ("foo::"), 'IdLoc' is the source location of 'bar', + /// 'CCLoc' is the location of '::' and 'II' is the identifier for 'bar'. + /// Returns a CXXScopeTy* object representing the C++ scope. + virtual CXXScopeTy *ActOnCXXNestedNameSpecifier(Scope *S, + const CXXScopeSpec &SS, + SourceLocation IdLoc, + SourceLocation CCLoc, + const IdentifierInfo &II); + + /// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global + /// scope or nested-name-specifier) is parsed, part of a declarator-id. + /// After this method is called, according to [C++ 3.4.3p3], names should be + /// looked up in the declarator-id's scope, until the declarator is parsed and + /// ActOnCXXExitDeclaratorScope is called. + /// The 'SS' should be a non-empty valid CXXScopeSpec. + virtual void ActOnCXXEnterDeclaratorScope(Scope *S, const CXXScopeSpec &SS); + + /// ActOnCXXExitDeclaratorScope - Called when a declarator that previously + /// invoked ActOnCXXEnterDeclaratorScope(), is finished. 'SS' is the same + /// CXXScopeSpec that was passed to ActOnCXXEnterDeclaratorScope as well. + /// Used to indicate that names should revert to being looked up in the + /// defining scope. + virtual void ActOnCXXExitDeclaratorScope(const CXXScopeSpec &SS); + // ParseObjCStringLiteral - Parse Objective-C string literals. virtual ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs, ExprTy **Strings, diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp new file mode 100644 index 0000000000..14805279b0 --- /dev/null +++ b/lib/Sema/SemaCXXScopeSpec.cpp @@ -0,0 +1,131 @@ +//===--- SemaCXXScopeSpec.cpp - Semantic Analysis for C++ scope specifiers-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements C++ semantic analysis for scope specifiers. +// +//===----------------------------------------------------------------------===// + +#include "Sema.h" +#include "clang/AST/ASTContext.h" +#include "clang/Parse/DeclSpec.h" +#include "clang/Basic/Diagnostic.h" +using namespace clang; + + +namespace { + Decl *LookupNestedName(DeclContext *LookupCtx, bool LookInParentCtx, + const IdentifierInfo &II, bool &IdIsUndeclared) { + IdentifierResolver::iterator + I = IdentifierResolver::begin(&II, LookupCtx, LookInParentCtx), + E = IdentifierResolver::end(); + + if (I == E) { + IdIsUndeclared = true; + return 0; + } + IdIsUndeclared = false; + + // C++ 3.4.3p1 : + // During the lookup for a name preceding the :: scope resolution operator, + // object, function, and enumerator names are ignored. If the name found is + // not a class-name or namespace-name, the program is ill-formed. + + for (; I != E; ++I) { + if (TypedefDecl *TD = dyn_cast(*I)) { + if (TD->getUnderlyingType()->isRecordType()) + break; + continue; + } + if (((*I)->getIdentifierNamespace()&Decl::IDNS_Tag) && !isa(*I)) + break; + } + + return (I != E ? *I : 0); + } +} // anonymous namespace + +/// ActOnCXXGlobalScopeSpecifier - Return the object that represents the +/// global scope ('::'). +Sema::CXXScopeTy *Sema::ActOnCXXGlobalScopeSpecifier(Scope *S, + SourceLocation CCLoc) { + return cast(Context.getTranslationUnitDecl()); +} + +/// ActOnCXXNestedNameSpecifier - Called during parsing of a +/// nested-name-specifier. e.g. for "foo::bar::" we parsed "foo::" and now +/// we want to resolve "bar::". 'SS' is empty or the previously parsed +/// nested-name part ("foo::"), 'IdLoc' is the source location of 'bar', +/// 'CCLoc' is the location of '::' and 'II' is the identifier for 'bar'. +/// Returns a CXXScopeTy* object representing the C++ scope. +Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S, + const CXXScopeSpec &SS, + SourceLocation IdLoc, + SourceLocation CCLoc, + const IdentifierInfo &II) { + DeclContext *DC = static_cast(SS.getScopeRep()); + Decl *SD; + bool IdIsUndeclared; + + if (DC) + SD = LookupNestedName(DC, false/*LookInParentCtx*/, II, IdIsUndeclared); + else + SD = LookupNestedName(CurContext, true/*LookInParent*/, II, IdIsUndeclared); + + if (SD) { + if (TypedefDecl *TD = dyn_cast(SD)) { + assert(TD->getUnderlyingType()->isRecordType() &&"Invalid Scope Decl!"); + SD = TD->getUnderlyingType()->getAsRecordType()->getDecl(); + } + + assert((isa(SD) || isa(SD)) && + "Invalid Scope Decl!"); + return cast(SD); + } + + unsigned DiagID; + if (!IdIsUndeclared) + DiagID = diag::err_expected_class_or_namespace; + else if (DC) + DiagID = diag::err_typecheck_no_member; + else + DiagID = diag::err_undeclared_var_use; + + if (DC) + Diag(IdLoc, DiagID, II.getName(), SS.getRange()); + else + Diag(IdLoc, DiagID, II.getName()); + + return 0; +} + +/// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global +/// scope or nested-name-specifier) is parsed, part of a declarator-id. +/// After this method is called, according to [C++ 3.4.3p3], names should be +/// looked up in the declarator-id's scope, until the declarator is parsed and +/// ActOnCXXExitDeclaratorScope is called. +/// The 'SS' should be a non-empty valid CXXScopeSpec. +void Sema::ActOnCXXEnterDeclaratorScope(Scope *S, const CXXScopeSpec &SS) { + assert(SS.isSet() && "Parser passed invalid CXXScopeSpec."); + assert(PreDeclaratorDC == 0 && "Previous declarator context not popped?"); + PreDeclaratorDC = CurContext; + CurContext = static_cast(SS.getScopeRep()); +} + +/// ActOnCXXExitDeclaratorScope - Called when a declarator that previously +/// invoked ActOnCXXEnterDeclaratorScope(), is finished. 'SS' is the same +/// CXXScopeSpec that was passed to ActOnCXXEnterDeclaratorScope as well. +/// Used to indicate that names should revert to being looked up in the +/// defining scope. +void Sema::ActOnCXXExitDeclaratorScope(const CXXScopeSpec &SS) { + assert(SS.isSet() && "Parser passed invalid CXXScopeSpec."); + assert(CurContext == static_cast(SS.getScopeRep()) && + "Context imbalance!"); + CurContext = PreDeclaratorDC; + PreDeclaratorDC = 0; +} diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 57a5aa8419..66e43c2d4c 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -30,7 +30,13 @@ using namespace clang; Sema::TypeTy *Sema::isTypeName(const IdentifierInfo &II, Scope *S, const CXXScopeSpec *SS) { - Decl *IIDecl = LookupDecl(&II, Decl::IDNS_Ordinary, S, false); + DeclContext *DC = 0; + if (SS) { + if (SS->isInvalid()) + return 0; + DC = static_cast(SS->getScopeRep()); + } + Decl *IIDecl = LookupDecl(&II, Decl::IDNS_Ordinary, S, DC, false); if (IIDecl && (isa(IIDecl) || isa(IIDecl) || @@ -44,16 +50,16 @@ std::string Sema::getTypeAsString(TypeTy *Type) { return Ty.getAsString(); } -DeclContext *Sema::getDCParent(DeclContext *DC) { - // If CurContext is a ObjC method, getParent() will return NULL. - if (isa(DC)) - return Context.getTranslationUnitDecl(); - - // A C++ inline method is parsed *after* the topmost class it was declared in - // is fully parsed (it's "complete"). - // The parsing of a C++ inline method happens at the declaration context of - // the topmost (non-nested) class it is declared in. +DeclContext *Sema::getContainingDC(DeclContext *DC) { if (CXXMethodDecl *MD = dyn_cast(DC)) { + // A C++ out-of-line method will return to the file declaration context. + if (!MD->isInlineDefinition()) + return LexicalFileContext; + + // A C++ inline method is parsed *after* the topmost class it was declared in + // is fully parsed (it's "complete"). + // The parsing of a C++ inline method happens at the declaration context of + // the topmost (non-nested) class it is declared in. assert(isa(MD->getParent()) && "C++ method not in Record."); DC = MD->getParent(); while (CXXRecordDecl *RD = dyn_cast(DC->getParent())) @@ -64,18 +70,25 @@ DeclContext *Sema::getDCParent(DeclContext *DC) { return DC; } + if (isa(DC) || isa(DC)) + return LexicalFileContext; + return DC->getParent(); } void Sema::PushDeclContext(DeclContext *DC) { - assert(getDCParent(DC) == CurContext && + assert(getContainingDC(DC) == CurContext && "The next DeclContext should be directly contained in the current one."); CurContext = DC; + if (CurContext->isFileContext()) + LexicalFileContext = CurContext; } void Sema::PopDeclContext() { assert(CurContext && "DeclContext imbalance!"); - CurContext = getDCParent(CurContext); + CurContext = getContainingDC(CurContext); + if (CurContext->isFileContext()) + LexicalFileContext = CurContext; } /// Add this decl to the scope shadowed decl chains. @@ -180,18 +193,20 @@ ObjCInterfaceDecl *Sema::getObjCInterfaceDecl(IdentifierInfo *Id) { /// LookupDecl - Look up the inner-most declaration in the specified /// namespace. -Decl *Sema::LookupDecl(const IdentifierInfo *II, unsigned NSI, - Scope *S, bool enableLazyBuiltinCreation) { +Decl *Sema::LookupDecl(const IdentifierInfo *II, unsigned NSI, Scope *S, + DeclContext *LookupCtx, bool enableLazyBuiltinCreation) { if (II == 0) return 0; unsigned NS = NSI; if (getLangOptions().CPlusPlus && (NS & Decl::IDNS_Ordinary)) NS |= Decl::IDNS_Tag; + IdentifierResolver::iterator + I = LookupCtx ? IdResolver.begin(II, LookupCtx, false/*LookInParentCtx*/) : + IdResolver.begin(II, CurContext, true/*LookInParentCtx*/); // Scan up the scope chain looking for a decl that matches this identifier // that is in the appropriate namespace. This search should not take long, as // shadowing of names is uncommon, and deep shadowing is extremely uncommon. - for (IdentifierResolver::iterator - I = IdResolver.begin(II, CurContext), E = IdResolver.end(); I != E; ++I) + for (; I != IdResolver.end(); ++I) if ((*I)->getIdentifierNamespace() & NS) return *I; @@ -199,7 +214,8 @@ Decl *Sema::LookupDecl(const IdentifierInfo *II, unsigned NSI, // corresponds to a compiler builtin, create the decl object for the builtin // now, injecting it into translation unit scope, and return it. if (NS & Decl::IDNS_Ordinary) { - if (enableLazyBuiltinCreation) { + if (enableLazyBuiltinCreation && + (LookupCtx == 0 || isa(LookupCtx))) { // If this is a builtin on this (or all) targets, create the decl. if (unsigned BuiltinID = II->getBuiltinID()) return LazilyCreateBuiltin((IdentifierInfo *)II, BuiltinID, S); @@ -749,11 +765,45 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { while ((S->getFlags() & Scope::DeclScope) == 0) S = S->getParent(); - // See if this is a redefinition of a variable in the same scope. - Decl *PrevDecl = LookupDecl(II, Decl::IDNS_Ordinary, S); + DeclContext *DC; + Decl *PrevDecl; ScopedDecl *New; bool InvalidDecl = false; + // See if this is a redefinition of a variable in the same scope. + if (!D.getCXXScopeSpec().isSet()) { + DC = CurContext; + PrevDecl = LookupDecl(II, Decl::IDNS_Ordinary, S); + } else { // Something like "int foo::x;" + DC = static_cast(D.getCXXScopeSpec().getScopeRep()); + PrevDecl = LookupDecl(II, Decl::IDNS_Ordinary, S, DC); + + // C++ 7.3.1.2p2: + // Members (including explicit specializations of templates) of a named + // namespace can also be defined outside that namespace by explicit + // qualification of the name being defined, provided that the entity being + // defined was already declared in the namespace and the definition appears + // after the point of declaration in a namespace that encloses the + // declarations namespace. + // + if (PrevDecl == 0) { + // No previous declaration in the qualifying scope. + Diag(D.getIdentifierLoc(), diag::err_typecheck_no_member, + II->getName(), D.getCXXScopeSpec().getRange()); + } else if (!CurContext->Encloses(DC)) { + // The qualifying scope doesn't enclose the original declaration. + // Emit diagnostic based on current scope. + SourceLocation L = D.getIdentifierLoc(); + SourceRange R = D.getCXXScopeSpec().getRange(); + if (isa(CurContext)) { + Diag(L, diag::err_invalid_declarator_in_function, II->getName(), R); + } else { + Diag(L, diag::err_invalid_declarator_scope, II->getName(), + cast(DC)->getName(), R); + } + } + } + // In C++, the previous declaration we find might be a tag type // (class or enum). In this case, the new declaration will hide the // tag type. @@ -775,7 +825,7 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { ProcessDeclAttributes(NewTD, D); // Merge the decl with the existing one if appropriate. If the decl is // in an outer scope, it isn't the same thing. - if (PrevDecl && isDeclInScope(PrevDecl, CurContext, S)) { + if (PrevDecl && isDeclInScope(PrevDecl, DC, S)) { NewTD = MergeTypeDefDecl(NewTD, PrevDecl); if (NewTD == 0) return 0; } @@ -812,14 +862,14 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { FunctionDecl *NewFD; if (D.getKind() == Declarator::DK_Constructor) { // This is a C++ constructor declaration. - assert(CurContext->isCXXRecord() && + assert(DC->isCXXRecord() && "Constructors can only be declared in a member context"); bool isInvalidDecl = CheckConstructorDeclarator(D, R, SC); // Create the new declaration NewFD = CXXConstructorDecl::Create(Context, - cast(CurContext), + cast(DC), D.getIdentifierLoc(), II, R, isExplicit, isInline, /*isImplicitlyDeclared=*/false); @@ -828,11 +878,11 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { NewFD->setInvalidDecl(); } else if (D.getKind() == Declarator::DK_Destructor) { // This is a C++ destructor declaration. - if (CurContext->isCXXRecord()) { + if (DC->isCXXRecord()) { bool isInvalidDecl = CheckDestructorDeclarator(D, R, SC); NewFD = CXXDestructorDecl::Create(Context, - cast(CurContext), + cast(DC), D.getIdentifierLoc(), II, R, isInline, /*isImplicitlyDeclared=*/false); @@ -843,14 +893,14 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { Diag(D.getIdentifierLoc(), diag::err_destructor_not_member); // Create a FunctionDecl to satisfy the function definition parsing // code path. - NewFD = FunctionDecl::Create(Context, CurContext, D.getIdentifierLoc(), + NewFD = FunctionDecl::Create(Context, DC, D.getIdentifierLoc(), II, R, SC, isInline, LastDeclarator, // FIXME: Move to DeclGroup... D.getDeclSpec().getSourceRange().getBegin()); NewFD->setInvalidDecl(); } } else if (D.getKind() == Declarator::DK_Conversion) { - if (!CurContext->isCXXRecord()) { + if (!DC->isCXXRecord()) { Diag(D.getIdentifierLoc(), diag::err_conv_function_not_member); return 0; @@ -858,21 +908,21 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { bool isInvalidDecl = CheckConversionDeclarator(D, R, SC); NewFD = CXXConversionDecl::Create(Context, - cast(CurContext), + cast(DC), D.getIdentifierLoc(), II, R, isInline, isExplicit); if (isInvalidDecl) NewFD->setInvalidDecl(); } - } else if (CurContext->isCXXRecord()) { + } else if (DC->isCXXRecord()) { // This is a C++ method declaration. - NewFD = CXXMethodDecl::Create(Context, cast(CurContext), + NewFD = CXXMethodDecl::Create(Context, cast(DC), D.getIdentifierLoc(), II, R, (SC == FunctionDecl::Static), isInline, LastDeclarator); } else { - NewFD = FunctionDecl::Create(Context, CurContext, + NewFD = FunctionDecl::Create(Context, DC, D.getIdentifierLoc(), II, R, SC, isInline, LastDeclarator, // FIXME: Move to DeclGroup... @@ -943,7 +993,7 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { llvm::SmallVector Params; for (FunctionTypeProto::arg_type_iterator ArgType = FT->arg_type_begin(); ArgType != FT->arg_type_end(); ++ArgType) { - Params.push_back(ParmVarDecl::Create(Context, CurContext, + Params.push_back(ParmVarDecl::Create(Context, DC, SourceLocation(), 0, *ArgType, VarDecl::None, 0, 0)); @@ -972,7 +1022,7 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { // Merge the decl with the existing one if appropriate. Since C functions // are in a flat namespace, make sure we consider decls in outer scopes. if (PrevDecl && - (!getLangOptions().CPlusPlus||isDeclInScope(PrevDecl, CurContext, S))) { + (!getLangOptions().CPlusPlus||isDeclInScope(PrevDecl, DC, S))) { bool Redeclaration = false; // If C++, determine whether NewFD is an overload of PrevDecl or @@ -1046,10 +1096,10 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { case DeclSpec::SCS_register: SC = VarDecl::Register; break; case DeclSpec::SCS_private_extern: SC = VarDecl::PrivateExtern; break; } - if (CurContext->isCXXRecord()) { + if (DC->isCXXRecord()) { assert(SC == VarDecl::Static && "Invalid storage class for member!"); // This is a static data member for a C++ class. - NewVD = CXXClassVarDecl::Create(Context, cast(CurContext), + NewVD = CXXClassVarDecl::Create(Context, cast(DC), D.getIdentifierLoc(), II, R, LastDeclarator); } else { @@ -1063,7 +1113,7 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { InvalidDecl = true; } } - NewVD = VarDecl::Create(Context, CurContext, D.getIdentifierLoc(), + NewVD = VarDecl::Create(Context, DC, D.getIdentifierLoc(), II, R, SC, LastDeclarator, // FIXME: Move to DeclGroup... D.getDeclSpec().getSourceRange().getBegin()); @@ -1090,7 +1140,7 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { } // Merge the decl with the existing one if appropriate. If the decl is // in an outer scope, it isn't the same thing. - if (PrevDecl && isDeclInScope(PrevDecl, CurContext, S)) { + if (PrevDecl && isDeclInScope(PrevDecl, DC, S)) { NewVD = MergeVarDecl(NewVD, PrevDecl); if (NewVD == 0) return 0; } @@ -1824,6 +1874,7 @@ Sema::DeclTy *Sema::FinalizeDeclaratorGroup(Scope *S, DeclTy *group) { /// to introduce parameters into function prototype scope. Sema::DeclTy * Sema::ActOnParamDeclarator(Scope *S, Declarator &D) { + // FIXME: disallow CXXScopeSpec for param declarators. const DeclSpec &DS = D.getDeclSpec(); // Verify C99 6.7.5.3p2: The only SCS allowed is 'register'. @@ -1953,6 +2004,10 @@ Sema::DeclTy *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, DeclTy *D) { Diag(Definition->getLocation(), diag::err_previous_definition); } + if (CXXMethodDecl *MD = dyn_cast(FD)) + if (isa(CurContext)) + MD->setInlineDefinition(true); + PushDeclContext(FD); // Check the validity of our function parameters @@ -2083,6 +2138,53 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagType, TagKind TK, case DeclSpec::TST_class: Kind = TagDecl::TK_class; break; case DeclSpec::TST_enum: Kind = TagDecl::TK_enum; break; } + + if (Name && SS.isNotEmpty()) { + DeclContext *DC = static_cast(SS.getScopeRep()); + if (DC == 0) { + // Invalid C++ scope specifier. + Name = 0; + goto CreateNewDecl; + } + + TagDecl *PrevDecl = + dyn_cast_or_null(LookupDecl(Name, Decl::IDNS_Tag, S, DC)); + if (PrevDecl == 0) { + // No tag member found. + Diag(NameLoc, diag::err_not_tag_in_scope, Name->getName(), + SS.getRange()); + Name = 0; + goto CreateNewDecl; + } + + if (PrevDecl->getTagKind() != Kind) { + Diag(KWLoc, diag::err_use_with_wrong_tag, Name->getName()); + Diag(PrevDecl->getLocation(), diag::err_previous_use); + // Recover by making this an anonymous redefinition. + Name = 0; + goto CreateNewDecl; + } + + // If this is a use or a forward declaration, we're good. + if (TK != TK_Definition) + return PrevDecl; + + // Diagnose attempts to redefine a tag. + if (PrevDecl->isDefinition()) { + Diag(NameLoc, diag::err_redefinition, Name->getName()); + Diag(PrevDecl->getLocation(), diag::err_previous_definition); + // If this is a redefinition, recover by making this struct be + // anonymous, which will make any later references get the previous + // definition. + Name = 0; + goto CreateNewDecl; + } + + // Okay, this is definition of a previously declared or referenced + // tag. Move the location of the decl to be the definition site. + PrevDecl->setLocation(NameLoc); + return PrevDecl; + } // Two code paths: a new one for structs/unions/classes where we create // separate decls for forward declarations, and an old (eventually to @@ -2090,6 +2192,8 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagType, TagKind TK, if (Kind != TagDecl::TK_enum) return ActOnTagStruct(S, Kind, TK, KWLoc, Name, NameLoc, Attr); + { + // If this is a named struct, check to see if there was a previous forward // declaration or definition. // Use ScopedDecl instead of TagDecl, because a NamespaceDecl may come up. @@ -2147,6 +2251,10 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagType, TagKind TK, } } } + + } // subscope in which an already declared tag is handled. + + CreateNewDecl: // If there is an identifier, use the location of the identifier as the // location of the decl, otherwise use the location of the struct/union diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 2ac5804d1e..8bdb389f1f 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -264,7 +264,14 @@ void Sema::CheckCXXDefaultArguments(FunctionDecl *FD) { /// the innermost class. bool Sema::isCurrentClassName(const IdentifierInfo &II, Scope *, const CXXScopeSpec *SS) { - if (CXXRecordDecl *CurDecl = dyn_cast_or_null(CurContext)) + CXXRecordDecl *CurDecl; + if (SS) { + DeclContext *DC = static_cast(SS->getScopeRep()); + CurDecl = dyn_cast_or_null(DC); + } else + CurDecl = dyn_cast_or_null(CurContext); + + if (CurDecl) return &II == CurDecl->getIdentifier(); else return false; @@ -1253,7 +1260,7 @@ Sema::DeclTy *Sema::ActOnStartNamespaceDef(Scope *NamespcScope, // in that declarative region, it is treated as an original-namespace-name. Decl *PrevDecl = - LookupDecl(II, Decl::IDNS_Tag | Decl::IDNS_Ordinary, DeclRegionScope, + LookupDecl(II, Decl::IDNS_Tag | Decl::IDNS_Ordinary, DeclRegionScope, 0, /*enableLazyBuiltinCreation=*/false); if (PrevDecl && isDeclInScope(PrevDecl, CurContext, DeclRegionScope)) { diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index d638b8dc98..3bb7c09ce8 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -335,12 +335,21 @@ static bool ShouldSnapshotBlockValueReference(BlockSemaInfo *CurBlock, /// ActOnIdentifierExpr - The parser read an identifier in expression context, /// validate it per-C99 6.5.1. HasTrailingLParen indicates whether this /// identifier is used in a function call context. +/// LookupCtx is only used for a C++ qualified-id (foo::bar) to indicate the +/// class or namespace that the identifier must be a member of. Sema::ExprResult Sema::ActOnIdentifierExpr(Scope *S, SourceLocation Loc, IdentifierInfo &II, bool HasTrailingLParen, const CXXScopeSpec *SS) { // Could be enum-constant, value decl, instance variable, etc. - Decl *D = LookupDecl(&II, Decl::IDNS_Ordinary, S); + Decl *D; + if (SS && !SS->isEmpty()) { + DeclContext *DC = static_cast(SS->getScopeRep()); + if (DC == 0) + return true; + D = LookupDecl(&II, Decl::IDNS_Ordinary, S, DC); + } else + D = LookupDecl(&II, Decl::IDNS_Ordinary, S); // If this reference is in an Objective-C method, then ivar lookup happens as // well. @@ -378,7 +387,11 @@ Sema::ExprResult Sema::ActOnIdentifierExpr(Scope *S, SourceLocation Loc, else { // If this name wasn't predeclared and if this is not a function call, // diagnose the problem. - return Diag(Loc, diag::err_undeclared_var_use, II.getName()); + if (SS && !SS->isEmpty()) + return Diag(Loc, diag::err_typecheck_no_member, + II.getName(), SS->getRange()); + else + return Diag(Loc, diag::err_undeclared_var_use, II.getName()); } } diff --git a/test/SemaCXX/nested-name-spec.cpp b/test/SemaCXX/nested-name-spec.cpp new file mode 100644 index 0000000000..bbca04cd9a --- /dev/null +++ b/test/SemaCXX/nested-name-spec.cpp @@ -0,0 +1,56 @@ +// RUN: clang -fsyntax-only -verify %s +namespace A { + struct C { + static int cx; + }; + int ax; + void Af(); +} + +A:: ; // expected-error {{expected unqualified-id}} +::A::ax::undef ex3; // expected-error {{expected a class or namespace}} expected-error {{expected '=', ',', ';', 'asm', or '__attribute__' after declarator}} +A::undef1::undef2 ex4; // expected-error {{no member named 'undef1'}} expected-error {{expected '=', ',', ';', 'asm', or '__attribute__' after declarator}} + +class C2 { + void m(); + int x; +}; + +void C2::m() { + x = 0; +} + +namespace B { + void ::A::Af() {} // expected-error {{definition or redeclaration for 'Af' not in a namespace enclosing 'A'}} +} + +void f1() { + void A::Af(); // expected-error {{definition or redeclaration for 'Af' not allowed inside a function}} +} + +void f2() { + A:: ; // expected-error {{expected unqualified-id}} + A::C::undef = 0; // expected-error {{no member named 'undef'}} + ::A::C::cx = 0; + int x = ::A::ax = A::C::cx; + x = sizeof(A::C); + x = sizeof(::A::C::cx); +} + +A::C c1; +struct A::C c2; +struct S : public A::C {}; +struct A::undef; // expected-error {{'undef' does not name a tag member in the specified scope}} + +void f3() { + N::x = 0; // expected-error {{use of undeclared identifier 'N'}} + int N; + N::x = 0; // expected-error {{expected a class or namespace}} + { int A; A::ax = 0; } + { enum A {}; A::ax = 0; } + { enum A { A }; A::ax = 0; } + { typedef int A; A::ax = 0; } + { typedef int A(); A::ax = 0; } + { typedef A::C A; A::ax = 0; } // expected-error {{no member named 'ax'}} + { typedef A::C A; A::cx = 0; } +}