3 * Copyright (C) 2006-2009 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
) {
109 if (!left
.check (analyzer
)) {
110 // skip on error in inner expression
115 if (left is MemberAccess
) {
116 var ma
= (MemberAccess
) left
;
118 if (!(ma
.symbol_reference is Signal
|| ma
.symbol_reference is DynamicProperty
) && ma
.value_type
== null) {
120 Report
.error (source_reference
, "unsupported lvalue in assignment");
123 if (ma
.prototype_access
) {
125 Report
.error (source_reference
, "Access to instance member `%s' denied".printf (ma
.symbol_reference
.get_full_name ()));
129 if (ma
.error
|| ma
.symbol_reference
== null) {
131 /* if no symbol found, skip this check */
135 if (ma
.symbol_reference is DynamicSignal
) {
136 // target_type not available for dynamic signals
137 } else if (ma
.symbol_reference is Signal
) {
138 var sig
= (Signal
) ma
.symbol_reference
;
139 right
.target_type
= new
DelegateType (sig
.get_delegate (ma
.inner
.value_type
, this
));
141 right
.formal_target_type
= ma
.formal_value_type
;
142 right
.target_type
= ma
.value_type
;
144 } else if (left is ElementAccess
) {
145 var ea
= (ElementAccess
) left
;
147 if (ea
.container
.value_type
.data_type
== analyzer
.string_type
.data_type
) {
149 Report
.error (ea
.source_reference
, "strings are immutable");
151 } else if (ea
.container is MemberAccess
&& ea
.container
.symbol_reference is Signal
) {
152 var ma
= (MemberAccess
) ea
.container
;
153 var sig
= (Signal
) ea
.container
.symbol_reference
;
154 right
.target_type
= new
DelegateType (sig
.get_delegate (ma
.inner
.value_type
, this
));
155 } else if (ea
.container
.value_type
.get_member ("set") is Method
) {
156 var set_call
= new
MethodCall (new
MemberAccess (ea
.container
, "set"));
157 foreach (Expression e
in ea
.get_indices ()) {
158 set_call
.add_argument (e
);
160 set_call
.add_argument (right
);
161 parent_node
.replace_expression (this
, set_call
);
162 return set_call
.check (analyzer
);
164 right
.target_type
= left
.value_type
;
166 } else if (left is PointerIndirection
) {
167 right
.target_type
= left
.value_type
;
170 Report
.error (source_reference
, "unsupported lvalue in assignment");
174 if (!right
.check (analyzer
)) {
175 // skip on error in inner expression
180 if (operator
!= AssignmentOperator
.SIMPLE
&& left is MemberAccess
) {
181 // transform into simple assignment
182 // FIXME: only do this if the backend doesn't support
183 // the assignment natively
185 var ma
= (MemberAccess
) left
;
187 if (!(ma
.symbol_reference is Signal
)) {
188 var old_value
= new
MemberAccess (ma
.inner
, ma
.member_name
);
190 var bin
= new
BinaryExpression (BinaryOperator
.PLUS
, old_value
, right
);
191 bin
.target_type
= right
.target_type
;
192 right
.target_type
= right
.target_type
.copy ();
193 right
.target_type
.value_owned
= false;
195 if (operator
== AssignmentOperator
.BITWISE_OR
) {
196 bin
.operator
= BinaryOperator
.BITWISE_OR
;
197 } else if (operator
== AssignmentOperator
.BITWISE_AND
) {
198 bin
.operator
= BinaryOperator
.BITWISE_AND
;
199 } else if (operator
== AssignmentOperator
.BITWISE_XOR
) {
200 bin
.operator
= BinaryOperator
.BITWISE_XOR
;
201 } else if (operator
== AssignmentOperator
.ADD
) {
202 bin
.operator
= BinaryOperator
.PLUS
;
203 } else if (operator
== AssignmentOperator
.SUB
) {
204 bin
.operator
= BinaryOperator
.MINUS
;
205 } else if (operator
== AssignmentOperator
.MUL
) {
206 bin
.operator
= BinaryOperator
.MUL
;
207 } else if (operator
== AssignmentOperator
.DIV
) {
208 bin
.operator
= BinaryOperator
.DIV
;
209 } else if (operator
== AssignmentOperator
.PERCENT
) {
210 bin
.operator
= BinaryOperator
.MOD
;
211 } else if (operator
== AssignmentOperator
.SHIFT_LEFT
) {
212 bin
.operator
= BinaryOperator
.SHIFT_LEFT
;
213 } else if (operator
== AssignmentOperator
.SHIFT_RIGHT
) {
214 bin
.operator
= BinaryOperator
.SHIFT_RIGHT
;
218 right
.check (analyzer
);
220 operator
= AssignmentOperator
.SIMPLE
;
224 if (left
.symbol_reference is Signal
) {
225 var sig
= (Signal
) left
.symbol_reference
;
227 var m
= right
.symbol_reference as Method
;
231 Report
.error (right
.source_reference
, "unsupported expression for signal handler");
235 var dynamic_sig
= sig as DynamicSignal
;
236 var right_ma
= right as MemberAccess
;
237 if (dynamic_sig
!= null) {
239 foreach (FormalParameter param
in dynamic_sig
.handler
.value_type
.get_parameters ()) {
241 // skip sender parameter
244 dynamic_sig
.add_parameter (param
.copy ());
247 right
.target_type
= new
DelegateType (sig
.get_delegate (new
ObjectType ((ObjectTypeSymbol
) sig
.parent_symbol
), this
));
248 } else if (!right
.value_type
.compatible (right
.target_type
)) {
249 var delegate_type
= (DelegateType
) right
.target_type
;
252 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
)));
254 } else if (right_ma
!= null && right_ma
.prototype_access
) {
256 Report
.error (right
.source_reference
, "Access to instance member `%s' denied".printf (m
.get_full_name ()));
259 } else if (left is MemberAccess
) {
260 var ma
= (MemberAccess
) left
;
262 if (ma
.symbol_reference is Property
) {
263 var prop
= (Property
) ma
.symbol_reference
;
265 var dynamic_prop
= prop as DynamicProperty
;
266 if (dynamic_prop
!= null) {
267 dynamic_prop
.property_type
= right
.value_type
.copy ();
268 left
.value_type
= dynamic_prop
.property_type
.copy ();
271 if (prop
.set_accessor
== null
272 || (!prop
.set_accessor
.writable
&& !(analyzer
.find_current_method () is CreationMethod
|| analyzer
.is_in_constructor ()))) {
274 Report
.error (ma
.source_reference
, "Property `%s' is read-only".printf (prop
.get_full_name ()));
276 } else if (!analyzer
.context
.deprecated
277 && !prop
.set_accessor
.writable
278 && analyzer
.find_current_method () is CreationMethod
) {
279 Report
.warning (ma
.source_reference
, "assigning to construct-only properties is deprecated, use Object (property: value) constructor chain up");
281 } else if (ma
.symbol_reference is LocalVariable
&& right
.value_type
== null) {
282 var local
= (LocalVariable
) ma
.symbol_reference
;
284 if (right
.symbol_reference is Method
&&
285 local
.variable_type is DelegateType
) {
286 var m
= (Method
) right
.symbol_reference
;
287 var dt
= (DelegateType
) local
.variable_type
;
288 var cb
= dt
.delegate_symbol
;
290 /* check whether method matches callback type */
291 if (!cb
.matches_method (m
)) {
293 Report
.error (source_reference
, "declaration of method `%s' doesn't match declaration of callback `%s'".printf (m
.get_full_name (), cb
.get_full_name ()));
297 right
.value_type
= local
.variable_type
;
300 Report
.error (source_reference
, "Assignment: Invalid callback assignment attempt");
303 } else if (ma
.symbol_reference is Field
&& right
.value_type
== null) {
304 var f
= (Field
) ma
.symbol_reference
;
306 if (right
.symbol_reference is Method
&&
307 f
.field_type is DelegateType
) {
308 var m
= (Method
) right
.symbol_reference
;
309 var dt
= (DelegateType
) f
.field_type
;
310 var cb
= dt
.delegate_symbol
;
312 /* check whether method matches callback type */
313 if (!cb
.matches_method (m
)) {
315 Report
.error (source_reference
, "declaration of method `%s' doesn't match declaration of callback `%s'".printf (m
.get_full_name (), cb
.get_full_name ()));
319 right
.value_type
= f
.field_type
;
322 Report
.error (source_reference
, "Assignment: Invalid callback assignment attempt");
327 if (left
.value_type
!= null && right
.value_type
!= null) {
328 /* if there was an error on either side,
329 * i.e. {left|right}.value_type == null, skip type check */
331 if (!right
.value_type
.compatible (left
.value_type
)) {
333 Report
.error (source_reference
, "Assignment: Cannot convert from `%s' to `%s'".printf (right
.value_type
.to_string (), left
.value_type
.to_string ()));
337 if (!(ma
.symbol_reference is Property
)) {
338 if (right
.value_type
.is_disposable ()) {
339 /* rhs transfers ownership of the expression */
340 if (!(left
.value_type is PointerType
) && !left
.value_type
.value_owned
) {
341 /* lhs doesn't own the value */
343 Report
.error (source_reference
, "Invalid assignment from owned expression to unowned variable");
345 } else if (left
.value_type
.value_owned
) {
346 /* lhs wants to own the value
347 * rhs doesn't transfer the ownership
348 * code generator needs to add reference
353 } else if (left is ElementAccess
) {
354 var ea
= (ElementAccess
) left
;
356 if (!right
.value_type
.compatible (left
.value_type
)) {
358 Report
.error (source_reference
, "Assignment: Cannot convert from `%s' to `%s'".printf (right
.value_type
.to_string (), left
.value_type
.to_string ()));
362 if (right
.value_type
.is_disposable ()) {
363 /* rhs transfers ownership of the expression */
365 DataType element_type
;
367 if (ea
.container
.value_type is ArrayType
) {
368 var array_type
= (ArrayType
) ea
.container
.value_type
;
369 element_type
= array_type
.element_type
;
371 var args
= ea
.container
.value_type
.get_type_arguments ();
372 assert (args
.size
== 1);
373 element_type
= args
.get (0);
376 if (!(element_type is PointerType
) && !element_type
.value_owned
) {
377 /* lhs doesn't own the value */
379 Report
.error (source_reference
, "Invalid assignment from owned expression to unowned variable");
382 } else if (left
.value_type
.value_owned
) {
383 /* lhs wants to own the value
384 * rhs doesn't transfer the ownership
385 * code generator needs to add reference
392 if (left
.value_type
!= null) {
393 value_type
= left
.value_type
.copy ();
394 value_type
.value_owned
= false;
399 add_error_types (left
.get_error_types ());
400 add_error_types (right
.get_error_types ());
405 public override void get_defined_variables (Collection
<LocalVariable
> collection
) {
406 right
.get_defined_variables (collection
);
407 left
.get_defined_variables (collection
);
408 var local
= left
.symbol_reference as LocalVariable
;
410 collection
.add (local
);
414 public override void get_used_variables (Collection
<LocalVariable
> collection
) {
415 var ma
= left as MemberAccess
;
416 var ea
= left as ElementAccess
;
417 if (ma
!= null && ma
.inner
!= null) {
418 ma
.inner
.get_used_variables (collection
);
419 } else if (ea
!= null) {
420 ea
.get_used_variables (collection
);
422 right
.get_used_variables (collection
);
426 public enum Vala
.AssignmentOperator
{