]> granicus.if.org Git - clang/commitdiff
Rework the way we find locally-scoped external declarations when we
authorDouglas Gregor <dgregor@apple.com>
Mon, 2 Mar 2009 00:19:53 +0000 (00:19 +0000)
committerDouglas Gregor <dgregor@apple.com>
Mon, 2 Mar 2009 00:19:53 +0000 (00:19 +0000)
need them to evaluate redeclarations or call a function that hasn't
already been declared. We now keep a DenseMap of these locally-scoped
declarations so that they are not visible but can be quickly found,
e.g., when we're looking for previous declarations or before we go
ahead and implicitly declare a function that's being called. Fixes
PR3672.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@65792 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/Decl.h
lib/AST/Decl.cpp
lib/Sema/IdentifierResolver.cpp
lib/Sema/IdentifierResolver.h
lib/Sema/Sema.h
lib/Sema/SemaDecl.cpp
lib/Sema/SemaExpr.cpp
test/Sema/function-redecl.c
test/Sema/var-redecl.c

index 9536eca300ca7db949689f25d49e38db0af2b6bb..8cd9f7b2511a285a60a9c8ed4b38fccf3d54fc29 100644 (file)
@@ -338,6 +338,10 @@ public:
     return false;
   }
   
+  /// \brief Determines whether this variable is a variable with
+  /// external, C linkage.
+  bool isExternC(ASTContext &Context) const;
+
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) {
     return D->getKind() >= VarFirst && D->getKind() <= VarLast;
@@ -627,6 +631,10 @@ public:
   /// the entry point into an executable program.
   bool isMain() const;
 
+  /// \brief Determines whether this function is a function with
+  /// external, C linkage.
+  bool isExternC(ASTContext &Context) const;
+
   /// getPreviousDeclaration - Return the previous declaration of this
   /// function.
   const FunctionDecl *getPreviousDeclaration() const {
index b30b86980f6cfb68e9c5135f458c18f55538e759..1ebfbf79dacff87a83048ee0884c68b73b9fd4c0 100644 (file)
@@ -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.
 ///
index 6efedb4895e74f946d7b7917b47f3986778dbed6..609c8723959cf3d3b0100675df9188aaf25c25e5 100644 (file)
@@ -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) {
index 067900eac1cdfd17bef147d4e6bb65b9003a8166..1843f4ebca1f7977bf0bc8705a6c061350955313 100644 (file)
@@ -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();
 
index 6e0b935b88a2547df9223f4090a28fbd6fec1195..30550c423d7cd1dbfca5326a31b393c4b44b0c24 100644 (file)
@@ -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,
index 8764cd9d77c1c7fda3c280eaf578b774e599ea69..33e392168847370e80bda8d1af022132b5bc8bf0 100644 (file)
@@ -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;
index 1a93039b03276fbe4e87e9e5a30b6c120fc49ca7..aca19cdca675fc8d393aba314755e8c7c04d42a4 100644 (file)
@@ -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;
 }
 
index e8252db343dcf2ab5c6248e5ca10f3e0ac2a54aa..4ba97c2a8fdc03e9e1d0f7bf0f4de83f9bccab3c 100644 (file)
@@ -74,6 +74,7 @@ void outer_test() {
   int outer5(int); // expected-error{{redefinition of 'outer5' as different kind of symbol}}
   int* outer6(int); // expected-note{{previous declaration is here}}
   int *outer7(int);
+  int outer8(int);
 
   int *ip7 = outer7(6);
 }
@@ -86,3 +87,9 @@ void outer_test2(int x) {
   int* ip = outer6(x); // expected-warning{{use of out-of-scope declaration of 'outer6'}}
   int *ip2 = outer7(x);
 }
+
+void outer_test3() {
+  int *(*fp)(int) = outer8; // expected-error{{use of undeclared identifier 'outer8'}}
+}
+
+static float outer8(float); // okay
index 317a0f578f63b08a24aae24971e7c54961dca921..82ce58bcb9cf7fe8cca1bb0590de4a66cbb7bd06 100644 (file)
@@ -49,3 +49,8 @@ void outer_shadowing_test() {
     }
   }
 }
+
+void g18(void) {
+  extern int g19;
+}
+int *p=&g19; // expected-error{{use of undeclared identifier 'g19'}}