Warn when using result variable with incompatible type to prepare possible
[vala-lang.git] / vala / valaassignment.vala
blobd24a7a39e55077fb07fa2e45ef03be3b5c39d6c8
1 /* valaassignment.vala
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
19 * Author:
20 * Jürg Billeter <j@bitron.ch>
23 using Gee;
25 /**
26 * Represents an assignment expression in the source code.
28 * Supports =, |=, &=, ^=, +=, -=, *=, /=, %=, <<=, >>=.
30 public class Vala.Assignment : Expression {
31 /**
32 * Left hand side of the assignment.
34 public Expression left {
35 get { return _left; }
36 set {
37 _left = value;
38 _left.parent_node = this;
42 /**
43 * Assignment operator.
45 public AssignmentOperator operator { get; set; }
47 /**
48 * Right hand side of the assignment.
50 public Expression right {
51 get { return _right; }
52 set {
53 _right = value;
54 _right.parent_node = this;
58 private Expression _left;
59 private Expression _right;
61 /**
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) {
71 this.right = right;
72 this.operator = operator;
73 this.source_reference = source_reference;
74 this.left = left;
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) {
90 left = new_node;
92 if (right == old_node) {
93 right = new_node;
97 public override bool is_pure () {
98 return false;
101 public override bool check (SemanticAnalyzer analyzer) {
102 if (checked) {
103 return !error;
106 checked = true;
108 left.lvalue = true;
110 if (!left.check (analyzer)) {
111 // skip on error in inner expression
112 error = true;
113 return false;
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) {
120 error = true;
121 Report.error (source_reference, "unsupported lvalue in assignment");
122 return false;
124 if (ma.prototype_access) {
125 error = true;
126 Report.error (source_reference, "Access to instance member `%s' denied".printf (ma.symbol_reference.get_full_name ()));
127 return false;
130 if (ma.error || ma.symbol_reference == null) {
131 error = true;
132 /* if no symbol found, skip this check */
133 return false;
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));
141 } else {
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));
151 } else {
152 right.target_type = left.value_type;
154 } else if (left is PointerIndirection) {
155 right.target_type = left.value_type;
156 } else {
157 error = true;
158 Report.error (source_reference, "unsupported lvalue in assignment");
159 return false;
162 if (!right.check (analyzer)) {
163 // skip on error in inner expression
164 error = true;
165 return false;
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;
205 right = bin;
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;
217 if (m == null) {
218 error = true;
219 Report.error (right.source_reference, "unsupported expression for signal handler");
220 return false;
223 var dynamic_sig = sig as DynamicSignal;
224 var right_ma = right as MemberAccess;
225 if (dynamic_sig != null) {
226 bool first = true;
227 foreach (FormalParameter param in dynamic_sig.handler.value_type.get_parameters ()) {
228 if (first) {
229 // skip sender parameter
230 first = false;
231 } else {
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;
239 error = true;
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)));
241 return false;
242 } else if (right_ma != null && right_ma.prototype_access) {
243 error = true;
244 Report.error (right.source_reference, "Access to instance member `%s' denied".printf (m.get_full_name ()));
245 return false;
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 ()))) {
261 ma.error = true;
262 Report.error (ma.source_reference, "Property `%s' is read-only".printf (prop.get_full_name ()));
263 return false;
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)) {
276 error = true;
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 ()));
278 return false;
281 right.value_type = local.variable_type;
282 } else {
283 error = true;
284 Report.error (source_reference, "Assignment: Invalid callback assignment attempt");
285 return false;
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)) {
298 f.error = true;
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 ()));
300 return false;
303 right.value_type = f.field_type;
304 } else {
305 error = true;
306 Report.error (source_reference, "Assignment: Invalid callback assignment attempt");
307 return false;
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)) {
316 error = true;
317 Report.error (source_reference, "Assignment: Cannot convert from `%s' to `%s'".printf (right.value_type.to_string (), left.value_type.to_string ()));
318 return false;
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 */
326 error = true;
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
333 * increment calls */
337 } else if (left is ElementAccess) {
338 var ea = (ElementAccess) left;
340 if (!right.value_type.compatible (left.value_type)) {
341 error = true;
342 Report.error (source_reference, "Assignment: Cannot convert from `%s' to `%s'".printf (right.value_type.to_string (), left.value_type.to_string ()));
343 return false;
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;
354 } else {
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 */
362 error = true;
363 Report.error (source_reference, "Invalid assignment from owned expression to unowned variable");
364 return false;
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
370 * increment calls */
372 } else {
373 return true;
376 if (left.value_type != null) {
377 value_type = left.value_type.copy ();
378 value_type.value_owned = false;
379 } else {
380 value_type = null;
383 add_error_types (left.get_error_types ());
384 add_error_types (right.get_error_types ());
386 return !error;
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;
393 if (local != null) {
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 {
411 NONE,
412 SIMPLE,
413 BITWISE_OR,
414 BITWISE_AND,
415 BITWISE_XOR,
416 ADD,
417 SUB,
418 MUL,
419 DIV,
420 PERCENT,
421 SHIFT_LEFT,
422 SHIFT_RIGHT