Line data Source code
1 : /* valabinaryexpression.vala
2 : *
3 : * Copyright (C) 2006-2010 Jürg Billeter
4 : *
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.
9 :
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.
14 :
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
18 : *
19 : * Author:
20 : * Jürg Billeter <j@bitron.ch>
21 : */
22 :
23 :
24 : /**
25 : * Represents an expression with two operands in the source code.
26 : *
27 : * Supports +, -, *, /, %, <<, >>, <, >, <=, >=, ==, !=, &, |, ^, &&, ||, ??.
28 : */
29 322468 : public class Vala.BinaryExpression : Expression {
30 : /**
31 : * The binary operator.
32 : */
33 353421 : public BinaryOperator operator { get; private set; }
34 :
35 : /**
36 : * The left operand.
37 : */
38 : public Expression left {
39 6492729 : get {
40 6492729 : return _left;
41 : }
42 321187 : private set {
43 918133 : _left = value;
44 321187 : _left.parent_node = this;
45 : }
46 : }
47 :
48 : /**
49 : * The right operand.
50 : */
51 : public Expression right {
52 6025480 : get {
53 6025480 : return _right;
54 : }
55 321006 : private set {
56 642012 : _right = value;
57 321006 : _right.parent_node = this;
58 : }
59 : }
60 :
61 333278 : public bool is_chained { get; private set; }
62 :
63 320931 : private Expression _left;
64 320931 : private Expression _right;
65 :
66 : /**
67 : * Creates a new binary expression.
68 : *
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
74 : */
75 953544 : public BinaryExpression (BinaryOperator op, Expression _left, Expression _right, SourceReference? source = null) {
76 317848 : operator = op;
77 317848 : left = _left;
78 317848 : right = _right;
79 317848 : is_chained = false;
80 317848 : source_reference = source;
81 : }
82 :
83 9249 : public BinaryExpression.chained (BinaryOperator op, Expression _left, Expression _right, SourceReference? source = null) {
84 3083 : operator = op;
85 3083 : left = _left;
86 3083 : right = _right;
87 3083 : is_chained = true;
88 3083 : source_reference = source;
89 : }
90 :
91 1039413 : public override void accept (CodeVisitor visitor) {
92 1039413 : visitor.visit_binary_expression (this);
93 :
94 1039413 : visitor.visit_expression (this);
95 : }
96 :
97 1039386 : public override void accept_children (CodeVisitor visitor) {
98 1039386 : left.accept (visitor);
99 1039386 : right.accept (visitor);
100 : }
101 :
102 269 : public override void replace_expression (Expression old_node, Expression new_node) {
103 269 : if (left == old_node) {
104 256 : left = new_node;
105 : }
106 269 : if (right == old_node) {
107 13 : right = new_node;
108 : }
109 : }
110 :
111 0 : public override string to_string () {
112 0 : return "(%s %s %s)".printf (_left.to_string (), operator.to_string (), _right.to_string ());
113 : }
114 :
115 380 : public override bool is_constant () {
116 380 : return left.is_constant () && right.is_constant ();
117 : }
118 :
119 0 : public override bool is_pure () {
120 0 : return left.is_pure () && right.is_pure ();
121 : }
122 :
123 1083 : public override bool is_non_null () {
124 1083 : return left.is_non_null () && right.is_non_null ();
125 : }
126 :
127 12745 : public override bool is_accessible (Symbol sym) {
128 12745 : return left.is_accessible (sym) && right.is_accessible (sym);
129 : }
130 :
131 525046 : public override void get_error_types (Collection<DataType> collection, SourceReference? source_reference = null) {
132 525046 : left.get_error_types (collection, source_reference);
133 525046 : right.get_error_types (collection, source_reference);
134 : }
135 :
136 321316 : public override bool check (CodeContext context) {
137 321316 : if (checked) {
138 23039 : return !error;
139 : }
140 :
141 298277 : checked = true;
142 :
143 : // some expressions are not in a block,
144 : // for example, expressions in method contracts
145 298277 : if (context.analyzer.current_symbol is Block
146 285495 : && (operator == BinaryOperator.AND || operator == BinaryOperator.OR)) {
147 : // convert conditional expression into if statement
148 : // required for flow analysis and exception handling
149 :
150 39207 : var local = new LocalVariable (context.analyzer.bool_type.copy (), get_temp_name (), null, source_reference);
151 39207 : var decl = new DeclarationStatement (local, source_reference);
152 :
153 39207 : var right_stmt = new ExpressionStatement (new Assignment (new MemberAccess.simple (local.name, right.source_reference), right, AssignmentOperator.SIMPLE, right.source_reference), right.source_reference);
154 :
155 39207 : 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);
156 :
157 39207 : var true_block = new Block (source_reference);
158 39207 : var false_block = new Block (source_reference);
159 :
160 39207 : if (operator == BinaryOperator.AND) {
161 28560 : true_block.add_statement (right_stmt);
162 28560 : false_block.add_statement (stmt);
163 : } else {
164 10647 : true_block.add_statement (stmt);
165 10647 : false_block.add_statement (right_stmt);
166 : }
167 :
168 39207 : var if_stmt = new IfStatement (left, true_block, false_block, source_reference);
169 :
170 39207 : insert_statement (context.analyzer.insert_block, decl);
171 39207 : insert_statement (context.analyzer.insert_block, if_stmt);
172 :
173 39207 : decl.check (context);
174 :
175 39207 : if (!if_stmt.check (context)) {
176 0 : error = true;
177 0 : return false;
178 : }
179 :
180 39207 : var ma = new MemberAccess.simple (local.name, source_reference);
181 39207 : ma.target_type = target_type;
182 39207 : ma.formal_target_type = formal_target_type;
183 :
184 39207 : parent_node.replace_expression (this, ma);
185 :
186 39207 : ma.check (context);
187 :
188 39207 : return true;
189 : }
190 :
191 259070 : if (operator == BinaryOperator.COALESCE) {
192 62 : if (target_type != null) {
193 62 : left.target_type = target_type.copy ();
194 62 : right.target_type = target_type.copy ();
195 : }
196 :
197 62 : if (!left.check (context)) {
198 0 : error = true;
199 0 : return false;
200 : }
201 :
202 62 : string temp_name = get_temp_name ();
203 :
204 : // right expression is checked under a block (required for short-circuiting)
205 62 : var right_local = new LocalVariable (null, temp_name, right, right.source_reference);
206 62 : var right_decl = new DeclarationStatement (right_local, right.source_reference);
207 62 : var true_block = new Block (source_reference);
208 62 : true_block.add_statement (right_decl);
209 :
210 62 : if (!true_block.check (context)) {
211 0 : error = true;
212 0 : return false;
213 : }
214 :
215 : // right expression may have been replaced by the check
216 62 : right = right_local.initializer;
217 :
218 62 : DataType local_type = null;
219 62 : bool cast_non_null = false;
220 62 : if (left.value_type is NullType && right.value_type != null) {
221 1 : Report.warning (left.source_reference, "left operand is always null");
222 1 : local_type = right.value_type.copy ();
223 1 : local_type.nullable = true;
224 1 : if (!right.value_type.nullable) {
225 1 : cast_non_null = true;
226 : }
227 61 : } else if (left.value_type != null) {
228 61 : local_type = left.value_type.copy ();
229 61 : if (right.value_type != null && right.value_type.value_owned) {
230 : // value owned if either left or right is owned
231 18 : local_type.value_owned = true;
232 : }
233 61 : if (context.experimental_non_null) {
234 3 : if (!local_type.nullable) {
235 1 : Report.warning (left.source_reference, "left operand is never null");
236 1 : if (right.value_type != null && right.value_type.nullable) {
237 1 : local_type.nullable = true;
238 1 : cast_non_null = true;
239 : }
240 2 : } else if (right.value_type != null && !right.value_type.nullable) {
241 62 : cast_non_null = true;
242 : }
243 : }
244 0 : } else if (right.value_type != null) {
245 0 : local_type = right.value_type.copy ();
246 : }
247 :
248 62 : if (local_type != null && right.value_type is ValueType && !right.value_type.nullable) {
249 : // immediate values in the right expression must always be boxed,
250 : // otherwise the local variable may receive a stale pointer to the stack
251 8 : local_type.value_owned = true;
252 : }
253 :
254 62 : var local = new LocalVariable (local_type, temp_name, left, source_reference);
255 62 : var decl = new DeclarationStatement (local, source_reference);
256 :
257 62 : insert_statement (context.analyzer.insert_block, decl);
258 :
259 62 : if (!decl.check (context)) {
260 0 : error = true;
261 0 : return false;
262 : }
263 :
264 : // replace the temporary local variable used to compute the type of the right expression by an assignment
265 62 : var right_stmt = new ExpressionStatement (new Assignment (new MemberAccess.simple (local.name, right.source_reference), right, AssignmentOperator.SIMPLE, right.source_reference), right.source_reference);
266 :
267 62 : true_block.remove_local_variable (right_local);
268 62 : true_block.replace_statement (right_decl, right_stmt);
269 :
270 62 : if (!right_stmt.check (context)) {
271 0 : error = true;
272 0 : return false;
273 : }
274 :
275 62 : var cond = new BinaryExpression (BinaryOperator.EQUALITY, new MemberAccess.simple (local.name, left.source_reference), new NullLiteral (source_reference), source_reference);
276 :
277 62 : var if_stmt = new IfStatement (cond, true_block, null, source_reference);
278 :
279 62 : insert_statement (context.analyzer.insert_block, if_stmt);
280 :
281 62 : if (!if_stmt.check (context)) {
282 0 : error = true;
283 0 : return false;
284 : }
285 :
286 62 : var replace_expr = SemanticAnalyzer.create_temp_access (local, target_type);
287 65 : if (cast_non_null && replace_expr.target_type != null) {
288 3 : var cast = new CastExpression.non_null (replace_expr, source_reference);
289 3 : cast.target_type = replace_expr.target_type.copy ();
290 3 : cast.target_type.nullable = false;
291 6 : replace_expr = cast;
292 : }
293 :
294 62 : parent_node.replace_expression (this, replace_expr);
295 :
296 62 : replace_expr.check (context);
297 :
298 62 : return true;
299 : }
300 :
301 : // enum-type inference
302 259008 : if (target_type != null && target_type.type_symbol is Enum
303 141 : && (operator == BinaryOperator.BITWISE_AND || operator == BinaryOperator.BITWISE_OR)) {
304 138 : left.target_type = target_type.copy ();
305 138 : right.target_type = target_type.copy ();
306 258870 : } else if (operator == BinaryOperator.IN) {
307 112 : right.check (context);
308 112 : if (right.value_type.type_symbol is Enum) {
309 36 : left.target_type = right.value_type.copy ();
310 : }
311 : }
312 259008 : left.check (context);
313 259008 : if (left.value_type != null && left.value_type.type_symbol is Enum
314 884 : && (operator == BinaryOperator.EQUALITY || operator == BinaryOperator.INEQUALITY)) {
315 696 : right.target_type = left.value_type.copy ();
316 : }
317 259008 : right.check (context);
318 259008 : if (right.value_type != null && right.value_type.type_symbol is Enum
319 857 : && (operator == BinaryOperator.EQUALITY || operator == BinaryOperator.INEQUALITY)) {
320 669 : left.target_type = right.value_type.copy ();
321 : //TODO bug 666035 -- re-check left how?
322 : }
323 :
324 259008 : if (!left.check (context) || !right.check (context)) {
325 : /* if there were any errors in inner expressions, skip type check */
326 1 : error = true;
327 1 : return false;
328 : }
329 :
330 259007 : if (left.value_type == null) {
331 0 : Report.error (left.source_reference, "invalid left operand");
332 0 : error = true;
333 0 : return false;
334 : }
335 :
336 259007 : if (operator != BinaryOperator.IN && right.value_type == null) {
337 0 : Report.error (right.source_reference, "invalid right operand");
338 0 : error = true;
339 0 : return false;
340 : }
341 :
342 259007 : if (left.value_type is FieldPrototype || left.value_type is PropertyPrototype) {
343 2 : error = true;
344 2 : Report.error (left.source_reference, "Access to instance member `%s' denied", left.symbol_reference.get_full_name ());
345 2 : return false;
346 : }
347 259005 : if (right.value_type is FieldPrototype || right.value_type is PropertyPrototype) {
348 0 : error = true;
349 0 : Report.error (right.source_reference, "Access to instance member `%s' denied", right.symbol_reference.get_full_name ());
350 0 : return false;
351 : }
352 :
353 259005 : left.target_type = left.value_type.copy ();
354 259005 : left.target_type.value_owned = false;
355 259005 : right.target_type = right.value_type.copy ();
356 259005 : right.target_type.value_owned = false;
357 :
358 293882 : if (operator == BinaryOperator.PLUS && !(left.value_type is PointerType)
359 34877 : && left.value_type.compatible (context.analyzer.string_type)) {
360 : // string concatenation
361 :
362 13329 : if (right.value_type == null || !right.value_type.compatible (context.analyzer.string_type)
363 13329 : || left is NullLiteral || right is NullLiteral) {
364 : // operands cannot be null
365 1 : error = true;
366 1 : Report.error (source_reference, "Operands must be strings");
367 1 : return false;
368 : }
369 :
370 13328 : value_type = context.analyzer.string_type.copy ();
371 13328 : if (left.is_constant () && right.is_constant ()) {
372 12804 : value_type.value_owned = false;
373 : } else {
374 524 : value_type.value_owned = true;
375 : }
376 :
377 13328 : value_type.check (context);
378 13328 : return !error;
379 245676 : } else if (operator == BinaryOperator.PLUS && left.value_type is ArrayType) {
380 : // array concatenation
381 :
382 66 : unowned ArrayType array_type = (ArrayType) left.value_type;
383 :
384 66 : if (array_type.inline_allocated) {
385 1 : error = true;
386 1 : Report.error (source_reference, "Array concatenation not supported for fixed length arrays");
387 : }
388 66 : if (right.value_type == null || !right.value_type.compatible (array_type.element_type)) {
389 0 : error = true;
390 0 : Report.error (source_reference, "Incompatible operand");
391 0 : return false;
392 : }
393 :
394 66 : right.target_type = array_type.element_type.copy ();
395 :
396 66 : value_type = array_type.copy ();
397 66 : value_type.value_owned = true;
398 :
399 66 : value_type.check (context);
400 66 : return !error;
401 : }
402 :
403 245610 : switch (operator) {
404 : case BinaryOperator.PLUS:
405 : case BinaryOperator.MINUS:
406 : case BinaryOperator.MUL:
407 : case BinaryOperator.DIV:
408 : // check for pointer arithmetic
409 79842 : if (left.value_type is PointerType) {
410 45360 : unowned PointerType pointer_type = (PointerType) left.value_type;
411 45360 : if (pointer_type.base_type is VoidType) {
412 0 : error = true;
413 0 : Report.error (source_reference, "Pointer arithmetic not supported for `void*'");
414 0 : return false;
415 : }
416 :
417 45360 : unowned Struct? offset_type = right.value_type.type_symbol as Struct;
418 45360 : if (offset_type != null && offset_type.is_integer_type ()) {
419 32589 : if (operator == BinaryOperator.PLUS
420 2 : || operator == BinaryOperator.MINUS) {
421 : // pointer arithmetic: pointer +/- offset
422 32589 : value_type = left.value_type.copy ();
423 : }
424 12771 : } else if (right.value_type is PointerType) {
425 : // pointer arithmetic: pointer - pointer
426 12771 : value_type = context.analyzer.size_t_type;
427 : }
428 : } else {
429 34482 : left.target_type.nullable = false;
430 34482 : right.target_type.nullable = false;
431 : }
432 :
433 79842 : if (value_type == null) {
434 34482 : value_type = context.analyzer.get_arithmetic_result_type (left.target_type, right.target_type);
435 : }
436 :
437 79842 : if (value_type == null) {
438 1 : error = true;
439 1 : Report.error (source_reference, "Arithmetic operation not supported for types `%s' and `%s'", left.value_type.to_string (), right.value_type.to_string ());
440 1 : return false;
441 : }
442 : break;
443 : case BinaryOperator.MOD:
444 : case BinaryOperator.SHIFT_LEFT:
445 : case BinaryOperator.SHIFT_RIGHT:
446 39 : left.target_type.nullable = false;
447 39 : right.target_type.nullable = false;
448 :
449 39 : value_type = context.analyzer.get_arithmetic_result_type (left.target_type, right.target_type);
450 :
451 39 : if (value_type == null) {
452 0 : error = true;
453 0 : Report.error (source_reference, "Arithmetic operation not supported for types `%s' and `%s'", left.value_type.to_string (), right.value_type.to_string ());
454 0 : return false;
455 : }
456 : break;
457 : case BinaryOperator.LESS_THAN:
458 : case BinaryOperator.GREATER_THAN:
459 : case BinaryOperator.LESS_THAN_OR_EQUAL:
460 : case BinaryOperator.GREATER_THAN_OR_EQUAL:
461 54748 : if (left.value_type.compatible (context.analyzer.string_type)
462 17 : && right.value_type.compatible (context.analyzer.string_type)) {
463 : // string comparison
464 109419 : } else if (left.value_type is PointerType && right.value_type is PointerType) {
465 : // pointer arithmetic
466 : } else {
467 : DataType resulting_type;
468 :
469 54708 : if (is_chained) {
470 2839 : unowned BinaryExpression lbe = (BinaryExpression) left;
471 2842 : if (lbe.right.value_type.compatible (context.analyzer.string_type)
472 3 : && right.value_type.compatible (context.analyzer.string_type)) {
473 3 : value_type = context.analyzer.bool_type;
474 3 : break;
475 : }
476 2836 : resulting_type = context.analyzer.get_arithmetic_result_type (lbe.right.target_type, right.target_type);
477 : } else {
478 51869 : resulting_type = context.analyzer.get_arithmetic_result_type (left.target_type, right.target_type);
479 : }
480 :
481 54705 : if (resulting_type == null) {
482 0 : error = true;
483 : unowned DataType left_type;
484 0 : if (is_chained) {
485 0 : unowned BinaryExpression lbe = (BinaryExpression) left;
486 0 : left_type = lbe.right.value_type;
487 : } else {
488 0 : left_type = left.value_type;
489 : }
490 0 : Report.error (source_reference, "Relational operation not supported for types `%s' and `%s'", left_type.to_string (), right.value_type.to_string ());
491 0 : return false;
492 : }
493 :
494 54705 : if (!is_chained) {
495 51869 : left.target_type = resulting_type.copy ();
496 : }
497 54705 : right.target_type = resulting_type.copy ();
498 54705 : left.target_type.nullable = false;
499 54705 : right.target_type.nullable = false;
500 : }
501 :
502 54728 : value_type = context.analyzer.bool_type;
503 54728 : break;
504 : case BinaryOperator.EQUALITY:
505 : case BinaryOperator.INEQUALITY:
506 : /* relational operation */
507 :
508 221154 : if (context.profile == Profile.GOBJECT) {
509 : // Implicit cast for comparison expression of GValue with other type
510 110535 : var gvalue_type = context.analyzer.gvalue_type.type_symbol;
511 110535 : if ((left.target_type.type_symbol == gvalue_type && right.target_type.type_symbol != gvalue_type)
512 110504 : || (left.target_type.type_symbol != gvalue_type && right.target_type.type_symbol == gvalue_type)) {
513 : Expression gvalue_expr;
514 : DataType target_type;
515 39 : if (left.target_type.type_symbol == gvalue_type) {
516 31 : gvalue_expr = left;
517 31 : target_type = right.target_type;
518 : } else {
519 8 : gvalue_expr = right;
520 8 : target_type = left.target_type;
521 : }
522 :
523 39 : var cast_expr = new CastExpression (gvalue_expr, target_type, gvalue_expr.source_reference);
524 39 : replace_expression (gvalue_expr, cast_expr);
525 39 : checked = false;
526 39 : return check (context);
527 : }
528 : }
529 :
530 : DataType resulting_type;
531 110619 : if (is_chained) {
532 16 : unowned BinaryExpression lbe = (BinaryExpression) left;
533 16 : resulting_type = context.analyzer.get_arithmetic_result_type (lbe.right.target_type, right.target_type);
534 16 : if (!right.value_type.compatible (lbe.right.value_type)
535 1 : && !lbe.right.value_type.compatible (right.value_type)) {
536 1 : Report.error (source_reference, "Equality operation: `%s' and `%s' are incompatible", right.value_type.to_string (), lbe.right.value_type.to_string ());
537 1 : error = true;
538 1 : return false;
539 : }
540 : } else {
541 110603 : resulting_type = context.analyzer.get_arithmetic_result_type (left.target_type, right.target_type);
542 110603 : if (!right.value_type.compatible (left.value_type)
543 2863 : && !left.value_type.compatible (right.value_type)) {
544 0 : Report.error (source_reference, "Equality operation: `%s' and `%s' are incompatible", right.value_type.to_string (), left.value_type.to_string ());
545 0 : error = true;
546 0 : return false;
547 : }
548 : }
549 :
550 110618 : if (resulting_type != null) {
551 : // numeric operation
552 38446 : if (!is_chained) {
553 38438 : left.target_type = resulting_type.copy ();
554 : }
555 38446 : right.target_type = resulting_type.copy ();
556 : }
557 :
558 110618 : left.target_type.value_owned = false;
559 110618 : right.target_type.value_owned = false;
560 :
561 110618 : if (left.value_type.nullable != right.value_type.nullable) {
562 : // if only one operand is nullable, make sure the other
563 : // operand is promoted to nullable as well,
564 : // reassign both, as get_arithmetic_result_type doesn't
565 : // take nullability into account
566 4382 : left.target_type.nullable = true;
567 4382 : right.target_type.nullable = true;
568 : }
569 :
570 110618 : value_type = context.analyzer.bool_type;
571 110618 : break;
572 : case BinaryOperator.BITWISE_AND:
573 : case BinaryOperator.BITWISE_OR:
574 : case BinaryOperator.BITWISE_XOR:
575 : // integer type or flags type
576 223 : left.target_type.nullable = false;
577 223 : right.target_type.nullable = false;
578 :
579 : // Don't falsely resolve to bool
580 223 : if (left.value_type.compatible (context.analyzer.bool_type)
581 6 : && !right.value_type.compatible (context.analyzer.bool_type)) {
582 2 : value_type = right.target_type.copy ();
583 : } else {
584 221 : value_type = left.target_type.copy ();
585 : }
586 : break;
587 : case BinaryOperator.AND:
588 : case BinaryOperator.OR:
589 5 : if (!left.value_type.compatible (context.analyzer.bool_type) || !right.value_type.compatible (context.analyzer.bool_type)) {
590 0 : error = true;
591 0 : Report.error (source_reference, "Operands must be boolean");
592 : }
593 5 : left.target_type.nullable = false;
594 5 : right.target_type.nullable = false;
595 :
596 5 : value_type = context.analyzer.bool_type;
597 5 : break;
598 : case BinaryOperator.IN:
599 191 : if (left.value_type.compatible (context.analyzer.int_type)
600 79 : && right.value_type.compatible (context.analyzer.int_type)) {
601 : // integers or enums
602 69 : left.target_type.nullable = false;
603 69 : right.target_type.nullable = false;
604 69 : if (left.value_type.type_symbol is Enum && right.value_type.type_symbol is Enum
605 36 : && left.value_type.type_symbol != right.value_type.type_symbol) {
606 1 : error = true;
607 1 : Report.error (source_reference, "Cannot look for `%s' in `%s'", left.value_type.to_string (), right.value_type.to_string ());
608 : }
609 43 : } else if (right.value_type is ArrayType) {
610 32 : if (!left.value_type.compatible (((ArrayType) right.value_type).element_type)) {
611 0 : error = true;
612 0 : Report.error (source_reference, "Cannot look for `%s' in `%s'", left.value_type.to_string (), right.value_type.to_string ());
613 : }
614 : } else {
615 : // otherwise require a bool contains () method
616 11 : var contains_method = right.value_type.get_member ("contains") as Method;
617 0 : if (contains_method == null) {
618 0 : Report.error (source_reference, "`%s' does not have a `contains' method", right.value_type.to_string ());
619 0 : error = true;
620 0 : return false;
621 : }
622 11 : if (contains_method.get_parameters ().size != 1) {
623 0 : Report.error (source_reference, "`%s' must have one parameter", contains_method.get_full_name ());
624 0 : error = true;
625 0 : return false;
626 : }
627 11 : if (!contains_method.return_type.compatible (context.analyzer.bool_type)) {
628 0 : Report.error (source_reference, "`%s' must return a boolean value", contains_method.get_full_name ());
629 0 : error = true;
630 0 : return false;
631 : }
632 :
633 11 : var contains_call = new MethodCall (new MemberAccess (right, "contains", source_reference), source_reference);
634 11 : contains_call.add_argument (left);
635 11 : parent_node.replace_expression (this, contains_call);
636 11 : return contains_call.check (context);
637 : }
638 :
639 101 : value_type = context.analyzer.bool_type;
640 101 : break;
641 : default:
642 0 : error = true;
643 0 : Report.error (source_reference, "internal error: unsupported binary operator");
644 0 : return false;
645 : }
646 :
647 245558 : value_type.check (context);
648 :
649 245558 : return !error;
650 : }
651 :
652 12251 : public override void emit (CodeGenerator codegen) {
653 12251 : left.emit (codegen);
654 12251 : right.emit (codegen);
655 :
656 12251 : codegen.visit_binary_expression (this);
657 :
658 12251 : codegen.visit_expression (this);
659 : }
660 :
661 510303 : public override void get_defined_variables (Collection<Variable> collection) {
662 510303 : left.get_defined_variables (collection);
663 510303 : right.get_defined_variables (collection);
664 : }
665 :
666 171085 : public override void get_used_variables (Collection<Variable> collection) {
667 171085 : left.get_used_variables (collection);
668 171085 : right.get_used_variables (collection);
669 : }
670 : }
671 :
672 : public enum Vala.BinaryOperator {
673 : NONE,
674 : PLUS,
675 : MINUS,
676 : MUL,
677 : DIV,
678 : MOD,
679 : SHIFT_LEFT,
680 : SHIFT_RIGHT,
681 : LESS_THAN,
682 : GREATER_THAN,
683 : LESS_THAN_OR_EQUAL,
684 : GREATER_THAN_OR_EQUAL,
685 : EQUALITY,
686 : INEQUALITY,
687 : BITWISE_AND,
688 : BITWISE_OR,
689 : BITWISE_XOR,
690 : AND,
691 : OR,
692 : IN,
693 : COALESCE;
694 :
695 : public unowned string to_string () {
696 0 : switch (this) {
697 0 : case PLUS: return "+";
698 0 : case MINUS: return "-";
699 0 : case MUL: return "*";
700 0 : case DIV: return "/";
701 0 : case MOD: return "%";
702 0 : case SHIFT_LEFT: return "<<";
703 0 : case SHIFT_RIGHT: return ">>";
704 0 : case LESS_THAN: return "<";
705 0 : case GREATER_THAN: return ">";
706 0 : case LESS_THAN_OR_EQUAL: return "<=";
707 0 : case GREATER_THAN_OR_EQUAL: return ">=";
708 0 : case EQUALITY: return "==";
709 0 : case INEQUALITY: return "!=";
710 0 : case BITWISE_AND: return "&";
711 0 : case BITWISE_OR: return "|";
712 0 : case BITWISE_XOR: return "^";
713 0 : case AND: return "&&";
714 0 : case OR: return "||";
715 0 : case IN: return "in";
716 0 : case COALESCE: return "??";
717 0 : default: assert_not_reached ();
718 : }
719 : }
720 : }
|