/* exprtype.c Expression type manipulation Copyright (C) 2013 Bill Currie Author: Bill Currie Date: 2013/06/27 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "diagnostic.h" #include "expr.h" #include "options.h" #include "type.h" #include "qc-parse.h" typedef struct { int op; type_t *type; type_t *a_cast; type_t *b_cast; expr_t *(*process)(int op, expr_t *e1, expr_t *e2); } expr_type_t; static expr_t *pointer_arithmetic (int op, expr_t *e1, expr_t *e2); static expr_t *inverse_multiply (int op, expr_t *e1, expr_t *e2); static expr_type_t string_string[] = { {'+', &type_string}, {EQ, &type_integer}, {NE, &type_integer}, {LE, &type_integer}, {GE, &type_integer}, {LT, &type_integer}, {GT, &type_integer}, {0, 0} }; static expr_type_t float_float[] = { {'+', &type_float}, {'-', &type_float}, {'*', &type_float}, {'/', &type_float}, {'&', &type_float}, {'|', &type_float}, {'^', &type_float}, {'%', &type_float}, {SHL, &type_float}, {SHR, &type_float}, {EQ, &type_integer}, {NE, &type_integer}, {LE, &type_integer}, {GE, &type_integer}, {LT, &type_integer}, {GT, &type_integer}, {0, 0} }; static expr_type_t float_vector[] = { {'*', &type_vector}, {0, 0} }; static expr_type_t float_quat[] = { {'*', &type_quaternion}, {'/', 0, 0, 0, inverse_multiply}, {0, 0} }; static expr_type_t float_integer[] = { {'+', &type_float, 0, &type_float}, {'-', &type_float, 0, &type_float}, {'*', &type_float, 0, &type_float}, {'/', &type_float, 0, &type_float}, {'&', &type_float, 0, &type_float}, {'|', &type_float, 0, &type_float}, {'^', &type_float, 0, &type_float}, {'%', &type_float, 0, &type_float}, {SHL, &type_float, 0, &type_float}, {SHR, &type_float, 0, &type_float}, {EQ, &type_integer, 0, &type_float}, {NE, &type_integer, 0, &type_float}, {LE, &type_integer, 0, &type_float}, {GE, &type_integer, 0, &type_float}, {LT, &type_integer, 0, &type_float}, {GT, &type_integer, 0, &type_float}, {0, 0} }; #define float_uinteger float_integer #define float_short float_integer static expr_type_t vector_float[] = { {'*', &type_vector}, {'/', 0, 0, 0, inverse_multiply}, {0, 0} }; static expr_type_t vector_vector[] = { {'+', &type_vector}, {'-', &type_vector}, {'*', &type_float}, {EQ, &type_integer}, {NE, &type_integer}, {0, 0} }; #define vector_integer vector_float #define vector_uinteger vector_float #define vector_short vector_float static expr_type_t entity_entity[] = { {EQ, &type_integer}, {NE, &type_integer}, {0, 0} }; static expr_type_t field_field[] = { {EQ, &type_integer}, {NE, &type_integer}, {0, 0} }; static expr_type_t func_func[] = { {EQ, &type_integer}, {NE, &type_integer}, {0, 0} }; static expr_type_t pointer_pointer[] = { {'-', &type_integer, &type_integer, &type_integer}, {EQ, &type_integer}, {NE, &type_integer}, {LE, &type_integer}, {GE, &type_integer}, {LT, &type_integer}, {GT, &type_integer}, {0, 0} }; static expr_type_t pointer_integer[] = { {'+', 0, 0, 0, pointer_arithmetic}, {'-', 0, 0, 0, pointer_arithmetic}, {0, 0} }; #define pointer_uinteger pointer_integer #define pointer_short pointer_integer static expr_type_t quat_float[] = { {'*', &type_quaternion}, {'/', &type_quaternion}, {0, 0} }; static expr_type_t quat_vector[] = { {'*', &type_vector}, {0, 0} }; static expr_type_t quat_quat[] = { {'+', &type_quaternion}, {'-', &type_quaternion}, {'*', &type_quaternion}, {EQ, &type_integer}, {NE, &type_integer}, {0, 0} }; static expr_type_t quat_integer[] = { {'*', &type_quaternion, 0, &type_float}, {'/', &type_quaternion, 0, &type_float}, {0, 0} }; #define quat_uinteger quat_integer #define quat_short quat_integer static expr_type_t integer_float[] = { {'+', &type_float, &type_float, 0}, {'-', &type_float, &type_float, 0}, {'*', &type_float, &type_float, 0}, {'/', &type_float, &type_float, 0}, {'&', &type_float, &type_float, 0}, {'|', &type_float, &type_float, 0}, {'^', &type_float, &type_float, 0}, {'%', &type_float, &type_float, 0}, {SHL, &type_integer, 0, &type_integer}, //FIXME? {SHR, &type_integer, 0, &type_integer}, //FIXME? {EQ, &type_integer, &type_float, 0}, {NE, &type_integer, &type_float, 0}, {LE, &type_integer, &type_float, 0}, {GE, &type_integer, &type_float, 0}, {LT, &type_integer, &type_float, 0}, {GT, &type_integer, &type_float, 0}, {0, 0} }; static expr_type_t integer_vector[] = { {'*', &type_vector, &type_float, 0}, {0, 0} }; static expr_type_t integer_pointer[] = { {'+', &type_pointer, 0, &type_integer}, {0, 0} }; static expr_type_t integer_quat[] = { {'*', &type_quaternion, &type_float, 0}, {'/', &type_quaternion, &type_float, 0}, {0, 0} }; static expr_type_t integer_integer[] = { {'+', &type_integer}, {'-', &type_integer}, {'*', &type_integer}, {'/', &type_integer}, {'&', &type_integer}, {'|', &type_integer}, {'^', &type_integer}, {'%', &type_integer}, {SHL, &type_integer}, {SHR, &type_integer}, {EQ, &type_integer}, {NE, &type_integer}, {LE, &type_integer}, {GE, &type_integer}, {LT, &type_integer}, {GT, &type_integer}, {0, 0} }; static expr_type_t integer_uinteger[] = { {'+', &type_integer}, {'-', &type_integer}, {'*', &type_integer}, {'/', &type_integer}, {'&', &type_integer}, {'|', &type_integer}, {'^', &type_integer}, {'%', &type_integer}, {SHL, &type_integer}, {SHR, &type_integer}, {EQ, &type_integer}, {NE, &type_integer}, {LE, &type_integer}, {GE, &type_integer}, {LT, &type_integer}, {GT, &type_integer}, {0, 0} }; static expr_type_t integer_short[] = { {'+', &type_integer, 0, &type_integer}, {'-', &type_integer, 0, &type_integer}, {'*', &type_integer, 0, &type_integer}, {'/', &type_integer, 0, &type_integer}, {'&', &type_integer, 0, &type_integer}, {'|', &type_integer, 0, &type_integer}, {'^', &type_integer, 0, &type_integer}, {'%', &type_integer, 0, &type_integer}, {SHL, &type_integer, 0, &type_integer}, {SHR, &type_integer, 0, &type_integer}, {EQ, &type_integer, 0, &type_integer}, {NE, &type_integer, 0, &type_integer}, {LE, &type_integer, 0, &type_integer}, {GE, &type_integer, 0, &type_integer}, {LT, &type_integer, 0, &type_integer}, {GT, &type_integer, 0, &type_integer}, {0, 0} }; #define uinteger_float integer_float #define uinteger_vector integer_vector #define uinteger_pointer integer_pointer #define uinteger_quat integer_quat static expr_type_t uinteger_integer[] = { {'+', &type_integer}, {'-', &type_integer}, {'*', &type_integer}, {'/', &type_integer}, {'&', &type_integer}, {'|', &type_integer}, {'^', &type_integer}, {'%', &type_integer}, {SHL, &type_uinteger}, {SHR, &type_uinteger}, {EQ, &type_integer}, {NE, &type_integer}, {LE, &type_integer}, {GE, &type_integer}, {LT, &type_integer}, {GT, &type_integer}, {0, 0} }; static expr_type_t uinteger_uinteger[] = { {'+', &type_uinteger}, {'-', &type_uinteger}, {'*', &type_uinteger}, {'/', &type_uinteger}, {'&', &type_uinteger}, {'|', &type_uinteger}, {'^', &type_uinteger}, {'%', &type_uinteger}, {SHL, &type_uinteger}, {SHR, &type_uinteger}, {EQ, &type_integer}, {NE, &type_integer}, {LE, &type_integer}, {GE, &type_integer}, {LT, &type_integer}, {GT, &type_integer}, {0, 0} }; #define uinteger_short uinteger_integer #define short_float integer_float #define short_vector integer_vector #define short_pointer integer_pointer #define short_quat integer_quat static expr_type_t short_integer[] = { {'+', &type_integer, &type_integer, 0}, {'-', &type_integer, &type_integer, 0}, {'*', &type_integer, &type_integer, 0}, {'/', &type_integer, &type_integer, 0}, {'&', &type_integer, &type_integer, 0}, {'|', &type_integer, &type_integer, 0}, {'^', &type_integer, &type_integer, 0}, {'%', &type_integer, &type_integer, 0}, {SHL, &type_short}, {SHR, &type_short}, {EQ, &type_integer, &type_integer, 0}, {NE, &type_integer, &type_integer, 0}, {LE, &type_integer, &type_integer, 0}, {GE, &type_integer, &type_integer, 0}, {LT, &type_integer, &type_integer, 0}, {GT, &type_integer, &type_integer, 0}, {0, 0} }; static expr_type_t short_uinteger[] = { {'+', &type_uinteger, &type_uinteger, 0}, {'-', &type_uinteger, &type_uinteger, 0}, {'*', &type_uinteger, &type_uinteger, 0}, {'/', &type_uinteger, &type_uinteger, 0}, {'&', &type_uinteger, &type_uinteger, 0}, {'|', &type_uinteger, &type_uinteger, 0}, {'^', &type_uinteger, &type_uinteger, 0}, {'%', &type_uinteger, &type_uinteger, 0}, {SHL, &type_short}, {SHR, &type_short}, {EQ, &type_integer, &type_uinteger, 0}, {NE, &type_integer, &type_uinteger, 0}, {LE, &type_integer, &type_uinteger, 0}, {GE, &type_integer, &type_uinteger, 0}, {LT, &type_integer, &type_uinteger, 0}, {GT, &type_integer, &type_uinteger, 0}, {0, 0} }; static expr_type_t short_short[] = { {'+', &type_short}, {'-', &type_short}, {'*', &type_short}, {'/', &type_short}, {'&', &type_short}, {'|', &type_short}, {'^', &type_short}, {'%', &type_short}, {SHL, &type_short}, {SHR, &type_short}, {EQ, &type_integer}, {NE, &type_integer}, {LE, &type_integer}, {GE, &type_integer}, {LT, &type_integer}, {GT, &type_integer}, {0, 0} }; static expr_type_t *string_x[] = { 0, // ev_void string_string, 0, // ev_float 0, // ev_vector 0, // ev_entity 0, // ev_field 0, // ev_func 0, // ev_pointer 0, // ev_quat 0, // ev_integer 0, // ev_uinteger 0, // ev_short }; static expr_type_t *float_x[] = { 0, // ev_void 0, // ev_string float_float, float_vector, 0, // ev_entity 0, // ev_field 0, // ev_func 0, // ev_pointer float_quat, float_integer, float_uinteger, float_short, }; static expr_type_t *vector_x[] = { 0, // ev_void 0, // ev_string vector_float, vector_vector, 0, // ev_entity 0, // ev_field 0, // ev_func 0, // ev_pointer 0, // ev_quaternion vector_integer, vector_uinteger, vector_short, }; static expr_type_t *entity_x[] = { 0, // ev_void 0, // ev_string 0, // ev_float 0, // ev_vector entity_entity, // ev_entity 0, // ev_field 0, // ev_func 0, // ev_pointer 0, // ev_quaternion 0, // ev_integer 0, // ev_uinteger 0, // ev_short }; static expr_type_t *field_x[] = { 0, // ev_void 0, // ev_string 0, // ev_float 0, // ev_vector 0, // ev_entity field_field, // ev_field 0, // ev_func 0, // ev_pointer 0, // ev_quaternion 0, // ev_integer 0, // ev_uinteger 0, // ev_short }; static expr_type_t *funcx[] = { 0, // ev_void 0, // ev_string 0, // ev_float 0, // ev_vector 0, // ev_entity 0, // ev_field func_func, // ev_func 0, // ev_pointer 0, // ev_quaternion 0, // ev_integer 0, // ev_uinteger 0, // ev_short }; static expr_type_t *pointer_x[] = { 0, // ev_void 0, // ev_string 0, // ev_float 0, // ev_vector 0, // ev_entity 0, // ev_field 0, // ev_func pointer_pointer, 0, // ev_quat pointer_integer, pointer_uinteger, pointer_short, }; static expr_type_t *quat_x[] = { 0, // ev_void 0, // ev_string quat_float, quat_vector, 0, // ev_entity 0, // ev_field 0, // ev_func 0, // ev_pointer quat_quat, quat_integer, quat_uinteger, quat_short, }; static expr_type_t *integer_x[] = { 0, // ev_void 0, // ev_string integer_float, integer_vector, 0, // ev_entity 0, // ev_field 0, // ev_func integer_pointer, integer_quat, integer_integer, integer_uinteger, integer_short, }; static expr_type_t *uinteger_x[] = { 0, // ev_void 0, // ev_string uinteger_float, uinteger_vector, 0, // ev_entity 0, // ev_field 0, // ev_func uinteger_pointer, uinteger_quat, uinteger_integer, uinteger_uinteger, uinteger_short, }; static expr_type_t *short_x[] = { 0, // ev_void 0, // ev_string short_float, short_vector, 0, // ev_entity 0, // ev_field 0, // ev_func short_pointer, short_quat, short_integer, short_uinteger, short_short, }; static expr_type_t **binary_expr_types[] = { 0, // ev_void string_x, float_x, vector_x, entity_x, field_x, funcx, pointer_x, quat_x, integer_x, uinteger_x, short_x, }; static expr_t * pointer_arithmetic (int op, expr_t *e1, expr_t *e2) { expr_t *e; type_t *ptype = get_type (e1); if (!is_pointer (ptype)) { ptype = get_type (e2); } if (!is_pointer (ptype)) { internal_error (e1, "pointer arithmetic on non-pointers"); } e1 = cast_expr (&type_integer, e1); e2 = cast_expr (&type_integer, e2); e = binary_expr (op, e1, e2); return cast_expr (ptype, e); } static expr_t * inverse_multiply (int op, expr_t *e1, expr_t *e2) { // There is no vector/float or quaternion/float instruction and adding // one would mean the engine would have to do 1/f every time expr_t *one = new_float_expr (1); return binary_expr ('*', e1, binary_expr ('/', one, e2)); } static expr_t * invalid_binary_expr (int op, expr_t *e1, expr_t *e2) { etype_t t1, t2; t1 = extract_type (e1); t2 = extract_type (e2); return error (e1, "invalid binary expression: %s %s %s", pr_type_name[t1], get_op_string (op), pr_type_name[t2]); } static expr_t * reimplement_binary_expr (int op, expr_t *e1, expr_t *e2) { expr_t *e; if (options.code.progsversion == PROG_ID_VERSION) { switch (op) { case '%': { expr_t *tmp1, *tmp2, *tmp3, *tmp4, *t1, *t2; e = new_block_expr (); t1 = new_temp_def_expr (&type_float); t2 = new_temp_def_expr (&type_float); tmp1 = new_temp_def_expr (&type_float); tmp2 = new_temp_def_expr (&type_float); tmp3 = new_temp_def_expr (&type_float); tmp4 = new_temp_def_expr (&type_float); append_expr (e, assign_expr (t1, e1)); e1 = binary_expr ('&', t1, t1); append_expr (e, assign_expr (tmp1, e1)); append_expr (e, assign_expr (t2, e2)); e2 = binary_expr ('&', t2, t2); append_expr (e, assign_expr (tmp2, e2)); e1 = binary_expr ('/', tmp1, tmp2); append_expr (e, assign_expr (tmp3, e1)); e2 = binary_expr ('&', tmp3, tmp3); append_expr (e, assign_expr (tmp4, e2)); e1 = binary_expr ('*', tmp2, tmp4); e2 = binary_expr ('-', tmp1, e1); e->e.block.result = e2; return e; } break; } } return 0; } static expr_t * check_precedence (int op, expr_t *e1, expr_t *e2) { if (e1->type == ex_uexpr && e1->e.expr.op == '!' && !e1->paren) { if (options.traditional) { if (op != AND && op != OR && op != '=') { notice (e1, "precedence of `!' and `%s' inverted for " "traditional code", get_op_string (op)); e1->e.expr.e1->paren = 1; return unary_expr ('!', binary_expr (op, e1->e.expr.e1, e2)); } } else if (op == '&' || op == '|') { if (options.warnings.precedence) warning (e1, "ambiguous logic. Suggest explicit parentheses " "with expressions involving ! and %s", get_op_string (op)); } } if (options.traditional) { if (e2->type == ex_expr && !e2->paren) { if (((op == '&' || op == '|') && (is_math_op (e2->e.expr.op) || is_compare (e2->e.expr.op))) || (op == '=' && (e2->e.expr.op == OR || e2->e.expr.op == AND))) { notice (e1, "precedence of `%s' and `%s' inverted for " "traditional code", get_op_string (op), get_op_string (e2->e.expr.op)); e1 = binary_expr (op, e1, e2->e.expr.e1); e1->paren = 1; return binary_expr (e2->e.expr.op, e1, e2->e.expr.e2); } if (((op == EQ || op == NE) && is_compare (e2->e.expr.op)) || (op == OR && e2->e.expr.op == AND) || (op == '|' && e2->e.expr.op == '&')) { notice (e1, "precedence of `%s' raised to `%s' for " "traditional code", get_op_string (op), get_op_string (e2->e.expr.op)); e1 = binary_expr (op, e1, e2->e.expr.e1); e1->paren = 1; return binary_expr (e2->e.expr.op, e1, e2->e.expr.e2); } } else if (e1->type == ex_expr && !e1->paren) { if (((op == '&' || op == '|') && (is_math_op (e1->e.expr.op) || is_compare (e1->e.expr.op))) || (op == '=' && (e1->e.expr.op == OR || e1->e.expr.op == AND))) { notice (e1, "precedence of `%s' and `%s' inverted for " "traditional code", get_op_string (op), get_op_string (e1->e.expr.op)); e2 = binary_expr (op, e1->e.expr.e2, e2); e2->paren = 1; return binary_expr (e1->e.expr.op, e1->e.expr.e1, e2); } } } else { if (e2->type == ex_expr && !e2->paren) { if ((op == '&' || op == '|' || op == '^') && (is_math_op (e2->e.expr.op) || is_compare (e2->e.expr.op))) { if (options.warnings.precedence) warning (e2, "suggest parentheses around %s in " "operand of %c", is_compare (e2->e.expr.op) ? "comparison" : get_op_string (e2->e.expr.op), op); } } if (e1->type == ex_expr && !e1->paren) { if ((op == '&' || op == '|' || op == '^') && (is_math_op (e1->e.expr.op) || is_compare (e1->e.expr.op))) { if (options.warnings.precedence) warning (e1, "suggest parentheses around %s in " "operand of %c", is_compare (e1->e.expr.op) ? "comparison" : get_op_string (e1->e.expr.op), op); } } } return 0; } expr_t * binary_expr (int op, expr_t *e1, expr_t *e2) { type_t *t1, *t2; etype_t et1, et2; expr_t *e; expr_type_t *expr_type; convert_name (e1); e1 = convert_vector (e1); if (e1->type == ex_block && e1->e.block.is_call && has_function_call (e2) && e1->e.block.result) { e = new_temp_def_expr (get_type (e1->e.block.result)); e1 = assign_expr (e, e1); } if (e1->type == ex_error) return e1; convert_name (e2); e2 = convert_vector (e2); if (e2->type == ex_error) return e2; if (e1->type == ex_bool) e1 = convert_from_bool (e1, get_type (e2)); if (e2->type == ex_bool) e2 = convert_from_bool (e2, get_type (e1)); if ((e = check_precedence (op, e1, e2))) return e; t1 = get_type (e1); t2 = get_type (e2); if (!t1 || !t2) internal_error (e1, "expr with no type"); if (op == EQ || op == NE) { if (e1->type == ex_nil) { t1 = t2; convert_nil (e1, t1); } else if (e2->type == ex_nil) { t2 = t1; convert_nil (e2, t2); } } et1 = low_level_type (t1); et2 = low_level_type (t2); if (et1 > ev_short || !binary_expr_types[et1]) return invalid_binary_expr(op, e1, e2); if (et2 > ev_short || !binary_expr_types[et1][et2]) return invalid_binary_expr(op, e1, e2); expr_type = binary_expr_types[et1][et2]; while (expr_type->op && expr_type->op != op) expr_type++; if (!expr_type->op) return invalid_binary_expr(op, e1, e2); if (expr_type->a_cast) e1 = cast_expr (expr_type->a_cast, e1); if (expr_type->b_cast) e2 = cast_expr (expr_type->b_cast, e2); if (expr_type->process) { return fold_constants (expr_type->process (op, e1, e2)); } if ((e = reimplement_binary_expr (op, e1, e2))) return fold_constants (e); e = new_binary_expr (op, e1, e2); e->e.expr.type = expr_type->type; if (is_compare (op) || is_logic (op)) { if (options.code.progsversion == PROG_ID_VERSION) e->e.expr.type = &type_float; } return fold_constants (e); }