diff --git a/src/cc65/compile.c b/src/cc65/compile.c index 108c80a28..f14774658 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 a76e60450..69484456f 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 53b0df4eb..236bc090a 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. diff --git a/test/err/bug2304-var-use.c b/test/err/bug2304-var-use.c new file mode 100644 index 000000000..8a88405e2 --- /dev/null +++ b/test/err/bug2304-var-use.c @@ -0,0 +1,15 @@ +/* Bug 2304 - Visibility of objects/functions undeclared in file scope but 'extern'-declared in unrelated block scopes */ + +void f1(void) +{ + extern int a; +} + +/* 'a' is still invisible in the file scope */ + +int main(void) +{ + return a * 0; /* Usage of 'a' should be an error */ +} + +int a = 42; diff --git a/test/misc/Makefile b/test/misc/Makefile index 811f7f462..cfcae0530 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -133,6 +133,12 @@ $(WORKDIR)/goto.$1.$2.prg: goto.c $(ISEQUAL) | $(WORKDIR) $(CC65) -t sim$2 -$1 -o $$@ $$< 2>$(WORKDIR)/goto.$1.$2.out $(ISEQUAL) $(WORKDIR)/goto.$1.$2.out goto.ref +# this one requires failure with --std=c89, it fails with --std=cc65 due to +# stricter checks +$(WORKDIR)/bug2304-implicit-func.$1.$2.prg: bug2304-implicit-func.c | $(WORKDIR) + $(if $(QUIET),echo misc/bug2304-implicit-func.$1.$2.prg) + $(NOT) $(CC65) --standard c89 -t sim$2 -$1 -o $$@ $$< $(NULLERR) + # should not compile until 3-byte struct by value tests are re-enabled $(WORKDIR)/struct-by-value.$1.$2.prg: struct-by-value.c | $(WORKDIR) $(if $(QUIET),echo misc/struct-by-value.$1.$2.prg) diff --git a/test/misc/bug2304-implicit-func.c b/test/misc/bug2304-implicit-func.c new file mode 100644 index 000000000..f6b7450ff --- /dev/null +++ b/test/misc/bug2304-implicit-func.c @@ -0,0 +1,21 @@ +/* Bug 2304 - Visibility of objects/functions undeclared in file scope but 'extern'-declared in unrelated block scopes */ + +/* This one should fail even in C89 */ + +void f1(void) +{ + extern unsigned int f(); +} + +/* 'f' is still invisible in the file scope */ + +int main(void) +{ + f(); /* Should be a conflict since the implicit function type is incompatible */ + return 0; +} + +unsigned int f() +{ + return 42; +}