From 85690480313c4a8b0efeb3761ee68456cfe57840 Mon Sep 17 00:00:00 2001 From: grischka Date: Sat, 22 Jun 2019 04:00:52 +0200 Subject: [PATCH] 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) --- tccasm.c | 2 +- tccgen.c | 185 ++++++++++++--------- tests/tests2/60_errors_and_warnings.c | 78 ++++++++- tests/tests2/60_errors_and_warnings.expect | 35 +++- 4 files changed, 214 insertions(+), 86 deletions(-) diff --git a/tccasm.c b/tccasm.c index 54d28dac..4207a1d1 100644 --- a/tccasm.c +++ b/tccasm.c @@ -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; } diff --git a/tccgen.c b/tccgen.c index a3096e3d..61f090e5 100644 --- a/tccgen.c +++ b/tccgen.c @@ -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); diff --git a/tests/tests2/60_errors_and_warnings.c b/tests/tests2/60_errors_and_warnings.c index 2101c831..b84eec2c 100644 --- a/tests/tests2/60_errors_and_warnings.c +++ b/tests/tests2/60_errors_and_warnings.c @@ -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 diff --git a/tests/tests2/60_errors_and_warnings.expect b/tests/tests2/60_errors_and_warnings.expect index 53d62cfa..f252355d 100644 --- a/tests/tests2/60_errors_and_warnings.expect +++ b/tests/tests2/60_errors_and_warnings.expect @@ -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'