3 * Copyright (C) 2006-2008 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>
26 * Represents an assignment expression in the source code.
28 * Supports =, |=, &=, ^=, +=, -=, *=, /=, %=, <<=, >>=.
30 public class Vala
.Assignment
: Expression
{
32 * Left hand side of the assignment.
34 public Expression left
{
38 _left
.parent_node
= this
;
43 * Assignment operator.
45 public AssignmentOperator operator
{ get; set; }
48 * Right hand side of the assignment.
50 public Expression right
{
51 get { return _right
; }
54 _right
.parent_node
= this
;
58 private Expression _left
;
59 private Expression _right
;
62 * Creates a new assignment.
64 * @param left left hand side
65 * @param operator assignment operator
66 * @param right right hand side
67 * @param source_reference reference to source code
68 * @return newly created assignment
70 public Assignment (Expression left
, Expression right
, AssignmentOperator operator
= AssignmentOperator
.SIMPLE
, SourceReference? source_reference
= null) {
72 this
.operator
= operator
;
73 this
.source_reference
= source_reference
;
77 public override void accept (CodeVisitor visitor
) {
78 visitor
.visit_assignment (this
);
80 visitor
.visit_expression (this
);
83 public override void accept_children (CodeVisitor visitor
) {
84 left
.accept (visitor
);
85 right
.accept (visitor
);
88 public override void replace_expression (Expression old_node
, Expression new_node
) {
89 if (left
== old_node
) {
92 if (right
== old_node
) {
97 public override bool is_pure () {
101 public override bool check (SemanticAnalyzer analyzer
) {
110 if (!left
.check (analyzer
)) {
111 // skip on error in inner expression
116 if (left is MemberAccess
) {
117 var ma
= (MemberAccess
) left
;
119 if (!(ma
.symbol_reference is Signal
|| ma
.symbol_reference is DynamicProperty
) && ma
.value_type
== null) {
121 Report
.error (source_reference
, "unsupported lvalue in assignment");
124 if (ma
.prototype_access
) {
126 Report
.error (source_reference
, "Access to instance member `%s' denied".printf (ma
.symbol_reference
.get_full_name ()));
130 if (ma
.error
|| ma
.symbol_reference
== null) {
132 /* if no symbol found, skip this check */
136 if (ma
.symbol_reference is DynamicSignal
) {
137 // target_type not available for dynamic signals
138 } else if (ma
.symbol_reference is Signal
) {
139 var sig
= (Signal
) ma
.symbol_reference
;
140 right
.target_type
= new
DelegateType (sig
.get_delegate (ma
.inner
.value_type
, this
));
142 right
.target_type
= ma
.value_type
;
144 } else if (left is ElementAccess
) {
145 var ea
= (ElementAccess
) left
;
147 if (ea
.container is MemberAccess
&& ea
.container
.symbol_reference is Signal
) {
148 var ma
= (MemberAccess
) ea
.container
;
149 var sig
= (Signal
) ea
.container
.symbol_reference
;
150 right
.target_type
= new
DelegateType (sig
.get_delegate (ma
.inner
.value_type
, this
));
152 right
.target_type
= left
.value_type
;
154 } else if (left is PointerIndirection
) {
155 right
.target_type
= left
.value_type
;
158 Report
.error (source_reference
, "unsupported lvalue in assignment");
162 if (!right
.check (analyzer
)) {
163 // skip on error in inner expression
168 if (operator
!= AssignmentOperator
.SIMPLE
&& left is MemberAccess
) {
169 // transform into simple assignment
170 // FIXME: only do this if the backend doesn't support
171 // the assignment natively
173 var ma
= (MemberAccess
) left
;
175 if (!(ma
.symbol_reference is Signal
)) {
176 var old_value
= new
MemberAccess (ma
.inner
, ma
.member_name
);
178 var bin
= new
BinaryExpression (BinaryOperator
.PLUS
, old_value
, new
ParenthesizedExpression (right
, right
.source_reference
));
179 bin
.target_type
= right
.target_type
;
180 right
.target_type
= right
.target_type
.copy ();
181 right
.target_type
.value_owned
= false;
183 if (operator
== AssignmentOperator
.BITWISE_OR
) {
184 bin
.operator
= BinaryOperator
.BITWISE_OR
;
185 } else if (operator
== AssignmentOperator
.BITWISE_AND
) {
186 bin
.operator
= BinaryOperator
.BITWISE_AND
;
187 } else if (operator
== AssignmentOperator
.BITWISE_XOR
) {
188 bin
.operator
= BinaryOperator
.BITWISE_XOR
;
189 } else if (operator
== AssignmentOperator
.ADD
) {
190 bin
.operator
= BinaryOperator
.PLUS
;
191 } else if (operator
== AssignmentOperator
.SUB
) {
192 bin
.operator
= BinaryOperator
.MINUS
;
193 } else if (operator
== AssignmentOperator
.MUL
) {
194 bin
.operator
= BinaryOperator
.MUL
;
195 } else if (operator
== AssignmentOperator
.DIV
) {
196 bin
.operator
= BinaryOperator
.DIV
;
197 } else if (operator
== AssignmentOperator
.PERCENT
) {
198 bin
.operator
= BinaryOperator
.MOD
;
199 } else if (operator
== AssignmentOperator
.SHIFT_LEFT
) {
200 bin
.operator
= BinaryOperator
.SHIFT_LEFT
;
201 } else if (operator
== AssignmentOperator
.SHIFT_RIGHT
) {
202 bin
.operator
= BinaryOperator
.SHIFT_RIGHT
;
206 right
.check (analyzer
);
208 operator
= AssignmentOperator
.SIMPLE
;
212 if (left
.symbol_reference is Signal
) {
213 var sig
= (Signal
) left
.symbol_reference
;
215 var m
= right
.symbol_reference as Method
;
219 Report
.error (right
.source_reference
, "unsupported expression for signal handler");
223 var dynamic_sig
= sig as DynamicSignal
;
224 var right_ma
= right as MemberAccess
;
225 if (dynamic_sig
!= null) {
227 foreach (FormalParameter param
in dynamic_sig
.handler
.value_type
.get_parameters ()) {
229 // skip sender parameter
232 dynamic_sig
.add_parameter (param
.copy ());
235 right
.target_type
= new
DelegateType (sig
.get_delegate (new
ObjectType ((ObjectTypeSymbol
) sig
.parent_symbol
), this
));
236 } else if (!right
.value_type
.compatible (right
.target_type
)) {
237 var delegate_type
= (DelegateType
) right
.target_type
;
240 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
)));
242 } else if (right_ma
!= null && right_ma
.prototype_access
) {
244 Report
.error (right
.source_reference
, "Access to instance member `%s' denied".printf (m
.get_full_name ()));
247 } else if (left is MemberAccess
) {
248 var ma
= (MemberAccess
) left
;
250 if (ma
.symbol_reference is Property
) {
251 var prop
= (Property
) ma
.symbol_reference
;
253 var dynamic_prop
= prop as DynamicProperty
;
254 if (dynamic_prop
!= null) {
255 dynamic_prop
.property_type
= right
.value_type
.copy ();
256 left
.value_type
= dynamic_prop
.property_type
.copy ();
259 if (prop
.set_accessor
== null
260 || (!prop
.set_accessor
.writable
&& !(analyzer
.find_current_method () is CreationMethod
|| analyzer
.is_in_constructor ()))) {
262 Report
.error (ma
.source_reference
, "Property `%s' is read-only".printf (prop
.get_full_name ()));
265 } else if (ma
.symbol_reference is LocalVariable
&& right
.value_type
== null) {
266 var local
= (LocalVariable
) ma
.symbol_reference
;
268 if (right
.symbol_reference is Method
&&
269 local
.variable_type is DelegateType
) {
270 var m
= (Method
) right
.symbol_reference
;
271 var dt
= (DelegateType
) local
.variable_type
;
272 var cb
= dt
.delegate_symbol
;
274 /* check whether method matches callback type */
275 if (!cb
.matches_method (m
)) {
277 Report
.error (source_reference
, "declaration of method `%s' doesn't match declaration of callback `%s'".printf (m
.get_full_name (), cb
.get_full_name ()));
281 right
.value_type
= local
.variable_type
;
284 Report
.error (source_reference
, "Assignment: Invalid callback assignment attempt");
287 } else if (ma
.symbol_reference is Field
&& right
.value_type
== null) {
288 var f
= (Field
) ma
.symbol_reference
;
290 if (right
.symbol_reference is Method
&&
291 f
.field_type is DelegateType
) {
292 var m
= (Method
) right
.symbol_reference
;
293 var dt
= (DelegateType
) f
.field_type
;
294 var cb
= dt
.delegate_symbol
;
296 /* check whether method matches callback type */
297 if (!cb
.matches_method (m
)) {
299 Report
.error (source_reference
, "declaration of method `%s' doesn't match declaration of callback `%s'".printf (m
.get_full_name (), cb
.get_full_name ()));
303 right
.value_type
= f
.field_type
;
306 Report
.error (source_reference
, "Assignment: Invalid callback assignment attempt");
311 if (left
.value_type
!= null && right
.value_type
!= null) {
312 /* if there was an error on either side,
313 * i.e. {left|right}.value_type == null, skip type check */
315 if (!right
.value_type
.compatible (left
.value_type
)) {
317 Report
.error (source_reference
, "Assignment: Cannot convert from `%s' to `%s'".printf (right
.value_type
.to_string (), left
.value_type
.to_string ()));
321 if (!(ma
.symbol_reference is Property
)) {
322 if (right
.value_type
.is_disposable ()) {
323 /* rhs transfers ownership of the expression */
324 if (!(left
.value_type is PointerType
) && !left
.value_type
.value_owned
) {
325 /* lhs doesn't own the value */
327 Report
.error (source_reference
, "Invalid assignment from owned expression to unowned variable");
329 } else if (left
.value_type
.value_owned
) {
330 /* lhs wants to own the value
331 * rhs doesn't transfer the ownership
332 * code generator needs to add reference
337 } else if (left is ElementAccess
) {
338 var ea
= (ElementAccess
) left
;
340 if (!right
.value_type
.compatible (left
.value_type
)) {
342 Report
.error (source_reference
, "Assignment: Cannot convert from `%s' to `%s'".printf (right
.value_type
.to_string (), left
.value_type
.to_string ()));
346 if (right
.value_type
.is_disposable ()) {
347 /* rhs transfers ownership of the expression */
349 DataType element_type
;
351 if (ea
.container
.value_type is ArrayType
) {
352 var array_type
= (ArrayType
) ea
.container
.value_type
;
353 element_type
= array_type
.element_type
;
355 var args
= ea
.container
.value_type
.get_type_arguments ();
356 assert (args
.size
== 1);
357 element_type
= args
.get (0);
360 if (!(element_type is PointerType
) && !element_type
.value_owned
) {
361 /* lhs doesn't own the value */
363 Report
.error (source_reference
, "Invalid assignment from owned expression to unowned variable");
366 } else if (left
.value_type
.value_owned
) {
367 /* lhs wants to own the value
368 * rhs doesn't transfer the ownership
369 * code generator needs to add reference
376 if (left
.value_type
!= null) {
377 value_type
= left
.value_type
.copy ();
378 value_type
.value_owned
= false;
383 add_error_types (left
.get_error_types ());
384 add_error_types (right
.get_error_types ());
389 public override void get_defined_variables (Collection
<LocalVariable
> collection
) {
390 right
.get_defined_variables (collection
);
391 left
.get_defined_variables (collection
);
392 var local
= left
.symbol_reference as LocalVariable
;
394 collection
.add (local
);
398 public override void get_used_variables (Collection
<LocalVariable
> collection
) {
399 var ma
= left as MemberAccess
;
400 var ea
= left as ElementAccess
;
401 if (ma
!= null && ma
.inner
!= null) {
402 ma
.inner
.get_used_variables (collection
);
403 } else if (ea
!= null) {
404 ea
.get_used_variables (collection
);
406 right
.get_used_variables (collection
);
410 public enum Vala
.AssignmentOperator
{