From 9791bfaf485ce7c60e0c957b85be23631aad064c Mon Sep 17 00:00:00 2001 From: bellard Date: Sat, 26 Apr 2003 20:49:46 +0000 Subject: [PATCH] more precise C type check support (const warnings) - added generic -W option support --- tcc.c | 363 +++++++++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 272 insertions(+), 91 deletions(-) diff --git a/tcc.c b/tcc.c index f9ad3d01..1091a56e 100644 --- a/tcc.c +++ b/tcc.c @@ -398,6 +398,11 @@ struct TCCState { /* if true, static linking is performed */ int static_link; + /* warning switches */ + int warn_write_strings; + int warn_unsupported; + int warn_error; + /* error handling */ void *error_opaque; void (*error_func)(void *opaque, const char *msg); @@ -438,8 +443,6 @@ struct TCCState { #define VT_LVAL_TYPE (VT_LVAL_BYTE | VT_LVAL_SHORT | VT_LVAL_UNSIGNED) /* types */ -#define VT_STRUCT_SHIFT 12 /* structure/enum name shift (20 bits left) */ - #define VT_INT 0 /* integer type */ #define VT_BYTE 1 /* signed byte type */ #define VT_SHORT 2 /* short type */ @@ -459,6 +462,8 @@ struct TCCState { #define VT_UNSIGNED 0x0010 /* unsigned type */ #define VT_ARRAY 0x0020 /* array type (also has VT_PTR) */ #define VT_BITFIELD 0x0040 /* bitfield modifier */ +#define VT_CONSTANT 0x0800 /* const modifier */ +#define VT_VOLATILE 0x1000 /* volatile modifier */ /* storage */ #define VT_EXTERN 0x00000080 /* extern definition */ @@ -466,6 +471,8 @@ struct TCCState { #define VT_TYPEDEF 0x00000200 /* typedef definition */ #define VT_INLINE 0x00000400 /* inline definition */ +#define VT_STRUCT_SHIFT 16 /* shift for bitfield shift values */ + /* type mask (except storage) */ #define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE) #define VT_TYPE (~(VT_STORAGE)) @@ -531,6 +538,14 @@ struct TCCState { #define TOK_A_SHL 0x81 #define TOK_A_SAR 0x82 +#ifndef offsetof +#define offsetof(type, field) ((size_t) &((type *)0)->field) +#endif + +#ifndef countof +#define countof(tab) (sizeof(tab) / sizeof((tab)[0])) +#endif + /* WARNING: the content of this string encodes token numbers */ static char tok_two_chars[] = "<=\236>=\235!=\225&&\240||\241++\244--\242==\224<<\1>>\2+=\253-=\255*=\252/=\257%=\245&=\246^=\336|=\374->\313..\250##\266"; @@ -678,9 +693,9 @@ static int type_size(CType *type, int *a); static inline CType *pointed_type(CType *type); static int pointed_size(CType *type); static int lvalue_type(int t); -static int is_compatible_types(CType *type1, CType *type2); static int parse_btype(CType *type, AttributeDef *ad); static void type_decl(CType *type, AttributeDef *ad, int *v, int td); +static int is_compatible_types(CType *type1, CType *type2); void error(const char *fmt, ...); void vpushi(int v); @@ -1166,7 +1181,7 @@ void error1(TCCState *s1, int is_warning, const char *fmt, va_list ap) } else { s1->error_func(s1->error_opaque, buf); } - if (!is_warning) + if (!is_warning || s1->warn_error) s1->nb_errors++; } @@ -5085,20 +5100,66 @@ static int pointed_size(CType *type) return type_size(pointed_type(type), &align); } -#if 0 -void check_pointer_types(SValue *p1, SValue *p2) +static inline int is_null_pointer(SValue *p) { - char buf1[256], buf2[256]; - int t1, t2; - t1 = p1->t; - t2 = p2->t; - if (!is_compatible_types(t1, t2)) { - type_to_str(buf1, sizeof(buf1), t1, NULL); - type_to_str(buf2, sizeof(buf2), t2, NULL); - error("incompatible pointers '%s' and '%s'", buf1, buf2); + if ((p->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST) + return 0; + return ((p->type.t & VT_BTYPE) == VT_INT && p->c.i == 0) || + ((p->type.t & VT_BTYPE) == VT_LLONG && p->c.ll == 0); +} + +static inline int is_integer_btype(int bt) +{ + return (bt == VT_BYTE || bt == VT_SHORT || + bt == VT_INT || bt == VT_LLONG); +} + +/* check types for comparison or substraction of pointers */ +static void check_comparison_pointer_types(SValue *p1, SValue *p2, int op) +{ + CType *type1, *type2, tmp_type1, tmp_type2; + int bt1, bt2; + + /* null pointers are accepted for all comparisons as gcc */ + if (is_null_pointer(p1) || is_null_pointer(p2)) + return; + type1 = &p1->type; + type2 = &p2->type; + bt1 = type1->t & VT_BTYPE; + bt2 = type2->t & VT_BTYPE; + /* accept comparison between pointer and integer with a warning */ + if ((is_integer_btype(bt1) || is_integer_btype(bt2)) && op != '-') { + warning("comparison between pointer and integer"); + return; + } + + /* both must be pointers or implicit function pointers */ + if (bt1 == VT_PTR) { + type1 = pointed_type(type1); + } else if (bt1 != VT_FUNC) + goto invalid_operands; + + if (bt2 == VT_PTR) { + type2 = pointed_type(type2); + } else if (bt2 != VT_FUNC) { + invalid_operands: + error("invalid operands to binary %s", get_tok_str(op, NULL)); + } + if ((type1->t & VT_BTYPE) == VT_VOID || + (type2->t & VT_BTYPE) == VT_VOID) + return; + tmp_type1 = *type1; + tmp_type2 = *type2; + tmp_type1.t &= ~(VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE); + tmp_type2.t &= ~(VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE); + if (!is_compatible_types(&tmp_type1, &tmp_type2)) { + /* gcc-like error if '-' is used */ + if (op == '-') + goto invalid_operands; + else + warning("comparison of distinct pointer types lacks a cast"); } } -#endif /* generic gen_op: handles types problems */ void gen_op(int op) @@ -5115,7 +5176,7 @@ void gen_op(int op) /* at least one operand is a pointer */ /* relationnal op: must be both pointers */ if (op >= TOK_ULT && op <= TOK_GT) { - // check_pointer_types(vtop, vtop - 1); + check_comparison_pointer_types(vtop - 1, vtop, op); /* pointers are handled are unsigned */ t = VT_INT | VT_UNSIGNED; goto std_op; @@ -5124,7 +5185,7 @@ void gen_op(int op) if (bt1 == VT_PTR && bt2 == VT_PTR) { if (op != '-') error("cannot use pointers here"); - // check_pointer_types(vtop - 1, vtop); + check_comparison_pointer_types(vtop - 1, vtop, op); /* XXX: check that types are compatible */ u = pointed_size(&vtop[-1].type); gen_opic(op); @@ -5511,55 +5572,58 @@ static void mk_pointer(CType *type) type->ref = s; } -static int is_compatible_types(CType *type1, CType *type2) +/* compare function types. OLD functions match any new functions */ +static int is_compatible_func(CType *type1, CType *type2) { Sym *s1, *s2; - int bt1, bt2, t1, t2; + + s1 = type1->ref; + s2 = type2->ref; + if (!is_compatible_types(&s1->type, &s2->type)) + return 0; + /* XXX: not complete */ + if (s1->c == FUNC_OLD || s2->c == FUNC_OLD) + return 1; + if (s1->c != s2->c) + return 0; + while (s1 != NULL) { + if (s2 == NULL) + return 0; + if (!is_compatible_types(&s1->type, &s2->type)) + return 0; + s1 = s1->next; + s2 = s2->next; + } + if (s2) + return 0; + return 1; +} + +/* return true if type1 and type2 are exactly the same (including + qualifiers). + + - enums are not checked as gcc __builtin_types_compatible_p () + */ +static int is_compatible_types(CType *type1, CType *type2) +{ + int bt1, t1, t2; t1 = type1->t & VT_TYPE; t2 = type2->t & VT_TYPE; + /* XXX: bitfields ? */ + if (t1 != t2) + return 0; + /* test more complicated cases */ bt1 = t1 & VT_BTYPE; - bt2 = t2 & VT_BTYPE; if (bt1 == VT_PTR) { type1 = pointed_type(type1); - /* if function, then convert implicitely to function pointer */ - if (bt2 != VT_FUNC) { - if (bt2 != VT_PTR) - return 0; - type2 = pointed_type(type2); - } - /* void matches everything */ - /* XXX: not fully compliant */ - if ((type1->t & VT_TYPE) == VT_VOID || (type2->t & VT_TYPE) == VT_VOID) - return 1; + type2 = pointed_type(type2); return is_compatible_types(type1, type2); - } else if (bt1 == VT_STRUCT || bt2 == VT_STRUCT) { + } else if (bt1 == VT_STRUCT) { return (type1->ref == type2->ref); } else if (bt1 == VT_FUNC) { - if (bt2 != VT_FUNC) - return 0; - s1 = type1->ref; - s2 = type2->ref; - if (!is_compatible_types(&s1->type, &s2->type)) - return 0; - /* XXX: not complete */ - if (s1->c == FUNC_OLD || s2->c == FUNC_OLD) - return 1; - if (s1->c != s2->c) - return 0; - while (s1 != NULL) { - if (s2 == NULL) - return 0; - if (!is_compatible_types(&s1->type, &s2->type)) - return 0; - s1 = s1->next; - s2 = s2->next; - } - if (s2) - return 0; - return 1; + return is_compatible_func(type1, type2); } else { - /* XXX: not complete */ return 1; } } @@ -5579,6 +5643,10 @@ void type_to_str(char *buf, int buf_size, t = type->t & VT_TYPE; bt = t & VT_BTYPE; buf[0] = '\0'; + if (t & VT_CONSTANT) + pstrcat(buf, buf_size, "const "); + if (t & VT_VOLATILE) + pstrcat(buf, buf_size, "volatile "); if (t & VT_UNSIGNED) pstrcat(buf, buf_size, "unsigned "); switch(bt) { @@ -5660,45 +5728,72 @@ void type_to_str(char *buf, int buf_size, casts if needed. */ static void gen_assign_cast(CType *dt) { - CType *st; + CType *st, *type1, *type2, tmp_type1, tmp_type2; char buf1[256], buf2[256]; int dbt, sbt; st = &vtop->type; /* source type */ dbt = dt->t & VT_BTYPE; sbt = st->t & VT_BTYPE; - if (dbt == VT_PTR) { + if (dt->t & VT_CONSTANT) + warning("assignment of read-only location"); + switch(dbt) { + case VT_PTR: /* special cases for pointers */ + /* '0' can also be a pointer */ + if (is_null_pointer(vtop)) + goto type_ok; + /* accept implicit pointer to integer cast with warning */ + if (is_integer_btype(sbt)) { + warning("assignment makes pointer from integer without a cast"); + goto type_ok; + } + type1 = pointed_type(dt); /* a function is implicitely a function pointer */ if (sbt == VT_FUNC) { - if (!is_compatible_types(pointed_type(dt), st)) + if ((type1->t & VT_BTYPE) != VT_VOID && + !is_compatible_types(pointed_type(dt), st)) goto error; else goto type_ok; } - /* '0' can also be a pointer */ - if (sbt == VT_INT && - ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) && - vtop->c.i == 0) - goto type_ok; - /* accept implicit pointer to integer cast with warning */ - if (sbt == VT_BYTE || sbt == VT_SHORT || - sbt == VT_INT || sbt == VT_LLONG) { - warning("assignment makes pointer from integer without a cast"); - goto type_ok; + if (sbt != VT_PTR) + goto error; + type2 = pointed_type(st); + if ((type1->t & VT_BTYPE) == VT_VOID || + (type2->t & VT_BTYPE) == VT_VOID) { + /* void * can match anything */ + } else { + /* exact type match, except for unsigned */ + tmp_type1 = *type1; + tmp_type2 = *type2; + tmp_type1.t &= ~(VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE); + tmp_type2.t &= ~(VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE); + if (!is_compatible_types(&tmp_type1, &tmp_type2)) + goto error; } - } else if (dbt == VT_BYTE || dbt == VT_SHORT || - dbt == VT_INT || dbt == VT_LLONG) { + /* check const and volatile */ + if ((!(type1->t & VT_CONSTANT) && (type2->t & VT_CONSTANT)) || + (!(type1->t & VT_VOLATILE) && (type2->t & VT_VOLATILE))) + warning("assignment discards qualifiers from pointer target type"); + break; + case VT_BYTE: + case VT_SHORT: + case VT_INT: + case VT_LLONG: if (sbt == VT_PTR || sbt == VT_FUNC) { warning("assignment makes integer from pointer without a cast"); - goto type_ok; } - } - if (!is_compatible_types(dt, st)) { - error: - type_to_str(buf1, sizeof(buf1), st, NULL); - type_to_str(buf2, sizeof(buf2), dt, NULL); - error("cannot cast '%s' to '%s'", buf1, buf2); + /* XXX: more tests */ + break; + case VT_STRUCT: + if (!is_compatible_types(dt, st)) { + error: + type_to_str(buf1, sizeof(buf1), st, NULL); + type_to_str(buf2, sizeof(buf2), dt, NULL); + error("cannot cast '%s' to '%s'", buf1, buf2); + } + break; } type_ok: gen_cast(dt); @@ -5717,6 +5812,9 @@ void vstore(void) /* optimize char/short casts */ delayed_cast = VT_MUSTCAST; vtop->type.t = ft & VT_TYPE; + /* XXX: factorize */ + if (ft & VT_CONSTANT) + warning("assignment of read-only location"); } else { delayed_cast = 0; gen_assign_cast(&vtop[-1].type); @@ -5899,7 +5997,8 @@ static void parse_attribute(AttributeDef *ad) ad->func_call = FUNC_STDCALL; break; default: - // warning("'%s' attribute ignored", get_tok_str(t, NULL)); + if (tcc_state->warn_unsupported) + warning("'%s' attribute ignored", get_tok_str(t, NULL)); /* skip parameters */ /* XXX: skip parenthesis too */ if (tok == '(') { @@ -5946,7 +6045,9 @@ static void struct_decl(CType *type, int u) v = anon_sym++; } type1.t = a; - s = sym_push(v | SYM_STRUCT, &type1, 0, 0); + /* we put an undefined size for struct/union */ + s = sym_push(v | SYM_STRUCT, &type1, 0, -1); + s->r = 0; /* default alignment is zero as gcc */ /* put struct/union/enum name in type */ do_decl: type->t = u; @@ -5954,7 +6055,7 @@ static void struct_decl(CType *type, int u) if (tok == '{') { next(); - if (s->c) + if (s->c != -1) error("struct/union/enum already defined"); /* cannot be empty */ c = 0; @@ -6169,9 +6270,15 @@ static int parse_btype(CType *type, AttributeDef *ad) case TOK_CONST1: case TOK_CONST2: case TOK_CONST3: + t |= VT_CONSTANT; + next(); + break; case TOK_VOLATILE1: case TOK_VOLATILE2: case TOK_VOLATILE3: + t |= VT_VOLATILE; + next(); + break; case TOK_REGISTER: case TOK_SIGNED1: case TOK_SIGNED2: @@ -6303,7 +6410,9 @@ static void post_type(CType *type, AttributeDef *ad) l = FUNC_OLD; skip(')'); t1 = type->t & VT_STORAGE; - type->t &= ~VT_STORAGE; + /* NOTE: const is ignored in returned type as it has a special + meaning in gcc / C++ */ + type->t &= ~(VT_STORAGE | VT_CONSTANT); post_type(type, ad); /* we push a anonymous symbol which will contain the function prototype */ s = sym_push(SYM_FIELD, type, ad->func_call, l); @@ -6343,24 +6452,30 @@ static void type_decl(CType *type, AttributeDef *ad, int *v, int td) { Sym *s; CType type1, *type2; - + int qualifiers; + while (tok == '*') { - next(); + qualifiers = 0; redo: + next(); switch(tok) { case TOK_CONST1: case TOK_CONST2: case TOK_CONST3: + qualifiers |= VT_CONSTANT; + goto redo; case TOK_VOLATILE1: case TOK_VOLATILE2: case TOK_VOLATILE3: + qualifiers |= VT_VOLATILE; + goto redo; case TOK_RESTRICT1: case TOK_RESTRICT2: case TOK_RESTRICT3: - next(); goto redo; } mk_pointer(type); + type->t |= qualifiers; } /* XXX: clarify attribute handling */ @@ -6557,6 +6672,8 @@ static void unary(void) /* string parsing */ t = VT_BYTE; str_init: + if (tcc_state->warn_write_strings) + t |= VT_CONSTANT; type.t = t; mk_pointer(&type); type.t |= VT_ARRAY; @@ -6652,10 +6769,13 @@ static void unary(void) unary_type(&type); } size = type_size(&type, &align); - if (t == TOK_SIZEOF) + if (t == TOK_SIZEOF) { + if (size < 0) + error("sizeof applied to an incomplete type"); vpushi(size); - else + } else { vpushi(align); + } break; case TOK_INC: @@ -7658,6 +7778,7 @@ static void init_putv(CType *type, Section *sec, unsigned long c, int saved_global_expr, bt, bit_pos, bit_size; void *ptr; unsigned long long bit_mask; + CType dtype; switch(expr_type) { case EXPR_VAL: @@ -7678,10 +7799,13 @@ static void init_putv(CType *type, Section *sec, unsigned long c, break; } + dtype = *type; + dtype.t &= ~VT_CONSTANT; /* need to do that to avoid false warning */ + if (sec) { /* XXX: not portable */ /* XXX: generate error if incorrect relocation */ - gen_assign_cast(type); + gen_assign_cast(&dtype); bt = type->t & VT_BTYPE; ptr = sec->data + c; /* XXX: make code faster ? */ @@ -7727,7 +7851,7 @@ static void init_putv(CType *type, Section *sec, unsigned long c, } vtop--; } else { - vset(type, VT_LOCAL, c); + vset(&dtype, VT_LOCAL, c); vswap(); vstore(); vpop(); @@ -7884,8 +8008,10 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c, if (!parse_btype(&type1, &ad1)) expect("cast"); type_decl(&type1, &ad1, &n, TYPE_ABSTRACT); - if (!is_compatible_types(type, &type1)) +#if 0 + if (!is_assignable_types(type, &type1)) error("invalid type for cast"); +#endif skip(')'); } no_oblock = 1; @@ -8545,7 +8671,7 @@ static int tcc_compile(TCCState *s1) parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM; next(); decl(VT_CONST); - if (tok != -1) + if (tok != TOK_EOF) expect("declaration"); /* end of translation unit info */ @@ -9321,6 +9447,43 @@ int tcc_set_output_type(TCCState *s, int output_type) return 0; } +#define WD_ALL 0x0001 /* warning is activated when using -Wall */ + +typedef struct WarningDef { + int offset; + int flags; + const char *name; +} WarningDef; + +static const WarningDef warning_defs[] = { + { offsetof(TCCState, warn_unsupported), 0, "unsupported" }, + { offsetof(TCCState, warn_write_strings), 0, "write-strings" }, + { offsetof(TCCState, warn_error), 0, "error" }, +}; + +/* set/reset a warning */ +int tcc_set_warning(TCCState *s, const char *warning_name, int value) +{ + int i; + const WarningDef *p; + if (!strcmp(warning_name, "all")) { + for(i = 0, p = warning_defs; i < countof(warning_defs); i++, p++) { + if (p->flags & WD_ALL) + *(int *)((uint8_t *)s + p->offset) = 1; + } + } else { + for(i = 0, p = warning_defs; i < countof(warning_defs); i++, p++) { + if (!strcmp(warning_name, p->name)) + goto found; + } + return -1; + found: + *(int *)((uint8_t *)s + p->offset) = value; + } + return 0; +} + + #if !defined(LIBTCC) /* extract the basename of a file */ @@ -9354,9 +9517,9 @@ static int64_t getclock_us(void) void help(void) { - printf("tcc version " TCC_VERSION " - Tiny C Compiler - Copyright (C) 2001, 2002 Fabrice Bellard\n" + printf("tcc version " TCC_VERSION " - Tiny C Compiler - Copyright (C) 2001-2003 Fabrice Bellard\n" "usage: tcc [-v] [-c] [-o outfile] [-Bdir] [-bench] [-Idir] [-Dsym[=val]] [-Usym]\n" - " [-g] [-b] [-bt N] [-Ldir] [-llib] [-shared] [-static]\n" + " [-Wwarn] [-g] [-b] [-bt N] [-Ldir] [-llib] [-shared] [-static]\n" " [infile1 infile2...] [-run infile args...]\n" "\n" "General options:\n" @@ -9366,6 +9529,7 @@ void help(void) " -Bdir set tcc internal library path\n" " -bench output compilation statistics\n" " -run run compiled source\n" + " -Wwarning set or reset (with 'no-' prefix) 'warning'\n" "Preprocessor options:\n" " -Idir add include path 'dir'\n" " -Dsym[=val] define 'sym' with value 'val'\n" @@ -9610,7 +9774,24 @@ int main(int argc, char **argv) case TCC_OPTION_v: printf("tcc version %s\n", TCC_VERSION); return 0; + case TCC_OPTION_W: + { + const char *p = optarg; + int value; + value = 1; + if (p[0] == 'n' && p[1] == 'o' && p[2] == '-') { + p += 2; + value = 0; + } + if (tcc_set_warning(s, p, value) < 0 && s->warn_unsupported) + goto unsupported_option; + } + break; default: + if (s->warn_unsupported) { + unsupported_option: + warning("unsupported option '%s'", r); + } break; } } -- 2.11.4.GIT