diff options
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/AST/Decl.cpp | 41 | ||||
-rw-r--r-- | clang/lib/Sema/IdentifierResolver.cpp | 33 | ||||
-rw-r--r-- | clang/lib/Sema/IdentifierResolver.h | 10 | ||||
-rw-r--r-- | clang/lib/Sema/Sema.h | 30 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 137 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 23 |
6 files changed, 182 insertions, 92 deletions
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index b30b86980f6..1ebfbf79dac 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -63,6 +63,28 @@ QualType ParmVarDecl::getOriginalType() const { return getType(); } +bool VarDecl::isExternC(ASTContext &Context) const { + if (!Context.getLangOptions().CPlusPlus) + return (getDeclContext()->isTranslationUnit() && + getStorageClass() != Static) || + (getDeclContext()->isFunctionOrMethod() && hasExternalStorage()); + + for (const DeclContext *DC = getDeclContext(); !DC->isTranslationUnit(); + DC = DC->getParent()) { + if (const LinkageSpecDecl *Linkage = dyn_cast<LinkageSpecDecl>(DC)) { + if (Linkage->getLanguage() == LinkageSpecDecl::lang_c) + return getStorageClass() != Static; + + break; + } + + if (DC->isFunctionOrMethod()) + return false; + } + + return false; +} + OriginalParmVarDecl *OriginalParmVarDecl::Create( ASTContext &C, DeclContext *DC, SourceLocation L, IdentifierInfo *Id, @@ -273,6 +295,25 @@ bool FunctionDecl::isMain() const { getIdentifier() && getIdentifier()->isStr("main"); } +bool FunctionDecl::isExternC(ASTContext &Context) const { + // In C, any non-static, non-overloadable function has external + // linkage. + if (!Context.getLangOptions().CPlusPlus) + return getStorageClass() != Static && !getAttr<OverloadableAttr>(); + + for (const DeclContext *DC = getDeclContext(); !DC->isTranslationUnit(); + DC = DC->getParent()) { + if (const LinkageSpecDecl *Linkage = dyn_cast<LinkageSpecDecl>(DC)) { + if (Linkage->getLanguage() == LinkageSpecDecl::lang_c) + return getStorageClass() != Static && !getAttr<OverloadableAttr>(); + + break; + } + } + + return false; +} + /// \brief Returns a value indicating whether this function /// corresponds to a builtin function. /// diff --git a/clang/lib/Sema/IdentifierResolver.cpp b/clang/lib/Sema/IdentifierResolver.cpp index 6efedb4895e..609c8723959 100644 --- a/clang/lib/Sema/IdentifierResolver.cpp +++ b/clang/lib/Sema/IdentifierResolver.cpp @@ -75,6 +75,18 @@ void IdentifierResolver::IdDeclInfo::RemoveDecl(NamedDecl *D) { assert(0 && "Didn't find this decl on its identifier's chain!"); } +bool +IdentifierResolver::IdDeclInfo::ReplaceDecl(NamedDecl *Old, NamedDecl *New) { + for (DeclsTy::iterator I = Decls.end(); I != Decls.begin(); --I) { + if (Old == *(I-1)) { + *(I - 1) = New; + return true; + } + } + + return false; +} + //===----------------------------------------------------------------------===// // IdentifierResolver Implementation @@ -192,6 +204,27 @@ void IdentifierResolver::RemoveDecl(NamedDecl *D) { return toIdDeclInfo(Ptr)->RemoveDecl(D); } +bool IdentifierResolver::ReplaceDecl(NamedDecl *Old, NamedDecl *New) { + assert(Old->getDeclName() == New->getDeclName() && + "Cannot replace a decl with another decl of a different name"); + + DeclarationName Name = Old->getDeclName(); + void *Ptr = Name.getFETokenInfo<void>(); + + if (!Ptr) + return false; + + if (isDeclPtr(Ptr)) { + if (Ptr == Old) { + Name.setFETokenInfo(New); + return true; + } + return false; + } + + return toIdDeclInfo(Ptr)->ReplaceDecl(Old, New); +} + /// begin - Returns an iterator for decls with name 'Name'. IdentifierResolver::iterator IdentifierResolver::begin(DeclarationName Name) { diff --git a/clang/lib/Sema/IdentifierResolver.h b/clang/lib/Sema/IdentifierResolver.h index 067900eac1c..1843f4ebca1 100644 --- a/clang/lib/Sema/IdentifierResolver.h +++ b/clang/lib/Sema/IdentifierResolver.h @@ -51,6 +51,11 @@ class IdentifierResolver { /// The decl must already be part of the decl chain. void RemoveDecl(NamedDecl *D); + /// Replaces the Old declaration with the New declaration. If the + /// replacement is successful, returns true. If the old + /// declaration was not found, returns false. + bool ReplaceDecl(NamedDecl *Old, NamedDecl *New); + private: DeclsTy Decls; }; @@ -167,6 +172,11 @@ public: /// The decl must already be part of the decl chain. void RemoveDecl(NamedDecl *D); + /// Replace the decl Old with the new declaration New on its + /// identifier chain. Returns true if the old declaration was found + /// (and, therefore, replaced). + bool ReplaceDecl(NamedDecl *Old, NamedDecl *New); + explicit IdentifierResolver(const LangOptions &LangOpt); ~IdentifierResolver(); diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 6e0b935b88a..30550c423d7 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -149,6 +149,33 @@ public: /// FieldCollector - Collects CXXFieldDecls during parsing of C++ classes. llvm::OwningPtr<CXXFieldCollector> FieldCollector; + /// \brief A mapping from external names to the most recent + /// locally-scoped external declaration with that name. + /// + /// This map contains external declarations introduced in local + /// scoped, e.g., + /// + /// \code + /// void f() { + /// void foo(int, int); + /// } + /// \endcode + /// + /// Here, the name "foo" will be associated with the declaration on + /// "foo" within f. This name is not visible outside of + /// "f". However, we still find it in two cases: + /// + /// - If we are declaring another external with the name "foo", we + /// can find "foo" as a previous declaration, so that the types + /// of this external declaration can be checked for + /// compatibility. + /// + /// - If we would implicitly declare "foo" (e.g., due to a call to + /// "foo" in C when no prototype or definition is visible), then + /// we find this declaration of "foo" and complain that it is + /// not visible. + llvm::DenseMap<DeclarationName, NamedDecl *> LocallyScopedExternalDecls; + IdentifierResolver IdResolver; // Enum values used by KnownFunctionIDs (see below). @@ -267,11 +294,12 @@ public: } DeclTy *ActOnDeclarator(Scope *S, Declarator &D, DeclTy *LastInGroup, bool IsFunctionDefinition); + void RegisterLocallyScopedExternCDecl(NamedDecl *ND, NamedDecl *PrevDecl, + Scope *S); NamedDecl* ActOnTypedefDeclarator(Scope* S, Declarator& D, DeclContext* DC, QualType R, Decl* LastDeclarator, Decl* PrevDecl, bool& InvalidDecl, bool &Redeclaration); - void InjectLocallyScopedExternalDeclaration(ValueDecl *VD); NamedDecl* ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC, QualType R, Decl* LastDeclarator, NamedDecl* PrevDecl, bool& InvalidDecl, diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 8764cd9d77c..33e39216884 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -1355,6 +1355,33 @@ static QualType TryToFixInvalidVariablyModifiedType(QualType T, return QualType(); } +/// \brief Register the given locally-scoped external C declaration so +/// that it can be found later for redeclarations +void +Sema::RegisterLocallyScopedExternCDecl(NamedDecl *ND, NamedDecl *PrevDecl, + Scope *S) { + assert(ND->getLexicalDeclContext()->isFunctionOrMethod() && + "Decl is not a locally-scoped decl!"); + // Note that we have a locally-scoped external with this name. + LocallyScopedExternalDecls[ND->getDeclName()] = ND; + + if (!PrevDecl) + return; + + // If there was a previous declaration of this variable, it may be + // in our identifier chain. Update the identifier chain with the new + // declaration. + if (IdResolver.ReplaceDecl(PrevDecl, ND)) { + // The previous declaration was found on the identifer resolver + // chain, so remove it from its scope. + while (S && !S->isDeclScope(PrevDecl)) + S = S->getParent(); + + if (S) + S->RemoveDecl(PrevDecl); + } +} + NamedDecl* Sema::ActOnTypedefDeclarator(Scope* S, Declarator& D, DeclContext* DC, QualType R, Decl* LastDeclarator, @@ -1476,63 +1503,6 @@ isOutOfScopePreviousDeclaration(NamedDecl *PrevDecl, DeclContext *DC, return true; } -/// \brief Inject a locally-scoped declaration with external linkage -/// into the appropriate namespace scope. -/// -/// Given a declaration of an entity with linkage that occurs within a -/// local scope, this routine inject that declaration into top-level -/// scope so that it will be visible for later uses and declarations -/// of the same entity. -void Sema::InjectLocallyScopedExternalDeclaration(ValueDecl *VD) { - // FIXME: We don't do this in C++ because, although we would like - // to get the extra checking that this operation implies, - // the declaration itself is not visible according to C++'s rules. - assert(!getLangOptions().CPlusPlus && - "Can't inject locally-scoped declarations in C++"); - IdentifierResolver::iterator I = IdResolver.begin(VD->getDeclName()), - IEnd = IdResolver.end(); - NamedDecl *PrevDecl = 0; - while (I != IEnd && !isa<TranslationUnitDecl>((*I)->getDeclContext())) { - PrevDecl = *I; - ++I; - } - - if (I == IEnd) { - // No name with this identifier has been declared at translation - // unit scope. Add this name into the appropriate scope. - if (PrevDecl) - IdResolver.AddShadowedDecl(VD, PrevDecl); - else - IdResolver.AddDecl(VD); - TUScope->AddDecl(VD); - return; - } - - if (isa<TagDecl>(*I)) { - // The first thing we found was a tag declaration, so insert - // this function so that it will be found before the tag - // declaration. - if (PrevDecl) - IdResolver.AddShadowedDecl(VD, PrevDecl); - else - IdResolver.AddDecl(VD); - TUScope->AddDecl(VD); - return; - } - - if (VD->declarationReplaces(*I)) { - // We found a previous declaration of the same entity. Replace - // that declaration with this one. - TUScope->RemoveDecl(*I); - TUScope->AddDecl(VD); - IdResolver.RemoveDecl(*I); - if (PrevDecl) - IdResolver.AddShadowedDecl(VD, PrevDecl); - else - IdResolver.AddDecl(VD); - } -} - NamedDecl* Sema::ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC, QualType R, Decl* LastDeclarator, @@ -1666,6 +1636,16 @@ Sema::ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC, isOutOfScopePreviousDeclaration(PrevDecl, DC, Context))) PrevDecl = 0; + if (!PrevDecl && NewVD->isExternC(Context)) { + // Since we did not find anything by this name and we're declaring + // an extern "C" variable, look for a non-visible extern "C" + // declaration with the same name. + llvm::DenseMap<DeclarationName, NamedDecl *>::iterator Pos + = LocallyScopedExternalDecls.find(Name); + if (Pos != LocallyScopedExternalDecls.end()) + PrevDecl = Pos->second; + } + // Merge the decl with the existing one if appropriate. if (PrevDecl) { if (isa<FieldDecl>(PrevDecl) && D.getCXXScopeSpec().isSet()) { @@ -1689,12 +1669,11 @@ Sema::ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC, } } - // If this is a locally-scoped extern variable in C, inject a - // declaration into translation unit scope so that all external - // declarations are visible. - if (!getLangOptions().CPlusPlus && CurContext->isFunctionOrMethod() && - NewVD->hasLinkage()) - InjectLocallyScopedExternalDeclaration(NewVD); + // If this is a locally-scoped extern C variable, update the map of + // such variables. + if (CurContext->isFunctionOrMethod() && NewVD->isExternC(Context) && + !InvalidDecl) + RegisterLocallyScopedExternCDecl(NewVD, PrevDecl, S); return NewVD; } @@ -1920,6 +1899,16 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, isOutOfScopePreviousDeclaration(PrevDecl, DC, Context))) PrevDecl = 0; + if (!PrevDecl && NewFD->isExternC(Context)) { + // Since we did not find anything by this name and we're declaring + // an extern "C" function, look for a non-visible extern "C" + // declaration with the same name. + llvm::DenseMap<DeclarationName, NamedDecl *>::iterator Pos + = LocallyScopedExternalDecls.find(Name); + if (Pos != LocallyScopedExternalDecls.end()) + PrevDecl = Pos->second; + } + // Merge or overload the declaration with an existing declaration of // the same name, if appropriate. bool OverloadableAttrRequired = false; @@ -2046,11 +2035,11 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, } } - // If this is a locally-scoped function in C, inject a declaration - // into translation unit scope so that all external declarations are - // visible. - if (!getLangOptions().CPlusPlus && CurContext->isFunctionOrMethod()) - InjectLocallyScopedExternalDeclaration(NewFD); + // If this is a locally-scoped extern C function, update the + // map of such names. + if (CurContext->isFunctionOrMethod() && NewFD->isExternC(Context) + && !InvalidDecl) + RegisterLocallyScopedExternCDecl(NewFD, PrevDecl, S); return NewFD; } @@ -2653,6 +2642,18 @@ Sema::DeclTy *Sema::ActOnFinishFunctionBody(DeclTy *D, StmtArg BodyArg) { /// call, forming a call to an implicitly defined function (per C99 6.5.1p2). NamedDecl *Sema::ImplicitlyDefineFunction(SourceLocation Loc, IdentifierInfo &II, Scope *S) { + // Before we produce a declaration for an implicitly defined + // function, see whether there was a locally-scoped declaration of + // this name as a function or variable. If so, use that + // (non-visible) declaration, and complain about it. + llvm::DenseMap<DeclarationName, NamedDecl *>::iterator Pos + = LocallyScopedExternalDecls.find(&II); + if (Pos != LocallyScopedExternalDecls.end()) { + Diag(Loc, diag::warn_use_out_of_scope_declaration) << Pos->second; + Diag(Pos->second->getLocation(), diag::note_previous_declaration); + return Pos->second; + } + // Extension in C99. Legal in C90, but warn about it. if (getLangOptions().C99) Diag(Loc, diag::ext_implicit_function_decl) << &II; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 1a93039b032..aca19cdca67 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -84,29 +84,6 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc) { Diag(D->getLocation(), diag::note_unavailable_here) << 0; } - if (D->getDeclContext()->isFunctionOrMethod() && - !D->getDeclContext()->Encloses(CurContext)) { - // We've found the name of a function or variable that was - // declared with external linkage within another function (and, - // therefore, a scope where we wouldn't normally see the - // declaration). Once we've made sure that no previous declaration - // was properly made visible, produce a warning. - bool HasGlobalScopedDeclaration = false; - for (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D); FD; - FD = FD->getPreviousDeclaration()) { - if (FD->getDeclContext()->isFileContext()) { - HasGlobalScopedDeclaration = true; - break; - } - } - // FIXME: do the same thing for variable declarations - - if (!HasGlobalScopedDeclaration) { - Diag(Loc, diag::warn_use_out_of_scope_declaration) << D; - Diag(D->getLocation(), diag::note_previous_declaration); - } - } - return false; } |