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
20 * Jürg Billeter <j@bitron.ch>
25 * Represents an expression with two operands in the source code.
27 * Supports +, -, *, /, %, <<, >>, <, >, <=, >=, ==, !=, &, |, ^, &&, ||, ??.
29 public class Vala
.BinaryExpression
: Expression
{
31 * The binary operator.
33 public BinaryOperator operator
{ get; set; }
38 public Expression left
{
44 _left
.parent_node
= this
;
51 public Expression right
{
57 _right
.parent_node
= this
;
61 public bool is_chained
{ get; private set; }
63 private Expression _left
;
64 private Expression _right
;
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) {
80 source_reference
= source
;
83 public BinaryExpression
.chained (BinaryOperator op
, Expression _left
, Expression _right
, SourceReference? source
= null) {
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
) {
106 if (right
== old_node
) {
111 private unowned
string get_operator_string () {
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
) {
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
);
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
)) {
200 var ma
= new MemberAccess
.simple (local
.name
, source_reference
);
201 ma
.target_type
= target_type
;
202 ma
.formal_target_type
= formal_target_type
;
205 parent_node
.replace_expression (this
, ma
);
210 if (operator
== BinaryOperator
.COALESCE
) {
211 if (!left
.check (context
)) {
216 if (!right
.check (context
)) {
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
)) {
272 if (!if_stmt
.check (context
)) {
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;
284 replace_expr
.check (context
);
286 parent_node
.replace_expression (this
, replace_expr
);
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 */
315 if (left
.value_type
== null) {
316 Report
.error (left
.source_reference
, "invalid left operand");
321 if (operator
!= BinaryOperator
.IN
&& right
.value_type
== null) {
322 Report
.error (right
.source_reference
, "invalid right operand");
327 if (left
.value_type is FieldPrototype
) {
329 Report
.error (left
.source_reference
, "Access to instance member `%s' denied".printf (left
.symbol_reference
.get_full_name ()));
332 if (right
.value_type is FieldPrototype
) {
334 Report
.error (right
.source_reference
, "Access to instance member `%s' denied".printf (right
.symbol_reference
.get_full_name ()));
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
) {
349 Report
.error (source_reference
, "Operands must be strings");
353 value_type
= context
.analyzer
.string_type
.copy ();
354 if (left
.is_constant () && right
.is_constant ()) {
355 value_type
.value_owned
= false;
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
)) {
366 Report
.error (source_reference
, "Incompatible operand");
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
) {
383 Report
.error (source_reference
, "Pointer arithmetic not supported for `void*'");
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
;
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) {
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 ()));
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) {
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 ()));
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
)) {
432 } else if (left
.value_type is PointerType
&& right
.value_type is PointerType
) {
433 // pointer arithmetic
435 DataType resulting_type
;
438 var lbe
= (BinaryExpression
) left
;
439 resulting_type
= context
.analyzer
.get_arithmetic_result_type (lbe
.right
.target_type
, right
.target_type
);
441 resulting_type
= context
.analyzer
.get_arithmetic_result_type (left
.target_type
, right
.target_type
);
444 if (resulting_type
== null) {
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 ()));
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 ()));
470 var resulting_type
= context
.analyzer
.get_arithmetic_result_type (left
.target_type
, right
.target_type
);
471 if (resulting_type
!= null) {
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
)) {
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
)) {
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 ()));
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 ()));
526 if (contains_method
.get_parameters ().size
!= 1) {
527 Report
.error (source_reference
, "`%s' must have one parameter".printf (contains_method
.get_full_name ()));
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 ()));
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
;
546 assert_not_reached ();
552 public override void emit (CodeGenerator 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
{
584 GREATER_THAN_OR_EQUAL
,