gio-unix-2.0: Add DesktopAppInfo.launch_uris_as_manager_with_fds()
[vala-gnome.git] / vala / valabinaryexpression.vala
blob6fac539fdb6c99a3be932db97006e203bb61bbe5
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 is_chained { get; private set; }
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 is_chained = false;
80 source_reference = source;
83 public BinaryExpression.chained (BinaryOperator op, Expression _left, Expression _right, SourceReference? source = null) {
84 operator = op;
85 left = _left;
86 right = _right;
87 is_chained = true;
88 source_reference = source;
91 public override void accept (CodeVisitor visitor) {
92 visitor.visit_binary_expression (this);
94 visitor.visit_expression (this);
97 public override void accept_children (CodeVisitor visitor) {
98 left.accept (visitor);
99 right.accept (visitor);
102 public override void replace_expression (Expression old_node, Expression new_node) {
103 if (left == old_node) {
104 left = new_node;
106 if (right == old_node) {
107 right = new_node;
111 private unowned string get_operator_string () {
112 switch (_operator) {
113 case BinaryOperator.PLUS: return "+";
114 case BinaryOperator.MINUS: return "-";
115 case BinaryOperator.MUL: return "*";
116 case BinaryOperator.DIV: return "/";
117 case BinaryOperator.MOD: return "%";
118 case BinaryOperator.SHIFT_LEFT: return "<<";
119 case BinaryOperator.SHIFT_RIGHT: return ">>";
120 case BinaryOperator.LESS_THAN: return "<";
121 case BinaryOperator.GREATER_THAN: return ">";
122 case BinaryOperator.LESS_THAN_OR_EQUAL: return "<=";
123 case BinaryOperator.GREATER_THAN_OR_EQUAL: return ">=";
124 case BinaryOperator.EQUALITY: return "==";
125 case BinaryOperator.INEQUALITY: return "!=";
126 case BinaryOperator.BITWISE_AND: return "&";
127 case BinaryOperator.BITWISE_OR: return "|";
128 case BinaryOperator.BITWISE_XOR: return "^";
129 case BinaryOperator.AND: return "&&";
130 case BinaryOperator.OR: return "||";
131 case BinaryOperator.IN: return "in";
132 case BinaryOperator.COALESCE: return "??";
133 default: assert_not_reached ();
137 public override string to_string () {
138 return _left.to_string () + get_operator_string () + _right.to_string ();
141 public override bool is_constant () {
142 return left.is_constant () && right.is_constant ();
145 public override bool is_pure () {
146 return left.is_pure () && right.is_pure ();
149 public override bool is_non_null () {
150 return left.is_non_null () && right.is_non_null ();
153 public override bool is_accessible (Symbol sym) {
154 return left.is_accessible (sym) && right.is_accessible (sym);
157 public override bool check (CodeContext context) {
158 if (checked) {
159 return !error;
162 checked = true;
164 // some expressions are not in a block,
165 // for example, expressions in method contracts
166 if (context.analyzer.current_symbol is Block
167 && (operator == BinaryOperator.AND || operator == BinaryOperator.OR)) {
168 // convert conditional expression into if statement
169 // required for flow analysis and exception handling
171 var local = new LocalVariable (context.analyzer.bool_type.copy (), get_temp_name (), null, source_reference);
172 var decl = new DeclarationStatement (local, source_reference);
173 decl.check (context);
175 var right_stmt = new ExpressionStatement (new Assignment (new MemberAccess.simple (local.name, right.source_reference), right, AssignmentOperator.SIMPLE, right.source_reference), right.source_reference);
177 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);
179 var true_block = new Block (source_reference);
180 var false_block = new Block (source_reference);
182 if (operator == BinaryOperator.AND) {
183 true_block.add_statement (right_stmt);
184 false_block.add_statement (stmt);
185 } else {
186 true_block.add_statement (stmt);
187 false_block.add_statement (right_stmt);
190 var if_stmt = new IfStatement (left, true_block, false_block, source_reference);
192 insert_statement (context.analyzer.insert_block, decl);
193 insert_statement (context.analyzer.insert_block, if_stmt);
195 if (!if_stmt.check (context)) {
196 error = true;
197 return false;
200 var ma = new MemberAccess.simple (local.name, source_reference);
201 ma.target_type = target_type;
202 ma.formal_target_type = formal_target_type;
203 ma.check (context);
205 parent_node.replace_expression (this, ma);
207 return true;
210 if (operator == BinaryOperator.COALESCE) {
211 if (!left.check (context)) {
212 error = true;
213 return false;
216 if (!right.check (context)) {
217 error = true;
218 return false;
221 DataType local_type = null;
222 bool cast_non_null = false;
223 if (left.value_type is NullType && right.value_type != null) {
224 Report.warning (left.source_reference, "left operand is always null");
225 local_type = right.value_type.copy ();
226 local_type.nullable = true;
227 if (!right.value_type.nullable) {
228 cast_non_null = true;
230 } else if (left.value_type != null) {
231 local_type = left.value_type.copy ();
232 if (right.value_type != null && right.value_type.value_owned) {
233 // value owned if either left or right is owned
234 local_type.value_owned = true;
236 if (context.experimental_non_null) {
237 if (!local_type.nullable) {
238 Report.warning (left.source_reference, "left operand is never null");
239 if (right.value_type != null && right.value_type.nullable) {
240 local_type.nullable = true;
241 cast_non_null = true;
243 } else if (right.value_type != null && !right.value_type.nullable) {
244 cast_non_null = true;
247 } else if (right.value_type != null) {
248 local_type = right.value_type.copy ();
251 var local = new LocalVariable (local_type, get_temp_name (), left, source_reference);
252 var decl = new DeclarationStatement (local, source_reference);
254 var right_stmt = new ExpressionStatement (new Assignment (new MemberAccess.simple (local.name, right.source_reference), right, AssignmentOperator.SIMPLE, right.source_reference), right.source_reference);
256 var true_block = new Block (source_reference);
258 true_block.add_statement (right_stmt);
260 var cond = new BinaryExpression (BinaryOperator.EQUALITY, new MemberAccess.simple (local.name, left.source_reference), new NullLiteral (source_reference), source_reference);
262 var if_stmt = new IfStatement (cond, true_block, null, source_reference);
264 insert_statement (context.analyzer.insert_block, decl);
265 insert_statement (context.analyzer.insert_block, if_stmt);
267 if (!decl.check (context)) {
268 error = true;
269 return false;
272 if (!if_stmt.check (context)) {
273 error = true;
274 return false;
277 var replace_expr = SemanticAnalyzer.create_temp_access (local, target_type);
278 if (cast_non_null && replace_expr.target_type != null) {
279 var cast = new CastExpression.non_null (replace_expr, source_reference);
280 cast.target_type = replace_expr.target_type.copy ();
281 cast.target_type.nullable = false;
282 replace_expr = cast;
284 replace_expr.check (context);
286 parent_node.replace_expression (this, replace_expr);
288 return true;
291 // enum-type inference
292 if (target_type != null && target_type.data_type is Enum
293 && (operator == BinaryOperator.BITWISE_AND || operator == BinaryOperator.BITWISE_OR)) {
294 left.target_type = target_type.copy ();
295 right.target_type = target_type.copy ();
297 left.check (context);
298 if (left.value_type != null && left.value_type.data_type is Enum
299 && (operator == BinaryOperator.EQUALITY || operator == BinaryOperator.INEQUALITY)) {
300 right.target_type = left.value_type.copy ();
302 right.check (context);
303 if (right.value_type != null && right.value_type.data_type is Enum
304 && (operator == BinaryOperator.EQUALITY || operator == BinaryOperator.INEQUALITY)) {
305 left.target_type = right.value_type.copy ();
306 //TODO bug 666035 -- re-check left how?
309 if (!left.check (context) || !right.check (context)) {
310 /* if there were any errors in inner expressions, skip type check */
311 error = true;
312 return false;
315 if (left.value_type == null) {
316 Report.error (left.source_reference, "invalid left operand");
317 error = true;
318 return false;
321 if (operator != BinaryOperator.IN && right.value_type == null) {
322 Report.error (right.source_reference, "invalid right operand");
323 error = true;
324 return false;
327 if (left.value_type is FieldPrototype) {
328 error = true;
329 Report.error (left.source_reference, "Access to instance member `%s' denied".printf (left.symbol_reference.get_full_name ()));
330 return false;
332 if (right.value_type is FieldPrototype) {
333 error = true;
334 Report.error (right.source_reference, "Access to instance member `%s' denied".printf (right.symbol_reference.get_full_name ()));
335 return false;
338 left.target_type = left.value_type.copy ();
339 left.target_type.value_owned = false;
340 right.target_type = right.value_type.copy ();
341 right.target_type.value_owned = false;
343 if (left.value_type.data_type == context.analyzer.string_type.data_type
344 && operator == BinaryOperator.PLUS) {
345 // string concatenation
347 if (right.value_type == null || right.value_type.data_type != context.analyzer.string_type.data_type) {
348 error = true;
349 Report.error (source_reference, "Operands must be strings");
350 return false;
353 value_type = context.analyzer.string_type.copy ();
354 if (left.is_constant () && right.is_constant ()) {
355 value_type.value_owned = false;
356 } else {
357 value_type.value_owned = true;
359 } else if (left.value_type is ArrayType && operator == BinaryOperator.PLUS) {
360 // array concatenation
362 var array_type = (ArrayType) left.value_type;
364 if (right.value_type == null || !right.value_type.compatible (array_type.element_type)) {
365 error = true;
366 Report.error (source_reference, "Incompatible operand");
367 return false;
370 right.target_type = array_type.element_type.copy ();
372 value_type = array_type.copy ();
373 value_type.value_owned = true;
374 } else if (operator == BinaryOperator.PLUS
375 || operator == BinaryOperator.MINUS
376 || operator == BinaryOperator.MUL
377 || operator == BinaryOperator.DIV) {
378 // check for pointer arithmetic
379 if (left.value_type is PointerType) {
380 var pointer_type = (PointerType) left.value_type;
381 if (pointer_type.base_type is VoidType) {
382 error = true;
383 Report.error (source_reference, "Pointer arithmetic not supported for `void*'");
384 return false;
387 var offset_type = right.value_type.data_type as Struct;
388 if (offset_type != null && offset_type.is_integer_type ()) {
389 if (operator == BinaryOperator.PLUS
390 || operator == BinaryOperator.MINUS) {
391 // pointer arithmetic: pointer +/- offset
392 value_type = left.value_type.copy ();
394 } else if (right.value_type is PointerType) {
395 // pointer arithmetic: pointer - pointer
396 value_type = context.analyzer.size_t_type;
398 } else {
399 left.target_type.nullable = false;
400 right.target_type.nullable = false;
403 if (value_type == null) {
404 value_type = context.analyzer.get_arithmetic_result_type (left.target_type, right.target_type);
407 if (value_type == null) {
408 error = true;
409 Report.error (source_reference, "Arithmetic operation not supported for types `%s' and `%s'".printf (left.value_type.to_string (), right.value_type.to_string ()));
410 return false;
412 } else if (operator == BinaryOperator.MOD
413 || operator == BinaryOperator.SHIFT_LEFT
414 || operator == BinaryOperator.SHIFT_RIGHT) {
415 left.target_type.nullable = false;
416 right.target_type.nullable = false;
418 value_type = context.analyzer.get_arithmetic_result_type (left.target_type, right.target_type);
420 if (value_type == null) {
421 error = true;
422 Report.error (source_reference, "Arithmetic operation not supported for types `%s' and `%s'".printf (left.value_type.to_string (), right.value_type.to_string ()));
423 return false;
425 } else if (operator == BinaryOperator.LESS_THAN
426 || operator == BinaryOperator.GREATER_THAN
427 || operator == BinaryOperator.LESS_THAN_OR_EQUAL
428 || operator == BinaryOperator.GREATER_THAN_OR_EQUAL) {
429 if (left.value_type.compatible (context.analyzer.string_type)
430 && right.value_type.compatible (context.analyzer.string_type)) {
431 // string comparison
432 } else if (left.value_type is PointerType && right.value_type is PointerType) {
433 // pointer arithmetic
434 } else {
435 DataType resulting_type;
437 if (is_chained) {
438 var lbe = (BinaryExpression) left;
439 resulting_type = context.analyzer.get_arithmetic_result_type (lbe.right.target_type, right.target_type);
440 } else {
441 resulting_type = context.analyzer.get_arithmetic_result_type (left.target_type, right.target_type);
444 if (resulting_type == null) {
445 error = true;
446 Report.error (source_reference, "Relational operation not supported for types `%s' and `%s'".printf (left.value_type.to_string (), right.value_type.to_string ()));
447 return false;
450 if (!is_chained) {
451 left.target_type = resulting_type.copy ();
453 right.target_type = resulting_type.copy ();
454 left.target_type.nullable = false;
455 right.target_type.nullable = false;
458 value_type = context.analyzer.bool_type;
459 } else if (operator == BinaryOperator.EQUALITY
460 || operator == BinaryOperator.INEQUALITY) {
461 /* relational operation */
463 if (!right.value_type.compatible (left.value_type)
464 && !left.value_type.compatible (right.value_type)) {
465 Report.error (source_reference, "Equality operation: `%s' and `%s' are incompatible".printf (right.value_type.to_string (), left.value_type.to_string ()));
466 error = true;
467 return false;
470 var resulting_type = context.analyzer.get_arithmetic_result_type (left.target_type, right.target_type);
471 if (resulting_type != null) {
472 // numeric operation
473 left.target_type = resulting_type.copy ();
474 right.target_type = resulting_type.copy ();
477 left.target_type.value_owned = false;
478 right.target_type.value_owned = false;
480 if (left.value_type.nullable != right.value_type.nullable) {
481 // if only one operand is nullable, make sure the other
482 // operand is promoted to nullable as well,
483 // reassign both, as get_arithmetic_result_type doesn't
484 // take nullability into account
485 left.target_type.nullable = true;
486 right.target_type.nullable = true;
489 value_type = context.analyzer.bool_type;
490 } else if (operator == BinaryOperator.BITWISE_AND
491 || operator == BinaryOperator.BITWISE_OR
492 || operator == BinaryOperator.BITWISE_XOR) {
493 // integer type or flags type
494 left.target_type.nullable = false;
495 right.target_type.nullable = false;
497 value_type = left.target_type.copy ();
498 } else if (operator == BinaryOperator.AND
499 || operator == BinaryOperator.OR) {
500 if (!left.value_type.compatible (context.analyzer.bool_type) || !right.value_type.compatible (context.analyzer.bool_type)) {
501 error = true;
502 Report.error (source_reference, "Operands must be boolean");
504 left.target_type.nullable = false;
505 right.target_type.nullable = false;
507 value_type = context.analyzer.bool_type;
508 } else if (operator == BinaryOperator.IN) {
509 if (left.value_type.compatible (context.analyzer.int_type)
510 && right.value_type.compatible (context.analyzer.int_type)) {
511 // integers or enums
512 left.target_type.nullable = false;
513 right.target_type.nullable = false;
514 } else if (right.value_type is ArrayType) {
515 if (!left.value_type.compatible (((ArrayType) right.value_type).element_type)) {
516 Report.error (source_reference, "Cannot look for `%s' in `%s'".printf (left.value_type.to_string (), right.value_type.to_string ()));
518 } else {
519 // otherwise require a bool contains () method
520 var contains_method = right.value_type.get_member ("contains") as Method;
521 if (contains_method == null) {
522 Report.error (source_reference, "`%s' does not have a `contains' method".printf (right.value_type.to_string ()));
523 error = true;
524 return false;
526 if (contains_method.get_parameters ().size != 1) {
527 Report.error (source_reference, "`%s' must have one parameter".printf (contains_method.get_full_name ()));
528 error = true;
529 return false;
531 if (!contains_method.return_type.compatible (context.analyzer.bool_type)) {
532 Report.error (source_reference, "`%s' must return a boolean value".printf (contains_method.get_full_name ()));
533 error = true;
534 return false;
537 var contains_call = new MethodCall (new MemberAccess (right, "contains", source_reference), source_reference);
538 contains_call.add_argument (left);
539 parent_node.replace_expression (this, contains_call);
540 return contains_call.check (context);
543 value_type = context.analyzer.bool_type;
545 } else {
546 assert_not_reached ();
549 return !error;
552 public override void emit (CodeGenerator codegen) {
553 left.emit (codegen);
554 right.emit (codegen);
556 codegen.visit_binary_expression (this);
558 codegen.visit_expression (this);
561 public override void get_defined_variables (Collection<Variable> collection) {
562 left.get_defined_variables (collection);
563 right.get_defined_variables (collection);
566 public override void get_used_variables (Collection<Variable> collection) {
567 left.get_used_variables (collection);
568 right.get_used_variables (collection);
572 public enum Vala.BinaryOperator {
573 NONE,
574 PLUS,
575 MINUS,
576 MUL,
577 DIV,
578 MOD,
579 SHIFT_LEFT,
580 SHIFT_RIGHT,
581 LESS_THAN,
582 GREATER_THAN,
583 LESS_THAN_OR_EQUAL,
584 GREATER_THAN_OR_EQUAL,
585 EQUALITY,
586 INEQUALITY,
587 BITWISE_AND,
588 BITWISE_OR,
589 BITWISE_XOR,
590 AND,
593 COALESCE