/* 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 "tools/qfcc/include/qfcc.h" #include "tools/qfcc/include/class.h" #include "tools/qfcc/include/def.h" #include "tools/qfcc/include/defspace.h" #include "tools/qfcc/include/diagnostic.h" #include "tools/qfcc/include/emit.h" #include "tools/qfcc/include/expr.h" #include "tools/qfcc/include/function.h" #include "tools/qfcc/include/idstuff.h" #include "tools/qfcc/include/method.h" #include "tools/qfcc/include/options.h" #include "tools/qfcc/include/reloc.h" #include "tools/qfcc/include/shared.h" #include "tools/qfcc/include/strpool.h" #include "tools/qfcc/include/struct.h" #include "tools/qfcc/include/symtab.h" #include "tools/qfcc/include/type.h" #include "tools/qfcc/include/value.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; } break; case ex_alias: return is_lvalue (expr->e.alias.expr); case ex_address: return 0; case ex_assign: return 0; case ex_uexpr: if (expr->e.expr.op == '.') { return 1; } break; case ex_branch: 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: case ex_selector: case ex_return: case ex_adjstk: case ex_with: case ex_args: case ex_horizontal: case ex_swizzle: case ex_extend: break; case ex_count: internal_error (expr, "invalid expression"); } 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)) { debug (dst, "casting %s to %s", src_type->name, dst_type->name); if (!src->implicit && !type_promotes (dst_type, src_type)) { if (is_double (src_type)) { warning (dst, "assignment of %s to %s (use a cast)\n", src_type->name, 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 int copy_elements (expr_t *block, expr_t *dst, expr_t *src, int base) { int index = 0; for (expr_t *e = src->e.vector.list; e; e = e->next) { if (e->type == ex_vector) { index += copy_elements (block, dst, e, index + base); } else { expr_t *dst_ele = array_expr (dst, new_int_expr (index + base)); append_expr (block, assign_expr (dst_ele, e)); index += type_width (get_type (e)); } } return index; } static expr_t * assign_vector_expr (expr_t *dst, expr_t *src) { if (src->type == ex_vector && dst->type != ex_vector) { expr_t *block = new_block_expr (); copy_elements (block, dst, src, 0); block->e.block.result = dst; return block; } 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) { 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_ptr (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, 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_assign_expr (dst, src); return expr; }