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
20
* JĆ¼rg Billeter <j@bitron.ch>
25
* Represents an assignment expression in the source code.
27
* Supports =, |=, &=, ^=, +=, -=, *=, /=, %=, <<=, >>=.
29
public class Vala.Assignment : Expression {
31
* Left hand side of the assignment.
33
public Expression left {
37
_left.parent_node = this;
42
* Assignment operator.
44
public AssignmentOperator operator { get; set; }
47
* Right hand side of the assignment.
49
public Expression right {
50
get { return _right; }
53
_right.parent_node = this;
57
private Expression _left;
58
private Expression _right;
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) {
71
this.operator = operator;
72
this.source_reference = source_reference;
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) {
91
if (right == old_node) {
96
public override bool is_pure () {
100
public override bool check (CodeContext context) {
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);
116
ExpressionStatement stmt = null;
117
foreach (var expr in tuple.get_expressions ()) {
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);
132
context.analyzer.replaced_nodes.add (this);
133
parent_node.replace_expression (this, stmt.expression);
134
return stmt.expression.check (context);
139
if (!left.check (context)) {
140
// skip on error in inner expression
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 ())) {
151
Report.error (source_reference, "unsupported lvalue in assignment");
154
if (ma.prototype_access) {
156
Report.error (source_reference, "Access to instance member `%s' denied".printf (ma.symbol_reference.get_full_name ()));
160
if (ma.error || ma.symbol_reference == null) {
162
/* if no symbol found, skip this check */
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));
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) {
186
Report.error (ea.source_reference, "strings are immutable");
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);
201
right.target_type = left.value_type;
203
} else if (left is PointerIndirection) {
204
right.target_type = left.value_type;
207
Report.error (source_reference, "unsupported lvalue in assignment");
211
if (!right.check (context)) {
212
// skip on error in inner expression
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;
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;
268
Report.error (right.source_reference, "unsupported expression for signal handler");
272
var dynamic_sig = sig as DynamicSignal;
273
var right_ma = right as MemberAccess;
274
if (dynamic_sig != null) {
276
foreach (Parameter param in dynamic_sig.handler.value_type.get_parameters ()) {
278
// skip sender parameter
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;
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)));
291
} else if (right_ma != null && right_ma.prototype_access) {
293
Report.error (right.source_reference, "Access to instance member `%s' denied".printf (m.get_full_name ()));
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 ()))) {
311
Report.error (ma.source_reference, "Property `%s' is read-only".printf (prop.get_full_name ()));
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 ()));
321
Report.error (ma.source_reference, "Cannot assign to construct-only properties, use Object (property: value) constructor chain up");
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)) {
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 ()));
341
right.value_type = variable.variable_type;
344
Report.error (source_reference, "Assignment: Invalid assignment attempt");
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)) {
355
Report.error (source_reference, "Assignment: Cannot convert from `%s' to `%s'".printf (right.value_type.to_string (), left.value_type.to_string ()));
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 */
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
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");
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)) {
399
Report.error (source_reference, "Assignment: Cannot convert from `%s' to `%s'".printf (right.value_type.to_string (), left.value_type.to_string ()));
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;
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 */
420
Report.error (source_reference, "Invalid assignment from owned expression to unowned variable");
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
433
if (left.value_type != null) {
434
value_type = left.value_type.copy ();
435
value_type.value_owned = false;
440
add_error_types (left.get_error_types ());
441
add_error_types (right.get_error_types ());
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) {
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;
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) &&
475
!(field is ArrayLengthField) &&
476
!(left.value_type.is_real_non_null_struct_type () && right is ObjectCreationExpression)) {
477
// visit_assignment not necessary
478
if (instance && ma.inner != null) {
479
ma.inner.emit (codegen);
482
right.emit (codegen);
483
var new_value = right.target_value;
486
codegen.store_local (local, new_value, false);
487
} else if (param != null) {
488
codegen.store_parameter (param, new_value);
489
} else if (field != null) {
490
codegen.store_field (field, instance && ma.inner != null ? ma.inner.target_value : null, new_value);
493
if (!(parent_node is ExpressionStatement)) {
494
// when load_variable is changed to use temporary
495
// variables, replace following code with this line
496
// target_value = new_value;
498
target_value = codegen.load_local (local);
499
} else if (param != null) {
500
target_value = codegen.load_parameter (param);
501
} else if (field != null) {
502
target_value = codegen.load_field (field, instance && ma.inner != null ? ma.inner.target_value : null);
506
codegen.visit_expression (this);
510
if (instance && ma.inner != null && property != null) {
511
ma.inner.emit (codegen);
513
// always process full lvalue
514
// current codegen depends on it
515
// should be removed when moving codegen from
516
// visit_assignment to emit_store_field/local/param
519
} else if (ea != null) {
520
// always process full lvalue
521
// current codegen depends on it
522
// should be removed when moving codegen from
523
// visit_assignment to emit_store_element
525
} else if (pi != null) {
526
// always process full lvalue
527
// current codegen depends on it
528
// should be removed when moving codegen from
529
// visit_assignment to emit_store_indirectZ
533
right.emit (codegen);
535
codegen.visit_assignment (this);
537
codegen.visit_expression (this);
540
public override void get_defined_variables (Collection<Variable> collection) {
541
right.get_defined_variables (collection);
542
left.get_defined_variables (collection);
543
var local = left.symbol_reference as LocalVariable;
544
var param = left.symbol_reference as Parameter;
546
collection.add (local);
547
} else if (param != null && param.direction == ParameterDirection.OUT) {
548
collection.add (param);
552
public override void get_used_variables (Collection<Variable> collection) {
553
var ma = left as MemberAccess;
554
var ea = left as ElementAccess;
555
if (ma != null && ma.inner != null) {
556
ma.inner.get_used_variables (collection);
557
} else if (ea != null) {
558
ea.get_used_variables (collection);
560
right.get_used_variables (collection);
564
public enum Vala.AssignmentOperator {