gtk+-4.0: Update to 3.93.0+f4c1a404
[vala-gnome.git] / vala / valaassignment.vala
blob1c62fcdc903e9a12817f21a7581ebf894cf6f6b7
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 is_accessible (Symbol sym) {
101 return left.is_accessible (sym) && right.is_accessible (sym);
104 public override bool check (CodeContext context) {
105 if (checked) {
106 return !error;
109 checked = true;
111 if (left is Tuple && operator == AssignmentOperator.SIMPLE && parent_node is ExpressionStatement) {
112 var tuple = (Tuple) left;
114 var local = new LocalVariable (null, get_temp_name (), right, right.source_reference);
115 var decl = new DeclarationStatement (local, source_reference);
116 decl.check (context);
117 insert_statement (context.analyzer.insert_block, decl);
119 int i = 0;
120 ExpressionStatement stmt = null;
121 foreach (var expr in tuple.get_expressions ()) {
122 if (stmt != null) {
123 stmt.check (context);
124 insert_statement (context.analyzer.insert_block, stmt);
127 var temp_access = new MemberAccess.simple (local.name, right.source_reference);
128 var ea = new ElementAccess (temp_access, expr.source_reference);
129 ea.append_index (new IntegerLiteral (i.to_string (), expr.source_reference));
130 var assign = new Assignment (expr, ea, operator, expr.source_reference);
131 stmt = new ExpressionStatement (assign, expr.source_reference);
133 i++;
136 context.analyzer.replaced_nodes.add (this);
137 parent_node.replace_expression (this, stmt.expression);
138 return stmt.expression.check (context);
141 left.lvalue = true;
143 if (!left.check (context)) {
144 // skip on error in inner expression
145 error = true;
146 return false;
149 if (left is MemberAccess) {
150 var ma = (MemberAccess) left;
152 if (ma.symbol_reference is Constant) {
153 error = true;
154 Report.error (source_reference, "Assignment to constant after initialization");
155 return false;
158 if ((!(ma.symbol_reference is Signal || ma.symbol_reference is DynamicProperty) && ma.value_type == null) ||
159 (ma.inner == null && ma.member_name == "this" && context.analyzer.is_in_instance_method ())) {
160 error = true;
161 Report.error (source_reference, "unsupported lvalue in assignment");
162 return false;
164 if (ma.prototype_access) {
165 error = true;
166 Report.error (source_reference, "Access to instance member `%s' denied".printf (ma.symbol_reference.get_full_name ()));
167 return false;
170 if (ma.error || ma.symbol_reference == null) {
171 error = true;
172 /* if no symbol found, skip this check */
173 return false;
176 if (ma.symbol_reference is DynamicSignal) {
177 // target_type not available for dynamic signals
178 if (!context.deprecated) {
179 Report.warning (source_reference, "deprecated syntax, use `connect' method instead");
181 } else if (ma.symbol_reference is Signal) {
182 if (!context.deprecated) {
183 Report.warning (source_reference, "deprecated syntax, use `connect' method instead");
185 var sig = (Signal) ma.symbol_reference;
186 right.target_type = new DelegateType (sig.get_delegate (ma.inner.value_type, this));
187 } else if (ma.symbol_reference is DynamicProperty) {
188 // target_type not available for dynamic properties
189 } else {
190 right.formal_target_type = ma.formal_value_type.copy ();
191 right.target_type = ma.value_type.copy ();
193 } else if (left is ElementAccess) {
194 var ea = (ElementAccess) left;
196 if (ea.container.value_type.data_type == context.analyzer.string_type.data_type) {
197 error = true;
198 Report.error (ea.source_reference, "strings are immutable");
199 return false;
200 } else if (ea.container is MemberAccess && ea.container.symbol_reference is Signal) {
201 var ma = (MemberAccess) ea.container;
202 var sig = (Signal) ea.container.symbol_reference;
203 right.target_type = new DelegateType (sig.get_delegate (ma.inner.value_type, this));
204 } else if (ea.container.value_type.get_member ("set") is Method) {
205 var set_call = new MethodCall (new MemberAccess (ea.container, "set", source_reference), source_reference);
206 foreach (Expression e in ea.get_indices ()) {
207 set_call.add_argument (e);
209 set_call.add_argument (right);
210 parent_node.replace_expression (this, set_call);
211 return set_call.check (context);
212 } else {
213 right.target_type = left.value_type;
215 } else if (left is PointerIndirection) {
216 right.target_type = left.value_type;
217 } else {
218 error = true;
219 Report.error (source_reference, "unsupported lvalue in assignment");
220 return false;
223 if (!right.check (context)) {
224 // skip on error in inner expression
225 error = true;
226 return false;
229 if (operator != AssignmentOperator.SIMPLE && left is MemberAccess) {
230 // transform into simple assignment
231 // FIXME: only do this if the backend doesn't support
232 // the assignment natively
234 var ma = (MemberAccess) left;
236 if (!(ma.symbol_reference is Signal)) {
237 var old_value = new MemberAccess (ma.inner, ma.member_name);
239 var bin = new BinaryExpression (BinaryOperator.PLUS, old_value, right, source_reference);
240 bin.target_type = right.target_type;
241 right.target_type = right.target_type.copy ();
242 right.target_type.value_owned = false;
244 switch (operator) {
245 case AssignmentOperator.BITWISE_OR: bin.operator = BinaryOperator.BITWISE_OR; break;
246 case AssignmentOperator.BITWISE_AND: bin.operator = BinaryOperator.BITWISE_AND; break;
247 case AssignmentOperator.BITWISE_XOR: bin.operator = BinaryOperator.BITWISE_XOR; break;
248 case AssignmentOperator.ADD: bin.operator = BinaryOperator.PLUS; break;
249 case AssignmentOperator.SUB: bin.operator = BinaryOperator.MINUS; break;
250 case AssignmentOperator.MUL: bin.operator = BinaryOperator.MUL; break;
251 case AssignmentOperator.DIV: bin.operator = BinaryOperator.DIV; break;
252 case AssignmentOperator.PERCENT: bin.operator = BinaryOperator.MOD; break;
253 case AssignmentOperator.SHIFT_LEFT: bin.operator = BinaryOperator.SHIFT_LEFT; break;
254 case AssignmentOperator.SHIFT_RIGHT: bin.operator = BinaryOperator.SHIFT_RIGHT; break;
257 right = bin;
258 right.check (context);
260 operator = AssignmentOperator.SIMPLE;
264 if (left.symbol_reference is Signal) {
265 var sig = (Signal) left.symbol_reference;
267 var m = right.symbol_reference as Method;
269 if (m == null) {
270 error = true;
271 Report.error (right.source_reference, "unsupported expression for signal handler");
272 return false;
275 var dynamic_sig = sig as DynamicSignal;
276 var right_ma = right as MemberAccess;
277 if (dynamic_sig != null) {
278 bool first = true;
279 foreach (Parameter param in dynamic_sig.handler.value_type.get_parameters ()) {
280 if (first) {
281 // skip sender parameter
282 first = false;
283 } else {
284 dynamic_sig.add_parameter (param.copy ());
287 right.target_type = new DelegateType (sig.get_delegate (new ObjectType ((ObjectTypeSymbol) sig.parent_symbol), this));
288 } else if (!right.value_type.compatible (right.target_type)) {
289 var delegate_type = (DelegateType) right.target_type;
291 error = true;
292 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.to_prototype_string (m.name)));
293 return false;
294 } else if (right_ma != null && right_ma.prototype_access) {
295 error = true;
296 Report.error (right.source_reference, "Access to instance member `%s' denied".printf (m.get_full_name ()));
297 return false;
299 } else if (left is MemberAccess) {
300 var ma = (MemberAccess) left;
302 if (ma.symbol_reference is Property) {
303 var prop = (Property) ma.symbol_reference;
305 var dynamic_prop = prop as DynamicProperty;
306 if (dynamic_prop != null) {
307 dynamic_prop.property_type = right.value_type.copy ();
308 left.value_type = dynamic_prop.property_type.copy ();
311 if (prop.set_accessor == null
312 || (!prop.set_accessor.writable && !(context.analyzer.find_current_method () is CreationMethod || context.analyzer.is_in_constructor ()))) {
313 ma.error = true;
314 Report.error (ma.source_reference, "Property `%s' is read-only".printf (prop.get_full_name ()));
315 return false;
316 } else if (!context.deprecated
317 && !prop.set_accessor.writable
318 && context.analyzer.find_current_method () is CreationMethod) {
319 if (ma.inner.symbol_reference != context.analyzer.find_current_method ().this_parameter) {
320 // trying to set construct-only property in creation method for foreign instance
321 Report.error (ma.source_reference, "Property `%s' is read-only".printf (prop.get_full_name ()));
322 return false;
323 } else {
324 Report.error (ma.source_reference, "Cannot assign to construct-only properties, use Object (property: value) constructor chain up");
325 return false;
328 } else if (ma.symbol_reference is Variable && right.value_type == null) {
329 var variable = (Variable) ma.symbol_reference;
331 if (right.symbol_reference is Method &&
332 variable.variable_type is DelegateType) {
333 var m = (Method) right.symbol_reference;
334 var dt = (DelegateType) variable.variable_type;
335 var cb = dt.delegate_symbol;
337 /* check whether method matches callback type */
338 if (!cb.matches_method (m, dt)) {
339 error = true;
340 Report.error (source_reference, "declaration of method `%s' doesn't match declaration of callback `%s'".printf (m.get_full_name (), cb.get_full_name ()));
341 return false;
344 right.value_type = variable.variable_type;
345 } else {
346 error = true;
347 Report.error (source_reference, "Assignment: Invalid assignment attempt");
348 return false;
352 if (left.value_type != null && right.value_type != null) {
353 /* if there was an error on either side,
354 * i.e. {left|right}.value_type == null, skip type check */
356 if (!right.value_type.compatible (left.value_type)) {
357 error = true;
358 Report.error (source_reference, "Assignment: Cannot convert from `%s' to `%s'".printf (right.value_type.to_string (), left.value_type.to_string ()));
359 return false;
362 if (!(ma.symbol_reference is Property)) {
363 if (right.value_type.is_disposable ()) {
364 /* rhs transfers ownership of the expression */
365 if (!(left.value_type is PointerType) && !left.value_type.value_owned) {
366 /* lhs doesn't own the value */
367 error = true;
368 Report.error (source_reference, "Invalid assignment from owned expression to unowned variable");
370 } else if (left.value_type.value_owned) {
371 /* lhs wants to own the value
372 * rhs doesn't transfer the ownership
373 * code generator needs to add reference
374 * increment calls */
379 var right_ma = right as MemberAccess;
380 if (right_ma != null && ma.symbol_reference == right_ma.symbol_reference) {
381 if (ma.symbol_reference is LocalVariable || ma.symbol_reference is Parameter) {
382 Report.warning (source_reference, "Assignment to same variable");
383 } else if (ma.symbol_reference is Field) {
384 var f = (Field) ma.symbol_reference;
385 if (f.binding == MemberBinding.STATIC) {
386 Report.warning (source_reference, "Assignment to same variable");
387 } else {
388 var ma_inner = ma.inner as MemberAccess;
389 var right_ma_inner = right_ma.inner as MemberAccess;
390 if (ma_inner != null && ma_inner.member_name == "this" && ma_inner.inner == null &&
391 right_ma_inner != null && right_ma_inner.member_name == "this" && right_ma_inner.inner == null) {
392 Report.warning (source_reference, "Assignment to same variable");
397 } else if (left is ElementAccess) {
398 var ea = (ElementAccess) left;
400 if (!right.value_type.compatible (left.value_type)) {
401 error = true;
402 Report.error (source_reference, "Assignment: Cannot convert from `%s' to `%s'".printf (right.value_type.to_string (), left.value_type.to_string ()));
403 return false;
406 if (right.value_type.is_disposable ()) {
407 /* rhs transfers ownership of the expression */
409 DataType element_type;
411 if (ea.container.value_type is ArrayType) {
412 var array_type = (ArrayType) ea.container.value_type;
413 element_type = array_type.element_type;
414 } else {
415 var args = ea.container.value_type.get_type_arguments ();
416 assert (args.size == 1);
417 element_type = args.get (0);
420 if (!(element_type is PointerType) && !element_type.value_owned) {
421 /* lhs doesn't own the value */
422 error = true;
423 Report.error (source_reference, "Invalid assignment from owned expression to unowned variable");
424 return false;
426 } else if (left.value_type.value_owned) {
427 /* lhs wants to own the value
428 * rhs doesn't transfer the ownership
429 * code generator needs to add reference
430 * increment calls */
432 } else {
433 return true;
436 if (left.value_type != null) {
437 value_type = left.value_type.copy ();
438 value_type.value_owned = false;
439 } else {
440 value_type = null;
443 add_error_types (left.get_error_types ());
444 add_error_types (right.get_error_types ());
446 return !error;
449 bool is_array_add () {
450 var binary = right as BinaryExpression;
451 if (binary != null && binary.left.value_type is ArrayType) {
452 if (binary.operator == BinaryOperator.PLUS) {
453 if (left.symbol_reference == binary.left.symbol_reference) {
454 return true;
459 return false;
462 public override void emit (CodeGenerator codegen) {
463 var ma = left as MemberAccess;
464 var ea = left as ElementAccess;
465 var pi = left as PointerIndirection;
466 if (ma != null) {
467 var local = ma.symbol_reference as LocalVariable;
468 var param = ma.symbol_reference as Parameter;
469 var field = ma.symbol_reference as Field;
470 var property = ma.symbol_reference as Property;
472 bool instance = (field != null && field.binding != MemberBinding.STATIC)
473 || (property != null && property.binding != MemberBinding.STATIC);
475 if (operator == AssignmentOperator.SIMPLE &&
476 (local != null || param != null || field != null) &&
477 !is_array_add () &&
478 !(field is ArrayLengthField) &&
479 !(left.value_type.is_real_non_null_struct_type () && right is ObjectCreationExpression)) {
480 // visit_assignment not necessary
481 if (instance && ma.inner != null) {
482 ma.inner.emit (codegen);
485 right.emit (codegen);
486 var new_value = right.target_value;
488 if (local != null) {
489 codegen.store_local (local, new_value, false, source_reference);
490 } else if (param != null) {
491 codegen.store_parameter (param, new_value, false, source_reference);
492 } else if (field != null) {
493 codegen.store_field (field, instance && ma.inner != null ? ma.inner.target_value : null, new_value, source_reference);
496 if (!(parent_node is ExpressionStatement)) {
497 // when load_variable is changed to use temporary
498 // variables, replace following code with this line
499 // target_value = new_value;
500 if (local != null) {
501 target_value = codegen.load_local (local);
502 } else if (param != null) {
503 target_value = codegen.load_parameter (param);
504 } else if (field != null) {
505 target_value = codegen.load_field (field, instance && ma.inner != null ? ma.inner.target_value : null);
509 codegen.visit_expression (this);
510 return;
513 if (instance && ma.inner != null && property != null) {
514 ma.inner.emit (codegen);
515 } else {
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_field/local/param
520 ma.emit (codegen);
522 } else if (ea != null) {
523 // always process full lvalue
524 // current codegen depends on it
525 // should be removed when moving codegen from
526 // visit_assignment to emit_store_element
527 ea.emit (codegen);
528 } else if (pi != null) {
529 // always process full lvalue
530 // current codegen depends on it
531 // should be removed when moving codegen from
532 // visit_assignment to emit_store_indirectZ
533 pi.emit (codegen);
536 right.emit (codegen);
538 codegen.visit_assignment (this);
540 codegen.visit_expression (this);
543 public override void get_defined_variables (Collection<Variable> collection) {
544 right.get_defined_variables (collection);
545 left.get_defined_variables (collection);
546 var local = left.symbol_reference as LocalVariable;
547 var param = left.symbol_reference as Parameter;
548 if (local != null) {
549 collection.add (local);
550 } else if (param != null && param.direction == ParameterDirection.OUT) {
551 collection.add (param);
555 public override void get_used_variables (Collection<Variable> collection) {
556 var ma = left as MemberAccess;
557 var ea = left as ElementAccess;
558 if (ma != null && ma.inner != null) {
559 ma.inner.get_used_variables (collection);
560 } else if (ea != null) {
561 ea.get_used_variables (collection);
563 right.get_used_variables (collection);
567 public enum Vala.AssignmentOperator {
568 NONE,
569 SIMPLE,
570 BITWISE_OR,
571 BITWISE_AND,
572 BITWISE_XOR,
573 ADD,
574 SUB,
575 MUL,
576 DIV,
577 PERCENT,
578 SHIFT_LEFT,
579 SHIFT_RIGHT