Module Name: src Committed By: rillig Date: Sat Jan 9 19:07:07 UTC 2021
Modified Files: src/usr.bin/xlint/lint1: tree.c Log Message: lint: split typeok into several smaller functions This reduces the number of local variables from 16 to around 5, in most of the smaller functions. No functional change. To generate a diff of this commit: cvs rdiff -u -r1.139 -r1.140 src/usr.bin/xlint/lint1/tree.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/usr.bin/xlint/lint1/tree.c diff -u src/usr.bin/xlint/lint1/tree.c:1.139 src/usr.bin/xlint/lint1/tree.c:1.140 --- src/usr.bin/xlint/lint1/tree.c:1.139 Sat Jan 9 18:21:08 2021 +++ src/usr.bin/xlint/lint1/tree.c Sat Jan 9 19:07:07 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: tree.c,v 1.139 2021/01/09 18:21:08 rillig Exp $ */ +/* $NetBSD: tree.c,v 1.140 2021/01/09 19:07:07 rillig Exp $ */ /* * Copyright (c) 1994, 1995 Jochen Pohl @@ -37,7 +37,7 @@ #include <sys/cdefs.h> #if defined(__RCSID) && !defined(lint) -__RCSID("$NetBSD: tree.c,v 1.139 2021/01/09 18:21:08 rillig Exp $"); +__RCSID("$NetBSD: tree.c,v 1.140 2021/01/09 19:07:07 rillig Exp $"); #endif #include <float.h> @@ -745,6 +745,274 @@ typeok_amper(mod_t *const mp, return true; } +static bool +typeok_star(tspec_t lt) +{ + /* until now there were no type checks for this operator */ + if (lt != PTR) { + /* cannot dereference non-pointer type */ + error(96); + return false; + } + return true; +} + +static bool +typeok_plus(op_t op, tspec_t lt, tspec_t rt) +{ + /* operands have scalar types (checked above) */ + if ((lt == PTR && !tspec_is_int(rt)) || + (rt == PTR && !tspec_is_int(lt))) { + warn_incompatible_types(op, lt, rt); + return false; + } + return true; +} + +static bool +typeok_minus(op_t op, tspec_t lt, type_t *lstp, tspec_t rt, type_t *rstp) +{ + /* operands have scalar types (checked above) */ + if (lt == PTR && (!tspec_is_int(rt) && rt != PTR)) { + warn_incompatible_types(op, lt, rt); + return false; + } else if (rt == PTR && lt != PTR) { + warn_incompatible_types(op, lt, rt); + return false; + } + if (lt == PTR && rt == PTR) { + if (!eqtype(lstp, rstp, 1, 0, NULL)) { + /* illegal pointer subtraction */ + error(116); + } + } + return true; +} + +static void +typeok_shr(mod_t *mp, + tnode_t *ln, tspec_t lt, tspec_t olt, + tspec_t rt, tspec_t ort) +{ + /* operands have integer types (checked above) */ + if (pflag && !tspec_is_uint(lt)) { + /* + * The left operand is signed. This means that + * the operation is (possibly) nonportable. + */ + if (ln->tn_op != CON) { + /* bitop. on signed value possibly nonportable */ + warning(117); + } else if (ln->tn_val->v_quad < 0) { + /* bitop. on signed value nonportable */ + warning(120); + } + } else if (!tflag && !sflag && + !tspec_is_uint(olt) && tspec_is_uint(ort)) { + /* + * The left operand would become unsigned in + * traditional C. + */ + if (hflag && + (ln->tn_op != CON || ln->tn_val->v_quad < 0)) { + /* semantics of '%s' change in ANSI C; ... */ + warning(118, mp->m_name); + } + } else if (!tflag && !sflag && + !tspec_is_uint(olt) && !tspec_is_uint(ort) && + psize(lt) < psize(rt)) { + /* + * In traditional C the left operand would be extended, + * possibly with 1, and then shifted. + */ + if (hflag && + (ln->tn_op != CON || ln->tn_val->v_quad < 0)) { + /* semantics of '%s' change in ANSI C; use ... */ + warning(118, mp->m_name); + } + } +} + +static void +typeok_shl(mod_t *mp, tspec_t lt, tspec_t rt) { + /* + * ANSI C does not perform balancing for shift operations, + * but traditional C does. If the width of the right operand + * is greater than the width of the left operand, than in + * traditional C the left operand would be extended to the + * width of the right operand. For SHL this may result in + * different results. + */ + if (psize(lt) < psize(rt)) { + /* + * XXX If both operands are constant, make sure + * that there is really a difference between + * ANSI C and traditional C. + */ + if (hflag) + /* semantics of '%s' change in ANSI C; use ... */ + warning(118, mp->m_name); + } +} + +static void +typeok_shift(tspec_t lt, tnode_t *rn, tspec_t rt) +{ + if (rn->tn_op == CON) { + if (!tspec_is_uint(rt) && rn->tn_val->v_quad < 0) { + /* negative shift */ + warning(121); + } else if ((uint64_t)rn->tn_val->v_quad == (uint64_t)size(lt)) { + /* shift equal to size of object */ + warning(267); + } else if ((uint64_t)rn->tn_val->v_quad > (uint64_t)size(lt)) { + /* shift greater than size of object */ + warning(122); + } + } +} + +static bool +typeok_eq(tnode_t *ln, tspec_t lt, tspec_t lst, + tnode_t *rn, tspec_t rt, tspec_t rst) +{ + if (lt == PTR && ((rt == PTR && rst == VOID) || + tspec_is_int(rt))) { + if (rn->tn_op == CON && rn->tn_val->v_quad == 0) + return true; + } + if (rt == PTR && ((lt == PTR && lst == VOID) || + tspec_is_int(lt))) { + if (ln->tn_op == CON && ln->tn_val->v_quad == 0) + return true; + } + return false; +} + +static bool +typeok_ordered_comparison(op_t op, mod_t *mp, + tnode_t *ln, type_t *ltp, tspec_t lt, + tnode_t *rn, type_t *rtp, tspec_t rt) +{ + if ((lt == PTR || rt == PTR) && lt != rt) { + if (tspec_is_int(lt) || tspec_is_int(rt)) { + const char *lx = lt == PTR ? + "pointer" : "integer"; + const char *rx = rt == PTR ? + "pointer" : "integer"; + /* illegal combination of %s (%s) and ... */ + warning(123, lx, type_name(ltp), + rx, type_name(rtp), mp->m_name); + } else { + warn_incompatible_types(op, lt, rt); + return false; + } + } else if (lt == PTR && rt == PTR) { + check_pointer_comparison(op, ln, rn); + } + return true; +} + +static bool +typeok_quest(tspec_t lt, tnode_t **rn) +{ + if (!tspec_is_scalar(lt)) { + /* first operand must have scalar type, op ? : */ + error(170); + return false; + } + while ((*rn)->tn_op == CVT) + *rn = (*rn)->tn_left; + lint_assert((*rn)->tn_op == COLON); + return true; +} + +static bool +typeok_colon(mod_t *mp, + tnode_t *ln, type_t *ltp, tspec_t lt, type_t *lstp, tspec_t lst, + tnode_t *rn, type_t *rtp, tspec_t rt, type_t *rstp, tspec_t rst) +{ + if (tspec_is_arith(lt) && tspec_is_arith(rt)) + return true; + + if (lt == STRUCT && rt == STRUCT && ltp->t_str == rtp->t_str) + return true; + if (lt == UNION && rt == UNION && ltp->t_str == rtp->t_str) + return true; + + /* combination of any pointer and 0, 0L or (void *)0 is ok */ + if (lt == PTR && ((rt == PTR && rst == VOID) || tspec_is_int(rt))) { + if (rn->tn_op == CON && rn->tn_val->v_quad == 0) + return true; + } + if (rt == PTR && ((lt == PTR && lst == VOID) || tspec_is_int(lt))) { + if (ln->tn_op == CON && ln->tn_val->v_quad == 0) + return true; + } + + if ((lt == PTR && tspec_is_int(rt)) || + (tspec_is_int(lt) && rt == PTR)) { + const char *lx = lt == PTR ? "pointer" : "integer"; + const char *rx = rt == PTR ? "pointer" : "integer"; + /* illegal combination of %s (%s) and %s (%s), op %s */ + warning(123, lx, type_name(ltp), + rx, type_name(rtp), mp->m_name); + return true; + } + + if (lt == VOID || rt == VOID) { + if (lt != VOID || rt != VOID) + /* incompatible types in conditional */ + warning(126); + return true; + } + + if (lt == PTR && rt == PTR && ((lst == VOID && rst == FUNC) || + (lst == FUNC && rst == VOID))) { + /* (void *)0 handled above */ + if (sflag) + /* ANSI C forbids conv. of %s to %s, op %s */ + warning(305, "function pointer", "'void *'", + mp->m_name); + return true; + } + + if (rt == PTR && lt == PTR) { + if (eqptrtype(lstp, rstp, 1)) + return true; + if (!eqtype(lstp, rstp, 1, 0, NULL)) + warn_incompatible_pointers(mp, ltp, rtp); + return true; + } + + /* incompatible types in conditional */ + error(126); + return false; +} + +static bool +typeok_assign(mod_t *mp, tnode_t *ln, type_t *ltp, tspec_t lt) +{ + if (!ln->tn_lvalue) { + if (ln->tn_op == CVT && ln->tn_cast && + ln->tn_left->tn_op == LOAD) { + if (ln->tn_type->t_tspec == PTR) + return true; + /* a cast does not yield an lvalue */ + error(163); + } + /* %soperand of '%s' must be lvalue */ + error(114, "left ", mp->m_name); + return false; + } else if (ltp->t_const || ((lt == STRUCT || lt == UNION) && + has_constant_member(ltp))) { + if (!tflag) + /* %soperand of '%s' must be modifiable lvalue */ + warning(115, "left ", mp->m_name); + } + return true; +} + /* * Perform most type checks. First the types are checked using * the information from modtab[]. After that it is done by hand for @@ -850,108 +1118,24 @@ typeok(op_t op, int arg, tnode_t *ln, tn return 0; break; case STAR: - /* until now there were no type checks for this operator */ - if (lt != PTR) { - /* cannot dereference non-pointer type */ - error(96); + if (!typeok_star(lt)) return 0; - } break; case PLUS: - /* operands have scalar types (checked above) */ - if ((lt == PTR && !tspec_is_int(rt)) || - (rt == PTR && !tspec_is_int(lt))) { - warn_incompatible_types(op, lt, rt); + if (!typeok_plus(op, lt, rt)) return 0; - } break; case MINUS: - /* operands have scalar types (checked above) */ - if (lt == PTR && (!tspec_is_int(rt) && rt != PTR)) { - warn_incompatible_types(op, lt, rt); - return 0; - } else if (rt == PTR && lt != PTR) { - warn_incompatible_types(op, lt, rt); + if (!typeok_minus(op, lt, lstp, rt, rstp)) return 0; - } - if (lt == PTR && rt == PTR) { - if (!eqtype(lstp, rstp, 1, 0, NULL)) { - /* illegal pointer subtraction */ - error(116); - } - } break; case SHR: - /* operands have integer types (checked above) */ - if (pflag && !tspec_is_uint(lt)) { - /* - * The left operand is signed. This means that - * the operation is (possibly) nonportable. - */ - if (ln->tn_op != CON) { - /* bitop. on signed value poss. nonportable */ - warning(117); - } else if (ln->tn_val->v_quad < 0) { - /* bitop. on signed value nonportable */ - warning(120); - } - } else if (!tflag && !sflag && - !tspec_is_uint(olt) && tspec_is_uint(ort)) { - /* - * The left operand would become unsigned in - * traditional C. - */ - if (hflag && - (ln->tn_op != CON || ln->tn_val->v_quad < 0)) { - /* semantics of '%s' change in ANSI C; ... */ - warning(118, mp->m_name); - } - } else if (!tflag && !sflag && - !tspec_is_uint(olt) && !tspec_is_uint(ort) && - psize(lt) < psize(rt)) { - /* - * In traditional C the left operand would be extended, - * possibly with 1, and then shifted. - */ - if (hflag && - (ln->tn_op != CON || ln->tn_val->v_quad < 0)) { - /* semantics of '%s' change in ANSI C; ... */ - warning(118, mp->m_name); - } - } + typeok_shr(mp, ln, lt, olt, rt, ort); goto shift; case SHL: - /* - * ANSI C does not perform balancing for shift operations, - * but traditional C does. If the width of the right operand - * is greater than the width of the left operand, than in - * traditional C the left operand would be extended to the - * width of the right operand. For SHL this may result in - * different results. - */ - if (psize(lt) < psize(rt)) { - /* - * XXX If both operands are constant, make sure - * that there is really a difference between - * ANSI C and traditional C. - */ - if (hflag) - /* semantics of '%s' change in ANSI C; ... */ - warning(118, mp->m_name); - } + typeok_shl(mp, lt, rt); shift: - if (rn->tn_op == CON) { - if (!tspec_is_uint(rt) && rn->tn_val->v_quad < 0) { - /* negative shift */ - warning(121); - } else if ((uint64_t)rn->tn_val->v_quad == (uint64_t)size(lt)) { - /* shift equal to size of object */ - warning(267); - } else if ((uint64_t)rn->tn_val->v_quad > (uint64_t)size(lt)) { - /* shift greater than size of object */ - warning(122); - } - } + typeok_shift(lt, rn, rt); break; case EQ: case NE: @@ -959,108 +1143,27 @@ typeok(op_t op, int arg, tnode_t *ln, tn * Accept some things which are allowed with EQ and NE, * but not with ordered comparisons. */ - if (lt == PTR && ((rt == PTR && rst == VOID) || - tspec_is_int(rt))) { - if (rn->tn_op == CON && rn->tn_val->v_quad == 0) - break; - } - if (rt == PTR && ((lt == PTR && lst == VOID) || - tspec_is_int(lt))) { - if (ln->tn_op == CON && ln->tn_val->v_quad == 0) - break; - } + if (typeok_eq(ln, lt, lst, rn, rt, rst)) + break; /* FALLTHROUGH */ case LT: case GT: case LE: case GE: - if ((lt == PTR || rt == PTR) && lt != rt) { - if (tspec_is_int(lt) || tspec_is_int(rt)) { - const char *lx = lt == PTR ? - "pointer" : "integer"; - const char *rx = rt == PTR ? - "pointer" : "integer"; - /* illegal combination of %s (%s) and ... */ - warning(123, lx, type_name(ltp), - rx, type_name(rtp), mp->m_name); - } else { - warn_incompatible_types(op, lt, rt); - return 0; - } - } else if (lt == PTR && rt == PTR) { - check_pointer_comparison(op, ln, rn); - } + if (!typeok_ordered_comparison(op, mp, + ln, ltp, lt, rn, rtp, rt)) + return 0; break; case QUEST: - if (!tspec_is_scalar(lt)) { - /* first operand must have scalar type, op ? : */ - error(170); + if (!typeok_quest(lt, &rn)) return 0; - } - while (rn->tn_op == CVT) - rn = rn->tn_left; - lint_assert(rn->tn_op == COLON); break; case COLON: - if (tspec_is_arith(lt) && tspec_is_arith(rt)) - break; - - if (lt == STRUCT && rt == STRUCT && ltp->t_str == rtp->t_str) - break; - if (lt == UNION && rt == UNION && ltp->t_str == rtp->t_str) - break; - - /* combination of any pointer and 0, 0L or (void *)0 is ok */ - if (lt == PTR && ((rt == PTR && rst == VOID) || - tspec_is_int(rt))) { - if (rn->tn_op == CON && rn->tn_val->v_quad == 0) - break; - } - if (rt == PTR && ((lt == PTR && lst == VOID) || - tspec_is_int(lt))) { - if (ln->tn_op == CON && ln->tn_val->v_quad == 0) - break; - } - - if ((lt == PTR && tspec_is_int(rt)) || - (tspec_is_int(lt) && rt == PTR)) { - const char *lx = lt == PTR ? "pointer" : "integer"; - const char *rx = rt == PTR ? "pointer" : "integer"; - /* illegal combination of %s (%s) and %s (%s), op %s */ - warning(123, lx, type_name(ltp), - rx, type_name(rtp), mp->m_name); - break; - } - - if (lt == VOID || rt == VOID) { - if (lt != VOID || rt != VOID) - /* incompatible types in conditional */ - warning(126); - break; - } - - if (lt == PTR && rt == PTR && ((lst == VOID && rst == FUNC) || - (lst == FUNC && rst == VOID))) { - /* (void *)0 handled above */ - if (sflag) - /* ANSI C forbids conv. of %s to %s, op %s */ - warning(305, "function pointer", "'void *'", - mp->m_name); - break; - } - - if (rt == PTR && lt == PTR) { - if (eqptrtype(lstp, rstp, 1)) - break; - if (!eqtype(lstp, rstp, 1, 0, NULL)) - warn_incompatible_pointers(mp, ltp, rtp); - break; - } - - /* incompatible types in conditional */ - error(126); - return 0; - + if (!typeok_colon(mp, + ln, ltp, lt, lstp, lst, + rn, rtp, rt, rstp, rst)) + return 0; + break; case ASSIGN: case INIT: case FARG: @@ -1094,23 +1197,8 @@ typeok(op_t op, int arg, tnode_t *ln, tn case ORASS: goto assign; assign: - if (!ln->tn_lvalue) { - if (ln->tn_op == CVT && ln->tn_cast && - ln->tn_left->tn_op == LOAD) { - if (ln->tn_type->t_tspec == PTR) - break; - /* a cast does not yield an lvalue */ - error(163); - } - /* %soperand of '%s' must be lvalue */ - error(114, "left ", mp->m_name); + if (!typeok_assign(mp, ln, ltp, lt)) return 0; - } else if (ltp->t_const || ((lt == STRUCT || lt == UNION) && - has_constant_member(ltp))) { - if (!tflag) - /* %soperand of '%s' must be modifiable lvalue */ - warning(115, "left ", mp->m_name); - } break; case COMMA: if (!modtab[ln->tn_op].m_sideeff)