work on local extern declarations

Example:
    int a = 1;
    void f(void)
    {
        int a = 2;
        {
             extern int a; // = 1 !!
             ....

To get this (more) correctly there is a new function to copy
syms between local to global stacks.

Also, this patch changes the meaning of VT_EXTERN back
to the simpler and IMO more useful notion of
    DECLARED but not (yet) DEFINED.
and that for both variables and functions.  That is, VT_EXTERN
in tcc doesn't have to do with the keyword 'extern' necessarily.

Also this patch does allow
    int x[];
as alias for
    extern int x[];
(as do gcc and msvc)
This commit is contained in:
grischka 2019-06-22 04:00:52 +02:00
parent cbbba01b46
commit 8569048031
4 changed files with 214 additions and 86 deletions

View File

@ -38,7 +38,7 @@ static Sym* asm_new_label1(TCCState *s1, int label, int is_local, int sh_num, in
static Sym *asm_label_find(int v)
{
Sym *sym = sym_find(v);
while (sym && sym->sym_scope)
while (sym && sym->sym_scope && !(sym->type.t & VT_STATIC))
sym = sym->prev_tok;
return sym;
}

185
tccgen.c
View File

@ -263,6 +263,7 @@ ST_FUNC int tccgen_compile(TCCState *s1)
section_sym = 0;
const_wanted = 0;
nocode_wanted = 0x80000000;
local_scope = 0;
/* define some often used types */
int_type.t = VT_INT;
@ -325,8 +326,7 @@ ST_FUNC void update_storage(Sym *sym)
esym->st_other = (esym->st_other & ~ELFW(ST_VISIBILITY)(-1))
| sym->a.visibility;
if ((sym->type.t & VT_STATIC)
|| (sym->type.t & (VT_EXTERN | VT_INLINE)) == VT_INLINE)
if (sym->type.t & (VT_STATIC | VT_INLINE))
sym_bind = STB_LOCAL;
else if (sym->a.weak)
sym_bind = STB_WEAK;
@ -408,7 +408,7 @@ ST_FUNC void put_extern_sym2(Sym *sym, int sh_num,
} else {
sym_type = STT_OBJECT;
}
if ((t & VT_STATIC) || (t & (VT_EXTERN | VT_INLINE)) == VT_INLINE)
if (t & (VT_STATIC | VT_INLINE))
sym_bind = STB_LOCAL;
else
sym_bind = STB_GLOBAL;
@ -927,8 +927,7 @@ static void merge_attr(AttributeDef *ad, AttributeDef *ad1)
/* Merge some type attributes. */
static void patch_type(Sym *sym, CType *type)
{
if ((!(type->t & VT_EXTERN) || IS_ENUM_VAL(sym->type.t))
&& (type->t & VT_BTYPE) != VT_FUNC) {
if (!(type->t & VT_EXTERN) || IS_ENUM_VAL(sym->type.t)) {
if (!(sym->type.t & VT_EXTERN))
tcc_error("redefinition of '%s'", get_tok_str(sym->v, NULL));
sym->type.t &= ~VT_EXTERN;
@ -955,29 +954,30 @@ static void patch_type(Sym *sym, CType *type)
tcc_warning("static storage ignored for redefinition of '%s'",
get_tok_str(sym->v, NULL));
/* Force external definition if unequal inline specifier
or an explicit extern one. */
if ((sym->type.t | type->t) & VT_STATIC) {
type->t |= sym->type.t & VT_INLINE;
sym->type.t |= type->t & VT_INLINE;
} else if (((type->t & VT_INLINE) != (sym->type.t & VT_INLINE)
|| (type->t | sym->type.t) & VT_EXTERN)
&& !static_proto) {
type->t &= ~VT_INLINE;
sym->type.t &= ~VT_INLINE;
/* set 'inline' if both agree or if one has static */
if ((type->t | sym->type.t) & VT_INLINE) {
if (!((type->t ^ sym->type.t) & VT_INLINE)
|| ((type->t | sym->type.t) & VT_STATIC))
static_proto |= VT_INLINE;
}
if (0 == (type->t & VT_EXTERN)) {
/* put complete type, use static from prototype, but don't
overwrite type.ref, it might contain parameter names */
sym->type.t = (type->t & ~VT_STATIC) | static_proto;
/* put complete type, use static from prototype */
sym->type.t = (type->t & ~(VT_STATIC|VT_INLINE)) | static_proto;
sym->type.ref = type->ref;
} else {
sym->type.t &= ~VT_INLINE | static_proto;
}
if (sym->type.ref->f.func_type == FUNC_OLD
&& type->ref->f.func_type != FUNC_OLD) {
sym->type.ref = type->ref;
}
} else {
if ((sym->type.t & VT_ARRAY) && type->ref->c >= 0) {
/* set array size if it was omitted in extern declaration */
if (sym->type.ref->c < 0)
sym->type.ref->c = type->ref->c;
else if (sym->type.ref->c != type->ref->c)
tcc_error("conflicting type for '%s'", get_tok_str(sym->v, NULL));
sym->type.ref->c = type->ref->c;
}
if ((type->t ^ sym->type.t) & VT_STATIC)
tcc_warning("storage mismatch for redefinition of '%s'",
@ -985,7 +985,6 @@ static void patch_type(Sym *sym, CType *type)
}
}
/* Merge some storage attributes. */
static void patch_storage(Sym *sym, AttributeDef *ad, CType *type)
{
@ -1003,29 +1002,55 @@ static void patch_storage(Sym *sym, AttributeDef *ad, CType *type)
update_storage(sym);
}
/* copy sym to other stack */
static Sym *sym_copy(Sym *s0, Sym **ps)
{
Sym *s;
s = sym_malloc(), *s = *s0;
s->prev = *ps, *ps = s;
if (s->v < SYM_FIRST_ANOM) {
ps = &table_ident[s->v - TOK_IDENT]->sym_identifier;
s->prev_tok = *ps, *ps = s;
}
return s;
}
/* copy a list of syms */
static void sym_copy_ref(Sym *s0, Sym **ps)
{
Sym *s, **sp = &s0->type.ref;
for (s = *sp, *sp = NULL; s; s = s->next)
sp = &(*sp = sym_copy(s, ps))->next;
}
/* define a new external reference to a symbol 'v' */
static Sym *external_sym(int v, CType *type, int r, AttributeDef *ad)
{
Sym *s;
Sym *s; int bt;
/* look for global symbol */
s = sym_find(v);
if (!s || (!IS_ASM_SYM(s) && !(s->type.t & VT_EXTERN)
&& (!(type->t & VT_EXTERN) || s->sym_scope)
&& (s->type.t & VT_BTYPE) != VT_FUNC)) {
if (s && !is_compatible_types(&s->type, type))
tcc_error("conflicting types for '%s'", get_tok_str(s->v, NULL));
while (s && s->sym_scope)
s = s->prev_tok;
if (!s) {
/* push forward reference */
s = sym_push(v, type, r | VT_CONST | VT_SYM, 0);
s = global_identifier_push(v, type->t, 0);
s->r |= r;
s->a = ad->a;
s->asm_label = ad->asm_label;
s->sym_scope = 0;
s->type.ref = type->ref;
bt = s->type.t & (VT_BTYPE|VT_ARRAY);
/* copy type to the global stack also */
if (local_scope && (bt == VT_FUNC || (bt & VT_ARRAY)))
sym_copy_ref(s, &global_stack);
} else {
if (s->type.ref == func_old_type.ref) {
s->type.ref = type->ref;
s->r = r | VT_CONST | VT_SYM;
s->type.t |= VT_EXTERN;
}
patch_storage(s, ad, type);
bt = s->type.t & VT_BTYPE;
}
/* push variables to local scope if any */
if (local_stack && bt != VT_FUNC)
s = sym_copy(s, &local_stack);
return s;
}
@ -2880,27 +2905,29 @@ static int is_compatible_func(CType *type1, CType *type2)
s1 = type1->ref;
s2 = type2->ref;
if (!is_compatible_types(&s1->type, &s2->type))
return 0;
/* check func_call */
if (s1->f.func_call != s2->f.func_call)
return 0;
/* XXX: not complete */
if (s1->f.func_type == FUNC_OLD || s2->f.func_type == FUNC_OLD)
return 1;
if (s1->f.func_type != s2->f.func_type)
if (s1->f.func_type != s2->f.func_type
&& s1->f.func_type != FUNC_OLD
&& s2->f.func_type != FUNC_OLD)
return 0;
while (s1 != NULL) {
if (s2 == NULL)
return 0;
/* we should check the function return type for FUNC_OLD too
but that causes problems with the internally used support
functions such as TOK_memmove */
if (s1->f.func_type == FUNC_OLD && !s1->next)
return 1;
if (s2->f.func_type == FUNC_OLD && !s2->next)
return 1;
for (;;) {
if (!is_compatible_unqualified_types(&s1->type, &s2->type))
return 0;
s1 = s1->next;
s2 = s2->next;
if (!s1)
return !s2;
if (!s2)
return 0;
}
if (s2)
return 0;
return 1;
}
/* return true if type1 and type2 are the same. If unqualified is
@ -2926,15 +2953,19 @@ static int compare_types(CType *type1, CType *type2, int unqualified)
/* XXX: bitfields ? */
if (t1 != t2)
return 0;
if ((t1 & VT_ARRAY)
&& !(type1->ref->c < 0
|| type2->ref->c < 0
|| type1->ref->c == type2->ref->c))
return 0;
/* test more complicated cases */
bt1 = t1 & (VT_BTYPE | VT_ARRAY);
bt1 = t1 & VT_BTYPE;
if (bt1 == VT_PTR) {
type1 = pointed_type(type1);
type2 = pointed_type(type2);
return is_compatible_types(type1, type2);
} else if (bt1 & VT_ARRAY) {
return type1->ref->c < 0 || type2->ref->c < 0
|| type1->ref->c == type2->ref->c;
} else if (bt1 == VT_STRUCT) {
return (type1->ref == type2->ref);
} else if (bt1 == VT_FUNC) {
@ -7285,9 +7316,6 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
sym = sym_push(v, type, r | VT_SYM, 0);
patch_storage(sym, ad, NULL);
}
/* Local statics have a scope until now (for
warnings), remove it here. */
sym->sym_scope = 0;
/* update symbol definition */
put_extern_sym(sym, sec, addr, size);
} else {
@ -7431,7 +7459,7 @@ static void gen_inline_functions(TCCState *s)
for (i = 0; i < s->nb_inline_fns; ++i) {
fn = s->inline_fns[i];
sym = fn->sym;
if (sym && (sym->c || !(sym->type.t & (VT_INLINE | VT_STATIC)) )) {
if (sym && (sym->c || !(sym->type.t & VT_INLINE))) {
/* the function was used or forced (and then not internal):
generate its code and convert it to a normal function */
fn->sym = NULL;
@ -7544,22 +7572,26 @@ static int decl0(int l, int is_for_loop_init, Sym *func_sym)
}
#endif
if ((type.t & VT_BTYPE) == VT_FUNC) {
if ((type.t & VT_STATIC) && (l == VT_LOCAL)) {
tcc_error("function without file scope cannot be static");
}
/* if old style function prototype, we accept a
declaration list */
sym = type.ref;
if (sym->f.func_type == FUNC_OLD && l == VT_CONST)
decl0(VT_CMP, 0, sym);
/* always compile 'extern inline' */
if (type.t & VT_EXTERN)
type.t &= ~VT_INLINE;
}
if (gnu_ext && (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3)) {
ad.asm_label = asm_label_instr();
/* parse one last attribute list, after asm label */
parse_attribute(&ad);
#if 0
/* gcc does not allow __asm__("label") with function definition,
but why not ... */
if (tok == '{')
expect(";");
#endif
}
#ifdef TCC_TARGET_PE
@ -7591,18 +7623,12 @@ static int decl0(int l, int is_for_loop_init, Sym *func_sym)
}
/* put function symbol */
type.t &= ~VT_EXTERN;
sym = external_sym(v, &type, 0, &ad);
/* This is the def, so overwrite any other parameter names
we got from prototypes. */
sym->type.ref = type.ref;
if (sym->c && elfsym(sym)->st_shndx != SHN_UNDEF
&& !(elfsym(sym)->st_other & ST_ASM_SET))
tcc_error("redefinition of '%s'", get_tok_str(sym->v, NULL));
/* static inline functions are just recorded as a kind
of macro. Their code will be emitted at the end of
the compilation unit only if they are used */
if ((sym->type.t & (VT_INLINE | VT_EXTERN)) == VT_INLINE) {
if (sym->type.t & VT_INLINE) {
struct InlineFunc *fn;
const char *filename;
@ -7669,16 +7695,15 @@ found:
has_init = (tok == '=');
if (has_init && (type.t & VT_VLA))
tcc_error("variable length array cannot be initialized");
if (((type.t & VT_EXTERN) && (!has_init || l != VT_CONST)) ||
((type.t & VT_BTYPE) == VT_FUNC) ||
((type.t & VT_ARRAY) && (type.t & VT_STATIC) &&
!has_init && l == VT_CONST && type.ref->c < 0)) {
if (((type.t & VT_EXTERN) && (!has_init || l != VT_CONST))
|| (type.t & VT_BTYPE) == VT_FUNC
/* as with GCC, uninitialized global arrays with no size
are considered extern: */
|| ((type.t & VT_ARRAY) && !has_init
&& l == VT_CONST && type.ref->c < 0)
) {
/* external variable or function */
/* NOTE: as GCC, uninitialized global static
arrays of null size are considered as
extern */
if ((type.t & VT_BTYPE) != VT_FUNC)
type.t |= VT_EXTERN;
type.t |= VT_EXTERN;
sym = external_sym(v, &type, r, &ad);
if (ad.alias_target) {
ElfSym *esym;
@ -7687,9 +7712,6 @@ found:
esym = elfsym(alias_target);
if (!esym)
tcc_error("unsupported forward __alias__ attribute");
/* Local statics have a scope until now (for
warnings), remove it here. */
sym->sym_scope = 0;
put_extern_sym2(sym, esym->st_shndx, esym->st_value, esym->st_size, 0);
}
} else {
@ -7699,8 +7721,7 @@ found:
r |= l;
if (has_init)
next();
else if (l == VT_CONST
&& (type.t & VT_BTYPE) != VT_FUNC)
else if (l == VT_CONST)
/* uninitialized global variables may be overridden */
type.t |= VT_EXTERN;
decl_initializer_alloc(&type, &ad, r, has_init, v, l);

View File

@ -196,6 +196,82 @@ void * _Alignas(16) p1;
const f t[3];
#elif defined test_incomplete_array_array
int t[][3];
int t[][3]; // gr: not an error, see below
/******************************************************************/
#elif defined test_extern_array
int iii[] = { 1,2,3 };
extern int iii[];
int x[];
int x[2];
int x[];
int x[2];
int x[];
extern int x[2];
extern int x[];
int x[3];
/******************************************************************/
#elif defined test_func_1 \
|| defined test_func_2 \
|| defined test_func_3 \
|| defined test_func_4 \
|| defined test_func_5
#if defined test_func_1
int hello(int);
#elif defined test_func_4
static int hello(int);
#endif
int main () {
#if defined test_func_5
static
#endif
int hello(int);
hello(123);
return 0;
}
int printf(const char*, ...);
#if defined test_func_3
static int hello(int a)
#elif defined test_func_5
int hello(int a, int b)
#else
int hello(int a)
#endif
{ printf("%s: a = %d\n", __FUNCTION__, a); return 0; }
/******************************************************************/
#elif defined test_var_1 \
|| defined test_var_2 \
|| defined test_var_3
#define P(n,v) printf("%-5s: %d ; %d\n", __FUNCTION__, n, v)
#if defined test_var_1
int xxx[];
#endif
int bar();
int printf(const char*, ...);
int main ()
{
#if !defined test_var_3
int xxx = 2;
#endif
{
extern int xxx[
#if defined test_var_3
2
#endif
];
P(1, xxx[0]);
xxx[0] += 2;
}
#if !defined test_var_3
P(2, xxx);
#endif
bar(123);
return 0;
}
int xxx[1] = {1};
int bar() { P(3, xxx[0]); return 0; }
/******************************************************************/
#endif

View File

@ -73,7 +73,7 @@
60_errors_and_warnings.c:153: error: ',' expected (got "a")
[test_conflicting_types]
60_errors_and_warnings.c:159: error: conflicting types for 'i'
60_errors_and_warnings.c:159: error: incompatible types for redefinition of 'i'
[test_nested_types]
60_errors_and_warnings.c:166: error: struct/union/enum already defined
@ -100,4 +100,35 @@
60_errors_and_warnings.c:196: error: declaration of an array of functions
[test_incomplete_array_array]
60_errors_and_warnings.c:199: error: unknown type size
[test_extern_array]
60_errors_and_warnings.c:212: error: incompatible types for redefinition of 'x'
[test_func_1]
hello: a = 123
[test_func_2]
hello: a = 123
[test_func_3]
60_errors_and_warnings.c:241: warning: static storage ignored for redefinition of 'hello'
hello: a = 123
[test_func_4]
hello: a = 123
[test_func_5]
60_errors_and_warnings.c:241: error: incompatible types for redefinition of 'hello'
[test_var_1]
main : 1 ; 1
main : 2 ; 2
bar : 3 ; 3
[test_var_2]
main : 1 ; 1
main : 2 ; 2
bar : 3 ; 3
[test_var_3]
60_errors_and_warnings.c:273: error: incompatible types for redefinition of 'xxx'