manual: Update from wiki.gnome.org
[vala-gnome.git] / vala / valabinaryexpression.vala
blob19287da85cfdebe8878cb69a7ca14b465be715f8
1 /* valabinaryexpression.vala
3 * Copyright (C) 2006-2010 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>
24 /**
25 * Represents an expression with two operands in the source code.
27 * Supports +, -, *, /, %, <<, >>, <, >, <=, >=, ==, !=, &, |, ^, &&, ||, ??.
29 public class Vala.BinaryExpression : Expression {
30 /**
31 * The binary operator.
33 public BinaryOperator operator { get; set; }
35 /**
36 * The left operand.
38 public Expression left {
39 get {
40 return _left;
42 set {
43 _left = value;
44 _left.parent_node = this;
48 /**
49 * The right operand.
51 public Expression right {
52 get {
53 return _right;
55 set {
56 _right = value;
57 _right.parent_node = this;
61 public bool chained;
63 private Expression _left;
64 private Expression _right;
66 /**
67 * Creates a new binary expression.
69 * @param op binary operator
70 * @param _left left operand
71 * @param _right right operand
72 * @param source reference to source code
73 * @return newly created binary expression
75 public BinaryExpression (BinaryOperator op, Expression _left, Expression _right, SourceReference? source = null) {
76 operator = op;
77 left = _left;
78 right = _right;
79 source_reference = source;
82 public override void accept (CodeVisitor visitor) {
83 visitor.visit_binary_expression (this);
85 visitor.visit_expression (this);
88 public override void accept_children (CodeVisitor visitor) {
89 left.accept (visitor);
90 right.accept (visitor);
93 public override void replace_expression (Expression old_node, Expression new_node) {
94 if (left == old_node) {
95 left = new_node;
97 if (right == old_node) {
98 right = new_node;
102 private unowned string get_operator_string () {
103 switch (_operator) {
104 case BinaryOperator.PLUS: return "+";
105 case BinaryOperator.MINUS: return "-";
106 case BinaryOperator.MUL: return "*";
107 case BinaryOperator.DIV: return "/";
108 case BinaryOperator.MOD: return "%";
109 case BinaryOperator.SHIFT_LEFT: return "<<";
110 case BinaryOperator.SHIFT_RIGHT: return ">>";
111 case BinaryOperator.LESS_THAN: return "<";
112 case BinaryOperator.GREATER_THAN: return ">";
113 case BinaryOperator.LESS_THAN_OR_EQUAL: return "<=";
114 case BinaryOperator.GREATER_THAN_OR_EQUAL: return ">=";
115 case BinaryOperator.EQUALITY: return "==";
116 case BinaryOperator.INEQUALITY: return "!=";
117 case BinaryOperator.BITWISE_AND: return "&";
118 case BinaryOperator.BITWISE_OR: return "|";
119 case BinaryOperator.BITWISE_XOR: return "^";
120 case BinaryOperator.AND: return "&&";
121 case BinaryOperator.OR: return "||";
122 case BinaryOperator.IN: return "in";
123 case BinaryOperator.COALESCE: return "??";
124 default: assert_not_reached ();
128 public override string to_string () {
129 return _left.to_string () + get_operator_string () + _right.to_string ();
132 public override bool is_constant () {
133 return left.is_constant () && right.is_constant ();
136 public override bool is_pure () {
137 return left.is_pure () && right.is_pure ();
140 public override bool is_non_null () {
141 return left.is_non_null () && right.is_non_null ();
144 public override bool is_accessible (Symbol sym) {
145 return left.is_accessible (sym) && right.is_accessible (sym);
148 public override bool check (CodeContext context) {
149 if (checked) {
150 return !error;
153 checked = true;
155 // some expressions are not in a block,
156 // for example, expressions in method contracts
157 if (context.analyzer.current_symbol is Block
158 && (operator == BinaryOperator.AND || operator == BinaryOperator.OR)) {
159 // convert conditional expression into if statement
160 // required for flow analysis and exception handling
162 var local = new LocalVariable (context.analyzer.bool_type.copy (), get_temp_name (), null, source_reference);
163 var decl = new DeclarationStatement (local, source_reference);
164 decl.check (context);
166 var right_stmt = new ExpressionStatement (new Assignment (new MemberAccess.simple (local.name, right.source_reference), right, AssignmentOperator.SIMPLE, right.source_reference), right.source_reference);
168 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);
170 var true_block = new Block (source_reference);
171 var false_block = new Block (source_reference);
173 if (operator == BinaryOperator.AND) {
174 true_block.add_statement (right_stmt);
175 false_block.add_statement (stmt);
176 } else {
177 true_block.add_statement (stmt);
178 false_block.add_statement (right_stmt);
181 var if_stmt = new IfStatement (left, true_block, false_block, source_reference);
183 insert_statement (context.analyzer.insert_block, decl);
184 insert_statement (context.analyzer.insert_block, if_stmt);
186 if (!if_stmt.check (context)) {
187 error = true;
188 return false;
191 var ma = new MemberAccess.simple (local.name, source_reference);
192 ma.target_type = target_type;
193 ma.formal_target_type = formal_target_type;
194 ma.check (context);
196 parent_node.replace_expression (this, ma);
198 return true;
201 if (operator == BinaryOperator.COALESCE) {
202 if (!left.check (context)) {
203 error = true;
204 return false;
207 if (!right.check (context)) {
208 error = true;
209 return false;
212 DataType local_type = null;
213 bool cast_non_null = false;
214 if (left.value_type is NullType && right.value_type != null) {
215 Report.warning (left.source_reference, "left operand is always null");
216 local_type = right.value_type.copy ();
217 local_type.nullable = true;
218 if (!right.value_type.nullable) {
219 cast_non_null = true;
221 } else if (left.value_type != null) {
222 local_type = left.value_type.copy ();
223 if (right.value_type != null && right.value_type.value_owned) {
224 // value owned if either left or right is owned
225 local_type.value_owned = true;
227 if (context.experimental_non_null) {
228 if (!local_type.nullable) {
229 Report.warning (left.source_reference, "left operand is never null");
230 if (right.value_type != null && right.value_type.nullable) {
231 local_type.nullable = true;
232 cast_non_null = true;
234 } else if (right.value_type != null && !right.value_type.nullable) {
235 cast_non_null = true;
238 } else if (right.value_type != null) {
239 local_type = right.value_type.copy ();
242 var local = new LocalVariable (local_type, get_temp_name (), left, source_reference);
243 var decl = new DeclarationStatement (local, source_reference);
245 var right_stmt = new ExpressionStatement (new Assignment (new MemberAccess.simple (local.name, right.source_reference), right, AssignmentOperator.SIMPLE, right.source_reference), right.source_reference);
247 var true_block = new Block (source_reference);
249 true_block.add_statement (right_stmt);
251 var cond = new BinaryExpression (BinaryOperator.EQUALITY, new MemberAccess.simple (local.name, left.source_reference), new NullLiteral (source_reference), source_reference);
253 var if_stmt = new IfStatement (cond, true_block, null, source_reference);
255 insert_statement (context.analyzer.insert_block, decl);
256 insert_statement (context.analyzer.insert_block, if_stmt);
258 if (!decl.check (context)) {
259 error = true;
260 return false;
263 if (!if_stmt.check (context)) {
264 error = true;
265 return false;
268 var replace_expr = SemanticAnalyzer.create_temp_access (local, target_type);
269 if (cast_non_null && replace_expr.target_type != null) {
270 var cast = new CastExpression.non_null (replace_expr, source_reference);
271 cast.target_type = replace_expr.target_type.copy ();
272 cast.target_type.nullable = false;
273 replace_expr = cast;
275 replace_expr.check (context);
277 parent_node.replace_expression (this, replace_expr);
279 return true;
282 if (!left.check (context) || !right.check (context)) {
283 /* if there were any errors in inner expressions, skip type check */
284 error = true;
285 return false;
288 if (left.value_type == null) {
289 Report.error (left.source_reference, "invalid left operand");
290 error = true;
291 return false;
294 if (operator != BinaryOperator.IN && right.value_type == null) {
295 Report.error (right.source_reference, "invalid right operand");
296 error = true;
297 return false;
300 if (left.value_type is FieldPrototype) {
301 error = true;
302 Report.error (left.source_reference, "Access to instance member `%s' denied".printf (left.symbol_reference.get_full_name ()));
303 return false;
305 if (right.value_type is FieldPrototype) {
306 error = true;
307 Report.error (right.source_reference, "Access to instance member `%s' denied".printf (right.symbol_reference.get_full_name ()));
308 return false;
311 left.target_type = left.value_type.copy ();
312 left.target_type.value_owned = false;
313 right.target_type = right.value_type.copy ();
314 right.target_type.value_owned = false;
316 if (left.value_type.data_type == context.analyzer.string_type.data_type
317 && operator == BinaryOperator.PLUS) {
318 // string concatenation
320 if (right.value_type == null || right.value_type.data_type != context.analyzer.string_type.data_type) {
321 error = true;
322 Report.error (source_reference, "Operands must be strings");
323 return false;
326 value_type = context.analyzer.string_type.copy ();
327 if (left.is_constant () && right.is_constant ()) {
328 value_type.value_owned = false;
329 } else {
330 value_type.value_owned = true;
332 } else if (left.value_type is ArrayType && operator == BinaryOperator.PLUS) {
333 // array concatenation
335 var array_type = (ArrayType) left.value_type;
337 if (right.value_type == null || !right.value_type.compatible (array_type.element_type)) {
338 error = true;
339 Report.error (source_reference, "Incompatible operand");
340 return false;
343 right.target_type = array_type.element_type.copy ();
345 value_type = array_type.copy ();
346 value_type.value_owned = true;
347 } else if (operator == BinaryOperator.PLUS
348 || operator == BinaryOperator.MINUS
349 || operator == BinaryOperator.MUL
350 || operator == BinaryOperator.DIV) {
351 // check for pointer arithmetic
352 if (left.value_type is PointerType) {
353 var pointer_type = (PointerType) left.value_type;
354 if (pointer_type.base_type is VoidType) {
355 error = true;
356 Report.error (source_reference, "Pointer arithmetic not supported for `void*'");
357 return false;
360 var offset_type = right.value_type.data_type as Struct;
361 if (offset_type != null && offset_type.is_integer_type ()) {
362 if (operator == BinaryOperator.PLUS
363 || operator == BinaryOperator.MINUS) {
364 // pointer arithmetic: pointer +/- offset
365 value_type = left.value_type.copy ();
367 } else if (right.value_type is PointerType) {
368 // pointer arithmetic: pointer - pointer
369 value_type = context.analyzer.size_t_type;
371 } else {
372 left.target_type.nullable = false;
373 right.target_type.nullable = false;
376 if (value_type == null) {
377 value_type = context.analyzer.get_arithmetic_result_type (left.target_type, right.target_type);
380 if (value_type == null) {
381 error = true;
382 Report.error (source_reference, "Arithmetic operation not supported for types `%s' and `%s'".printf (left.value_type.to_string (), right.value_type.to_string ()));
383 return false;
385 } else if (operator == BinaryOperator.MOD
386 || operator == BinaryOperator.SHIFT_LEFT
387 || operator == BinaryOperator.SHIFT_RIGHT) {
388 left.target_type.nullable = false;
389 right.target_type.nullable = false;
391 value_type = context.analyzer.get_arithmetic_result_type (left.target_type, right.target_type);
393 if (value_type == null) {
394 error = true;
395 Report.error (source_reference, "Arithmetic operation not supported for types `%s' and `%s'".printf (left.value_type.to_string (), right.value_type.to_string ()));
396 return false;
398 } else if (operator == BinaryOperator.LESS_THAN
399 || operator == BinaryOperator.GREATER_THAN
400 || operator == BinaryOperator.LESS_THAN_OR_EQUAL
401 || operator == BinaryOperator.GREATER_THAN_OR_EQUAL) {
402 if (left.value_type.compatible (context.analyzer.string_type)
403 && right.value_type.compatible (context.analyzer.string_type)) {
404 // string comparison
405 } else if (left.value_type is PointerType && right.value_type is PointerType) {
406 // pointer arithmetic
407 } else {
408 DataType resulting_type;
410 if (chained) {
411 var lbe = (BinaryExpression) left;
412 resulting_type = context.analyzer.get_arithmetic_result_type (lbe.right.target_type, right.target_type);
413 } else {
414 resulting_type = context.analyzer.get_arithmetic_result_type (left.target_type, right.target_type);
417 if (resulting_type == null) {
418 error = true;
419 Report.error (source_reference, "Relational operation not supported for types `%s' and `%s'".printf (left.value_type.to_string (), right.value_type.to_string ()));
420 return false;
423 if (!chained) {
424 left.target_type = resulting_type.copy ();
426 right.target_type = resulting_type.copy ();
427 left.target_type.nullable = false;
428 right.target_type.nullable = false;
431 value_type = context.analyzer.bool_type;
432 } else if (operator == BinaryOperator.EQUALITY
433 || operator == BinaryOperator.INEQUALITY) {
434 /* relational operation */
436 if (!right.value_type.compatible (left.value_type)
437 && !left.value_type.compatible (right.value_type)) {
438 Report.error (source_reference, "Equality operation: `%s' and `%s' are incompatible".printf (right.value_type.to_string (), left.value_type.to_string ()));
439 error = true;
440 return false;
443 var resulting_type = context.analyzer.get_arithmetic_result_type (left.target_type, right.target_type);
444 if (resulting_type != null) {
445 // numeric operation
446 left.target_type = resulting_type.copy ();
447 right.target_type = resulting_type.copy ();
450 left.target_type.value_owned = false;
451 right.target_type.value_owned = false;
453 if (left.value_type.nullable != right.value_type.nullable) {
454 // if only one operand is nullable, make sure the other
455 // operand is promoted to nullable as well,
456 // reassign both, as get_arithmetic_result_type doesn't
457 // take nullability into account
458 left.target_type.nullable = true;
459 right.target_type.nullable = true;
462 value_type = context.analyzer.bool_type;
463 } else if (operator == BinaryOperator.BITWISE_AND
464 || operator == BinaryOperator.BITWISE_OR
465 || operator == BinaryOperator.BITWISE_XOR) {
466 // integer type or flags type
467 left.target_type.nullable = false;
468 right.target_type.nullable = false;
470 value_type = left.target_type.copy ();
471 } else if (operator == BinaryOperator.AND
472 || operator == BinaryOperator.OR) {
473 if (!left.value_type.compatible (context.analyzer.bool_type) || !right.value_type.compatible (context.analyzer.bool_type)) {
474 error = true;
475 Report.error (source_reference, "Operands must be boolean");
477 left.target_type.nullable = false;
478 right.target_type.nullable = false;
480 value_type = context.analyzer.bool_type;
481 } else if (operator == BinaryOperator.IN) {
482 if (left.value_type.compatible (context.analyzer.int_type)
483 && right.value_type.compatible (context.analyzer.int_type)) {
484 // integers or enums
485 left.target_type.nullable = false;
486 right.target_type.nullable = false;
487 } else if (right.value_type is ArrayType) {
488 if (!left.value_type.compatible (((ArrayType) right.value_type).element_type)) {
489 Report.error (source_reference, "Cannot look for `%s' in `%s'".printf (left.value_type.to_string (), right.value_type.to_string ()));
491 } else {
492 // otherwise require a bool contains () method
493 var contains_method = right.value_type.get_member ("contains") as Method;
494 if (contains_method == null) {
495 Report.error (source_reference, "`%s' does not have a `contains' method".printf (right.value_type.to_string ()));
496 error = true;
497 return false;
499 if (contains_method.get_parameters ().size != 1) {
500 Report.error (source_reference, "`%s' must have one parameter".printf (contains_method.get_full_name ()));
501 error = true;
502 return false;
504 if (!contains_method.return_type.compatible (context.analyzer.bool_type)) {
505 Report.error (source_reference, "`%s' must return a boolean value".printf (contains_method.get_full_name ()));
506 error = true;
507 return false;
510 var contains_call = new MethodCall (new MemberAccess (right, "contains", source_reference), source_reference);
511 contains_call.add_argument (left);
512 parent_node.replace_expression (this, contains_call);
513 return contains_call.check (context);
516 value_type = context.analyzer.bool_type;
518 } else {
519 assert_not_reached ();
522 return !error;
525 public override void emit (CodeGenerator codegen) {
526 left.emit (codegen);
527 right.emit (codegen);
529 codegen.visit_binary_expression (this);
531 codegen.visit_expression (this);
534 public override void get_defined_variables (Collection<Variable> collection) {
535 left.get_defined_variables (collection);
536 right.get_defined_variables (collection);
539 public override void get_used_variables (Collection<Variable> collection) {
540 left.get_used_variables (collection);
541 right.get_used_variables (collection);
545 public enum Vala.BinaryOperator {
546 NONE,
547 PLUS,
548 MINUS,
549 MUL,
550 DIV,
551 MOD,
552 SHIFT_LEFT,
553 SHIFT_RIGHT,
554 LESS_THAN,
555 GREATER_THAN,
556 LESS_THAN_OR_EQUAL,
557 GREATER_THAN_OR_EQUAL,
558 EQUALITY,
559 INEQUALITY,
560 BITWISE_AND,
561 BITWISE_OR,
562 BITWISE_XOR,
563 AND,
566 COALESCE