Repository: cc65/cc65 (C)

Issue:
[cc65] Visibility of objects/functions undeclared in file scope but 'extern'-declared in unrelated block scopes
```c
void f1(void)
{
    extern void f(int); /* this type 'void (int)' would be incompatible with an implicitly declaration of 'f' */
    extern int a;
}

/* Neither 'f' nor 'a' is visible in the file scope */

int f2(void)
{
    f(0);       /* Should be an error anyways */
    return a;   /* Should be an error */
}
```
Note: the call to `f` should always fail no matter it is implicitly declared or not depending on the language standard. Even if `f` was implicitly declared in `f2`, its implicitly declared type would be `int ()` conflicted with the earlier `extern` declaration in `f1` although which was not visible in `f2`.




Fix (patch):
diff --git a/src/cc65/compile.c b/src/cc65/compile.c
index 108c80a285..f147746583 100644
--- a/src/cc65/compile.c
+++ b/src/cc65/compile.c
@@ -163,19 +163,19 @@ static void Parse (void)
                 break;
             }
 
-            /* Check if we must reserve storage for the variable. We do this,
-            **
-            **   - if it is not a typedef or function,
-            **   - if we don't had a storage class given ("int i")
-            **   - if the storage class is explicitly specified as static,
-            **   - or if there is an initialization.
-            **
-            ** This means that "extern int i;" will not get storage allocated
-            ** in this translation unit.
-            */
+            /* The symbol is now visible in the file scope */
             if ((Decl.StorageClass & SC_TYPEMASK) != SC_FUNC &&
                 (Decl.StorageClass & SC_TYPEMASK) != SC_TYPEDEF) {
-                /* The variable is visible in the file scope */
+                /* Check if we must reserve storage for the variable. We do this,
+                **
+                **   - if it is not a typedef or function,
+                **   - if we don't had a storage class given ("int i")
+                **   - if the storage class is explicitly specified as static,
+                **   - or if there is an initialization.
+                **
+                ** This means that "extern int i;" will not get storage allocated
+                ** in this translation unit.
+                */
                 if ((Decl.StorageClass & SC_STORAGEMASK) == SC_NONE     ||
                     (Decl.StorageClass & SC_STORAGEMASK) == SC_STATIC   ||
                     ((Decl.StorageClass & SC_STORAGEMASK) == SC_EXTERN &&
@@ -189,7 +189,6 @@ static void Parse (void)
             ** or semicolon, it must be followed by a function body.
             */
             if ((Decl.StorageClass & SC_TYPEMASK) == SC_FUNC) {
-                /* The function is now visible in the file scope */
                 if (CurTok.Tok == TOK_LCURLY) {
                     /* A definition */
                     Decl.StorageClass |= SC_DEF;
diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c
index a76e604509..69484456fe 100644
--- a/src/cc65/symtab.c
+++ b/src/cc65/symtab.c
@@ -557,8 +557,10 @@ static SymEntry* FindSymInTable (const SymTable* T, const char* Name, unsigned H
 
 
 
-static SymEntry* FindSymInTree (const SymTable* Tab, const char* Name)
-/* Find the symbol with the given name in the table tree that starts with T */
+static SymEntry* FindVisibleSymInTree (const SymTable* Tab, const char* Name)
+/* Find the visible symbol with the given name in the table tree that starts
+** with Tab.
+*/
 {
     /* Get the hash over the name */
     unsigned Hash = HashStr (Name);
@@ -574,7 +576,7 @@ static SymEntry* FindSymInTree (const SymTable* Tab, const char* Name)
         }
 
         /* Bail out if we found it */
-        if (E != 0) {
+        if (E != 0 && (Tab != SymTab0 || (E->Flags & SC_LOCALSCOPE) == 0)) {
             return E;
         }
 
@@ -589,9 +591,9 @@ static SymEntry* FindSymInTree (const SymTable* Tab, const char* Name)
 
 
 SymEntry* FindSym (const char* Name)
-/* Find the symbol with the given name */
+/* Find with the given name the symbol visible in the current scope */
 {
-    return FindSymInTree (SymTab, Name);
+    return FindVisibleSymInTree (SymTab, Name);
 }
 
 
@@ -613,9 +615,9 @@ SymEntry* FindLocalSym (const char* Name)
 
 
 SymEntry* FindTagSym (const char* Name)
-/* Find the symbol with the given name in the tag table */
+/* Find with the given name the tag symbol visible in the current scope */
 {
-    return FindSymInTree (TagTab, Name);
+    return FindVisibleSymInTree (TagTab, Name);
 }
 
 
@@ -1356,6 +1358,13 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags)
                    Name);
             Entry = 0;
         } else if ((Flags & SC_TYPEMASK) != SC_TYPEDEF) {
+            /* If we are adding the symbol in the file scope, it is now
+            ** visible there.
+            */
+            if (SymTab == SymTab0) {
+                Entry->Flags &= ~SC_LOCALSCOPE;
+            }
+
             /* The C standard specifies that the result is undefined if the
             ** same thing has both internal and external linkage. Most
             ** compilers choose to either give an error at compile time, or
@@ -1415,6 +1424,13 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags)
     }
 
     if (Entry == 0) {
+        /* Hide the symbol in the file scope if we are declaring it in a
+        ** local scope.
+        */
+        if (Tab == SymTab0 && SymTab != SymTab0) {
+            Flags |= SC_LOCALSCOPE;
+        }
+
         /* Create a new entry */
         Entry = NewSymEntry (Name, Flags);
 
diff --git a/src/cc65/symtab.h b/src/cc65/symtab.h
index 53b0df4ebb..236bc090a8 100644
--- a/src/cc65/symtab.h
+++ b/src/cc65/symtab.h
@@ -142,7 +142,7 @@ void LeaveStructLevel (void);
 
 
 SymEntry* FindSym (const char* Name);
-/* Find the symbol with the given name */
+/* Find with the given name the symbol visible in the current scope */
 
 SymEntry* FindGlobalSym (const char* Name);
 /* Find the symbol with the given name in the global symbol table only */
@@ -151,7 +151,7 @@ SymEntry* FindLocalSym (const char* Name);
 /* Find the symbol with the given name in the current symbol table only */
 
 SymEntry* FindTagSym (const char* Name);
-/* Find the symbol with the given name in the tag table */
+/* Find with the given name the tag symbol visible in the current scope */
 
 SymEntry FindStructField (const Type* TypeArray, const char* Name);
 /* Find a struct/union field in the fields list.