/* expr_assign.c assignment expression construction and manipulations Copyright (C) 2001 Bill Currie Author: Bill Currie Date: 2001/06/15 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 #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #include #include "QF/alloc.h" #include "QF/dstring.h" #include "QF/mathlib.h" #include "QF/sys.h" #include "QF/va.h" #include "qfcc.h" #include "class.h" #include "def.h" #include "defspace.h" #include "diagnostic.h" #include "emit.h" #include "expr.h" #include "function.h" #include "idstuff.h" #include "method.h" #include "options.h" #include "reloc.h" #include "shared.h" #include "strpool.h" #include "struct.h" #include "symtab.h" #include "type.h" #include "value.h" #include "qc-parse.h" static expr_t * check_assign_logic_precedence (expr_t *dst, expr_t *src) { if (src->type == ex_expr && !src->paren && is_logic (src->e.expr.op)) { // traditional QuakeC gives = higher precedence than && and || expr_t *assignment; notice (src, "precedence of `=' and `%s' inverted for " "traditional code", get_op_string (src->e.expr.op)); // change {a = (b logic c)} to {(a = b) logic c} assignment = assign_expr (dst, src->e.expr.e1); assignment->paren = 1; // protect assignment from binary_expr return binary_expr (src->e.expr.op, assignment, src->e.expr.e2); } return 0; } int is_lvalue (const expr_t *expr) { switch (expr->type) { case ex_def: return !expr->e.def->constant; case ex_symbol: switch (expr->e.symbol->sy_type) { case sy_name: break; case sy_var: return 1; case sy_const: break; case sy_type: break; case sy_expr: break; case sy_func: break; case sy_class: break; case sy_convert: break; } break; case ex_temp: return 1; case ex_expr: if (expr->e.expr.op == '.') { return 1; } if (expr->e.expr.op == 'A') { return is_lvalue (expr->e.expr.e1); } break; case ex_uexpr: if (expr->e.expr.op == '.') { return 1; } if (expr->e.expr.op == 'A') { return is_lvalue (expr->e.expr.e1); } break; case ex_memset: case ex_compound: case ex_state: case ex_bool: case ex_label: case ex_labelref: case ex_block: case ex_vector: case ex_nil: case ex_value: case ex_error: break; } return 0; } static expr_t * check_valid_lvalue (expr_t *expr) { if (!is_lvalue (expr)) { if (options.traditional) { warning (expr, "invalid lvalue in assignment"); return 0; } return error (expr, "invalid lvalue in assignment"); } return 0; } static expr_t * check_types_compatible (expr_t *dst, expr_t *src) { type_t *dst_type = get_type (dst); type_t *src_type = get_type (src); if (dst_type == src_type) { return 0; } if (type_assignable (dst_type, src_type)) { if (is_scalar (dst_type) && is_scalar (src_type)) { if (!src->implicit) { if (is_double (src_type)) { warning (dst, "assignment of double to %s (use a cast)\n", dst_type->name); } } // the types are different but cast-compatible expr_t *new = cast_expr (dst_type, src); // the cast was a no-op, so the types are compatible at the // low level (very true for default type <-> enum) if (new != src) { return assign_expr (dst, new); } } return 0; } // traditional qcc is a little sloppy if (!options.traditional) { return type_mismatch (dst, src, '='); } if (is_func (dst_type) && is_func (src_type)) { warning (dst, "assignment between disparate function types"); return 0; } if (is_float (dst_type) && is_vector (src_type)) { warning (dst, "assignment of vector to float"); src = field_expr (src, new_name_expr ("x")); return assign_expr (dst, src); } if (is_vector (dst_type) && is_float (src_type)) { warning (dst, "assignment of float to vector"); dst = field_expr (dst, new_name_expr ("x")); return assign_expr (dst, src); } return type_mismatch (dst, src, '='); } static expr_t * assign_vector_expr (expr_t *dst, expr_t *src) { expr_t *dx, *sx; expr_t *dy, *sy; expr_t *dz, *sz; expr_t *dw, *sw; expr_t *ds, *ss; expr_t *dv, *sv; expr_t *block; if (src->type == ex_vector) { src = convert_vector (src); if (src->type != ex_vector) { // src was constant and thus converted return assign_expr (dst, src); } } if (src->type == ex_vector && dst->type != ex_vector) { if (src->e.vector.type == &type_vector) { // guaranteed to have three elements sx = src->e.vector.list; sy = sx->next; sz = sy->next; dx = field_expr (dst, new_name_expr ("x")); dy = field_expr (dst, new_name_expr ("y")); dz = field_expr (dst, new_name_expr ("z")); block = new_block_expr (); append_expr (block, assign_expr (dx, sx)); append_expr (block, assign_expr (dy, sy)); append_expr (block, assign_expr (dz, sz)); block->e.block.result = dst; return block; } if (src->e.vector.type == &type_quaternion) { // guaranteed to have two or four elements if (src->e.vector.list->next->next) { // four vals: x, y, z, w sx = src->e.vector.list; sy = sx->next; sz = sy->next; sw = sz->next; dx = field_expr (dst, new_name_expr ("x")); dy = field_expr (dst, new_name_expr ("y")); dz = field_expr (dst, new_name_expr ("z")); dw = field_expr (dst, new_name_expr ("w")); block = new_block_expr (); append_expr (block, assign_expr (dx, sx)); append_expr (block, assign_expr (dy, sy)); append_expr (block, assign_expr (dz, sz)); append_expr (block, assign_expr (dw, sw)); block->e.block.result = dst; return block; } else { // v, s sv = src->e.vector.list; ss = sv->next; dv = field_expr (dst, new_name_expr ("v")); ds = field_expr (dst, new_name_expr ("s")); block = new_block_expr (); append_expr (block, assign_expr (dv, sv)); append_expr (block, assign_expr (ds, ss)); block->e.block.result = dst; return block; } } internal_error (src, "bogus vector expression"); } return 0; } static __attribute__((pure)) int is_memset (expr_t *e) { return e->type == ex_memset; } expr_t * assign_expr (expr_t *dst, expr_t *src) { int op = '='; expr_t *expr; type_t *dst_type, *src_type; convert_name (dst); if (dst->type == ex_error) { return dst; } if ((expr = check_valid_lvalue (dst))) { return expr; } dst_type = get_type (dst); if (!dst_type) { internal_error (dst, "dst_type broke in assign_expr"); } if (src && !is_memset (src)) { convert_name (src); if (src->type == ex_error) { return src; } if (options.traditional && (expr = check_assign_logic_precedence (dst, src))) { return expr; } } else { if (!src && is_scalar (dst_type)) { return error (dst, "empty scalar initializer"); } src = new_nil_expr (); } if (src->type == ex_compound) { src = initialized_temp_expr (dst_type, src); if (src->type == ex_error) { return src; } } src_type = get_type (src); if (!src_type) { internal_error (src, "src_type broke in assign_expr"); } if (is_pointer (dst_type) && is_array (src_type)) { // assigning an array to a pointer is the same as taking the address of // the array but using the type of the array elements src = address_expr (src, 0, src_type->t.fldptr.type); src_type = get_type (src); } if (src->type == ex_bool) { // boolean expressions are chains of tests, so extract the result // of the tests src = convert_from_bool (src, dst_type); if (src->type == ex_error) { return src; } src_type = get_type (src); } if (!is_nil (src)) { if ((expr = check_types_compatible (dst, src))) { // expr might be a valid expression, but if so, // check_types_compatible will take care of everything return expr; } if ((expr = assign_vector_expr (dst, src))) { return expr; } } else { convert_nil (src, dst_type); } expr = new_binary_expr (op, dst, src); expr->e.expr.type = dst_type; return expr; }