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 public 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 check (CodeContext context
) {
151 // some expressions are not in a block,
152 // for example, expressions in method contracts
153 if (context
.analyzer
.current_symbol is Block
154 && (operator
== BinaryOperator
.AND
|| operator
== BinaryOperator
.OR
)) {
155 // convert conditional expression into if statement
156 // required for flow analysis and exception handling
158 var local
= new
LocalVariable (context
.analyzer
.bool_type
.copy (), get_temp_name (), null, source_reference
);
159 var decl
= new
DeclarationStatement (local
, source_reference
);
160 decl
.check (context
);
162 var right_stmt
= new
ExpressionStatement (new
Assignment (new MemberAccess
.simple (local
.name
, right
.source_reference
), right
, AssignmentOperator
.SIMPLE
, right
.source_reference
), right
.source_reference
);
164 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
);
166 var true_block
= new
Block (source_reference
);
167 var false_block
= new
Block (source_reference
);
169 if (operator
== BinaryOperator
.AND
) {
170 true_block
.add_statement (right_stmt
);
171 false_block
.add_statement (stmt
);
173 true_block
.add_statement (stmt
);
174 false_block
.add_statement (right_stmt
);
177 var if_stmt
= new
IfStatement (left
, true_block
, false_block
, source_reference
);
179 insert_statement (context
.analyzer
.insert_block
, decl
);
180 insert_statement (context
.analyzer
.insert_block
, if_stmt
);
182 if (!if_stmt
.check (context
)) {
187 var ma
= new MemberAccess
.simple (local
.name
, source_reference
);
188 ma
.target_type
= target_type
;
191 parent_node
.replace_expression (this
, ma
);
196 if (operator
== BinaryOperator
.COALESCE
) {
197 var local
= new
LocalVariable (null, get_temp_name (), left
, source_reference
);
198 var decl
= new
DeclarationStatement (local
, source_reference
);
199 decl
.check (context
);
201 var right_stmt
= new
ExpressionStatement (new
Assignment (new MemberAccess
.simple (local
.name
, right
.source_reference
), right
, AssignmentOperator
.SIMPLE
, right
.source_reference
), right
.source_reference
);
203 var true_block
= new
Block (source_reference
);
205 true_block
.add_statement (right_stmt
);
207 var cond
= new
BinaryExpression (BinaryOperator
.EQUALITY
, new MemberAccess
.simple (local
.name
, left
.source_reference
), new
NullLiteral (source_reference
), source_reference
);
209 var if_stmt
= new
IfStatement (cond
, true_block
, null, source_reference
);
211 insert_statement (context
.analyzer
.insert_block
, decl
);
212 insert_statement (context
.analyzer
.insert_block
, if_stmt
);
214 if (!if_stmt
.check (context
)) {
219 var ma
= new MemberAccess
.simple (local
.name
, source_reference
);
220 ma
.target_type
= target_type
;
223 parent_node
.replace_expression (this
, ma
);
228 if (!left
.check (context
) || !right
.check (context
)) {
229 /* if there were any errors in inner expressions, skip type check */
234 if (left
.value_type
== null) {
235 Report
.error (left
.source_reference
, "invalid left operand");
240 if (operator
!= BinaryOperator
.IN
&& right
.value_type
== null) {
241 Report
.error (right
.source_reference
, "invalid right operand");
246 if (left
.value_type
.data_type
== context
.analyzer
.string_type
.data_type
247 && operator
== BinaryOperator
.PLUS
) {
248 // string concatenation
250 if (context
.profile
== Profile
.DOVA
) {
251 var concat_call
= new
MethodCall (new
MemberAccess (left
, "concat", source_reference
), source_reference
);
252 concat_call
.add_argument (right
);
253 concat_call
.target_type
= target_type
;
254 parent_node
.replace_expression (this
, concat_call
);
255 return concat_call
.check (context
);
258 if (right
.value_type
== null || right
.value_type
.data_type
!= context
.analyzer
.string_type
.data_type
) {
260 Report
.error (source_reference
, "Operands must be strings");
264 value_type
= context
.analyzer
.string_type
.copy ();
265 if (left
.is_constant () && right
.is_constant ()) {
266 value_type
.value_owned
= false;
268 value_type
.value_owned
= true;
270 } else if (context
.profile
== Profile
.DOVA
&& left
.value_type
.data_type
== context
.analyzer
.list_type
.data_type
271 && operator
== BinaryOperator
.PLUS
) {
272 // list concatenation
274 var concat_call
= new
MethodCall (new
MemberAccess (left
, "concat", source_reference
), source_reference
);
275 concat_call
.add_argument (right
);
276 concat_call
.target_type
= target_type
;
277 parent_node
.replace_expression (this
, concat_call
);
278 return concat_call
.check (context
);
279 } else if (context
.profile
!= Profile
.DOVA
&& left
.value_type is ArrayType
&& operator
== BinaryOperator
.PLUS
) {
280 // array concatenation
282 var array_type
= (ArrayType
) left
.value_type
;
284 if (right
.value_type
== null || !right
.value_type
.compatible (array_type
.element_type
)) {
286 Report
.error (source_reference
, "Incompatible operand");
290 right
.target_type
= array_type
.element_type
.copy ();
292 value_type
= array_type
.copy ();
293 value_type
.value_owned
= true;
294 } else if (operator
== BinaryOperator
.PLUS
295 || operator
== BinaryOperator
.MINUS
296 || operator
== BinaryOperator
.MUL
297 || operator
== BinaryOperator
.DIV
) {
298 // check for pointer arithmetic
299 if (left
.value_type is PointerType
) {
300 var pointer_type
= (PointerType
) left
.value_type
;
301 if (pointer_type
.base_type is VoidType
) {
303 Report
.error (source_reference
, "Pointer arithmetic not supported for `void*'");
307 var offset_type
= right
.value_type
.data_type as Struct
;
308 if (offset_type
!= null && offset_type
.is_integer_type ()) {
309 if (operator
== BinaryOperator
.PLUS
310 || operator
== BinaryOperator
.MINUS
) {
311 // pointer arithmetic: pointer +/- offset
312 value_type
= left
.value_type
.copy ();
314 } else if (right
.value_type is PointerType
) {
315 // pointer arithmetic: pointer - pointer
316 if (context
.profile
== Profile
.DOVA
) {
317 value_type
= context
.analyzer
.long_type
;
319 value_type
= context
.analyzer
.size_t_type
;
324 if (value_type
== null) {
325 value_type
= context
.analyzer
.get_arithmetic_result_type (left
.value_type
, right
.value_type
);
328 if (value_type
== null) {
330 Report
.error (source_reference
, "Arithmetic operation not supported for types `%s' and `%s'".printf (left
.value_type
.to_string (), right
.value_type
.to_string ()));
333 } else if (operator
== BinaryOperator
.MOD
334 || operator
== BinaryOperator
.SHIFT_LEFT
335 || operator
== BinaryOperator
.SHIFT_RIGHT
336 || operator
== BinaryOperator
.BITWISE_XOR
) {
337 value_type
= context
.analyzer
.get_arithmetic_result_type (left
.value_type
, right
.value_type
);
339 if (value_type
== null) {
341 Report
.error (source_reference
, "Arithmetic operation not supported for types `%s' and `%s'".printf (left
.value_type
.to_string (), right
.value_type
.to_string ()));
344 } else if (operator
== BinaryOperator
.LESS_THAN
345 || operator
== BinaryOperator
.GREATER_THAN
346 || operator
== BinaryOperator
.LESS_THAN_OR_EQUAL
347 || operator
== BinaryOperator
.GREATER_THAN_OR_EQUAL
) {
348 if (left
.value_type
.compatible (context
.analyzer
.string_type
)
349 && right
.value_type
.compatible (context
.analyzer
.string_type
)) {
351 } else if (left
.value_type is PointerType
&& right
.value_type is PointerType
) {
352 // pointer arithmetic
354 DataType resulting_type
;
357 var lbe
= (BinaryExpression
) left
;
358 resulting_type
= context
.analyzer
.get_arithmetic_result_type (lbe
.right
.value_type
, right
.value_type
);
360 resulting_type
= context
.analyzer
.get_arithmetic_result_type (left
.value_type
, right
.value_type
);
363 if (resulting_type
== null) {
365 Report
.error (source_reference
, "Relational operation not supported for types `%s' and `%s'".printf (left
.value_type
.to_string (), right
.value_type
.to_string ()));
369 left
.target_type
= left
.value_type
.copy ();
370 left
.target_type
.nullable
= false;
371 left
.target_type
.value_owned
= false;
372 right
.target_type
= right
.value_type
.copy ();
373 right
.target_type
.nullable
= false;
374 right
.target_type
.value_owned
= false;
377 value_type
= context
.analyzer
.bool_type
;
378 } else if (operator
== BinaryOperator
.EQUALITY
379 || operator
== BinaryOperator
.INEQUALITY
) {
380 /* relational operation */
382 if (!right
.value_type
.compatible (left
.value_type
)
383 && !left
.value_type
.compatible (right
.value_type
)) {
384 Report
.error (source_reference
, "Equality operation: `%s' and `%s' are incompatible".printf (right
.value_type
.to_string (), left
.value_type
.to_string ()));
389 left
.target_type
= left
.value_type
.copy ();
390 left
.target_type
.value_owned
= false;
391 right
.target_type
= right
.value_type
.copy ();
392 right
.target_type
.value_owned
= false;
394 if (left
.value_type
.nullable
!= right
.value_type
.nullable
) {
395 // if only one operand is nullable, make sure the other operand is promoted to nullable as well
396 if (!left
.value_type
.nullable
) {
397 left
.target_type
.nullable
= true;
398 } else if (!right
.value_type
.nullable
) {
399 right
.target_type
.nullable
= true;
403 if (left
.value_type
.compatible (context
.analyzer
.string_type
)
404 && right
.value_type
.compatible (context
.analyzer
.string_type
)) {
406 if (context
.profile
== Profile
.DOVA
) {
407 var string_ma
= new MemberAccess
.simple ("string", source_reference
);
408 string_ma
.qualified
= true;
409 var equals_call
= new
MethodCall (new
MemberAccess (string_ma
, "equals", source_reference
), source_reference
);
410 equals_call
.add_argument (left
);
411 equals_call
.add_argument (right
);
412 if (operator
== BinaryOperator
.EQUALITY
) {
413 parent_node
.replace_expression (this
, equals_call
);
414 return equals_call
.check (context
);
416 var not
= new
UnaryExpression (UnaryOperator
.LOGICAL_NEGATION
, equals_call
, source_reference
);
417 parent_node
.replace_expression (this
, not
);
418 return not
.check (context
);
423 value_type
= context
.analyzer
.bool_type
;
424 } else if (operator
== BinaryOperator
.BITWISE_AND
425 || operator
== BinaryOperator
.BITWISE_OR
) {
426 // integer type or flags type
428 value_type
= left
.value_type
;
429 } else if (operator
== BinaryOperator
.AND
430 || operator
== BinaryOperator
.OR
) {
431 if (!left
.value_type
.compatible (context
.analyzer
.bool_type
) || !right
.value_type
.compatible (context
.analyzer
.bool_type
)) {
433 Report
.error (source_reference
, "Operands must be boolean");
436 value_type
= context
.analyzer
.bool_type
;
437 } else if (operator
== BinaryOperator
.IN
) {
438 if (left
.value_type
.compatible (context
.analyzer
.int_type
)
439 && right
.value_type
.compatible (context
.analyzer
.int_type
)) {
441 } else if (right
.value_type is ArrayType
) {
442 if (!left
.value_type
.compatible (((ArrayType
) right
.value_type
).element_type
)) {
443 Report
.error (source_reference
, "Cannot look for `%s' in `%s'".printf (left
.value_type
.to_string (), right
.value_type
.to_string ()));
446 // otherwise require a bool contains () method
447 var contains_method
= right
.value_type
.get_member ("contains") as Method
;
448 if (contains_method
== null) {
449 Report
.error (source_reference
, "`%s' does not have a `contains' method".printf (right
.value_type
.to_string ()));
453 if (contains_method
.get_parameters ().size
!= 1) {
454 Report
.error (source_reference
, "`%s' must have one parameter".printf (contains_method
.get_full_name ()));
458 if (!contains_method
.return_type
.compatible (context
.analyzer
.bool_type
)) {
459 Report
.error (source_reference
, "`%s' must return a boolean value".printf (contains_method
.get_full_name ()));
464 var contains_call
= new
MethodCall (new
MemberAccess (right
, "contains", source_reference
), source_reference
);
465 contains_call
.add_argument (left
);
466 parent_node
.replace_expression (this
, contains_call
);
467 return contains_call
.check (context
);
470 value_type
= context
.analyzer
.bool_type
;
473 assert_not_reached ();
479 public override void emit (CodeGenerator codegen
) {
481 right
.emit (codegen
);
483 codegen
.visit_binary_expression (this
);
485 codegen
.visit_expression (this
);
488 public override void get_defined_variables (Collection
<LocalVariable
> collection
) {
489 left
.get_defined_variables (collection
);
490 right
.get_defined_variables (collection
);
493 public override void get_used_variables (Collection
<LocalVariable
> collection
) {
494 left
.get_used_variables (collection
);
495 right
.get_used_variables (collection
);
499 public enum Vala
.BinaryOperator
{
511 GREATER_THAN_OR_EQUAL
,