From fdc18d307aafce6e8833b0eb26c1313da88cfc9a Mon Sep 17 00:00:00 2001 From: matthias Date: Mon, 10 Jul 2017 17:44:53 +0200 Subject: [PATCH] mutiples fix for _Generic * check that _Generic don't match unsigned char * with char * this case is usefull as with -funsigned-char, 'char *' are unsigned * change VT_LONG so it's now a qualifier VT_LONG are never use for code generation, but only durring parsing state, in _Generic we need to be able to make diference between 'long' and 'long long' So VT_LONG is now use as a type qualifier, it's old behaviour is still here, but we can keep trace of what was a long and what wasn't * add TOK_CLONG and TOK_CULONG tcc was directly converting value like '7171L' into TOK_CLLONG or TOK_CINT depending of the machine architecture. because of that, we was unable to make diference between a long and a long long, which doesn't work with _Generic. So now 7171L is a TOK_CLONG, and we can handle _Generic properly * check that _Generic can make diference between long and long long * uncomment "type match twice" as it should now pass tests on any platforms * add inside_generic global the point of this variable is to use VT_LONG in comparaison only when we are evaluating a _Generic. problem is with my lastest patchs tcc can now make the diference between a 'long long' and a 'long', but in 64 bit stddef.h typedef uint64_t as typedef signed long long int int64_t and stdint.h as unsigned long int, so tcc break when stdint.h and stddef.h are include together. Another solution woud be to modifie include/stddef.h so it define uint64_t as unsigned long int when processor is 64 bit, but this could break some legacy code, so for now, VT_LONG are use only inside generc. * check that _Generic parse first argument correctly * check that _Generic evaluate correctly exresion like "f() / 2" --- tcc.h | 13 ++++++-- tccgen.c | 56 +++++++++++++++++++++++++--------- tccpp.c | 39 +++++++++++++++++++++-- tests/tests2/94_generic.c | 16 ++++++++-- tests/tests2/94_generic.expect | 5 ++- 5 files changed, 106 insertions(+), 23 deletions(-) diff --git a/tcc.h b/tcc.h index 4b272f5..05371bd 100644 --- a/tcc.h +++ b/tcc.h @@ -849,7 +849,6 @@ struct filespec { #define VT_DOUBLE 9 /* IEEE double */ #define VT_LDOUBLE 10 /* IEEE long double */ #define VT_BOOL 11 /* ISOC99 boolean type */ -#define VT_LONG 12 /* long integer (NEVER USED as type, only during parsing) */ #define VT_QLONG 13 /* 128-bit integer. Only used for x86-64 ABI */ #define VT_QFLOAT 14 /* 128-bit float. Only used for x86-64 ABI */ @@ -860,6 +859,7 @@ struct filespec { #define VT_CONSTANT 0x0100 /* const modifier */ #define VT_VOLATILE 0x0200 /* volatile modifier */ #define VT_VLA 0x0400 /* VLA type (also has VT_PTR and VT_ARRAY) */ +#define VT_LONG 0x0800 /* storage */ #define VT_EXTERN 0x00001000 /* extern definition */ @@ -939,7 +939,16 @@ struct filespec { #define TOK_TWOSHARPS 0xca /* ## preprocessing token */ #define TOK_PLCHLDR 0xcb /* placeholder token as defined in C99 */ #define TOK_NOSUBST 0xcc /* means following token has already been pp'd */ -#define TOK_PPJOIN 0xce /* A '##' in the right position to mean pasting */ +#define TOK_PPJOIN 0xcd /* A '##' in the right position to mean pasting */ + +#define TOK_CLONG 0xce /* long constant */ +#define TOK_CULONG 0xcf /* unsigned long constant */ + + +#if defined TCC_TARGET_X86_64 && !defined TCC_TARGET_PE + #define TCC_LONG_ARE_64_BIT +#endif + #define TOK_SHL 0x01 /* shift left */ #define TOK_SAR 0x02 /* signed shift right */ diff --git a/tccgen.c b/tccgen.c index d53c6b7..2754192 100644 --- a/tccgen.c +++ b/tccgen.c @@ -42,6 +42,7 @@ ST_DATA Sym *local_label_stack; static int local_scope; static int in_sizeof; static int section_sym; +static int inside_generic; ST_DATA int vlas_in_scope; /* number of VLAs that are currently in scope */ ST_DATA int vla_sp_root_loc; /* vla_sp_loc for SP before any VLAs were pushed */ @@ -2218,6 +2219,10 @@ redo: } else if (bt1 == VT_LLONG || bt2 == VT_LLONG) { /* cast to biggest op */ t = VT_LLONG; + /* check if we need to keep type as long or as long long */ + if ((t1 & VT_LONG && (t2 & (VT_BTYPE | VT_LONG)) != VT_LLONG) || + (t2 & VT_LONG && (t1 & (VT_BTYPE | VT_LONG)) != VT_LLONG)) + t |= VT_LONG; /* convert to unsigned if it does not fit in a long long */ if ((t1 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_LLONG | VT_UNSIGNED) || (t2 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_LLONG | VT_UNSIGNED)) @@ -2226,7 +2231,11 @@ redo: } else { /* integer operations */ t = VT_INT; - /* convert to unsigned if it does not fit in an integer */ + + if ((t1 & VT_LONG) || (t2 & VT_LONG)) + t |= VT_LONG; + + /* convert to unsigned if it does not fit in an integer */ if ((t1 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_INT | VT_UNSIGNED) || (t2 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_INT | VT_UNSIGNED)) t |= VT_UNSIGNED; @@ -2723,6 +2732,11 @@ static int compare_types(CType *type1, CType *type2, int unqualified) t1 &= ~(VT_CONSTANT | VT_VOLATILE); t2 &= ~(VT_CONSTANT | VT_VOLATILE); } + + if (!inside_generic) { + t1 &= ~VT_LONG; + t2 &= ~VT_LONG; + } /* Default Vs explicit signedness only matters for char */ if ((t1 & VT_BTYPE) != VT_BYTE) { t1 &= ~VT_DEFSIGN; @@ -2799,6 +2813,12 @@ static void type_to_str(char *buf, int buf_size, tstr = "enum "; goto tstruct; } + + if (!bt && VT_LONG & t) { + tstr = "long"; + goto add_tstr; + } + switch(bt) { case VT_VOID: tstr = "void"; @@ -2815,9 +2835,6 @@ static void type_to_str(char *buf, int buf_size, case VT_INT: tstr = "int"; goto add_tstr; - case VT_LONG: - tstr = "long"; - goto add_tstr; case VT_LLONG: tstr = "long long"; goto add_tstr; @@ -3960,10 +3977,10 @@ static int parse_btype(CType *type, AttributeDef *ad) case TOK_LONG: if ((t & VT_BTYPE) == VT_DOUBLE) { #ifndef TCC_TARGET_PE - t = (t & ~VT_BTYPE) | VT_LDOUBLE; + t = (t & ~(VT_LONG | VT_BTYPE)) | VT_LDOUBLE; #endif - } else if ((t & VT_BTYPE) == VT_LONG) { - t = (t & ~VT_BTYPE) | VT_LLONG; + } else if (t & VT_LONG) { + t = (t & ~(VT_LONG | VT_BTYPE)) | VT_LLONG; } else { u = VT_LONG; goto basic_type; @@ -3984,11 +4001,11 @@ static int parse_btype(CType *type, AttributeDef *ad) u = VT_FLOAT; goto basic_type; case TOK_DOUBLE: - if ((t & VT_BTYPE) == VT_LONG) { + if (t & VT_LONG) { #ifdef TCC_TARGET_PE - t = (t & ~VT_BTYPE) | VT_DOUBLE; + t = (t & ~(VT_LONG | VT_BTYPE)) | VT_DOUBLE; #else - t = (t & ~VT_BTYPE) | VT_LDOUBLE; + t = (t & ~(VT_LONG | VT_BTYPE)) | VT_LDOUBLE; #endif } else { u = VT_DOUBLE; @@ -4122,7 +4139,7 @@ the_end: } /* long is never used as type */ - if ((t & VT_BTYPE) == VT_LONG) + if (t & VT_LONG) #if PTR_SIZE == 8 && !defined TCC_TARGET_PE t = (t & ~VT_BTYPE) | VT_LLONG; #else @@ -4552,7 +4569,16 @@ ST_FUNC void unary(void) case TOK_CLDOUBLE: t = VT_LDOUBLE; goto push_tokc; - + case TOK_CLONG: + case TOK_CULONG: + #ifdef TCC_LONG_ARE_64_BIT + t = VT_LLONG | VT_LONG; + #else + t = VT_INT | VT_LONG; + #endif + if (tok == TOK_CULONG) + t |= VT_UNSIGNED; + goto push_tokc; case TOK___FUNCTION__: if (!gnu_ext) goto tok_identifier; @@ -4906,8 +4932,9 @@ ST_FUNC void unary(void) next(); skip('('); + inside_generic = 1; expr_type(&controlling_type, expr_eq); - controlling_type.t &= ~(VT_CONSTANT|VT_VOLATILE|VT_ARRAY); + controlling_type.t &= ~(VT_CONSTANT | VT_VOLATILE | VT_ARRAY); for (;;) { learn = 0; skip(','); @@ -4926,7 +4953,7 @@ ST_FUNC void unary(void) type_decl(&cur_type, &ad_tmp, &itmp, TYPE_ABSTRACT); if (compare_types(&controlling_type, &cur_type, 0)) { if (has_match) { - // tcc_error("type match twice"); + tcc_error("type match twice"); } has_match = 1; learn = 1; @@ -4950,6 +4977,7 @@ ST_FUNC void unary(void) } begin_macro(str, 1); next(); + inside_generic = 0; expr_eq(); if (tok != TOK_EOF) expect(","); diff --git a/tccpp.c b/tccpp.c index 8d003d7..5209b39 100644 --- a/tccpp.c +++ b/tccpp.c @@ -480,6 +480,8 @@ ST_FUNC const char *get_tok_str(int v, CValue *cv) switch(v) { case TOK_CINT: case TOK_CUINT: + case TOK_CLONG: + case TOK_CULONG: case TOK_CLLONG: case TOK_CULLONG: /* XXX: not quite exact, but only useful for testing */ @@ -1014,6 +1016,10 @@ static inline int tok_size(const int *p) case TOK_LCHAR: case TOK_CFLOAT: case TOK_LINENUM: +#ifndef TCC_LONG_ARE_64_BIT + case TOK_CLONG; + case TOK_CULONG; +#endif return 1 + 1; case TOK_STR: case TOK_LSTR: @@ -1023,6 +1029,10 @@ static inline int tok_size(const int *p) case TOK_CDOUBLE: case TOK_CLLONG: case TOK_CULLONG: +#ifdef TCC_LONG_ARE_64_BIT + case TOK_CLONG; + case TOK_CULONG; +#endif return 1 + 2; case TOK_CLDOUBLE: return 1 + LDOUBLE_SIZE / 4; @@ -1138,6 +1148,10 @@ static void tok_str_add2(TokenString *s, int t, CValue *cv) case TOK_LCHAR: case TOK_CFLOAT: case TOK_LINENUM: +#ifndef TCC_LONG_ARE_64_BIT + case TOK_CLONG: + case TOK_CULONG: +#endif str[len++] = cv->tab[0]; break; case TOK_PPNUM: @@ -1158,6 +1172,10 @@ static void tok_str_add2(TokenString *s, int t, CValue *cv) case TOK_CDOUBLE: case TOK_CLLONG: case TOK_CULLONG: +#ifdef TCC_LONG_ARE_64_BIT + case TOK_CLONG: + case TOK_CULONG: +#endif #if LDOUBLE_SIZE == 8 case TOK_CLDOUBLE: #endif @@ -1213,6 +1231,10 @@ static inline void TOK_GET(int *t, const int **pp, CValue *cv) case TOK_CCHAR: case TOK_LCHAR: case TOK_LINENUM: +#ifndef TCC_LONG_ARE_64_BIT + case TOK_CLONG: + case TOK_CULONG: +#endif tab[0] = *p++; cv->i = (*t == TOK_CUINT) ? (unsigned)cv->i : (int)cv->i; break; @@ -1230,6 +1252,10 @@ static inline void TOK_GET(int *t, const int **pp, CValue *cv) case TOK_CDOUBLE: case TOK_CLLONG: case TOK_CULLONG: +#ifdef TCC_LONG_ARE_64_BIT + case TOK_CLONG: + case TOK_CULONG: +#endif n = 2; goto copy; case TOK_CLDOUBLE: @@ -2407,9 +2433,7 @@ static void parse_number(const char *p) if (lcount && *(p - 1) != ch) tcc_error("incorrect integer suffix: %s", p1); lcount++; -#if !defined TCC_TARGET_X86_64 || defined TCC_TARGET_PE if (lcount == 2) -#endif must_64bit = 1; ch = *p++; } else if (t == 'U') { @@ -2426,6 +2450,13 @@ static void parse_number(const char *p) if (n & 0xffffffff00000000LL || must_64bit) { tok = TOK_CLLONG; n1 = n >> 32; + } else if (lcount) { +#ifdef TCC_LONG_ARE_64_BIT + n1 = n >> 32; +#else + n1 = n; +#endif + tok = TOK_CLONG; } else { tok = TOK_CINT; n1 = n; @@ -2435,7 +2466,9 @@ static void parse_number(const char *p) if (ucount || ((n1 >> 31) && (b != 10))) { if (tok == TOK_CLLONG) tok = TOK_CULLONG; - else + else if (tok == TOK_CLONG) + tok = TOK_CULONG; + else tok = TOK_CUINT; /* If decimal and no unsigned suffix, bump to 64 bits or throw error */ } else if (n1 >> 31) { diff --git a/tests/tests2/94_generic.c b/tests/tests2/94_generic.c index fad028b..4f989f0 100644 --- a/tests/tests2/94_generic.c +++ b/tests/tests2/94_generic.c @@ -20,6 +20,8 @@ int b_f() return 10; } +typedef int int_type1; + #define gen_sw(a) _Generic(a, const char *: 1, default: 8, int: 123); int main() @@ -28,24 +30,32 @@ int main() struct b titi; const int * const ptr; const char *ti; + int_type1 i2; i = _Generic(a, int: a_f, const int: b_f)(); printf("%d\n", i); + i = _Generic(a, int: a_f() / 2, const int: b_f() / 2); + printf("%d\n", i); i = _Generic(ptr, int *:1, int * const:2, default:20); printf("%d\n", i); i = gen_sw(a); printf("%d\n", i); i = _Generic(titi, struct a:1, struct b:2, default:20); printf("%d\n", i); + i = _Generic(i2, char: 1, int : 0); + printf("%d\n", i); i = _Generic(a, char:1, int[4]:2, default:5); printf("%d\n", i); i = _Generic(17, int :1, int **:2); printf("%d\n", i); - i = _Generic(17L, int :1, long :2); + i = _Generic(17L, int :1, long :2, long long : 3); printf("%d\n", i); - i = _Generic("17, io", const char *:1, char *:3, const int :2); + i = _Generic("17, io", char *: 3, const char *: 1); printf("%d\n", i); - i = _Generic(ti, const char *:1, char *:3, const int :2); + i = _Generic(ti, const unsigned char *:1, const char *:4, char *:3, + const signed char *:2); printf("%d\n", i); + printf("%s\n", _Generic(i + 2L, long: "long", int: "int", + long long: "long long")); return 0; } diff --git a/tests/tests2/94_generic.expect b/tests/tests2/94_generic.expect index 254a84e..b75a7d1 100644 --- a/tests/tests2/94_generic.expect +++ b/tests/tests2/94_generic.expect @@ -1,9 +1,12 @@ 20 +10 20 123 2 +0 5 1 2 3 -1 +4 +long