Warn when using result variable with incompatible type to prepare possible
[vala-lang.git] / vala / valabinaryexpression.vala
blob8b3f0994fd8dc0ef459760fdb9a5889ccb078a5e
1 /* valabinaryexpression.vala
3 * Copyright (C) 2006-2009 Jürg Billeter
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 * Author:
20 * Jürg Billeter <j@bitron.ch>
23 using Gee;
25 /**
26 * Represents an expression with two operands in the source code.
28 * Supports +, -, *, /, %, <<, >>, <, >, <=, >=, ==, !=, &, |, ^, &&, ||.
30 public class Vala.BinaryExpression : Expression {
31 /**
32 * The binary operator.
34 public BinaryOperator operator { get; set; }
36 /**
37 * The left operand.
39 public Expression left {
40 get {
41 return _left;
43 set {
44 _left = value;
45 _left.parent_node = this;
49 /**
50 * The right operand.
52 public Expression right {
53 get {
54 return _right;
56 set {
57 _right = value;
58 _right.parent_node = this;
62 private Expression _left;
63 private Expression _right;
65 /**
66 * Creates a new binary expression.
68 * @param op binary operator
69 * @param left left operand
70 * @param right right operand
71 * @param source reference to source code
72 * @return newly created binary expression
74 public BinaryExpression (BinaryOperator op, Expression _left, Expression _right, SourceReference? source = null) {
75 operator = op;
76 left = _left;
77 right = _right;
78 source_reference = source;
81 public override void accept (CodeVisitor visitor) {
82 visitor.visit_binary_expression (this);
84 visitor.visit_expression (this);
87 public override void accept_children (CodeVisitor visitor) {
88 left.accept (visitor);
89 right.accept (visitor);
92 public override void replace_expression (Expression old_node, Expression new_node) {
93 if (left == old_node) {
94 left = new_node;
96 if (right == old_node) {
97 right = new_node;
101 private string get_operator_string () {
102 switch (_operator) {
103 case BinaryOperator.PLUS: return "+";
104 case BinaryOperator.MINUS: return "-";
105 case BinaryOperator.MUL: return "*";
106 case BinaryOperator.DIV: return "/";
107 case BinaryOperator.MOD: return "%";
108 case BinaryOperator.SHIFT_LEFT: return "<<";
109 case BinaryOperator.SHIFT_RIGHT: return ">>";
110 case BinaryOperator.LESS_THAN: return "<";
111 case BinaryOperator.GREATER_THAN: return ">";
112 case BinaryOperator.LESS_THAN_OR_EQUAL: return "<=";
113 case BinaryOperator.GREATER_THAN_OR_EQUAL: return ">=";
114 case BinaryOperator.EQUALITY: return "==";
115 case BinaryOperator.INEQUALITY: return "!+";
116 case BinaryOperator.BITWISE_AND: return "&";
117 case BinaryOperator.BITWISE_OR: return "|";
118 case BinaryOperator.BITWISE_XOR: return "^";
119 case BinaryOperator.AND: return "&&";
120 case BinaryOperator.OR: return "||";
121 case BinaryOperator.IN: return "in";
124 assert_not_reached ();
127 public override string to_string () {
128 return _left.to_string () + get_operator_string () + _right.to_string ();
131 public override bool is_constant () {
132 return left.is_constant () && right.is_constant ();
135 public override bool is_pure () {
136 return left.is_pure () && right.is_pure ();
139 public override bool is_non_null () {
140 return left.is_non_null () && right.is_non_null ();
143 public override bool check (SemanticAnalyzer analyzer) {
144 if (checked) {
145 return !error;
148 checked = true;
150 // some expressions are not in a block,
151 // for example, expressions in method contracts
152 if (analyzer.current_symbol is Block
153 && (operator == BinaryOperator.AND || operator == BinaryOperator.OR)) {
154 var old_insert_block = analyzer.insert_block;
155 analyzer.insert_block = prepare_condition_split (analyzer);
157 // convert conditional expression into if statement
158 // required for flow analysis and exception handling
160 var local = new LocalVariable (analyzer.bool_type.copy (), get_temp_name (), null, source_reference);
161 var decl = new DeclarationStatement (local, source_reference);
162 decl.check (analyzer);
164 var right_stmt = new ExpressionStatement (new Assignment (new MemberAccess.simple (local.name, right.source_reference), right, AssignmentOperator.SIMPLE, right.source_reference), right.source_reference);
166 var stmt = new ExpressionStatement (new Assignment (new MemberAccess.simple (local.name, left.source_reference), new BooleanLiteral ((operator == BinaryOperator.OR), left.source_reference), AssignmentOperator.SIMPLE, left.source_reference), left.source_reference);
168 var true_block = new Block (source_reference);
169 var false_block = new Block (source_reference);
171 if (operator == BinaryOperator.AND) {
172 true_block.add_statement (right_stmt);
173 false_block.add_statement (stmt);
174 } else {
175 true_block.add_statement (stmt);
176 false_block.add_statement (right_stmt);
179 var if_stmt = new IfStatement (left, true_block, false_block, source_reference);
181 insert_statement (analyzer.insert_block, decl);
182 insert_statement (analyzer.insert_block, if_stmt);
184 if (!if_stmt.check (analyzer)) {
185 return false;
187 analyzer.insert_block = old_insert_block;
189 var ma = new MemberAccess.simple (local.name, source_reference);
190 ma.target_type = target_type;
191 ma.check (analyzer);
193 parent_node.replace_expression (this, ma);
195 return true;
198 if (!left.check (analyzer) || !right.check (analyzer)) {
199 /* if there were any errors in inner expressions, skip type check */
200 error = true;
201 return false;
204 if (left.value_type == null) {
205 Report.error (left.source_reference, "invalid left operand");
206 error = true;
207 return false;
210 if (operator != BinaryOperator.IN && right.value_type == null) {
211 Report.error (right.source_reference, "invalid right operand");
212 error = true;
213 return false;
216 if (left.value_type.data_type == analyzer.string_type.data_type
217 && operator == BinaryOperator.PLUS) {
218 // string concatenation
220 if (right.value_type == null || right.value_type.data_type != analyzer.string_type.data_type) {
221 error = true;
222 Report.error (source_reference, "Operands must be strings");
223 return false;
226 value_type = analyzer.string_type.copy ();
227 if (left.is_constant () && right.is_constant ()) {
228 value_type.value_owned = false;
229 } else {
230 value_type.value_owned = true;
232 } else if (left.value_type is ArrayType && operator == BinaryOperator.PLUS) {
233 // array concatenation
235 var array_type = (ArrayType) left.value_type;
237 if (right.value_type == null || !right.value_type.compatible (array_type.element_type)) {
238 error = true;
239 Report.error (source_reference, "Incompatible operand");
240 return false;
243 value_type = array_type.copy ();
244 value_type.value_owned = true;
245 } else if (operator == BinaryOperator.PLUS
246 || operator == BinaryOperator.MINUS
247 || operator == BinaryOperator.MUL
248 || operator == BinaryOperator.DIV) {
249 // check for pointer arithmetic
250 if (left.value_type is PointerType) {
251 var offset_type = right.value_type.data_type as Struct;
252 if (offset_type != null && offset_type.is_integer_type ()) {
253 if (operator == BinaryOperator.PLUS
254 || operator == BinaryOperator.MINUS) {
255 // pointer arithmetic: pointer +/- offset
256 value_type = left.value_type.copy ();
258 } else if (right.value_type is PointerType) {
259 // pointer arithmetic: pointer - pointer
260 value_type = analyzer.size_t_type;
264 if (value_type == null) {
265 value_type = analyzer.get_arithmetic_result_type (left.value_type, right.value_type);
268 if (value_type == null) {
269 error = true;
270 Report.error (source_reference, "Arithmetic operation not supported for types `%s' and `%s'".printf (left.value_type.to_string (), right.value_type.to_string ()));
271 return false;
273 } else if (operator == BinaryOperator.MOD
274 || operator == BinaryOperator.SHIFT_LEFT
275 || operator == BinaryOperator.SHIFT_RIGHT
276 || operator == BinaryOperator.BITWISE_XOR) {
277 value_type = analyzer.get_arithmetic_result_type (left.value_type, right.value_type);
279 if (value_type == null) {
280 error = true;
281 Report.error (source_reference, "Arithmetic operation not supported for types `%s' and `%s'".printf (left.value_type.to_string (), right.value_type.to_string ()));
282 return false;
284 } else if (operator == BinaryOperator.LESS_THAN
285 || operator == BinaryOperator.GREATER_THAN
286 || operator == BinaryOperator.LESS_THAN_OR_EQUAL
287 || operator == BinaryOperator.GREATER_THAN_OR_EQUAL) {
288 if (left.value_type.compatible (analyzer.string_type)
289 && right.value_type.compatible (analyzer.string_type)) {
290 // string comparison
291 } else if (left.value_type is PointerType && right.value_type is PointerType) {
292 // pointer arithmetic
293 } else {
294 var resulting_type = analyzer.get_arithmetic_result_type (left.value_type, right.value_type);
296 if (resulting_type == null) {
297 error = true;
298 Report.error (source_reference, "Relational operation not supported for types `%s' and `%s'".printf (left.value_type.to_string (), right.value_type.to_string ()));
299 return false;
303 value_type = analyzer.bool_type;
304 } else if (operator == BinaryOperator.EQUALITY
305 || operator == BinaryOperator.INEQUALITY) {
306 /* relational operation */
308 if (!right.value_type.compatible (left.value_type)
309 && !left.value_type.compatible (right.value_type)) {
310 Report.error (source_reference, "Equality operation: `%s' and `%s' are incompatible".printf (right.value_type.to_string (), left.value_type.to_string ()));
311 error = true;
312 return false;
315 if (left.value_type.compatible (analyzer.string_type)
316 && right.value_type.compatible (analyzer.string_type)) {
317 // string comparison
320 value_type = analyzer.bool_type;
321 } else if (operator == BinaryOperator.BITWISE_AND
322 || operator == BinaryOperator.BITWISE_OR) {
323 // integer type or flags type
325 value_type = left.value_type;
326 } else if (operator == BinaryOperator.AND
327 || operator == BinaryOperator.OR) {
328 if (!left.value_type.compatible (analyzer.bool_type) || !right.value_type.compatible (analyzer.bool_type)) {
329 error = true;
330 Report.error (source_reference, "Operands must be boolean");
333 value_type = analyzer.bool_type;
334 } else if (operator == BinaryOperator.IN) {
335 if (left.value_type.compatible (analyzer.int_type)
336 && right.value_type.compatible (analyzer.int_type)) {
337 // integers or enums
338 } else {
339 // otherwise require a bool contains () method
340 var contains_method = right.value_type.get_member ("contains") as Method;
341 if (contains_method == null) {
342 Report.error (source_reference, "`%s' does not have a `contains' method".printf (right.value_type.to_string ()));
343 error = true;
344 return false;
346 if (contains_method.get_parameters ().size != 1) {
347 Report.error (source_reference, "`%s' must have one parameter".printf (contains_method.get_full_name ()));
348 error = true;
349 return false;
351 if (!contains_method.return_type.compatible (analyzer.bool_type)) {
352 Report.error (source_reference, "`%s' must return a boolean value".printf (contains_method.get_full_name ()));
353 error = true;
354 return false;
357 var contains_call = new MethodCall (new MemberAccess (right, "contains"));
358 contains_call.add_argument (left);
359 parent_node.replace_expression (this, contains_call);
360 return contains_call.check (analyzer);
363 value_type = analyzer.bool_type;
365 } else {
366 assert_not_reached ();
369 return !error;
372 public override void get_defined_variables (Collection<LocalVariable> collection) {
373 left.get_defined_variables (collection);
374 right.get_defined_variables (collection);
377 public override void get_used_variables (Collection<LocalVariable> collection) {
378 left.get_used_variables (collection);
379 right.get_used_variables (collection);
383 public enum Vala.BinaryOperator {
384 NONE,
385 PLUS,
386 MINUS,
387 MUL,
388 DIV,
389 MOD,
390 SHIFT_LEFT,
391 SHIFT_RIGHT,
392 LESS_THAN,
393 GREATER_THAN,
394 LESS_THAN_OR_EQUAL,
395 GREATER_THAN_OR_EQUAL,
396 EQUALITY,
397 INEQUALITY,
398 BITWISE_AND,
399 BITWISE_OR,
400 BITWISE_XOR,
401 AND,