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
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 (SemanticAnalyzer analyzer
) {
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
);
116 ExpressionStatement stmt
= null;
117 foreach (var expr
in tuple
.get_expressions ()) {
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
);
132 analyzer
.replaced_nodes
.add (this
);
133 parent_node
.replace_expression (this
, stmt
.expression
);
134 return stmt
.expression
.check (analyzer
);
139 if (!left
.check (analyzer
)) {
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" && 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 (!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
));
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
) {
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"));
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
);
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 (analyzer
)) {
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 (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
;
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 (FormalParameter 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
&& !(analyzer
.find_current_method () is CreationMethod
|| analyzer
.is_in_constructor ()))) {
311 Report
.error (ma
.source_reference
, "Property `%s' is read-only".printf (prop
.get_full_name ()));
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 ()));
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
)) {
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 ()));
340 right
.value_type
= local
.variable_type
;
343 Report
.error (source_reference
, "Assignment: Invalid callback assignment attempt");
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
)) {
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 ()));
362 right
.value_type
= f
.field_type
;
365 Report
.error (source_reference
, "Assignment: Invalid callback assignment attempt");
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
)) {
376 Report
.error (source_reference
, "Assignment: Cannot convert from `%s' to `%s'".printf (right
.value_type
.to_string (), left
.value_type
.to_string ()));
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 */
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
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");
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
)) {
420 Report
.error (source_reference
, "Assignment: Cannot convert from `%s' to `%s'".printf (right
.value_type
.to_string (), left
.value_type
.to_string ()));
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
;
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 */
441 Report
.error (source_reference
, "Invalid assignment from owned expression to unowned variable");
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
454 if (left
.value_type
!= null) {
455 value_type
= left
.value_type
.copy ();
456 value_type
.value_owned
= false;
461 add_error_types (left
.get_error_types ());
462 add_error_types (right
.get_error_types ());
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
;
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
{