Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -873,7 +873,7 @@ bool isLocalVarDecl() const { if (getKind() != Decl::Var) return false; - if (const DeclContext *DC = getDeclContext()) + if (const DeclContext *DC = getLexicalDeclContext()) return DC->getRedeclContext()->isFunctionOrMethod(); return false; } @@ -883,7 +883,7 @@ bool isFunctionOrMethodVarDecl() const { if (getKind() != Decl::Var) return false; - const DeclContext *DC = getDeclContext()->getRedeclContext(); + const DeclContext *DC = getLexicalDeclContext()->getRedeclContext(); return DC->isFunctionOrMethod() && DC->getDeclKind() != Decl::Block; } @@ -959,7 +959,7 @@ if (K == ParmVar || K == ImplicitParam) return false; - if (getDeclContext()->getRedeclContext()->isFileContext()) + if (getLexicalDeclContext()->getRedeclContext()->isFileContext()) return true; if (isStaticDataMember()) @@ -3454,10 +3454,8 @@ // If the declaration was previously visible, a redeclaration of it remains // visible even if it wouldn't be visible by itself. - // FIXME: Once we handle local extern decls properly, this should inherit - // the visibility from MostRecent, not from PrevDecl. static_cast(this)->IdentifierNamespace |= - PrevDecl->getIdentifierNamespace() & + MostRecent->getIdentifierNamespace() & (Decl::IDNS_Ordinary | Decl::IDNS_Tag | Decl::IDNS_Type); } else { // Make this first. Index: include/clang/AST/DeclBase.h =================================================================== --- include/clang/AST/DeclBase.h +++ include/clang/AST/DeclBase.h @@ -159,7 +159,12 @@ /// This declaration is a C++ operator declared in a non-class /// context. All such operators are also in IDNS_Ordinary. /// C++ lexical operator lookup looks for these. - IDNS_NonMemberOperator = 0x0400 + IDNS_NonMemberOperator = 0x0400, + + /// This declaration is a function-local extern declaration of a + /// variable or function. This may also be IDNS_Ordinary if it + /// has been declared outside any function. + IDNS_LocalExtern = 0x0800 }; /// ObjCDeclQualifier - 'Qualifiers' written next to the return and @@ -820,6 +825,32 @@ bool isFunctionOrFunctionTemplate() const; /// \brief Changes the namespace of this declaration to reflect that it's + /// a function-local extern declaration. + /// + /// These declarations appear in the lexical context of the extern + /// declaration, but in the semantic context of the enclosing namespace + /// scope. + void setLocalExternDecl() { + assert((IdentifierNamespace == IDNS_Ordinary || + IdentifierNamespace == IDNS_OrdinaryFriend) && + "namespace is not ordinary"); + + Decl *Prev = getPreviousDecl(); + IdentifierNamespace &= ~IDNS_Ordinary; + + IdentifierNamespace |= IDNS_LocalExtern; + if (Prev && Prev->getIdentifierNamespace() & IDNS_Ordinary) + IdentifierNamespace |= IDNS_Ordinary; + } + + /// \brief Determine whether this is a block-scope declaration with linkage. + /// This will either be a local variable declaration declared 'extern', or a + /// local function declaration. + bool isLocalExternDecl() { + return IdentifierNamespace & IDNS_LocalExtern; + } + + /// \brief Changes the namespace of this declaration to reflect that it's /// the object of a friend declaration. /// /// These declarations appear in the lexical context of the friending @@ -829,22 +860,25 @@ void setObjectOfFriendDecl(bool PerformFriendInjection = false) { unsigned OldNS = IdentifierNamespace; assert((OldNS & (IDNS_Tag | IDNS_Ordinary | - IDNS_TagFriend | IDNS_OrdinaryFriend)) && + IDNS_TagFriend | IDNS_OrdinaryFriend | + IDNS_LocalExtern)) && "namespace includes neither ordinary nor tag"); assert(!(OldNS & ~(IDNS_Tag | IDNS_Ordinary | IDNS_Type | - IDNS_TagFriend | IDNS_OrdinaryFriend)) && + IDNS_TagFriend | IDNS_OrdinaryFriend | + IDNS_LocalExtern)) && "namespace includes other than ordinary or tag"); Decl *Prev = getPreviousDecl(); - IdentifierNamespace = 0; + IdentifierNamespace &= ~(IDNS_Ordinary | IDNS_Tag | IDNS_Type); + if (OldNS & (IDNS_Tag | IDNS_TagFriend)) { IdentifierNamespace |= IDNS_TagFriend; - if (PerformFriendInjection || + if (PerformFriendInjection || (Prev && Prev->getIdentifierNamespace() & IDNS_Tag)) IdentifierNamespace |= IDNS_Tag | IDNS_Type; } - if (OldNS & (IDNS_Ordinary | IDNS_OrdinaryFriend)) { + if (OldNS & (IDNS_Ordinary | IDNS_OrdinaryFriend | IDNS_LocalExtern)) { IdentifierNamespace |= IDNS_OrdinaryFriend; if (PerformFriendInjection || (Prev && Prev->getIdentifierNamespace() & IDNS_Ordinary)) Index: include/clang/Sema/Lookup.h =================================================================== --- include/clang/Sema/Lookup.h +++ include/clang/Sema/Lookup.h @@ -613,6 +613,13 @@ return Filter(*this); } + void setFindLocalExtern(bool FindLocalExtern) { + if (FindLocalExtern) + IDNS |= Decl::IDNS_LocalExtern; + else + IDNS &= ~Decl::IDNS_LocalExtern; + } + private: void diagnose() { if (isAmbiguous()) Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -1455,6 +1455,7 @@ bool diagnoseQualifiedDeclaration(CXXScopeSpec &SS, DeclContext *DC, DeclarationName Name, SourceLocation Loc); + static bool adjustContextForLocalExternDecl(DeclContext *&DC); void DiagnoseFunctionSpecifiers(const DeclSpec &DS); void CheckShadow(Scope *S, VarDecl *D, const LookupResult& R); void CheckShadow(Scope *S, VarDecl *D); @@ -6432,8 +6433,9 @@ void BuildVariableInstantiation(VarDecl *NewVar, VarDecl *OldVar, const MultiLevelTemplateArgumentList &TemplateArgs, - LateInstantiatedAttrVec *LateAttrs = 0, - LocalInstantiationScope *StartingScope = 0, + LateInstantiatedAttrVec *LateAttrs, + DeclContext *Owner, + LocalInstantiationScope *StartingScope, bool InstantiatingVarTemplate = false); void InstantiateVariableInitializer( VarDecl *Var, VarDecl *OldVar, Index: lib/Sema/SemaAccess.cpp =================================================================== --- lib/Sema/SemaAccess.cpp +++ lib/Sema/SemaAccess.cpp @@ -1483,7 +1483,9 @@ DeclContext *DC = D->getDeclContext(); if (FunctionDecl *FN = dyn_cast(D)) { - if (!DC->isFunctionOrMethod()) + if (D->getLexicalDeclContext()->isFunctionOrMethod()) + DC = D->getLexicalDeclContext(); + else DC = FN; } else if (TemplateDecl *TD = dyn_cast(D)) { DC = cast(TD->getTemplatedDecl()); Index: lib/Sema/SemaCodeComplete.cpp =================================================================== --- lib/Sema/SemaCodeComplete.cpp +++ lib/Sema/SemaCodeComplete.cpp @@ -715,8 +715,8 @@ return CCP_Unlikely; // Context-based decisions. - const DeclContext *DC = ND->getDeclContext()->getRedeclContext(); - if (DC->isFunctionOrMethod() || isa(DC)) { + const DeclContext *LexicalDC = ND->getLexicalDeclContext(); + if (LexicalDC->isFunctionOrMethod()) { // _cmd is relatively rare if (const ImplicitParamDecl *ImplicitParam = dyn_cast(ND)) @@ -726,6 +726,8 @@ return CCP_LocalDeclaration; } + + const DeclContext *DC = ND->getDeclContext()->getRedeclContext(); if (DC->isRecord() || isa(DC)) return CCP_MemberDeclaration; @@ -876,8 +878,8 @@ for (; I != IEnd; ++I) { // A tag declaration does not hide a non-tag declaration. if (I->first->hasTagIdentifierNamespace() && - (IDNS & (Decl::IDNS_Member | Decl::IDNS_Ordinary | - Decl::IDNS_ObjCProtocol))) + (IDNS & (Decl::IDNS_Member | Decl::IDNS_Ordinary | + Decl::IDNS_LocalExtern | Decl::IDNS_ObjCProtocol))) continue; // Protocols are in distinct namespaces from everything else. @@ -1038,7 +1040,9 @@ bool ResultBuilder::IsOrdinaryName(const NamedDecl *ND) const { ND = cast(ND->getUnderlyingDecl()); - unsigned IDNS = Decl::IDNS_Ordinary; + // If name lookup finds a local extern declaration, then we are in a + // context where it behaves like an ordinary name. + unsigned IDNS = Decl::IDNS_Ordinary | Decl::IDNS_LocalExtern; if (SemaRef.getLangOpts().CPlusPlus) IDNS |= Decl::IDNS_Tag | Decl::IDNS_Namespace | Decl::IDNS_Member; else if (SemaRef.getLangOpts().ObjC1) { @@ -1056,7 +1060,7 @@ if (isa(ND) || isa(ND)) return false; - unsigned IDNS = Decl::IDNS_Ordinary; + unsigned IDNS = Decl::IDNS_Ordinary | Decl::IDNS_LocalExtern; if (SemaRef.getLangOpts().CPlusPlus) IDNS |= Decl::IDNS_Tag | Decl::IDNS_Namespace | Decl::IDNS_Member; else if (SemaRef.getLangOpts().ObjC1) { @@ -1083,7 +1087,7 @@ bool ResultBuilder::IsOrdinaryNonValueName(const NamedDecl *ND) const { ND = cast(ND->getUnderlyingDecl()); - unsigned IDNS = Decl::IDNS_Ordinary; + unsigned IDNS = Decl::IDNS_Ordinary | Decl::IDNS_LocalExtern; if (SemaRef.getLangOpts().CPlusPlus) IDNS |= Decl::IDNS_Tag | Decl::IDNS_Namespace; Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -1020,12 +1020,12 @@ if (AddToContext) CurContext->addDecl(D); - // Out-of-line definitions shouldn't be pushed into scope in C++. - // Out-of-line variable and function definitions shouldn't even in C. - if ((getLangOpts().CPlusPlus || isa(D) || isa(D)) && - D->isOutOfLine() && + // Out-of-line definitions shouldn't be pushed into scope in C++, unless they + // are function-local declarations. + if (getLangOpts().CPlusPlus && D->isOutOfLine() && !D->getDeclContext()->getRedeclContext()->Equals( - D->getLexicalDeclContext()->getRedeclContext())) + D->getLexicalDeclContext()->getRedeclContext()) && + !D->getLexicalDeclContext()->isFunctionOrMethod()) return; // Template instantiations should also not be pushed into scope. @@ -2422,7 +2422,9 @@ ? New->getTypeSourceInfo()->getType()->castAs() : NewType)->getResultType(); QualType ResQT; - if (!Context.hasSameType(OldDeclaredReturnType, NewDeclaredReturnType)) { + if (!Context.hasSameType(OldDeclaredReturnType, NewDeclaredReturnType) && + !((NewQType->isDependentType() || OldQType->isDependentType()) && + New->isLocalExternDecl())) { if (NewDeclaredReturnType->isObjCObjectPointerType() && OldDeclaredReturnType->isObjCObjectPointerType()) ResQT = Context.mergeObjCGCQualifiers(NewQType, OldQType); @@ -2574,6 +2576,14 @@ if (OldQTypeForComparison == NewQType) return MergeCompatibleFunctionDecls(New, Old, S, MergeTypeWithOld); + if ((NewQType->isDependentType() || OldQType->isDependentType()) && + New->isLocalExternDecl()) { + // It's OK if we couldn't merge types for a local function declaraton + // if either the old or new type is dependent. We'll merge the types + // when we instantiate the function. + return false; + } + // Fall through for conflicting redeclarations and redefinitions. } @@ -2706,7 +2716,7 @@ // local declaration will produce a hard error; if it doesn't // remain visible, a single bogus local redeclaration (which is // actually only a warning) could break all the downstream code. - if (!New->getDeclContext()->isFunctionOrMethod()) + if (!New->getLexicalDeclContext()->isFunctionOrMethod()) New->getIdentifier()->setBuiltinID(Builtin::NotBuiltin); return false; @@ -2817,18 +2827,21 @@ NewArray->getElementType())) MergedT = New->getType(); } else if (Old->getType()->isArrayType() && - New->getType()->isIncompleteArrayType()) { + New->getType()->isIncompleteArrayType()) { const ArrayType *OldArray = Context.getAsArrayType(Old->getType()); const ArrayType *NewArray = Context.getAsArrayType(New->getType()); if (Context.hasSameType(OldArray->getElementType(), NewArray->getElementType())) MergedT = Old->getType(); - } else if (New->getType()->isObjCObjectPointerType() - && Old->getType()->isObjCObjectPointerType()) { - MergedT = Context.mergeObjCGCQualifiers(New->getType(), - Old->getType()); + } else if (New->getType()->isObjCObjectPointerType() && + Old->getType()->isObjCObjectPointerType()) { + MergedT = Context.mergeObjCGCQualifiers(New->getType(), + Old->getType()); } } else { + // C 6.2.7p2: + // All declarations that refer to the same object or function shall have + // compatible type. MergedT = Context.mergeTypes(New->getType(), Old->getType()); } if (MergedT.isNull()) { @@ -4303,8 +4316,15 @@ // If this has an identifier and is not an invalid redeclaration or // function template specialization, add it to the scope stack. if (New->getDeclName() && AddToScope && - !(D.isRedeclaration() && New->isInvalidDecl())) - PushOnScopeChains(New, S); + !(D.isRedeclaration() && New->isInvalidDecl())) { + // Only make a locally-scoped extern declaration visible if it is the first + // declaration of this entity. Qualified lookup for such an entity should + // only find this declaration if there is no visible declaration of it. + bool AddToContext = !D.isRedeclaration() || !New->isLocalExternDecl(); + PushOnScopeChains(New, S, AddToContext); + if (!AddToContext) + CurContext->addHiddenDecl(New); + } return New; } @@ -4803,6 +4823,30 @@ llvm_unreachable("Unexpected context"); } +/// Adjust the \c DeclContext for a function or variable that might be a +/// function-local external declaration. +bool Sema::adjustContextForLocalExternDecl(DeclContext *&DC) { + if (!DC->isFunctionOrMethod()) + return false; + + // If this is a local extern function or variable declared within a function + // template, don't add it into the enclosing namespace scope until it is + // instantiated; it might have a dependent type right now. + if (DC->isDependentContext()) + return true; + + // C++11 [basic.link]p7: + // When a block scope declaration of an entity with linkage is not found to + // refer to some other declaration, then that entity is a member of the + // innermost enclosing namespace. + // + // Per C++11 [namespace.def]p6, the innermost enclosing namespace is a + // semantically-enclosing namespace, not a lexically-enclosing one. + while (!DC->isFileContext() && !isa(DC)) + DC = DC->getParent(); + return true; +} + bool Sema::HandleVariableRedeclaration(Decl *D, CXXScopeSpec &SS) { // If this is a redeclaration of a variable template or a forward // declaration of a variable template partial specialization @@ -4830,6 +4874,10 @@ VarDecl::StorageClass SC = StorageClassSpecToVarDeclStorageClass(D.getDeclSpec()); + DeclContext *OriginalDC = DC; + bool IsLocalExternDecl = SC == SC_Extern && + adjustContextForLocalExternDecl(DC); + if (getLangOpts().OpenCL && !getOpenCLOptions().cl_khr_fp16) { // OpenCL v1.2 s6.1.1.1: reject declaring variables of the half and // half array type (unless the cl_khr_fp16 extension is enabled). @@ -5160,6 +5208,9 @@ if (NewTemplate) NewTemplate->setLexicalDeclContext(CurContext); + if (IsLocalExternDecl) + NewVD->setLocalExternDecl(); + if (DeclSpec::TSCS TSCS = D.getDeclSpec().getThreadStorageClassSpec()) { if (NewVD->hasLocalStorage()) { // C++11 [dcl.stc]p4: @@ -5287,7 +5338,7 @@ // scope and are out-of-semantic-context declarations (if the new // declaration has linkage). FilterLookupForScope( - Previous, DC, S, shouldConsiderLinkage(NewVD), + Previous, OriginalDC, S, shouldConsiderLinkage(NewVD), IsExplicitSpecialization || IsVariableTemplateSpecialization); // Check whether the previous declaration is in the same block scope. This @@ -5296,7 +5347,7 @@ NewVD->isLocalVarDecl() && NewVD->hasExternalStorage()) NewVD->setPreviousDeclInSameBlockScope( Previous.isSingleResult() && !Previous.isShadowed() && - isDeclInScope(Previous.getFoundDecl(), DC, S, false)); + isDeclInScope(Previous.getFoundDecl(), OriginalDC, S, false)); if (!getLangOpts().CPlusPlus) { D.setRedeclaration(CheckVariableDeclaration(NewVD, Previous)); @@ -5553,12 +5604,8 @@ LookupResult &Previous) { if (!S.getLangOpts().CPlusPlus) { // In C, when declaring a global variable, look for a corresponding 'extern' - // variable declared in function scope. - // - // FIXME: The corresponding case in C++ does not work. We should instead - // set the semantic DC for an extern local variable to be the innermost - // enclosing namespace, and ensure they are only found by redeclaration - // lookup. + // variable declared in function scope. We don't need this in C++, because + // we find local extern decls in the surrounding file-scope DeclContext. if (ND->getDeclContext()->getRedeclContext()->isTranslationUnit()) { if (NamedDecl *Prev = S.findLocallyScopedExternCDecl(ND->getDeclName())) { Previous.clear(); @@ -6042,6 +6089,7 @@ bool FDisConst = MD && MD->isConst(); bool IsMember = MD || !IsLocalFriend; + // FIXME: These notes are poorly worded for the local friend case. if (unsigned Idx = NearMatch->second) { ParmVarDecl *FDParam = FD->getParamDecl(Idx-1); SourceLocation Loc = FDParam->getTypeSpecStartLoc(); @@ -6465,6 +6513,9 @@ bool isVirtualOkay = false; + DeclContext *OriginalDC = DC; + bool IsLocalExternDecl = adjustContextForLocalExternDecl(DC); + FunctionDecl *NewFD = CreateNewFunctionDecl(*this, D, DC, R, TInfo, SC, isVirtualOkay); if (!NewFD) return 0; @@ -6472,6 +6523,14 @@ if (OriginalLexicalContext && OriginalLexicalContext->isObjCContainer()) NewFD->setTopLevelDeclInObjCContainer(); + // Set the lexical context. If this is a function-scope declaration, or has a + // C++ scope specifier, or is the object of a friend declaration, the lexical + // context will be different from the semantic context. + NewFD->setLexicalDeclContext(CurContext); + + if (IsLocalExternDecl) + NewFD->setLocalExternDecl(); + if (getLangOpts().CPlusPlus) { bool isInline = D.getDeclSpec().isInlineSpecified(); bool isVirtual = D.getDeclSpec().isVirtualSpecified(); @@ -6499,12 +6558,7 @@ isFunctionTemplateSpecialization = false; if (D.isInvalidType()) NewFD->setInvalidDecl(); - - // Set the lexical context. If the declarator has a C++ - // scope specifier, or is the object of a friend declaration, the - // lexical context will be different from the semantic context. - NewFD->setLexicalDeclContext(CurContext); - + // Match up the template parameter lists with the scope specifier, then // determine whether we have a template or a template specialization. bool Invalid = false; @@ -6760,7 +6814,7 @@ } // Filter out previous declarations that don't match the scope. - FilterLookupForScope(Previous, DC, S, shouldConsiderLinkage(NewFD), + FilterLookupForScope(Previous, OriginalDC, S, shouldConsiderLinkage(NewFD), isExplicitSpecialization || isFunctionTemplateSpecialization); @@ -9208,21 +9262,21 @@ // Don't warn for OpenCL kernels. if (FD->hasAttr()) return false; - + bool MissingPrototype = true; for (const FunctionDecl *Prev = FD->getPreviousDecl(); Prev; Prev = Prev->getPreviousDecl()) { // Ignore any declarations that occur in function or method // scope, because they aren't visible from the header. - if (Prev->getDeclContext()->isFunctionOrMethod()) + if (Prev->getLexicalDeclContext()->isFunctionOrMethod()) continue; - + MissingPrototype = !Prev->getType()->isFunctionProtoType(); if (FD->getNumParams() == 0) PossibleZeroParamPrototype = Prev; break; } - + return MissingPrototype; } Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -438,9 +438,9 @@ // declaration (not even to the same value). // // C++ [dcl.fct.default]p6: - // Except for member functions of class templates, the default arguments - // in a member function definition that appears outside of the class - // definition are added to the set of default arguments provided by the + // Except for member functions of class templates, the default arguments + // in a member function definition that appears outside of the class + // definition are added to the set of default arguments provided by the // member function declaration in the class definition. for (unsigned p = 0, NumParams = Old->getNumParams(); p < NumParams; ++p) { ParmVarDecl *OldParam = Old->getParamDecl(p); @@ -450,9 +450,18 @@ bool NewParamHasDfl = NewParam->hasDefaultArg(); NamedDecl *ND = Old; - if (S && !isDeclInScope(ND, New->getDeclContext(), S)) + + // The declaration context corresponding to the scope is the semantic + // parent, unless this is a local function declaration, in which case + // it is that surrounding function. + DeclContext *ScopeDC = New->getLexicalDeclContext(); + if (!ScopeDC->isFunctionOrMethod()) + ScopeDC = New->getDeclContext(); + if (S && !isDeclInScope(ND, ScopeDC, S) && + !New->getDeclContext()->isRecord()) // Ignore default parameters of old decl if they are not in - // the same scope. + // the same scope and this is not an out-of-line definition of + // a member function. OldParamHasDfl = false; if (OldParamHasDfl && NewParamHasDfl) { @@ -11401,6 +11410,7 @@ // declared the function in, if we were permitted to, for error recovery. DC = FunctionContainingLocalClass; } + adjustContextForLocalExternDecl(DC); // C++ [class.friend]p6: // A function can be defined in a friend declaration of a class if and Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -2459,7 +2459,7 @@ // turn off ADL anyway). if (isa(D)) D = cast(D)->getTargetDecl(); - else if (D->getDeclContext()->isFunctionOrMethod()) + else if (D->getLexicalDeclContext()->isFunctionOrMethod()) return false; // C++0x [basic.lookup.argdep]p3: Index: lib/Sema/SemaLookup.cpp =================================================================== --- lib/Sema/SemaLookup.cpp +++ lib/Sema/SemaLookup.cpp @@ -223,6 +223,8 @@ if (Redeclaration) IDNS |= Decl::IDNS_TagFriend | Decl::IDNS_OrdinaryFriend; } + if (Redeclaration) + IDNS |= Decl::IDNS_LocalExtern; break; case Sema::LookupOperatorName: @@ -847,6 +849,26 @@ return std::make_pair(Lexical, false); } +namespace { +/// An RAII object to specify that we want to find block scope extern +/// declarations. +struct FindLocalExternScope { + FindLocalExternScope(LookupResult &R) + : R(R), OldFindLocalExtern(R.getIdentifierNamespace() & + Decl::IDNS_LocalExtern) { + R.setFindLocalExtern(true); + } + void restore() { + R.setFindLocalExtern(OldFindLocalExtern); + } + ~FindLocalExternScope() { + restore(); + } + LookupResult &R; + bool OldFindLocalExtern; +}; +} + bool Sema::CppLookupName(LookupResult &R, Scope *S) { assert(getLangOpts().CPlusPlus && "Can perform only C++ lookup"); @@ -891,6 +913,10 @@ bool VisitedUsingDirectives = false; bool LeftStartingScope = false; DeclContext *OutsideOfTemplateParamDC = 0; + + // When performing a scope lookup, we want to find local extern decls. + FindLocalExternScope FindLocals(R); + for (; S && !isNamespaceOrTranslationUnitScope(S); S = S->getParent()) { DeclContext *Ctx = static_cast(S->getEntity()); @@ -1046,7 +1072,12 @@ UDirs.visitScopeChain(Initial, S); UDirs.done(); } - + + // If we're not performing redeclaration lookup, do not look for local + // extern declarations outside of a function scope. + if (!R.isForRedeclaration()) + FindLocals.restore(); + // Lookup namespace scope, and global scope. // Unqualified name lookup in C++ requires looking into scopes // that aren't strictly lexical, and therefore we walk through the @@ -1292,6 +1323,9 @@ S = S->getParent(); } + // When performing a scope lookup, we want to find local extern decls. + FindLocalExternScope FindLocals(R); + // 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 @@ -1361,6 +1395,7 @@ R.resolveKind(); } + return true; } } else { @@ -2858,7 +2893,11 @@ NamedDecl *D = *I; // If the only declaration here is an ordinary friend, consider // it only if it was declared in an associated classes. - if (D->getIdentifierNamespace() == Decl::IDNS_OrdinaryFriend) { + if ((D->getIdentifierNamespace() & Decl::IDNS_Ordinary) == 0) { + // If it's neither ordinarily visible nor a friend, we can't find it. + if ((D->getIdentifierNamespace() & Decl::IDNS_OrdinaryFriend) == 0) + continue; + bool DeclaredInAssociatedClass = false; for (Decl *DI = D; DI; DI = DI->getPreviousDecl()) { DeclContext *LexDC = DI->getLexicalDeclContext(); @@ -3160,6 +3199,7 @@ (!S->getParent() && !Visited.alreadyVisitedContext((DeclContext *)S->getEntity())) || ((DeclContext *)S->getEntity())->isFunctionOrMethod()) { + FindLocalExternScope FindLocals(Result); // Walk through the declarations in this Scope. for (Scope::decl_iterator D = S->decl_begin(), DEnd = S->decl_end(); D != DEnd; ++D) { Index: lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -347,8 +347,12 @@ return 0; } + DeclContext *DC = Owner; + if (D->isLocalExternDecl()) + SemaRef.adjustContextForLocalExternDecl(DC); + // Build the instantiated declaration. - VarDecl *Var = VarDecl::Create(SemaRef.Context, Owner, D->getInnerLocStart(), + VarDecl *Var = VarDecl::Create(SemaRef.Context, DC, D->getInnerLocStart(), D->getLocation(), D->getIdentifier(), DI->getType(), DI, D->getStorageClass()); @@ -361,7 +365,7 @@ if (SubstQualifier(D, Var)) return 0; - SemaRef.BuildVariableInstantiation(Var, D, TemplateArgs, LateAttrs, + SemaRef.BuildVariableInstantiation(Var, D, TemplateArgs, LateAttrs, Owner, StartingScope, InstantiatingVarTemplate); return Var; } @@ -1199,11 +1203,13 @@ } // If we're instantiating a local function declaration, put the result - // in the owner; otherwise we need to find the instantiated context. + // in the enclosing namespace; otherwise we need to find the instantiated + // context. DeclContext *DC; - if (D->getDeclContext()->isFunctionOrMethod()) + if (D->isLocalExternDecl()) { DC = Owner; - else if (isFriend && QualifierLoc) { + SemaRef.adjustContextForLocalExternDecl(DC); + } else if (isFriend && QualifierLoc) { CXXScopeSpec SS; SS.Adopt(QualifierLoc); DC = SemaRef.computeDeclContext(SS); @@ -1227,8 +1233,11 @@ if (QualifierLoc) Function->setQualifierInfo(QualifierLoc); + if (D->isLocalExternDecl()) + Function->setLocalExternDecl(); + DeclContext *LexicalDC = Owner; - if (!isFriend && D->isOutOfLine()) { + if (!isFriend && D->isOutOfLine() && !D->isLocalExternDecl()) { assert(D->getDeclContext()->isFileContext()); LexicalDC = D->getDeclContext(); } @@ -1294,8 +1303,11 @@ bool isExplicitSpecialization = false; - LookupResult Previous(SemaRef, Function->getDeclName(), SourceLocation(), - Sema::LookupOrdinaryName, Sema::ForRedeclaration); + LookupResult Previous( + SemaRef, Function->getDeclName(), SourceLocation(), + D->isLocalExternDecl() ? Sema::LookupRedeclarationWithLinkage + : Sema::LookupOrdinaryName, + Sema::ForRedeclaration); if (DependentFunctionTemplateSpecializationInfo *Info = D->getDependentSpecializationInfo()) { @@ -1427,6 +1439,9 @@ } } + if (Function->isLocalExternDecl() && !Function->getPreviousDecl()) + DC->makeDeclVisibleInContext(PrincipalDecl); + if (Function->isOverloadedOperator() && !DC->isRecord() && PrincipalDecl->isInIdentifierNamespace(Decl::IDNS_Ordinary)) PrincipalDecl->setNonMemberOperator(); @@ -2358,7 +2373,7 @@ return 0; SemaRef.BuildVariableInstantiation(Var, D, TemplateArgs, LateAttrs, - StartingScope); + Owner, StartingScope); return Var; } @@ -2680,7 +2695,7 @@ if (VarDecl *Def = PartialSpec->getDefinition(SemaRef.getASTContext())) PartialSpec = cast(Def); SemaRef.BuildVariableInstantiation(InstPartialSpec, PartialSpec, TemplateArgs, - LateAttrs, StartingScope); + LateAttrs, Owner, StartingScope); InstPartialSpec->setInit(PartialSpec->getInit()); return InstPartialSpec; @@ -3335,13 +3350,19 @@ void Sema::BuildVariableInstantiation( VarDecl *NewVar, VarDecl *OldVar, const MultiLevelTemplateArgumentList &TemplateArgs, - LateInstantiatedAttrVec *LateAttrs, LocalInstantiationScope *StartingScope, + LateInstantiatedAttrVec *LateAttrs, DeclContext *Owner, + LocalInstantiationScope *StartingScope, bool InstantiatingVarTemplate) { + // If we are instantiating a local extern declaration, the + // instantiation belongs lexically to the containing function. // If we are instantiating a static data member defined // out-of-line, the instantiation will have the same lexical // context (which will be a namespace scope) as the template. - if (OldVar->isOutOfLine()) + if (OldVar->isLocalExternDecl()) { + NewVar->setLocalExternDecl(); + NewVar->setLexicalDeclContext(Owner); + } else if (OldVar->isOutOfLine()) NewVar->setLexicalDeclContext(OldVar->getLexicalDeclContext()); NewVar->setTSCSpec(OldVar->getTSCSpec()); NewVar->setInitStyle(OldVar->getInitStyle()); @@ -3361,11 +3382,13 @@ if (NewVar->hasAttrs()) CheckAlignasUnderalignment(NewVar); - LookupResult Previous(*this, NewVar->getDeclName(), NewVar->getLocation(), - Sema::LookupOrdinaryName, Sema::ForRedeclaration); + LookupResult Previous( + *this, NewVar->getDeclName(), NewVar->getLocation(), + NewVar->isLocalExternDecl() ? Sema::LookupRedeclarationWithLinkage + : Sema::LookupOrdinaryName, + Sema::ForRedeclaration); - if (NewVar->getLexicalDeclContext()->isFunctionOrMethod() && - OldVar->getPreviousDecl()) { + if (NewVar->isLocalExternDecl() && OldVar->getPreviousDecl()) { // We have a previous declaration. Use that one, so we merge with the // right type. if (NamedDecl *NewPrev = FindInstantiatedDecl( @@ -3376,13 +3399,13 @@ LookupQualifiedName(Previous, NewVar->getDeclContext(), false); CheckVariableDeclaration(NewVar, Previous); - if (OldVar->isOutOfLine()) { - OldVar->getLexicalDeclContext()->addDecl(NewVar); - if (!InstantiatingVarTemplate) + if (!InstantiatingVarTemplate) { + NewVar->getLexicalDeclContext()->addHiddenDecl(NewVar); + if (!NewVar->isLocalExternDecl() || !NewVar->getPreviousDecl()) NewVar->getDeclContext()->makeDeclVisibleInContext(NewVar); - } else { - if (!InstantiatingVarTemplate) - NewVar->getDeclContext()->addDecl(NewVar); + } + + if (!OldVar->isOutOfLine()) { if (NewVar->getDeclContext()->isFunctionOrMethod()) CurrentInstantiationScope->InstantiatedLocal(OldVar, NewVar); } Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -940,7 +940,13 @@ VD->VarDeclBits.ARCPseudoStrong = Record[Idx++]; VD->VarDeclBits.IsConstexpr = Record[Idx++]; VD->VarDeclBits.PreviousDeclInSameBlockScope = Record[Idx++]; - VD->setCachedLinkage(Linkage(Record[Idx++])); + Linkage VarLinkage = Linkage(Record[Idx++]); + VD->setCachedLinkage(VarLinkage); + + // Reconstruct the one piece of the IdentifierNamespace that we need. + if (VarLinkage != NoLinkage && + VD->getLexicalDeclContext()->isFunctionOrMethod()) + VD->setLocalExternDecl(); // Only true variables (not parameters or implicit parameters) can be merged. if (VD->getKind() != Decl::ParmVar && VD->getKind() != Decl::ImplicitParam) @@ -2141,8 +2147,6 @@ // // FIXME: In this case, the declaration should only be visible if a module // that makes it visible has been imported. - // FIXME: This is not correct in the case where previous is a local extern - // declaration and D is a friend declaraton. D->IdentifierNamespace |= previous->IdentifierNamespace & (Decl::IDNS_Ordinary | Decl::IDNS_Tag | Decl::IDNS_Type); Index: test/CXX/basic/basic.link/p7.cpp =================================================================== --- test/CXX/basic/basic.link/p7.cpp +++ test/CXX/basic/basic.link/p7.cpp @@ -0,0 +1,73 @@ +// RUN: %clang_cc1 -verify -std=c++1y %s + +// Example from the standard. +namespace X { + void p() { + q(); // expected-error {{undeclared}} + extern void q(); + } + void middle() { + q(); // expected-error {{undeclared}} + } + void q() { /*...*/ } + void bottom() { + q(); + } +} +int q(); + +namespace Test1 { + void f() { + extern int a; // expected-note {{previous}} + int g(void); // expected-note {{previous}} + } + double a; // expected-error {{different type: 'double' vs 'int'}} + double g(); // expected-error {{differ only in their return type}} +} + +namespace Test2 { + void f() { + extern int a; // expected-note {{previous}} + int g(void); // expected-note {{previous}} + } + void h() { + extern double a; // expected-error {{different type: 'double' vs 'int'}} + double g(void); // expected-error {{differ only in their return type}} + } +} + +namespace Test3 { + constexpr void (*f())() { + void h(); + return &h; + } + constexpr void (*g())() { + void h(); + return &h; + } + static_assert(f() == g(), ""); +} + +namespace Test4 { + template + constexpr void (*f())() { + void h(); + return &h; + } + static_assert(f() == f(), ""); + void h(); + static_assert(f() == &h, ""); +} + +namespace Test5 { + constexpr auto f() -> void (*)() { + void g(); + struct X { + friend void g(); + static constexpr auto h() -> void (*)() { return g; } + }; + return X::h(); + } + void g(); + static_assert(f() == g, ""); +} Index: test/CXX/dcl.decl/dcl.meaning/dcl.array/p3.cpp =================================================================== --- test/CXX/dcl.decl/dcl.meaning/dcl.array/p3.cpp +++ test/CXX/dcl.decl/dcl.meaning/dcl.array/p3.cpp @@ -61,6 +61,8 @@ int x = sizeof(array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}} } int y = sizeof(array); + extern int array[]; + int z = sizeof(array); } } @@ -71,6 +73,19 @@ int x = sizeof(array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}} } int y = sizeof(array); + extern int array[]; + int z = sizeof(array); +} + +namespace test8 { + extern int array[]; + void test() { + extern int array[100]; + int x = sizeof(array); + } + int y = sizeof(array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}} + extern int array[]; + int z = sizeof(array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}} } namespace dependent { @@ -143,10 +158,52 @@ } template void n() { - extern T n_var; + extern T n_var; // expected-error {{redefinition of 'n_var' with a different type: 'double' vs 'int'}} expected-note {{previous}} + extern T n_fn(); // expected-error {{functions that differ only in their return type cannot be overloaded}} expected-note {{previous}} } template void n(); - // FIXME: Diagnose this! - float n_var; - template void n(); + template void n(); // expected-note {{in instantiation of}} + + template void o() { + extern T o_var; // expected-note {{previous}} + extern T o_fn(); // expected-note {{previous}} + } + template void o(); + float o_var; // expected-error {{redefinition of 'o_var' with a different type: 'float' vs 'int'}} + float o_fn(); // expected-error {{functions that differ only in their return type cannot be overloaded}} + + int p_var; + int p_fn(); + template void p() { + extern T p_var; + extern T p_fn(); + } +} + +namespace use_outside_ns { + namespace A { + extern int a[3]; + extern int b[]; + extern int c[3]; + void f() { + extern int a[]; + extern int b[3]; + } + template void x() { + extern T c; + extern T d; + } + extern int d[3]; + template void x(); + } + int w = sizeof(A::a); + int x = sizeof(A::b); // expected-error {{incomplete}} + int y = sizeof(A::c); + int z = sizeof(A::d); + namespace A { + int g() { return sizeof(a); } + int h() { return sizeof(b); } // expected-error {{incomplete}} + int i() { return sizeof(c); } + int j() { return sizeof(d); } + } } Index: test/CXX/drs/dr0xx.cpp =================================================================== --- test/CXX/drs/dr0xx.cpp +++ test/CXX/drs/dr0xx.cpp @@ -280,7 +280,7 @@ // dr28: na -namespace dr29 { // dr29: no +namespace dr29 { // dr29: 3.4 void dr29_f0(); // expected-note {{here}} void g0() { void dr29_f0(); } extern "C++" void g0_cxx() { void dr29_f0(); } @@ -291,17 +291,14 @@ extern "C" void g1_c() { void dr29_f1(); } extern "C++" void g1_cxx() { void dr29_f1(); } // expected-error {{different language linkage}} - // FIXME: We should reject this. - void g2() { void dr29_f2(); } - extern "C" void dr29_f2(); - - // FIXME: We should reject this. - extern "C" void g3() { void dr29_f3(); } - extern "C++" void dr29_f3(); - - // FIXME: We should reject this. - extern "C++" void g4() { void dr29_f4(); } - extern "C" void dr29_f4(); + void g2() { void dr29_f2(); } // expected-note {{here}} + extern "C" void dr29_f2(); // expected-error {{different language linkage}} + + extern "C" void g3() { void dr29_f3(); } // expected-note {{here}} + extern "C++" void dr29_f3(); // expected-error {{different language linkage}} + + extern "C++" void g4() { void dr29_f4(); } // expected-note {{here}} + extern "C" void dr29_f4(); // expected-error {{different language linkage}} extern "C" void g5(); extern "C++" void dr29_f5(); Index: test/CodeGenCXX/mangle.cpp =================================================================== --- test/CodeGenCXX/mangle.cpp +++ test/CodeGenCXX/mangle.cpp @@ -9,7 +9,7 @@ // CHECK: @_ZGVZN1N1gEvE1a = internal global //CHECK: @pr5966_i = external global -//CHECK: @_ZL8pr5966_i = internal global +//CHECK: @_ZL8pr5966_j = internal global // CHECK-LABEL: define zeroext i1 @_ZplRK1YRA100_P1X bool operator+(const Y&, X* (&xs)[100]) { return false; } @@ -314,10 +314,10 @@ pr5966_i = 0; } -static int pr5966_i; +static int pr5966_j; void pr5966_bar() { - pr5966_i = 0; + pr5966_j = 0; } namespace test0 { @@ -652,10 +652,10 @@ foo(); } - static char foo() {} + static char bar() {} void test1() { - // CHECK: call signext i8 @_ZN6test24L3fooEv() - foo(); + // CHECK: call signext i8 @_ZN6test24L3barEv() + bar(); } } Index: test/Index/usrs.m =================================================================== --- test/Index/usrs.m +++ test/Index/usrs.m @@ -118,7 +118,7 @@ // CHECK: usrs.m c:objc(cs)Foo Extent=[34:1 - 45:2] // CHECK: usrs.m c:objc(cs)Foo(im)godzilla Extent=[35:1 - 39:2] // CHECK: usrs.m c:usrs.m@402objc(cs)Foo(im)godzilla@a Extent=[36:3 - 36:19] -// CHECK: usrs.m c:objc(cs)Foo(im)godzilla@z Extent=[37:3 - 37:15] +// CHECK: usrs.m c:@z Extent=[37:3 - 37:15] // CHECK: usrs.m c:objc(cs)Foo(cm)kingkong Extent=[40:1 - 43:2] // CHECK: usrs.m c:usrs.m@470objc(cs)Foo(cm)kingkong@local_var Extent=[41:3 - 41:16] // CHECK: usrs.m c:objc(cs)Foo(py)d1 Extent=[44:1 - 44:15] Index: test/SemaCXX/blocks-1.cpp =================================================================== --- test/SemaCXX/blocks-1.cpp +++ test/SemaCXX/blocks-1.cpp @@ -1,5 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s -fblocks -std=c++11 -// expected-no-diagnostics +// RUN: %clang_cc1 -fsyntax-only -verify %s -fblocks -std=c++1y extern "C" int exit(int); @@ -57,3 +56,18 @@ foo(a); }; } + +namespace LocalDecls { + void f() { + (void) ^{ + extern int a; // expected-note {{previous}} + extern int b(); // expected-note {{previous}} + }; + } + void g() { + (void) ^{ + extern float a; // expected-error {{different type}} + extern float b(); // expected-error {{cannot be overloaded}} + }; + } +} Index: test/SemaCXX/cxx0x-initializer-references.cpp =================================================================== --- test/SemaCXX/cxx0x-initializer-references.cpp +++ test/SemaCXX/cxx0x-initializer-references.cpp @@ -36,10 +36,10 @@ }; void call() { - void f(const int&); + one f(const int&); f({1}); - void g(int&); // expected-note {{passing argument}} + one g(int&); // expected-note {{passing argument}} g({1}); // expected-error {{cannot bind to an initializer list temporary}} int i = 0; g({i}); Index: test/SemaCXX/extern-c.cpp =================================================================== --- test/SemaCXX/extern-c.cpp +++ test/SemaCXX/extern-c.cpp @@ -29,16 +29,27 @@ } } -extern "C" { - void test4_f() { - extern int test4_b; // expected-note {{declared with C language linkage here}} +namespace N { + extern "C" { + void test4_f() { + extern int test4_b; // expected-note {{declared with C language linkage here}} + } } } static float test4_b; // expected-error {{declaration of 'test4_b' in global scope conflicts with declaration with C language linkage}} extern "C" { - void test5_f() { - extern int test5_b; // expected-note {{declared with C language linkage here}} + void test4c_f() { + extern int test4_c; // expected-note {{previous}} + } +} +static float test4_c; // expected-error {{redefinition of 'test4_c' with a different type: 'float' vs 'int'}} + +namespace N { + extern "C" { + void test5_f() { + extern int test5_b; // expected-note {{declared with C language linkage here}} + } } } extern "C" { @@ -46,6 +57,15 @@ } extern "C" { + void test5c_f() { + extern int test5_c; // expected-note {{previous}} + } +} +extern "C" { + static float test5_c; // expected-error {{redefinition of 'test5_c' with a different type: 'float' vs 'int'}} +} + +extern "C" { void f() { extern int test6_b; } Index: test/SemaCXX/function-redecl.cpp =================================================================== --- test/SemaCXX/function-redecl.cpp +++ test/SemaCXX/function-redecl.cpp @@ -4,17 +4,14 @@ namespace N { void f1() { void foo(int); // okay - void bar(int); + void bar(int); // expected-note 2{{previous declaration is here}} } void foo(int); // expected-note 2{{previous declaration is here}} void f2() { int foo(int); // expected-error {{functions that differ only in their return type cannot be overloaded}} - // FIXME: We should be able to diagnose the conflict between this - // declaration of 'bar' and the previous one, even though they come - // from different lexical scopes. - int bar(int); // expected-note {{previous declaration is here}} + int bar(int); // expected-error {{functions that differ only in their return type cannot be overloaded}} int baz(int); // expected-note {{previous declaration is here}} { Index: test/SemaCXX/warn-unreachable.cpp =================================================================== --- test/SemaCXX/warn-unreachable.cpp +++ test/SemaCXX/warn-unreachable.cpp @@ -62,8 +62,8 @@ struct S { int mem; } s; - S &foor() __attribute__((noreturn)); - foor() + S &foonr() __attribute__((noreturn)); + foonr() .mem; // expected-warning {{will never be executed}} } Index: www/cxx_dr_status.html =================================================================== --- www/cxx_dr_status.html +++ www/cxx_dr_status.html @@ -212,7 +212,7 @@ 29 CD1 Linkage of locally declared functions - No + SVN 30