codegen: Use destroy_variable in store_*
[vala-lang.git] / vala / valaassignment.vala
blob9fb81c6b6d69e6fe84b52f237224946c296bc286
1 /* valaassignment.vala
3 * Copyright (C) 2006-2011 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 (CodeContext context) {
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 (context);
113 insert_statement (context.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 (context);
120 insert_statement (context.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 context.analyzer.replaced_nodes.add (this);
133 parent_node.replace_expression (this, stmt.expression);
134 return stmt.expression.check (context);
137 left.lvalue = true;
139 if (!left.check (context)) {
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" && context.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 (!context.deprecated) {
169 Report.warning (source_reference, "deprecated syntax, use `connect' method instead");
171 } else if (ma.symbol_reference is Signal) {
172 if (!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 == context.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", source_reference), source_reference);
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 (context);
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 (context)) {
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 (context);
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 (Parameter 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 && !(context.analyzer.find_current_method () is CreationMethod || context.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 (!context.deprecated
314 && !prop.set_accessor.writable
315 && context.analyzer.find_current_method () is CreationMethod) {
316 if (ma.inner.symbol_reference != context.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.error (ma.source_reference, "Cannot assign to construct-only properties, use Object (property: value) constructor chain up");
322 return false;
325 } else if (ma.symbol_reference is Variable && right.value_type == null) {
326 var variable = (Variable) ma.symbol_reference;
328 if (right.symbol_reference is Method &&
329 variable.variable_type is DelegateType) {
330 var m = (Method) right.symbol_reference;
331 var dt = (DelegateType) variable.variable_type;
332 var cb = dt.delegate_symbol;
334 /* check whether method matches callback type */
335 if (!cb.matches_method (m, dt)) {
336 error = true;
337 Report.error (source_reference, "declaration of method `%s' doesn't match declaration of callback `%s'".printf (m.get_full_name (), cb.get_full_name ()));
338 return false;
341 right.value_type = variable.variable_type;
342 } else {
343 error = true;
344 Report.error (source_reference, "Assignment: Invalid assignment attempt");
345 return false;
349 if (left.value_type != null && right.value_type != null) {
350 /* if there was an error on either side,
351 * i.e. {left|right}.value_type == null, skip type check */
353 if (!right.value_type.compatible (left.value_type)) {
354 error = true;
355 Report.error (source_reference, "Assignment: Cannot convert from `%s' to `%s'".printf (right.value_type.to_string (), left.value_type.to_string ()));
356 return false;
359 if (!(ma.symbol_reference is Property)) {
360 if (right.value_type.is_disposable ()) {
361 /* rhs transfers ownership of the expression */
362 if (!(left.value_type is PointerType) && !left.value_type.value_owned) {
363 /* lhs doesn't own the value */
364 error = true;
365 Report.error (source_reference, "Invalid assignment from owned expression to unowned variable");
367 } else if (left.value_type.value_owned) {
368 /* lhs wants to own the value
369 * rhs doesn't transfer the ownership
370 * code generator needs to add reference
371 * increment calls */
376 var right_ma = right as MemberAccess;
377 if (right_ma != null && ma.symbol_reference == right_ma.symbol_reference) {
378 if (ma.symbol_reference is LocalVariable || ma.symbol_reference is Parameter) {
379 Report.warning (source_reference, "Assignment to same variable");
380 } else if (ma.symbol_reference is Field) {
381 var f = (Field) ma.symbol_reference;
382 if (f.binding == MemberBinding.STATIC) {
383 Report.warning (source_reference, "Assignment to same variable");
384 } else {
385 var ma_inner = ma.inner as MemberAccess;
386 var right_ma_inner = right_ma.inner as MemberAccess;
387 if (ma_inner != null && ma_inner.member_name == "this" && ma_inner.inner == null &&
388 right_ma_inner != null && right_ma_inner.member_name == "this" && right_ma_inner.inner == null) {
389 Report.warning (source_reference, "Assignment to same variable");
394 } else if (left is ElementAccess) {
395 var ea = (ElementAccess) left;
397 if (!right.value_type.compatible (left.value_type)) {
398 error = true;
399 Report.error (source_reference, "Assignment: Cannot convert from `%s' to `%s'".printf (right.value_type.to_string (), left.value_type.to_string ()));
400 return false;
403 if (right.value_type.is_disposable ()) {
404 /* rhs transfers ownership of the expression */
406 DataType element_type;
408 if (ea.container.value_type is ArrayType) {
409 var array_type = (ArrayType) ea.container.value_type;
410 element_type = array_type.element_type;
411 } else {
412 var args = ea.container.value_type.get_type_arguments ();
413 assert (args.size == 1);
414 element_type = args.get (0);
417 if (!(element_type is PointerType) && !element_type.value_owned) {
418 /* lhs doesn't own the value */
419 error = true;
420 Report.error (source_reference, "Invalid assignment from owned expression to unowned variable");
421 return false;
423 } else if (left.value_type.value_owned) {
424 /* lhs wants to own the value
425 * rhs doesn't transfer the ownership
426 * code generator needs to add reference
427 * increment calls */
429 } else {
430 return true;
433 if (left.value_type != null) {
434 value_type = left.value_type.copy ();
435 value_type.value_owned = false;
436 } else {
437 value_type = null;
440 add_error_types (left.get_error_types ());
441 add_error_types (right.get_error_types ());
443 return !error;
446 bool is_array_add () {
447 var binary = right as BinaryExpression;
448 if (binary != null && binary.left.value_type is ArrayType) {
449 if (binary.operator == BinaryOperator.PLUS) {
450 if (left.symbol_reference == binary.left.symbol_reference) {
451 return true;
456 return false;
459 public override void emit (CodeGenerator codegen) {
460 var ma = left as MemberAccess;
461 var ea = left as ElementAccess;
462 var pi = left as PointerIndirection;
463 if (ma != null) {
464 var local = ma.symbol_reference as LocalVariable;
465 var param = ma.symbol_reference as Parameter;
466 var field = ma.symbol_reference as Field;
467 var property = ma.symbol_reference as Property;
469 bool instance = (field != null && field.binding != MemberBinding.STATIC)
470 || (property != null && property.binding != MemberBinding.STATIC);
472 if (operator == AssignmentOperator.SIMPLE &&
473 (local != null || param != null || field != null) &&
474 !is_array_add ()) {
475 // visit_assignment not necessary
476 if (instance && ma.inner != null) {
477 ma.inner.emit (codegen);
480 right.emit (codegen);
481 var new_value = right.target_value;
483 if (local != null) {
484 codegen.store_local (local, new_value, false);
485 } else if (param != null) {
486 codegen.store_parameter (param, new_value);
487 } else if (field != null) {
488 codegen.store_field (field, instance ? ma.inner : null, new_value);
491 // when load_variable is changed to use temporary
492 // variables, replace following code with this line
493 // target_value = new_value;
494 if (local != null) {
495 target_value = codegen.load_local (local);
496 } else if (param != null) {
497 target_value = codegen.load_parameter (param);
498 } else if (field != null) {
499 target_value = codegen.load_field (field, instance ? ma.inner : null);
502 codegen.visit_expression (this);
503 return;
506 if (field != null) {
507 // always process full lvalue
508 // current codegen depends on it
509 // should be removed when moving codegen from
510 // visit_assignment to emit_store_field
511 ma.emit (codegen);
512 } else if (instance && ma.inner != null) {
513 ma.inner.emit (codegen);
515 } else if (ea != null) {
516 // always process full lvalue
517 // current codegen depends on it
518 // should be removed when moving codegen from
519 // visit_assignment to emit_store_element
520 ea.emit (codegen);
521 } else if (pi != null) {
522 // always process full lvalue
523 // current codegen depends on it
524 // should be removed when moving codegen from
525 // visit_assignment to emit_store_indirectZ
526 pi.emit (codegen);
529 right.emit (codegen);
531 codegen.visit_assignment (this);
533 codegen.visit_expression (this);
536 public override void get_defined_variables (Collection<LocalVariable> collection) {
537 right.get_defined_variables (collection);
538 left.get_defined_variables (collection);
539 var local = left.symbol_reference as LocalVariable;
540 if (local != null) {
541 collection.add (local);
545 public override void get_used_variables (Collection<LocalVariable> collection) {
546 var ma = left as MemberAccess;
547 var ea = left as ElementAccess;
548 if (ma != null && ma.inner != null) {
549 ma.inner.get_used_variables (collection);
550 } else if (ea != null) {
551 ea.get_used_variables (collection);
553 right.get_used_variables (collection);
557 public enum Vala.AssignmentOperator {
558 NONE,
559 SIMPLE,
560 BITWISE_OR,
561 BITWISE_AND,
562 BITWISE_XOR,
563 ADD,
564 SUB,
565 MUL,
566 DIV,
567 PERCENT,
568 SHIFT_LEFT,
569 SHIFT_RIGHT