From 38ab621b55efbf74a130c5423ecaa93de05f3454 Mon Sep 17 00:00:00 2001 From: Michael Matz Date: Sat, 18 Jan 2020 02:36:29 +0100 Subject: [PATCH] Factor out common type combination as there's overlap between handling types for binary and ternay operations. Factor this into a single routine (combine_types). This uses the structure that gen_op was following, and expr_cond was using as well in the past, which I find easier to reconvene with the standard language. But it reuses the new functions for diagnostics to improve (a little) on what GCC or clang produce :) --- tccgen.c | 704 +++++++++++++----------------- tests/tests2/33_ternary_op.expect | 4 +- 2 files changed, 317 insertions(+), 391 deletions(-) diff --git a/tccgen.c b/tccgen.c index 145847bf..f1d8bcd0 100644 --- a/tccgen.c +++ b/tccgen.c @@ -2608,59 +2608,208 @@ static inline int is_null_pointer(SValue *p) ); } -/* check types for comparison or subtraction of pointers */ -static void check_comparison_pointer_types(SValue *p1, SValue *p2, int op) +/* compare function types. OLD functions match any new functions */ +static int is_compatible_func(CType *type1, CType *type2) { - 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 != '-') { - if (op != TOK_LOR && op != TOK_LAND ) - tcc_warning("comparison between pointer and integer"); - return; + Sym *s1, *s2; + + s1 = type1->ref; + s2 = type2->ref; + if (s1->f.func_call != s2->f.func_call) + return 0; + if (s1->f.func_type != s2->f.func_type + && s1->f.func_type != FUNC_OLD + && s2->f.func_type != FUNC_OLD) + 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; + } +} + +/* return true if type1 and type2 are the same. If unqualified is + true, qualifiers on the types are ignored. + */ +static int compare_types(CType *type1, CType *type2, int unqualified) +{ + int bt1, t1, t2; + + t1 = type1->t & VT_TYPE; + t2 = type2->t & VT_TYPE; + if (unqualified) { + /* strip qualifiers before comparing */ + t1 &= ~(VT_CONSTANT | VT_VOLATILE); + t2 &= ~(VT_CONSTANT | VT_VOLATILE); } - /* both must be pointers or implicit function pointers */ + /* Default Vs explicit signedness only matters for char */ + if ((t1 & VT_BTYPE) != VT_BYTE) { + t1 &= ~VT_DEFSIGN; + t2 &= ~VT_DEFSIGN; + } + /* 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; 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: - tcc_error("invalid operands to binary %s", get_tok_str(op, NULL)); + return is_compatible_types(type1, type2); + } else if (bt1 == VT_STRUCT) { + return (type1->ref == type2->ref); + } else if (bt1 == VT_FUNC) { + return is_compatible_func(type1, type2); + } else if (IS_ENUM(type1->t) || IS_ENUM(type2->t)) { + return type1->ref == type2->ref; + } else { + return 1; } - if ((type1->t & VT_BTYPE) == VT_VOID || - (type2->t & VT_BTYPE) == VT_VOID) - return; - tmp_type1 = *type1; - tmp_type2 = *type2; - tmp_type1.t &= ~(VT_DEFSIGN | VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE); - tmp_type2.t &= ~(VT_DEFSIGN | 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 - tcc_warning("comparison of distinct pointer types lacks a cast"); +} + +/* Check if OP1 and OP2 can be "combined" with operation OP, the combined + type is stored in DEST if non-null (except for pointer plus/minus) . */ +static int combine_types(CType *dest, SValue *op1, SValue *op2, int op) +{ + CType *type1 = &op1->type, *type2 = &op2->type, type; + int t1 = type1->t, t2 = type2->t, bt1 = t1 & VT_BTYPE, bt2 = t2 & VT_BTYPE; + int ret = 1; + + type.t = VT_VOID; + type.ref = NULL; + + if (bt1 == VT_VOID || bt2 == VT_VOID) { + ret = op == '?' ? 1 : 0; + /* NOTE: as an extension, we accept void on only one side */ + type.t = VT_VOID; + } else if (bt1 == VT_PTR || bt2 == VT_PTR) { + if (op == '+') ; /* Handled in caller */ + /* http://port70.net/~nsz/c/c99/n1256.html#6.5.15p6 */ + /* If one is a null ptr constant the result type is the other. */ + else if (is_null_pointer (op2)) type = *type1; + else if (is_null_pointer (op1)) type = *type2; + else if (bt1 != bt2) { + /* accept comparison or cond-expr between pointer and integer + with a warning */ + if ((op == '?' || (op >= TOK_ULT && op <= TOK_LOR)) + && (is_integer_btype(bt1) || is_integer_btype(bt2))) + tcc_warning("pointer/integer mismatch in %s", + op == '?' ? "conditional expression" : "comparison"); + else if (op != '-' || !is_integer_btype(bt2)) + ret = 0; + type = *(bt1 == VT_PTR ? type1 : type2); + } else { + CType *pt1 = pointed_type(type1); + CType *pt2 = pointed_type(type2); + int pbt1 = pt1->t & VT_BTYPE; + int pbt2 = pt2->t & VT_BTYPE; + int newquals, copied = 0; + if (pbt1 != VT_VOID && pbt2 != VT_VOID + && !compare_types(pt1, pt2, 1/*unqualif*/)) { + if (op != '?' && (op < TOK_ULT || op > TOK_LOR)) + ret = 0; + else + type_incompatibility_warning(type1, type2, + op == '?' + ? "pointer type mismatch in conditional expression ('%s' and '%s')" + : "pointer type mismatch in comparison('%s' and '%s')"); + } + if (op == '?') { + /* pointers to void get preferred, otherwise the + pointed to types minus qualifs should be compatible */ + type = *((pbt1 == VT_VOID) ? type1 : type2); + /* combine qualifs */ + newquals = ((pt1->t | pt2->t) & (VT_CONSTANT | VT_VOLATILE)); + if ((~pointed_type(&type)->t & (VT_CONSTANT | VT_VOLATILE)) + & newquals) + { + /* copy the pointer target symbol */ + type.ref = sym_push(SYM_FIELD, &type.ref->type, + 0, type.ref->c); + copied = 1; + pointed_type(&type)->t |= newquals; + } + /* pointers to incomplete arrays get converted to + pointers to completed ones if possible */ + if (pt1->t & VT_ARRAY + && pt2->t & VT_ARRAY + && pointed_type(&type)->ref->c < 0 + && (pt1->ref->c > 0 || pt2->ref->c > 0)) + { + if (!copied) + type.ref = sym_push(SYM_FIELD, &type.ref->type, + 0, type.ref->c); + pointed_type(&type)->ref = + sym_push(SYM_FIELD, &pointed_type(&type)->ref->type, + 0, pointed_type(&type)->ref->c); + pointed_type(&type)->ref->c = + 0 < pt1->ref->c ? pt1->ref->c : pt2->ref->c; + } + } + } + if (op >= TOK_ULT && op <= TOK_LOR) + type.t = VT_SIZE_T; + } else if (bt1 == VT_STRUCT || bt2 == VT_STRUCT) { + if (op != '?' || !compare_types(type1, type2, 1)) + ret = 0; + type = *type1; + } else if (is_float(bt1) || is_float(bt2)) { + if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) { + type.t = VT_LDOUBLE; + } else if (bt1 == VT_DOUBLE || bt2 == VT_DOUBLE) { + type.t = VT_DOUBLE; + } else { + type.t = VT_FLOAT; + } + } else if (bt1 == VT_LLONG || bt2 == VT_LLONG) { + /* cast to biggest op */ + type.t = VT_LLONG | VT_LONG; + if (bt1 == VT_LLONG) + type.t &= t1; + if (bt2 == VT_LLONG) + type.t &= t2; + /* 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)) + type.t |= VT_UNSIGNED; + } else { + /* integer operations */ + type.t = VT_INT | (VT_LONG & (t1 | t2)); + /* 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)) + type.t |= VT_UNSIGNED; } + if (dest) + *dest = type; + return ret; } /* generic gen_op: handles types problems */ ST_FUNC void gen_op(int op) { int u, t1, t2, bt1, bt2, t; - CType type1; + CType type1, combtype; redo: t1 = vtop[-1].type.t; @@ -2668,9 +2817,7 @@ redo: bt1 = t1 & VT_BTYPE; bt2 = t2 & VT_BTYPE; - if (bt1 == VT_STRUCT || bt2 == VT_STRUCT) { - tcc_error("operation on a struct"); - } else if (bt1 == VT_FUNC || bt2 == VT_FUNC) { + if (bt1 == VT_FUNC || bt2 == VT_FUNC) { if (bt2 == VT_FUNC) { mk_pointer(&vtop->type); gaddrof(); @@ -2682,25 +2829,18 @@ redo: vswap(); } goto redo; + } else if (!combine_types(&combtype, vtop - 1, vtop, op)) { + tcc_error_noabort("invalid operand types for binary operation"); + vpop(); } else if (bt1 == VT_PTR || bt2 == VT_PTR) { /* at least one operand is a pointer */ /* relational op: must be both pointers */ - if (op >= TOK_ULT && op <= TOK_LOR) { - check_comparison_pointer_types(vtop - 1, vtop, op); - /* pointers are handled are unsigned */ -#if PTR_SIZE == 8 - t = VT_LLONG | VT_UNSIGNED; -#else - t = VT_INT | VT_UNSIGNED; -#endif + if (op >= TOK_ULT && op <= TOK_LOR) goto std_op; - } /* if both pointers, then it must be the '-' op */ if (bt1 == VT_PTR && bt2 == VT_PTR) { if (op != '-') tcc_error("cannot use pointers here"); - check_comparison_pointer_types(vtop - 1, vtop, op); - /* XXX: check that types are compatible */ if (vtop[-1].type.t & VT_VLA) { vla_runtime_pointed_size(&vtop[-1].type); } else { @@ -2759,46 +2899,21 @@ redo: /* put again type if gen_opic() swaped operands */ vtop->type = type1; } - } else if (is_float(bt1) || is_float(bt2)) { - /* compute bigger type and do implicit casts */ - if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) { - t = VT_LDOUBLE; - } else if (bt1 == VT_DOUBLE || bt2 == VT_DOUBLE) { - t = VT_DOUBLE; - } else { - t = VT_FLOAT; - } - /* floats can only be used for a few operations */ - if (op != '+' && op != '-' && op != '*' && op != '/' && - (op < TOK_ULT || op > TOK_GT)) - tcc_error("invalid operands for binary operation"); - goto std_op; - } else if (op == TOK_SHR || op == TOK_SAR || op == TOK_SHL) { - t = bt1 == VT_LLONG ? VT_LLONG : VT_INT; - if ((t1 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (t | VT_UNSIGNED)) - t |= VT_UNSIGNED; - t |= (VT_LONG & t1); - goto std_op; - } else if (bt1 == VT_LLONG || bt2 == VT_LLONG) { - /* cast to biggest op */ - t = VT_LLONG | VT_LONG; - if (bt1 == VT_LLONG) - t &= t1; - if (bt2 == VT_LLONG) - t &= t2; - /* 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)) - t |= VT_UNSIGNED; - goto std_op; } else { - /* integer operations */ - t = VT_INT | (VT_LONG & (t1 | t2)); - /* 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; + /* floats can only be used for a few operations */ + if (is_float(combtype.t) + && op != '+' && op != '-' && op != '*' && op != '/' + && (op < TOK_ULT || op > TOK_LOR)) + tcc_error("invalid operands for binary operation"); + else if (op == TOK_SHR || op == TOK_SAR || op == TOK_SHL) { + t = bt1 == VT_LLONG ? VT_LLONG : VT_INT; + if ((t1 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (t | VT_UNSIGNED)) + t |= VT_UNSIGNED; + t |= (VT_LONG & t1); + combtype.t = t; + } std_op: + t = t2 = combtype.t; /* XXX: currently, some unsigned operations are explicit, so we modify them here */ if (t & VT_UNSIGNED) { @@ -2818,20 +2933,18 @@ redo: op = TOK_UGE; } vswap(); - type1.t = t; - type1.ref = NULL; - gen_cast(&type1); + gen_cast_s(t); vswap(); /* special case for shifts and long long: we keep the shift as an integer */ if (op == TOK_SHR || op == TOK_SAR || op == TOK_SHL) - type1.t = VT_INT; - gen_cast(&type1); + t2 = VT_INT; + gen_cast_s(t2); if (is_float(t)) gen_opif(op); else gen_opic(op); - if (op >= TOK_ULT && op <= TOK_GT) { + if (op >= TOK_ULT && op <= TOK_LOR) { /* relational op: the result is an int */ vtop->type.t = VT_INT; } else { @@ -3235,85 +3348,6 @@ ST_FUNC void mk_pointer(CType *type) type->ref = s; } -/* compare function types. OLD functions match any new functions */ -static int is_compatible_func(CType *type1, CType *type2) -{ - Sym *s1, *s2; - - s1 = type1->ref; - s2 = type2->ref; - if (s1->f.func_call != s2->f.func_call) - return 0; - if (s1->f.func_type != s2->f.func_type - && s1->f.func_type != FUNC_OLD - && s2->f.func_type != FUNC_OLD) - 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; - } -} - -/* return true if type1 and type2 are the same. If unqualified is - true, qualifiers on the types are ignored. - */ -static int compare_types(CType *type1, CType *type2, int unqualified) -{ - int bt1, t1, t2; - - t1 = type1->t & VT_TYPE; - t2 = type2->t & VT_TYPE; - if (unqualified) { - /* strip qualifiers before comparing */ - t1 &= ~(VT_CONSTANT | VT_VOLATILE); - t2 &= ~(VT_CONSTANT | VT_VOLATILE); - } - - /* Default Vs explicit signedness only matters for char */ - if ((t1 & VT_BTYPE) != VT_BYTE) { - t1 &= ~VT_DEFSIGN; - t2 &= ~VT_DEFSIGN; - } - /* 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; - if (bt1 == VT_PTR) { - type1 = pointed_type(type1); - type2 = pointed_type(type2); - return is_compatible_types(type1, type2); - } else if (bt1 == VT_STRUCT) { - return (type1->ref == type2->ref); - } else if (bt1 == VT_FUNC) { - return is_compatible_func(type1, type2); - } else if (IS_ENUM(type1->t) || IS_ENUM(type2->t)) { - return type1->ref == type2->ref; - } else { - return 1; - } -} - /* return true if type1 and type2 are exactly the same (including qualifiers). */ @@ -5930,16 +5964,15 @@ static int is_cond_bool(SValue *sv) static void expr_cond(void) { - int tt, u, r1, r2, rc, t1, t2, bt1, bt2, islv, c, g; + int tt, u, r1, r2, rc, t1, t2, islv, c, g; SValue sv; - CType type, type1, type2; + CType type; int ncw_prev; expr_lor(); if (tok == '?') { next(); c = condition_3way(); - ncw_prev = nocode_wanted; g = (tok == ':' && gnu_ext); tt = 0; if (!g) { @@ -5957,218 +5990,111 @@ static void expr_cond(void) tt = gvtst(0, 0); } - if (1) { - if (c == 0) - nocode_wanted++; - if (!g) - gexpr(); + ncw_prev = nocode_wanted; + if (c == 0) + nocode_wanted++; + if (!g) + gexpr(); - if (c < 0 && vtop->r == VT_CMP) { - t1 = gvtst(0, 0); - vpushi(0); - gvtst_set(0, t1); - } - - if ((vtop->type.t & VT_BTYPE) == VT_FUNC) - mk_pointer(&vtop->type); - type1 = vtop->type; - sv = *vtop; /* save value to handle it later */ - vtop--; /* no vpop so that FP stack is not flushed */ - - if (g) { - u = tt; - } else if (c < 0) { - u = gjmp(0); - gsym(tt); - } else - u = 0; - - nocode_wanted = ncw_prev; - if (c == 1) - nocode_wanted++; - skip(':'); - expr_cond(); - - if (c < 0 && is_cond_bool(vtop) && is_cond_bool(&sv)) { - if (sv.r == VT_CMP) { - t1 = sv.jtrue; - t2 = u; - } else { - t1 = gvtst(0, 0); - t2 = gjmp(0); - gsym(u); - vpushv(&sv); - } - gvtst_set(0, t1); - gvtst_set(1, t2); - nocode_wanted = ncw_prev; - // tcc_warning("two conditions expr_cond"); - return; - } - - if ((vtop->type.t & VT_BTYPE) == VT_FUNC) - mk_pointer(&vtop->type); - type2=vtop->type; - t1 = type1.t; - bt1 = t1 & VT_BTYPE; - t2 = type2.t; - bt2 = t2 & VT_BTYPE; - type.ref = NULL; - - /* cast operands to correct type according to ISOC rules */ - if (bt1 == VT_VOID || bt2 == VT_VOID) { - type.t = VT_VOID; /* NOTE: as an extension, we accept void on only one side */ - } else if (bt1 == VT_BOOL && bt2 == VT_BOOL) { - type = type1; - } else if (is_float(bt1) && is_integer_btype(bt2)) { - type = type1; - } else if (is_integer_btype(bt1) && is_float(bt2)) { - type = type2; - } else if (is_float(bt1) && is_float(bt2)) { - if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) { - type.t = VT_LDOUBLE; - - } else if (bt1 == VT_DOUBLE || bt2 == VT_DOUBLE) { - type.t = VT_DOUBLE; - } else { - type.t = VT_FLOAT; - } - } else if ((bt1 == VT_LLONG && is_integer_btype(bt2)) || - (bt2 == VT_LLONG && is_integer_btype(bt1))) { - /* cast to biggest op */ - type.t = VT_LLONG | VT_LONG; - if (bt1 == VT_LLONG) - type.t &= t1; - if (bt2 == VT_LLONG) - type.t &= t2; - /* 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)) - type.t |= VT_UNSIGNED; - } - /* http://port70.net/~nsz/c/c99/n1256.html#6.5.15p6 */ - /* If one is a null ptr constant the result type - is the other. */ - else if (bt1 == VT_PTR && is_null_pointer(vtop)) { - type = type1; - } else if (is_null_pointer(&sv) && bt2 == VT_PTR) { - type = type2; - } else if (bt1 == VT_PTR && is_integer_btype(bt2)) { - type = type1; - tcc_warning("pointer/integer mismatch"); - } else if (is_integer_btype(bt1) && bt2 == VT_PTR) { - type = type2; - tcc_warning("pointer/integer mismatch"); - } else if (bt1==VT_PTR && bt2 == VT_PTR) { - CType *pt1 = pointed_type(&type1); - CType *pt2 = pointed_type(&type2); - int pbt1 = pt1->t & VT_BTYPE; - int pbt2 = pt2->t & VT_BTYPE; - int newquals, copied = 0; - /* pointers to void get preferred, otherwise the - pointed to types minus qualifs should be compatible */ - type = (pbt1 == VT_VOID) ? type1 : type2; - if (pbt1 != VT_VOID && pbt2 != VT_VOID && ! compare_types(pt1, pt2, 1/*unqualif*/)) { - type_incompatibility_warning(&type1, &type2, "incompatible pointer types"); - // result is void* - type.t = VT_VOID; - mk_pointer(&type); - } - /* combine qualifs */ - newquals = ((pt1->t | pt2->t) & (VT_CONSTANT | VT_VOLATILE)); - if ((~pointed_type(&type)->t & (VT_CONSTANT | VT_VOLATILE)) - & newquals) - { - /* copy the pointer target symbol */ - type.ref = sym_push(SYM_FIELD, &type.ref->type, - 0, type.ref->c); - copied = 1; - pointed_type(&type)->t |= newquals; - } - /* pointers to incomplete arrays get converted to - pointers to completed ones if possible */ - if (pt1->t & VT_ARRAY - && pt2->t & VT_ARRAY - && pointed_type(&type)->ref->c < 0 - && (pt1->ref->c > 0 || pt2->ref->c > 0)) - { - if (!copied) - type.ref = sym_push(SYM_FIELD, &type.ref->type, - 0, type.ref->c); - pointed_type(&type)->ref = - sym_push(SYM_FIELD, &pointed_type(&type)->ref->type, - 0, pointed_type(&type)->ref->c); - pointed_type(&type)->ref->c = - 0 < pt1->ref->c ? pt1->ref->c : pt2->ref->c; - } - } else if (bt1 == VT_STRUCT && bt2 == VT_STRUCT) { - /* test structure compatibility */ - if (type1.ref != type2.ref) { - type_incompatibility_error(&type1, &type2,"different struct/union types '%s' vs. '%s'"); - } else { - type = bt1 == VT_STRUCT ? type1 : type2; - } - } else if (is_integer_btype(bt1) && is_integer_btype(bt2)) { - /* integer operations */ - type.t = VT_INT | (VT_LONG & (t1 | t2)); - /* convert to unsigned if one of both is unsigned */ - if ((t1 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_INT | VT_UNSIGNED) || - (t2 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_INT | VT_UNSIGNED)) - type.t |= VT_UNSIGNED; - } else { - type_incompatibility_error(&type1, &type2, "type '%s' is incompatible to '%s'"); - } - - /* keep structs lvalue by transforming `(expr ? a : b)` to `*(expr ? &a : &b)` so - that `(expr ? a : b).mem` does not error with "lvalue expected" */ - islv = (vtop->r & VT_LVAL) && (sv.r & VT_LVAL) && VT_STRUCT == (type.t & VT_BTYPE); - - /* now we convert second operand */ - if (c != 1) { - gen_cast(&type); - if (islv) { - mk_pointer(&vtop->type); - gaddrof(); - } else if (VT_STRUCT == (vtop->type.t & VT_BTYPE)) - gaddrof(); - } - - rc = RC_TYPE(type.t); - /* for long longs, we use fixed registers to avoid having - to handle a complicated move */ - if (USING_TWO_WORDS(type.t)) - rc = RC_RET(type.t); - - tt = r2 = 0; - if (c < 0) { - r2 = gv(rc); - tt = gjmp(0); - } - gsym(u); - nocode_wanted = ncw_prev; - - /* this is horrible, but we must also convert first - operand */ - if (c != 0) { - *vtop = sv; - gen_cast(&type); - if (islv) { - mk_pointer(&vtop->type); - gaddrof(); - } else if (VT_STRUCT == (vtop->type.t & VT_BTYPE)) - gaddrof(); - } - - if (c < 0) { - r1 = gv(rc); - move_reg(r2, r1, islv ? VT_PTR : type.t); - vtop->r = r2; - gsym(tt); - } - - if (islv) - indir(); + if (c < 0 && vtop->r == VT_CMP) { + t1 = gvtst(0, 0); + vpushi(0); + gvtst_set(0, t1); + gv(RC_INT); } + + if ((vtop->type.t & VT_BTYPE) == VT_FUNC) + mk_pointer(&vtop->type); + sv = *vtop; /* save value to handle it later */ + vtop--; /* no vpop so that FP stack is not flushed */ + + if (g) { + u = tt; + } else if (c < 0) { + u = gjmp(0); + gsym(tt); + } else + u = 0; + + nocode_wanted = ncw_prev; + if (c == 1) + nocode_wanted++; + skip(':'); + expr_cond(); + + if (c < 0 && is_cond_bool(vtop) && is_cond_bool(&sv)) { + if (sv.r == VT_CMP) { + t1 = sv.jtrue; + t2 = u; + } else { + t1 = gvtst(0, 0); + t2 = gjmp(0); + gsym(u); + vpushv(&sv); + } + gvtst_set(0, t1); + gvtst_set(1, t2); + nocode_wanted = ncw_prev; + // tcc_warning("two conditions expr_cond"); + return; + } + + if ((vtop->type.t & VT_BTYPE) == VT_FUNC) + mk_pointer(&vtop->type); + + /* cast operands to correct type according to ISOC rules */ + if (!combine_types(&type, &sv, vtop, '?')) + type_incompatibility_error(&sv.type, &vtop->type, + "type mismatch in conditional expression (have '%s' and '%s')"); + /* keep structs lvalue by transforming `(expr ? a : b)` to `*(expr ? &a : &b)` so + that `(expr ? a : b).mem` does not error with "lvalue expected" */ + islv = (vtop->r & VT_LVAL) && (sv.r & VT_LVAL) && VT_STRUCT == (type.t & VT_BTYPE); + + /* now we convert second operand */ + if (c != 1) { + gen_cast(&type); + if (islv) { + mk_pointer(&vtop->type); + gaddrof(); + } else if (VT_STRUCT == (vtop->type.t & VT_BTYPE)) + gaddrof(); + } + + rc = RC_TYPE(type.t); + /* for long longs, we use fixed registers to avoid having + to handle a complicated move */ + if (USING_TWO_WORDS(type.t)) + rc = RC_RET(type.t); + + tt = r2 = 0; + if (c < 0) { + r2 = gv(rc); + tt = gjmp(0); + } + gsym(u); + nocode_wanted = ncw_prev; + + /* this is horrible, but we must also convert first + operand */ + if (c != 0) { + *vtop = sv; + gen_cast(&type); + if (islv) { + mk_pointer(&vtop->type); + gaddrof(); + } else if (VT_STRUCT == (vtop->type.t & VT_BTYPE)) + gaddrof(); + } + + if (c < 0) { + r1 = gv(rc); + move_reg(r2, r1, islv ? VT_PTR : type.t); + vtop->r = r2; + gsym(tt); + } + + if (islv) + indir(); } } diff --git a/tests/tests2/33_ternary_op.expect b/tests/tests2/33_ternary_op.expect index adeae238..497be0e4 100644 --- a/tests/tests2/33_ternary_op.expect +++ b/tests/tests2/33_ternary_op.expect @@ -1,4 +1,4 @@ -33_ternary_op.c:26: warning: pointer/integer mismatch +33_ternary_op.c:26: warning: pointer/integer mismatch in conditional expression 0 1 4 @@ -9,4 +9,4 @@ 21 24 27 -152 \ No newline at end of file +152