Support constructor chain up to GObject using Object (...)
[vala-lang.git] / vala / valabinaryexpression.vala
blob27477239e5f91885ad163c5a3ea2fe717d544b07
1 /* valabinaryexpression.vala
3 * Copyright (C) 2006-2009 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 private Expression _left;
62 private Expression _right;
64 /**
65 * Creates a new binary expression.
67 * @param op binary operator
68 * @param left left operand
69 * @param right right operand
70 * @param source reference to source code
71 * @return newly created binary expression
73 public BinaryExpression (BinaryOperator op, Expression _left, Expression _right, SourceReference? source = null) {
74 operator = op;
75 left = _left;
76 right = _right;
77 source_reference = source;
80 public override void accept (CodeVisitor visitor) {
81 visitor.visit_binary_expression (this);
83 visitor.visit_expression (this);
86 public override void accept_children (CodeVisitor visitor) {
87 left.accept (visitor);
88 right.accept (visitor);
91 public override void replace_expression (Expression old_node, Expression new_node) {
92 if (left == old_node) {
93 left = new_node;
95 if (right == old_node) {
96 right = new_node;
100 private string get_operator_string () {
101 switch (_operator) {
102 case BinaryOperator.PLUS: return "+";
103 case BinaryOperator.MINUS: return "-";
104 case BinaryOperator.MUL: return "*";
105 case BinaryOperator.DIV: return "/";
106 case BinaryOperator.MOD: return "%";
107 case BinaryOperator.SHIFT_LEFT: return "<<";
108 case BinaryOperator.SHIFT_RIGHT: return ">>";
109 case BinaryOperator.LESS_THAN: return "<";
110 case BinaryOperator.GREATER_THAN: return ">";
111 case BinaryOperator.LESS_THAN_OR_EQUAL: return "<=";
112 case BinaryOperator.GREATER_THAN_OR_EQUAL: return ">=";
113 case BinaryOperator.EQUALITY: return "==";
114 case BinaryOperator.INEQUALITY: return "!+";
115 case BinaryOperator.BITWISE_AND: return "&";
116 case BinaryOperator.BITWISE_OR: return "|";
117 case BinaryOperator.BITWISE_XOR: return "^";
118 case BinaryOperator.AND: return "&&";
119 case BinaryOperator.OR: return "||";
120 case BinaryOperator.IN: return "in";
123 assert_not_reached ();
126 public override string to_string () {
127 return _left.to_string () + get_operator_string () + _right.to_string ();
130 public override bool is_constant () {
131 return left.is_constant () && right.is_constant ();
134 public override bool is_pure () {
135 return left.is_pure () && right.is_pure ();
138 public override bool is_non_null () {
139 return left.is_non_null () && right.is_non_null ();
142 bool in_assert () {
143 CodeNode expr = this;
144 while (expr != null && !(expr is MethodCall && ((MethodCall) expr).is_assert)) {
145 expr = expr.parent_node;
147 return (expr != null);
150 public override bool check (SemanticAnalyzer analyzer) {
151 if (checked) {
152 return !error;
155 checked = true;
157 // some expressions are not in a block,
158 // for example, expressions in method contracts
160 // also don't convert expressions in asserts to not execute
161 // assert expressions when disabled on the C level and
162 // avoid unusable assertion messages
163 // reachability analysis and error handling should never be
164 // necessary for assertion expressions, so it is ok to avoid
165 // the split
166 if (analyzer.current_symbol is Block && !in_assert ()
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 (analyzer.bool_type.copy (), get_temp_name (), null, source_reference);
172 var decl = new DeclarationStatement (local, source_reference);
173 decl.check (analyzer);
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 (analyzer.insert_block, decl);
193 insert_statement (analyzer.insert_block, if_stmt);
195 if (!if_stmt.check (analyzer)) {
196 error = true;
197 return false;
200 var ma = new MemberAccess.simple (local.name, source_reference);
201 ma.target_type = target_type;
202 ma.check (analyzer);
204 parent_node.replace_expression (this, ma);
206 return true;
209 if (!left.check (analyzer) || !right.check (analyzer)) {
210 /* if there were any errors in inner expressions, skip type check */
211 error = true;
212 return false;
215 if (left.value_type == null) {
216 Report.error (left.source_reference, "invalid left operand");
217 error = true;
218 return false;
221 if (operator != BinaryOperator.IN && right.value_type == null) {
222 Report.error (right.source_reference, "invalid right operand");
223 error = true;
224 return false;
227 if (left.value_type.data_type == analyzer.string_type.data_type
228 && operator == BinaryOperator.PLUS) {
229 // string concatenation
231 if (right.value_type == null || right.value_type.data_type != analyzer.string_type.data_type) {
232 error = true;
233 Report.error (source_reference, "Operands must be strings");
234 return false;
237 value_type = analyzer.string_type.copy ();
238 if (left.is_constant () && right.is_constant ()) {
239 value_type.value_owned = false;
240 } else {
241 value_type.value_owned = true;
243 } else if (left.value_type is ArrayType && operator == BinaryOperator.PLUS) {
244 // array concatenation
246 var array_type = (ArrayType) left.value_type;
248 if (right.value_type == null || !right.value_type.compatible (array_type.element_type)) {
249 error = true;
250 Report.error (source_reference, "Incompatible operand");
251 return false;
254 value_type = array_type.copy ();
255 value_type.value_owned = true;
256 } else if (operator == BinaryOperator.PLUS
257 || operator == BinaryOperator.MINUS
258 || operator == BinaryOperator.MUL
259 || operator == BinaryOperator.DIV) {
260 // check for pointer arithmetic
261 if (left.value_type is PointerType) {
262 var offset_type = right.value_type.data_type as Struct;
263 if (offset_type != null && offset_type.is_integer_type ()) {
264 if (operator == BinaryOperator.PLUS
265 || operator == BinaryOperator.MINUS) {
266 // pointer arithmetic: pointer +/- offset
267 value_type = left.value_type.copy ();
269 } else if (right.value_type is PointerType) {
270 // pointer arithmetic: pointer - pointer
271 value_type = analyzer.size_t_type;
275 if (value_type == null) {
276 value_type = analyzer.get_arithmetic_result_type (left.value_type, right.value_type);
279 if (value_type == null) {
280 error = true;
281 Report.error (source_reference, "Arithmetic operation not supported for types `%s' and `%s'".printf (left.value_type.to_string (), right.value_type.to_string ()));
282 return false;
284 } else if (operator == BinaryOperator.MOD
285 || operator == BinaryOperator.SHIFT_LEFT
286 || operator == BinaryOperator.SHIFT_RIGHT
287 || operator == BinaryOperator.BITWISE_XOR) {
288 value_type = analyzer.get_arithmetic_result_type (left.value_type, right.value_type);
290 if (value_type == null) {
291 error = true;
292 Report.error (source_reference, "Arithmetic operation not supported for types `%s' and `%s'".printf (left.value_type.to_string (), right.value_type.to_string ()));
293 return false;
295 } else if (operator == BinaryOperator.LESS_THAN
296 || operator == BinaryOperator.GREATER_THAN
297 || operator == BinaryOperator.LESS_THAN_OR_EQUAL
298 || operator == BinaryOperator.GREATER_THAN_OR_EQUAL) {
299 if (left.value_type.compatible (analyzer.string_type)
300 && right.value_type.compatible (analyzer.string_type)) {
301 // string comparison
302 } else if (left.value_type is PointerType && right.value_type is PointerType) {
303 // pointer arithmetic
304 } else {
305 var resulting_type = analyzer.get_arithmetic_result_type (left.value_type, right.value_type);
307 if (resulting_type == null) {
308 error = true;
309 Report.error (source_reference, "Relational operation not supported for types `%s' and `%s'".printf (left.value_type.to_string (), right.value_type.to_string ()));
310 return false;
314 value_type = analyzer.bool_type;
315 } else if (operator == BinaryOperator.EQUALITY
316 || operator == BinaryOperator.INEQUALITY) {
317 /* relational operation */
319 if (!right.value_type.compatible (left.value_type)
320 && !left.value_type.compatible (right.value_type)) {
321 Report.error (source_reference, "Equality operation: `%s' and `%s' are incompatible".printf (right.value_type.to_string (), left.value_type.to_string ()));
322 error = true;
323 return false;
326 if (left.value_type.compatible (analyzer.string_type)
327 && right.value_type.compatible (analyzer.string_type)) {
328 // string comparison
331 value_type = analyzer.bool_type;
332 } else if (operator == BinaryOperator.BITWISE_AND
333 || operator == BinaryOperator.BITWISE_OR) {
334 // integer type or flags type
336 value_type = left.value_type;
337 } else if (operator == BinaryOperator.AND
338 || operator == BinaryOperator.OR) {
339 if (!left.value_type.compatible (analyzer.bool_type) || !right.value_type.compatible (analyzer.bool_type)) {
340 error = true;
341 Report.error (source_reference, "Operands must be boolean");
344 value_type = analyzer.bool_type;
345 } else if (operator == BinaryOperator.IN) {
346 if (left.value_type.compatible (analyzer.int_type)
347 && right.value_type.compatible (analyzer.int_type)) {
348 // integers or enums
349 } else {
350 // otherwise require a bool contains () method
351 var contains_method = right.value_type.get_member ("contains") as Method;
352 if (contains_method == null) {
353 Report.error (source_reference, "`%s' does not have a `contains' method".printf (right.value_type.to_string ()));
354 error = true;
355 return false;
357 if (contains_method.get_parameters ().size != 1) {
358 Report.error (source_reference, "`%s' must have one parameter".printf (contains_method.get_full_name ()));
359 error = true;
360 return false;
362 if (!contains_method.return_type.compatible (analyzer.bool_type)) {
363 Report.error (source_reference, "`%s' must return a boolean value".printf (contains_method.get_full_name ()));
364 error = true;
365 return false;
368 var contains_call = new MethodCall (new MemberAccess (right, "contains"));
369 contains_call.add_argument (left);
370 parent_node.replace_expression (this, contains_call);
371 return contains_call.check (analyzer);
374 value_type = analyzer.bool_type;
376 } else {
377 assert_not_reached ();
380 return !error;
383 public override void get_defined_variables (Collection<LocalVariable> collection) {
384 left.get_defined_variables (collection);
385 right.get_defined_variables (collection);
388 public override void get_used_variables (Collection<LocalVariable> collection) {
389 left.get_used_variables (collection);
390 right.get_used_variables (collection);
394 public enum Vala.BinaryOperator {
395 NONE,
396 PLUS,
397 MINUS,
398 MUL,
399 DIV,
400 MOD,
401 SHIFT_LEFT,
402 SHIFT_RIGHT,
403 LESS_THAN,
404 GREATER_THAN,
405 LESS_THAN_OR_EQUAL,
406 GREATER_THAN_OR_EQUAL,
407 EQUALITY,
408 INEQUALITY,
409 BITWISE_AND,
410 BITWISE_OR,
411 BITWISE_XOR,
412 AND,