girparser: Fix parameter index calculation
[vala-lang.git] / vala / valaassignment.vala
blob575ff966fd8a1e6c29f8e292d355e2318407bd24
1 /* valaassignment.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
19 * Author:
20 * Jürg Billeter <j@bitron.ch>
24 /**
25 * Represents an assignment expression in the source code.
27 * Supports =, |=, &=, ^=, +=, -=, *=, /=, %=, <<=, >>=.
29 public class Vala.Assignment : Expression {
30 /**
31 * Left hand side of the assignment.
33 public Expression left {
34 get { return _left; }
35 set {
36 _left = value;
37 _left.parent_node = this;
41 /**
42 * Assignment operator.
44 public AssignmentOperator operator { get; set; }
46 /**
47 * Right hand side of the assignment.
49 public Expression right {
50 get { return _right; }
51 set {
52 _right = value;
53 _right.parent_node = this;
57 private Expression _left;
58 private Expression _right;
60 /**
61 * Creates a new assignment.
63 * @param left left hand side
64 * @param operator assignment operator
65 * @param right right hand side
66 * @param source_reference reference to source code
67 * @return newly created assignment
69 public Assignment (Expression left, Expression right, AssignmentOperator operator = AssignmentOperator.SIMPLE, SourceReference? source_reference = null) {
70 this.right = right;
71 this.operator = operator;
72 this.source_reference = source_reference;
73 this.left = left;
76 public override void accept (CodeVisitor visitor) {
77 visitor.visit_assignment (this);
79 visitor.visit_expression (this);
82 public override void accept_children (CodeVisitor visitor) {
83 left.accept (visitor);
84 right.accept (visitor);
87 public override void replace_expression (Expression old_node, Expression new_node) {
88 if (left == old_node) {
89 left = new_node;
91 if (right == old_node) {
92 right = new_node;
96 public override bool is_pure () {
97 return false;
100 public override bool check (SemanticAnalyzer analyzer) {
101 if (checked) {
102 return !error;
105 checked = true;
107 if (left is Tuple && operator == AssignmentOperator.SIMPLE && parent_node is ExpressionStatement) {
108 var tuple = (Tuple) left;
110 var local = new LocalVariable (null, get_temp_name (), right, right.source_reference);
111 var decl = new DeclarationStatement (local, source_reference);
112 decl.check (analyzer);
113 insert_statement (analyzer.insert_block, decl);
115 int i = 0;
116 ExpressionStatement stmt = null;
117 foreach (var expr in tuple.get_expressions ()) {
118 if (stmt != null) {
119 stmt.check (analyzer);
120 insert_statement (analyzer.insert_block, stmt);
123 var temp_access = new MemberAccess.simple (local.name, right.source_reference);
124 var ea = new ElementAccess (temp_access, expr.source_reference);
125 ea.append_index (new IntegerLiteral (i.to_string (), expr.source_reference));
126 var assign = new Assignment (expr, ea, operator, expr.source_reference);
127 stmt = new ExpressionStatement (assign, expr.source_reference);
129 i++;
132 analyzer.replaced_nodes.add (this);
133 parent_node.replace_expression (this, stmt.expression);
134 return stmt.expression.check (analyzer);
137 left.lvalue = true;
139 if (!left.check (analyzer)) {
140 // skip on error in inner expression
141 error = true;
142 return false;
145 if (left is MemberAccess) {
146 var ma = (MemberAccess) left;
148 if ((!(ma.symbol_reference is Signal || ma.symbol_reference is DynamicProperty) && ma.value_type == null) ||
149 (ma.inner == null && ma.member_name == "this" && analyzer.is_in_instance_method ())) {
150 error = true;
151 Report.error (source_reference, "unsupported lvalue in assignment");
152 return false;
154 if (ma.prototype_access) {
155 error = true;
156 Report.error (source_reference, "Access to instance member `%s' denied".printf (ma.symbol_reference.get_full_name ()));
157 return false;
160 if (ma.error || ma.symbol_reference == null) {
161 error = true;
162 /* if no symbol found, skip this check */
163 return false;
166 if (ma.symbol_reference is DynamicSignal) {
167 // target_type not available for dynamic signals
168 if (!analyzer.context.deprecated) {
169 Report.warning (source_reference, "deprecated syntax, use `connect' method instead");
171 } else if (ma.symbol_reference is Signal) {
172 if (!analyzer.context.deprecated) {
173 Report.warning (source_reference, "deprecated syntax, use `connect' method instead");
175 var sig = (Signal) ma.symbol_reference;
176 right.target_type = new DelegateType (sig.get_delegate (ma.inner.value_type, this));
177 } else {
178 right.formal_target_type = ma.formal_value_type;
179 right.target_type = ma.value_type;
181 } else if (left is ElementAccess) {
182 var ea = (ElementAccess) left;
184 if (ea.container.value_type.data_type == analyzer.string_type.data_type) {
185 error = true;
186 Report.error (ea.source_reference, "strings are immutable");
187 return false;
188 } else if (ea.container is MemberAccess && ea.container.symbol_reference is Signal) {
189 var ma = (MemberAccess) ea.container;
190 var sig = (Signal) ea.container.symbol_reference;
191 right.target_type = new DelegateType (sig.get_delegate (ma.inner.value_type, this));
192 } else if (ea.container.value_type.get_member ("set") is Method) {
193 var set_call = new MethodCall (new MemberAccess (ea.container, "set"));
194 foreach (Expression e in ea.get_indices ()) {
195 set_call.add_argument (e);
197 set_call.add_argument (right);
198 parent_node.replace_expression (this, set_call);
199 return set_call.check (analyzer);
200 } else {
201 right.target_type = left.value_type;
203 } else if (left is PointerIndirection) {
204 right.target_type = left.value_type;
205 } else {
206 error = true;
207 Report.error (source_reference, "unsupported lvalue in assignment");
208 return false;
211 if (!right.check (analyzer)) {
212 // skip on error in inner expression
213 error = true;
214 return false;
217 if (operator != AssignmentOperator.SIMPLE && left is MemberAccess) {
218 // transform into simple assignment
219 // FIXME: only do this if the backend doesn't support
220 // the assignment natively
222 var ma = (MemberAccess) left;
224 if (!(ma.symbol_reference is Signal)) {
225 var old_value = new MemberAccess (ma.inner, ma.member_name);
227 var bin = new BinaryExpression (BinaryOperator.PLUS, old_value, right, source_reference);
228 bin.target_type = right.target_type;
229 right.target_type = right.target_type.copy ();
230 right.target_type.value_owned = false;
232 if (operator == AssignmentOperator.BITWISE_OR) {
233 bin.operator = BinaryOperator.BITWISE_OR;
234 } else if (operator == AssignmentOperator.BITWISE_AND) {
235 bin.operator = BinaryOperator.BITWISE_AND;
236 } else if (operator == AssignmentOperator.BITWISE_XOR) {
237 bin.operator = BinaryOperator.BITWISE_XOR;
238 } else if (operator == AssignmentOperator.ADD) {
239 bin.operator = BinaryOperator.PLUS;
240 } else if (operator == AssignmentOperator.SUB) {
241 bin.operator = BinaryOperator.MINUS;
242 } else if (operator == AssignmentOperator.MUL) {
243 bin.operator = BinaryOperator.MUL;
244 } else if (operator == AssignmentOperator.DIV) {
245 bin.operator = BinaryOperator.DIV;
246 } else if (operator == AssignmentOperator.PERCENT) {
247 bin.operator = BinaryOperator.MOD;
248 } else if (operator == AssignmentOperator.SHIFT_LEFT) {
249 bin.operator = BinaryOperator.SHIFT_LEFT;
250 } else if (operator == AssignmentOperator.SHIFT_RIGHT) {
251 bin.operator = BinaryOperator.SHIFT_RIGHT;
254 right = bin;
255 right.check (analyzer);
257 operator = AssignmentOperator.SIMPLE;
261 if (left.symbol_reference is Signal) {
262 var sig = (Signal) left.symbol_reference;
264 var m = right.symbol_reference as Method;
266 if (m == null) {
267 error = true;
268 Report.error (right.source_reference, "unsupported expression for signal handler");
269 return false;
272 var dynamic_sig = sig as DynamicSignal;
273 var right_ma = right as MemberAccess;
274 if (dynamic_sig != null) {
275 bool first = true;
276 foreach (FormalParameter param in dynamic_sig.handler.value_type.get_parameters ()) {
277 if (first) {
278 // skip sender parameter
279 first = false;
280 } else {
281 dynamic_sig.add_parameter (param.copy ());
284 right.target_type = new DelegateType (sig.get_delegate (new ObjectType ((ObjectTypeSymbol) sig.parent_symbol), this));
285 } else if (!right.value_type.compatible (right.target_type)) {
286 var delegate_type = (DelegateType) right.target_type;
288 error = true;
289 Report.error (right.source_reference, "method `%s' is incompatible with signal `%s', expected `%s'".printf (right.value_type.to_string (), right.target_type.to_string (), delegate_type.delegate_symbol.get_prototype_string (m.name)));
290 return false;
291 } else if (right_ma != null && right_ma.prototype_access) {
292 error = true;
293 Report.error (right.source_reference, "Access to instance member `%s' denied".printf (m.get_full_name ()));
294 return false;
296 } else if (left is MemberAccess) {
297 var ma = (MemberAccess) left;
299 if (ma.symbol_reference is Property) {
300 var prop = (Property) ma.symbol_reference;
302 var dynamic_prop = prop as DynamicProperty;
303 if (dynamic_prop != null) {
304 dynamic_prop.property_type = right.value_type.copy ();
305 left.value_type = dynamic_prop.property_type.copy ();
308 if (prop.set_accessor == null
309 || (!prop.set_accessor.writable && !(analyzer.find_current_method () is CreationMethod || analyzer.is_in_constructor ()))) {
310 ma.error = true;
311 Report.error (ma.source_reference, "Property `%s' is read-only".printf (prop.get_full_name ()));
312 return false;
313 } else if (!analyzer.context.deprecated
314 && !prop.set_accessor.writable
315 && analyzer.find_current_method () is CreationMethod) {
316 if (ma.inner.symbol_reference != analyzer.find_current_method ().this_parameter) {
317 // trying to set construct-only property in creation method for foreign instance
318 Report.error (ma.source_reference, "Property `%s' is read-only".printf (prop.get_full_name ()));
319 return false;
320 } else {
321 Report.warning (ma.source_reference, "assigning to construct-only properties is deprecated, use Object (property: value) constructor chain up");
324 } else if (ma.symbol_reference is LocalVariable && right.value_type == null) {
325 var local = (LocalVariable) ma.symbol_reference;
327 if (right.symbol_reference is Method &&
328 local.variable_type is DelegateType) {
329 var m = (Method) right.symbol_reference;
330 var dt = (DelegateType) local.variable_type;
331 var cb = dt.delegate_symbol;
333 /* check whether method matches callback type */
334 if (!cb.matches_method (m)) {
335 error = true;
336 Report.error (source_reference, "declaration of method `%s' doesn't match declaration of callback `%s'".printf (m.get_full_name (), cb.get_full_name ()));
337 return false;
340 right.value_type = local.variable_type;
341 } else {
342 error = true;
343 Report.error (source_reference, "Assignment: Invalid callback assignment attempt");
344 return false;
346 } else if (ma.symbol_reference is Field && right.value_type == null) {
347 var f = (Field) ma.symbol_reference;
349 if (right.symbol_reference is Method &&
350 f.field_type is DelegateType) {
351 var m = (Method) right.symbol_reference;
352 var dt = (DelegateType) f.field_type;
353 var cb = dt.delegate_symbol;
355 /* check whether method matches callback type */
356 if (!cb.matches_method (m)) {
357 f.error = true;
358 Report.error (source_reference, "declaration of method `%s' doesn't match declaration of callback `%s'".printf (m.get_full_name (), cb.get_full_name ()));
359 return false;
362 right.value_type = f.field_type;
363 } else {
364 error = true;
365 Report.error (source_reference, "Assignment: Invalid callback assignment attempt");
366 return false;
370 if (left.value_type != null && right.value_type != null) {
371 /* if there was an error on either side,
372 * i.e. {left|right}.value_type == null, skip type check */
374 if (!right.value_type.compatible (left.value_type)) {
375 error = true;
376 Report.error (source_reference, "Assignment: Cannot convert from `%s' to `%s'".printf (right.value_type.to_string (), left.value_type.to_string ()));
377 return false;
380 if (!(ma.symbol_reference is Property)) {
381 if (right.value_type.is_disposable ()) {
382 /* rhs transfers ownership of the expression */
383 if (!(left.value_type is PointerType) && !left.value_type.value_owned) {
384 /* lhs doesn't own the value */
385 error = true;
386 Report.error (source_reference, "Invalid assignment from owned expression to unowned variable");
388 } else if (left.value_type.value_owned) {
389 /* lhs wants to own the value
390 * rhs doesn't transfer the ownership
391 * code generator needs to add reference
392 * increment calls */
397 var right_ma = right as MemberAccess;
398 if (right_ma != null && ma.symbol_reference == right_ma.symbol_reference) {
399 if (ma.symbol_reference is LocalVariable || ma.symbol_reference is FormalParameter) {
400 Report.warning (source_reference, "Assignment to same variable");
401 } else if (ma.symbol_reference is Field) {
402 var f = (Field) ma.symbol_reference;
403 if (f.binding == MemberBinding.STATIC) {
404 Report.warning (source_reference, "Assignment to same variable");
405 } else {
406 var ma_inner = ma.inner as MemberAccess;
407 var right_ma_inner = right_ma.inner as MemberAccess;
408 if (ma_inner != null && ma_inner.member_name == "this" && ma_inner.inner == null &&
409 right_ma_inner != null && right_ma_inner.member_name == "this" && right_ma_inner.inner == null) {
410 Report.warning (source_reference, "Assignment to same variable");
415 } else if (left is ElementAccess) {
416 var ea = (ElementAccess) left;
418 if (!right.value_type.compatible (left.value_type)) {
419 error = true;
420 Report.error (source_reference, "Assignment: Cannot convert from `%s' to `%s'".printf (right.value_type.to_string (), left.value_type.to_string ()));
421 return false;
424 if (right.value_type.is_disposable ()) {
425 /* rhs transfers ownership of the expression */
427 DataType element_type;
429 if (ea.container.value_type is ArrayType) {
430 var array_type = (ArrayType) ea.container.value_type;
431 element_type = array_type.element_type;
432 } else {
433 var args = ea.container.value_type.get_type_arguments ();
434 assert (args.size == 1);
435 element_type = args.get (0);
438 if (!(element_type is PointerType) && !element_type.value_owned) {
439 /* lhs doesn't own the value */
440 error = true;
441 Report.error (source_reference, "Invalid assignment from owned expression to unowned variable");
442 return false;
444 } else if (left.value_type.value_owned) {
445 /* lhs wants to own the value
446 * rhs doesn't transfer the ownership
447 * code generator needs to add reference
448 * increment calls */
450 } else {
451 return true;
454 if (left.value_type != null) {
455 value_type = left.value_type.copy ();
456 value_type.value_owned = false;
457 } else {
458 value_type = null;
461 add_error_types (left.get_error_types ());
462 add_error_types (right.get_error_types ());
464 return !error;
467 public override void get_defined_variables (Collection<LocalVariable> collection) {
468 right.get_defined_variables (collection);
469 left.get_defined_variables (collection);
470 var local = left.symbol_reference as LocalVariable;
471 if (local != null) {
472 collection.add (local);
476 public override void get_used_variables (Collection<LocalVariable> collection) {
477 var ma = left as MemberAccess;
478 var ea = left as ElementAccess;
479 if (ma != null && ma.inner != null) {
480 ma.inner.get_used_variables (collection);
481 } else if (ea != null) {
482 ea.get_used_variables (collection);
484 right.get_used_variables (collection);
488 public enum Vala.AssignmentOperator {
489 NONE,
490 SIMPLE,
491 BITWISE_OR,
492 BITWISE_AND,
493 BITWISE_XOR,
494 ADD,
495 SUB,
496 MUL,
497 DIV,
498 PERCENT,
499 SHIFT_LEFT,
500 SHIFT_RIGHT