Release 0.7.8
[vala-lang.git] / vala / valaforeachstatement.vala
blobb12440e3031ebabbb5deb3e8a09b81b551baf648
1 /* valaforeachstatement.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 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 element type
99 * @param id element variable name
100 * @param col loop body
101 * @param source reference to source code
102 * @return newly created foreach statement
104 public ForeachStatement (DataType? type_reference, string variable_name, Expression collection, Block body, SourceReference source_reference) {
105 base (source_reference);
106 this.variable_name = variable_name;
107 this.collection = collection;
108 this.body = body;
109 this.type_reference = type_reference;
112 public override void accept (CodeVisitor visitor) {
113 if (use_iterator) {
114 base.accept (visitor);
115 return;
118 visitor.visit_foreach_statement (this);
121 public override void accept_children (CodeVisitor visitor) {
122 if (use_iterator) {
123 base.accept_children (visitor);
124 return;
127 collection.accept (visitor);
128 visitor.visit_end_full_expression (collection);
130 if (type_reference != null) {
131 type_reference.accept (visitor);
134 body.accept (visitor);
137 public override void replace_expression (Expression old_node, Expression new_node) {
138 if (collection == old_node) {
139 collection = new_node;
143 public override void replace_type (DataType old_type, DataType new_type) {
144 if (type_reference == old_type) {
145 type_reference = new_type;
149 public override bool check (SemanticAnalyzer analyzer) {
150 if (checked) {
151 return !error;
154 checked = true;
156 // analyze collection expression first, used for type inference
157 if (!collection.check (analyzer)) {
158 // ignore inner error
159 error = true;
160 return false;
161 } else if (collection.value_type == null) {
162 Report.error (collection.source_reference, "invalid collection expression");
163 error = true;
164 return false;
167 var collection_type = collection.value_type.copy ();
168 collection.target_type = collection_type.copy ();
170 if (collection_type.is_array ()) {
171 var array_type = (ArrayType) collection_type;
173 return check_without_iterator (analyzer, collection_type, array_type.element_type);
174 } else if (collection_type.compatible (analyzer.glist_type) || collection_type.compatible (analyzer.gslist_type)) {
175 if (collection_type.get_type_arguments ().size != 1) {
176 error = true;
177 Report.error (collection.source_reference, "missing type argument for collection");
178 return false;
181 return check_without_iterator (analyzer, collection_type, collection_type.get_type_arguments ().get (0));
182 } else if (collection_type.compatible (analyzer.gvaluearray_type)) {
183 return check_without_iterator (analyzer, collection_type, analyzer.gvalue_type);
184 } else {
185 return check_with_iterator (analyzer, collection_type);
189 bool check_with_iterator (SemanticAnalyzer analyzer, DataType collection_type) {
190 use_iterator = true;
192 var iterator_method = collection_type.get_member ("iterator") as Method;
193 if (iterator_method == null) {
194 Report.error (collection.source_reference, "`%s' does not have an `iterator' method".printf (collection_type.to_string ()));
195 error = true;
196 return false;
198 if (iterator_method.get_parameters ().size != 0) {
199 Report.error (collection.source_reference, "`%s' must not have any parameters".printf (iterator_method.get_full_name ()));
200 error = true;
201 return false;
203 var iterator_type = iterator_method.return_type.get_actual_type (collection_type, null, this);
204 if (iterator_type is VoidType) {
205 Report.error (collection.source_reference, "`%s' must return an iterator".printf (iterator_method.get_full_name ()));
206 error = true;
207 return false;
209 var next_method = iterator_type.get_member ("next") as Method;
210 if (next_method == null) {
211 Report.error (collection.source_reference, "`%s' does not have a `next' method".printf (iterator_type.to_string ()));
212 error = true;
213 return false;
215 if (next_method.get_parameters ().size != 0) {
216 Report.error (collection.source_reference, "`%s' must not have any parameters".printf (next_method.get_full_name ()));
217 error = true;
218 return false;
220 if (!next_method.return_type.compatible (analyzer.bool_type)) {
221 Report.error (collection.source_reference, "`%s' must return a boolean value".printf (next_method.get_full_name ()));
222 error = true;
223 return false;
225 var get_method = iterator_type.get_member ("get") as Method;
226 if (get_method == null) {
227 Report.error (collection.source_reference, "`%s' does not have a `get' method".printf (iterator_type.to_string ()));
228 error = true;
229 return false;
231 if (get_method.get_parameters ().size != 0) {
232 Report.error (collection.source_reference, "`%s' must not have any parameters".printf (get_method.get_full_name ()));
233 error = true;
234 return false;
236 var element_type = get_method.return_type.get_actual_type (iterator_type, null, this);
237 if (element_type is VoidType) {
238 Report.error (collection.source_reference, "`%s' must return an element".printf (get_method.get_full_name ()));
239 error = true;
240 return false;
243 // analyze element type
244 if (type_reference == null) {
245 // var type
246 type_reference = element_type.copy ();
247 } else if (!element_type.compatible (type_reference)) {
248 error = true;
249 Report.error (source_reference, "Foreach: Cannot convert from `%s' to `%s'".printf (element_type.to_string (), type_reference.to_string ()));
250 return false;
251 } else if (element_type.is_disposable () && element_type.value_owned && !type_reference.value_owned) {
252 error = true;
253 Report.error (source_reference, "Foreach: Invalid assignment from owned expression to unowned variable");
254 return false;
257 var iterator_call = new MethodCall (new MemberAccess (collection, "iterator"));
258 add_statement (new DeclarationStatement (new LocalVariable (iterator_type, "_%s_it".printf (variable_name), iterator_call, source_reference), source_reference));
260 var next_call = new MethodCall (new MemberAccess (new MemberAccess.simple ("_%s_it".printf (variable_name), source_reference), "next", source_reference), source_reference);
261 var loop = new WhileStatement (next_call, body, source_reference);
262 add_statement (loop);
264 var get_call = new MethodCall (new MemberAccess (new MemberAccess.simple ("_%s_it".printf (variable_name), source_reference), "get", source_reference), source_reference);
265 body.insert_statement (0, new DeclarationStatement (new LocalVariable (type_reference, variable_name, get_call, source_reference), source_reference));
267 checked = false;
268 return base.check (analyzer);
271 bool check_without_iterator (SemanticAnalyzer analyzer, DataType collection_type, DataType element_type) {
272 // analyze element type
273 if (type_reference == null) {
274 // var type
275 type_reference = element_type.copy ();
276 } else if (!element_type.compatible (type_reference)) {
277 error = true;
278 Report.error (source_reference, "Foreach: Cannot convert from `%s' to `%s'".printf (element_type.to_string (), type_reference.to_string ()));
279 return false;
282 element_variable = new LocalVariable (type_reference, variable_name);
284 body.scope.add (variable_name, element_variable);
286 body.add_local_variable (element_variable);
287 element_variable.active = true;
288 element_variable.checked = true;
290 // analyze body
291 owner = analyzer.current_symbol.scope;
292 analyzer.current_symbol = this;
294 body.check (analyzer);
296 foreach (LocalVariable local in get_local_variables ()) {
297 local.active = false;
300 analyzer.current_symbol = analyzer.current_symbol.parent_symbol;
302 collection_variable = new LocalVariable (collection_type, "%s_collection".printf (variable_name));
304 add_local_variable (collection_variable);
305 collection_variable.active = true;
307 add_error_types (collection.get_error_types ());
308 add_error_types (body.get_error_types ());
310 return !error;
313 public override void get_defined_variables (Collection<LocalVariable> collection) {
314 collection.add (element_variable);