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
;
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) {
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
) {
97 if (right
== old_node
) {
102 private unowned
string get_operator_string () {
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
) {
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
);
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
)) {
191 var ma
= new MemberAccess
.simple (local
.name
, source_reference
);
192 ma
.target_type
= target_type
;
193 ma
.formal_target_type
= formal_target_type
;
196 parent_node
.replace_expression (this
, ma
);
201 if (operator
== BinaryOperator
.COALESCE
) {
202 if (!left
.check (context
)) {
207 if (!right
.check (context
)) {
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
)) {
263 if (!if_stmt
.check (context
)) {
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;
275 replace_expr
.check (context
);
277 parent_node
.replace_expression (this
, replace_expr
);
282 if (!left
.check (context
) || !right
.check (context
)) {
283 /* if there were any errors in inner expressions, skip type check */
288 if (left
.value_type
== null) {
289 Report
.error (left
.source_reference
, "invalid left operand");
294 if (operator
!= BinaryOperator
.IN
&& right
.value_type
== null) {
295 Report
.error (right
.source_reference
, "invalid right operand");
300 if (left
.value_type is FieldPrototype
) {
302 Report
.error (left
.source_reference
, "Access to instance member `%s' denied".printf (left
.symbol_reference
.get_full_name ()));
305 if (right
.value_type is FieldPrototype
) {
307 Report
.error (right
.source_reference
, "Access to instance member `%s' denied".printf (right
.symbol_reference
.get_full_name ()));
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
) {
322 Report
.error (source_reference
, "Operands must be strings");
326 value_type
= context
.analyzer
.string_type
.copy ();
327 if (left
.is_constant () && right
.is_constant ()) {
328 value_type
.value_owned
= false;
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
)) {
339 Report
.error (source_reference
, "Incompatible operand");
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
) {
356 Report
.error (source_reference
, "Pointer arithmetic not supported for `void*'");
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
;
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) {
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 ()));
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) {
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 ()));
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
)) {
405 } else if (left
.value_type is PointerType
&& right
.value_type is PointerType
) {
406 // pointer arithmetic
408 DataType resulting_type
;
411 var lbe
= (BinaryExpression
) left
;
412 resulting_type
= context
.analyzer
.get_arithmetic_result_type (lbe
.right
.target_type
, right
.target_type
);
414 resulting_type
= context
.analyzer
.get_arithmetic_result_type (left
.target_type
, right
.target_type
);
417 if (resulting_type
== null) {
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 ()));
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 ()));
443 var resulting_type
= context
.analyzer
.get_arithmetic_result_type (left
.target_type
, right
.target_type
);
444 if (resulting_type
!= null) {
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
)) {
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
)) {
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 ()));
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 ()));
499 if (contains_method
.get_parameters ().size
!= 1) {
500 Report
.error (source_reference
, "`%s' must have one parameter".printf (contains_method
.get_full_name ()));
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 ()));
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
;
519 assert_not_reached ();
525 public override void emit (CodeGenerator 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
{
557 GREATER_THAN_OR_EQUAL
,