Release 0.7.8
[vala-lang.git] / vala / valaassignment.vala
blob2ae05665c69db0dbfc911e8d335513fc1101dbf8
1 /* valaassignment.vala
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
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 check (SemanticAnalyzer analyzer) {
101 if (checked) {
102 return !error;
105 checked = true;
107 left.lvalue = true;
109 if (!left.check (analyzer)) {
110 // skip on error in inner expression
111 error = true;
112 return false;
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) {
119 error = true;
120 Report.error (source_reference, "unsupported lvalue in assignment");
121 return false;
123 if (ma.prototype_access) {
124 error = true;
125 Report.error (source_reference, "Access to instance member `%s' denied".printf (ma.symbol_reference.get_full_name ()));
126 return false;
129 if (ma.error || ma.symbol_reference == null) {
130 error = true;
131 /* if no symbol found, skip this check */
132 return false;
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));
140 } else {
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) {
148 error = true;
149 Report.error (ea.source_reference, "strings are immutable");
150 return false;
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);
163 } else {
164 right.target_type = left.value_type;
166 } else if (left is PointerIndirection) {
167 right.target_type = left.value_type;
168 } else {
169 error = true;
170 Report.error (source_reference, "unsupported lvalue in assignment");
171 return false;
174 if (!right.check (analyzer)) {
175 // skip on error in inner expression
176 error = true;
177 return false;
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;
217 right = bin;
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;
229 if (m == null) {
230 error = true;
231 Report.error (right.source_reference, "unsupported expression for signal handler");
232 return false;
235 var dynamic_sig = sig as DynamicSignal;
236 var right_ma = right as MemberAccess;
237 if (dynamic_sig != null) {
238 bool first = true;
239 foreach (FormalParameter param in dynamic_sig.handler.value_type.get_parameters ()) {
240 if (first) {
241 // skip sender parameter
242 first = false;
243 } else {
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;
251 error = true;
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)));
253 return false;
254 } else if (right_ma != null && right_ma.prototype_access) {
255 error = true;
256 Report.error (right.source_reference, "Access to instance member `%s' denied".printf (m.get_full_name ()));
257 return false;
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 ()))) {
273 ma.error = true;
274 Report.error (ma.source_reference, "Property `%s' is read-only".printf (prop.get_full_name ()));
275 return false;
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)) {
292 error = true;
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 ()));
294 return false;
297 right.value_type = local.variable_type;
298 } else {
299 error = true;
300 Report.error (source_reference, "Assignment: Invalid callback assignment attempt");
301 return false;
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)) {
314 f.error = true;
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 ()));
316 return false;
319 right.value_type = f.field_type;
320 } else {
321 error = true;
322 Report.error (source_reference, "Assignment: Invalid callback assignment attempt");
323 return false;
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)) {
332 error = true;
333 Report.error (source_reference, "Assignment: Cannot convert from `%s' to `%s'".printf (right.value_type.to_string (), left.value_type.to_string ()));
334 return false;
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 */
342 error = true;
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
349 * increment calls */
353 } else if (left is ElementAccess) {
354 var ea = (ElementAccess) left;
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 (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;
370 } else {
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 */
378 error = true;
379 Report.error (source_reference, "Invalid assignment from owned expression to unowned variable");
380 return false;
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
386 * increment calls */
388 } else {
389 return true;
392 if (left.value_type != null) {
393 value_type = left.value_type.copy ();
394 value_type.value_owned = false;
395 } else {
396 value_type = null;
399 add_error_types (left.get_error_types ());
400 add_error_types (right.get_error_types ());
402 return !error;
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;
409 if (local != null) {
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 {
427 NONE,
428 SIMPLE,
429 BITWISE_OR,
430 BITWISE_AND,
431 BITWISE_XOR,
432 ADD,
433 SUB,
434 MUL,
435 DIV,
436 PERCENT,
437 SHIFT_LEFT,
438 SHIFT_RIGHT