ccode: Properly depend on in-tree built libvala
[vala-gnome.git] / vala / valaforeachstatement.vala
blob416fb55d2dc44a2afc726b770cbf9c0393329804
1 /* valaforeachstatement.vala
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
19 * Author:
20 * Jürg Billeter <j@bitron.ch>
24 /**
25 * Represents a foreach statement in the source code. Foreach statements iterate
26 * over the elements of a collection.
28 public class Vala.ForeachStatement : Block {
29 /**
30 * Specifies the element type.
32 public DataType? type_reference {
33 get { return _data_type; }
34 set {
35 _data_type = value;
36 if (_data_type != null) {
37 _data_type.parent_node = this;
42 /**
43 * Specifies the element variable name.
45 public string variable_name { get; set; }
47 /**
48 * Specifies the container.
50 public Expression collection {
51 get {
52 return _collection;
54 set {
55 _collection = value;
56 _collection.parent_node = this;
60 /**
61 * Specifies the loop body.
63 public Block body {
64 get {
65 return _body;
67 set {
68 _body = value;
69 _body.parent_node = this;
73 public bool use_iterator { get; private set; }
75 /**
76 * Specifies the declarator for the generated element variable.
78 public LocalVariable element_variable { get; set; }
80 /**
81 * Specifies the declarator for the generated collection variable.
83 public LocalVariable collection_variable { get; set; }
85 /**
86 * Specifies the declarator for the generated iterator variable.
88 public LocalVariable iterator_variable { get; set; }
90 private Expression _collection;
91 private Block _body;
93 private DataType _data_type;
95 /**
96 * Creates a new foreach statement.
98 * @param type_reference element type
99 * @param variable_name element variable name
100 * @param collection container
101 * @param body loop body
102 * @param source_reference reference to source code
103 * @return newly created foreach statement
105 public ForeachStatement (DataType? type_reference, string variable_name, Expression collection, Block body, SourceReference source_reference) {
106 base (source_reference);
107 this.variable_name = variable_name;
108 this.collection = collection;
109 this.body = body;
110 this.type_reference = type_reference;
113 public override void accept (CodeVisitor visitor) {
114 if (use_iterator) {
115 base.accept (visitor);
116 return;
119 visitor.visit_foreach_statement (this);
122 public override void accept_children (CodeVisitor visitor) {
123 if (use_iterator) {
124 base.accept_children (visitor);
125 return;
128 collection.accept (visitor);
129 visitor.visit_end_full_expression (collection);
131 if (type_reference != null) {
132 type_reference.accept (visitor);
135 body.accept (visitor);
138 public override void replace_expression (Expression old_node, Expression new_node) {
139 if (collection == old_node) {
140 collection = new_node;
144 public override void replace_type (DataType old_type, DataType new_type) {
145 if (type_reference == old_type) {
146 type_reference = new_type;
150 public override bool check (CodeContext context) {
151 if (checked) {
152 return !error;
155 checked = true;
157 // analyze collection expression first, used for type inference
158 if (!collection.check (context)) {
159 // ignore inner error
160 error = true;
161 return false;
162 } else if (collection.value_type == null) {
163 Report.error (collection.source_reference, "invalid collection expression");
164 error = true;
165 return false;
168 var collection_type = collection.value_type.copy ();
169 collection.target_type = collection_type.copy ();
171 if (collection_type.is_array ()) {
172 var array_type = (ArrayType) collection_type;
174 // can't use inline-allocated array for temporary variable
175 array_type.inline_allocated = false;
177 return check_without_iterator (context, collection_type, array_type.element_type);
178 } else if (context.profile == Profile.GOBJECT && (collection_type.compatible (context.analyzer.glist_type) || collection_type.compatible (context.analyzer.gslist_type))) {
179 if (collection_type.get_type_arguments ().size != 1) {
180 error = true;
181 Report.error (collection.source_reference, "missing type argument for collection");
182 return false;
185 return check_without_iterator (context, collection_type, collection_type.get_type_arguments ().get (0));
186 } else if (context.profile == Profile.GOBJECT && collection_type.compatible (context.analyzer.gvaluearray_type)) {
187 return check_without_iterator (context, collection_type, context.analyzer.gvalue_type);
188 } else {
189 return check_with_iterator (context, collection_type);
193 bool check_with_index (CodeContext context, DataType collection_type) {
194 var get_method = collection_type.get_member ("get") as Method;
195 if (get_method == null) {
196 return false;
198 if (get_method.get_parameters ().size != 1) {
199 return false;
201 var size_property = collection_type.get_member ("size") as Property;
202 if (size_property == null) {
203 return false;
206 add_statement (new DeclarationStatement (new LocalVariable (null, "_%s_list".printf (variable_name), collection, source_reference), source_reference));
207 add_statement (new DeclarationStatement (new LocalVariable (null, "_%s_size".printf (variable_name), new MemberAccess (new MemberAccess.simple ("_%s_list".printf (variable_name), source_reference), "size", source_reference), source_reference), source_reference));
208 add_statement (new DeclarationStatement (new LocalVariable (null, "_%s_index".printf (variable_name), new UnaryExpression (UnaryOperator.MINUS, new IntegerLiteral ("1", source_reference), source_reference), source_reference), source_reference));
209 var next = new UnaryExpression (UnaryOperator.INCREMENT, new MemberAccess.simple ("_%s_index".printf (variable_name), source_reference), source_reference);
210 var conditional = new BinaryExpression (BinaryOperator.LESS_THAN, next, new MemberAccess.simple ("_%s_size".printf (variable_name), source_reference), source_reference);
211 var loop = new WhileStatement (conditional, body, source_reference);
212 add_statement (loop);
214 var get_call = new MethodCall (new MemberAccess (new MemberAccess.simple ("_%s_list".printf (variable_name), source_reference), "get", source_reference), source_reference);
215 get_call.add_argument (new MemberAccess.simple ("_%s_index".printf (variable_name), source_reference));
216 body.insert_statement (0, new DeclarationStatement (new LocalVariable (type_reference, variable_name, get_call, source_reference), source_reference));
218 checked = false;
219 return base.check (context);
222 bool check_with_iterator (CodeContext context, DataType collection_type) {
223 use_iterator = true;
225 if (check_with_index (context, collection_type)) {
226 return true;
229 var iterator_method = collection_type.get_member ("iterator") as Method;
230 if (iterator_method == null) {
231 Report.error (collection.source_reference, "`%s' does not have an `iterator' method".printf (collection_type.to_string ()));
232 error = true;
233 return false;
235 if (iterator_method.get_parameters ().size != 0) {
236 Report.error (collection.source_reference, "`%s' must not have any parameters".printf (iterator_method.get_full_name ()));
237 error = true;
238 return false;
240 var iterator_type = iterator_method.return_type.get_actual_type (collection_type, null, this);
241 if (iterator_type is VoidType) {
242 Report.error (collection.source_reference, "`%s' must return an iterator".printf (iterator_method.get_full_name ()));
243 error = true;
244 return false;
247 var iterator_call = new MethodCall (new MemberAccess (collection, "iterator", source_reference), source_reference);
248 add_statement (new DeclarationStatement (new LocalVariable (iterator_type, "_%s_it".printf (variable_name), iterator_call, source_reference), source_reference));
250 var next_value_method = iterator_type.get_member ("next_value") as Method;
251 var next_method = iterator_type.get_member ("next") as Method;
252 if (next_value_method != null) {
253 if (next_value_method.get_parameters ().size != 0) {
254 Report.error (collection.source_reference, "`%s' must not have any parameters".printf (next_value_method.get_full_name ()));
255 error = true;
256 return false;
258 var element_type = next_value_method.return_type.get_actual_type (iterator_type, null, this);
259 if (!element_type.nullable) {
260 Report.error (collection.source_reference, "return type of `%s' must be nullable".printf (next_value_method.get_full_name ()));
261 error = true;
262 return false;
265 if (!analyze_element_type (element_type)) {
266 return false;
269 add_statement (new DeclarationStatement (new LocalVariable (type_reference, variable_name, null, source_reference), source_reference));
271 var next_value_call = new MethodCall (new MemberAccess (new MemberAccess.simple ("_%s_it".printf (variable_name), source_reference), "next_value", source_reference), source_reference);
272 var assignment = new Assignment (new MemberAccess (null, variable_name, source_reference), next_value_call, AssignmentOperator.SIMPLE, source_reference);
273 var conditional = new BinaryExpression (BinaryOperator.INEQUALITY, assignment, new NullLiteral (source_reference), source_reference);
274 var loop = new WhileStatement (conditional, body, source_reference);
275 add_statement (loop);
276 } else if (next_method != null) {
277 if (next_method.get_parameters ().size != 0) {
278 Report.error (collection.source_reference, "`%s' must not have any parameters".printf (next_method.get_full_name ()));
279 error = true;
280 return false;
282 if (!next_method.return_type.compatible (context.analyzer.bool_type)) {
283 Report.error (collection.source_reference, "`%s' must return a boolean value".printf (next_method.get_full_name ()));
284 error = true;
285 return false;
287 var get_method = iterator_type.get_member ("get") as Method;
288 if (get_method == null) {
289 Report.error (collection.source_reference, "`%s' does not have a `get' method".printf (iterator_type.to_string ()));
290 error = true;
291 return false;
293 if (get_method.get_parameters ().size != 0) {
294 Report.error (collection.source_reference, "`%s' must not have any parameters".printf (get_method.get_full_name ()));
295 error = true;
296 return false;
298 var element_type = get_method.return_type.get_actual_type (iterator_type, null, this);
299 if (element_type is VoidType) {
300 Report.error (collection.source_reference, "`%s' must return an element".printf (get_method.get_full_name ()));
301 error = true;
302 return false;
305 if (!analyze_element_type (element_type)) {
306 return false;
309 var next_call = new MethodCall (new MemberAccess (new MemberAccess.simple ("_%s_it".printf (variable_name), source_reference), "next", source_reference), source_reference);
310 var loop = new WhileStatement (next_call, body, source_reference);
311 add_statement (loop);
313 var get_call = new MethodCall (new MemberAccess (new MemberAccess.simple ("_%s_it".printf (variable_name), source_reference), "get", source_reference), source_reference);
314 body.insert_statement (0, new DeclarationStatement (new LocalVariable (type_reference, variable_name, get_call, source_reference), source_reference));
315 } else {
316 Report.error (collection.source_reference, "`%s' does not have a `next_value' or `next' method".printf (iterator_type.to_string ()));
317 error = true;
318 return false;
321 checked = false;
322 return base.check (context);
325 bool analyze_element_type (DataType element_type) {
326 // analyze element type
327 if (type_reference == null) {
328 // var type
329 type_reference = element_type.copy ();
330 } else if (!element_type.compatible (type_reference)) {
331 error = true;
332 Report.error (source_reference, "Foreach: Cannot convert from `%s' to `%s'".printf (element_type.to_string (), type_reference.to_string ()));
333 return false;
334 } else if (element_type.is_disposable () && element_type.value_owned && !type_reference.value_owned) {
335 error = true;
336 Report.error (source_reference, "Foreach: Invalid assignment from owned expression to unowned variable");
337 return false;
340 return true;
343 bool check_without_iterator (CodeContext context, DataType collection_type, DataType element_type) {
344 // analyze element type
345 if (type_reference == null) {
346 // var type
347 type_reference = element_type.copy ();
348 } else if (!element_type.compatible (type_reference)) {
349 error = true;
350 Report.error (source_reference, "Foreach: Cannot convert from `%s' to `%s'".printf (element_type.to_string (), type_reference.to_string ()));
351 return false;
354 element_variable = new LocalVariable (type_reference, variable_name, null, source_reference);
356 body.scope.add (variable_name, element_variable);
358 body.add_local_variable (element_variable);
359 element_variable.active = true;
360 element_variable.checked = true;
362 // analyze body
363 owner = context.analyzer.current_symbol.scope;
364 context.analyzer.current_symbol = this;
366 // call add_local_variable to check for shadowed variable
367 add_local_variable (element_variable);
368 remove_local_variable (element_variable);
370 body.check (context);
372 foreach (LocalVariable local in get_local_variables ()) {
373 local.active = false;
376 context.analyzer.current_symbol = context.analyzer.current_symbol.parent_symbol;
378 collection_variable = new LocalVariable (collection_type.copy (), "%s_collection".printf (variable_name));
380 add_local_variable (collection_variable);
381 collection_variable.active = true;
383 add_error_types (collection.get_error_types ());
384 add_error_types (body.get_error_types ());
386 return !error;
389 public override void emit (CodeGenerator codegen) {
390 if (use_iterator) {
391 base.emit (codegen);
392 return;
395 collection.emit (codegen);
396 codegen.visit_end_full_expression (collection);
398 element_variable.active = true;
399 collection_variable.active = true;
400 if (iterator_variable != null) {
401 iterator_variable.active = true;
404 codegen.visit_foreach_statement (this);
407 public override void get_defined_variables (Collection<Variable> collection) {
408 collection.add (element_variable);