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
20 * Jürg Billeter <j@bitron.ch>
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
{
30 * Specifies the element type.
32 public DataType? type_reference
{
33 get { return _data_type
; }
36 if (_data_type
!= null) {
37 _data_type
.parent_node
= this
;
43 * Specifies the element variable name.
45 public string variable_name
{ get; set; }
48 * Specifies the container.
50 public Expression collection
{
56 _collection
.parent_node
= this
;
61 * Specifies the loop body.
69 _body
.parent_node
= this
;
73 public bool use_iterator
{ get; private set; }
76 * Specifies the declarator for the generated element variable.
78 public LocalVariable element_variable
{ get; set; }
81 * Specifies the declarator for the generated collection variable.
83 public LocalVariable collection_variable
{ get; set; }
86 * Specifies the declarator for the generated iterator variable.
88 public LocalVariable iterator_variable
{ get; set; }
90 private Expression _collection
;
93 private DataType _data_type
;
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
;
109 this
.type_reference
= type_reference
;
112 public override void accept (CodeVisitor visitor
) {
114 base.accept (visitor
);
118 visitor
.visit_foreach_statement (this
);
121 public override void accept_children (CodeVisitor visitor
) {
123 base.accept_children (visitor
);
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
) {
156 // analyze collection expression first, used for type inference
157 if (!collection
.check (analyzer
)) {
158 // ignore inner error
161 } else if (collection
.value_type
== null) {
162 Report
.error (collection
.source_reference
, "invalid collection expression");
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) {
177 Report
.error (collection
.source_reference
, "missing type argument for collection");
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
);
185 return check_with_iterator (analyzer
, collection_type
);
189 bool check_with_iterator (SemanticAnalyzer analyzer
, DataType collection_type
) {
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 ()));
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 ()));
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 ()));
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 ()));
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 ()));
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 ()));
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 ()));
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 ()));
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 ()));
243 // analyze element type
244 if (type_reference
== null) {
246 type_reference
= element_type
.copy ();
247 } else if (!element_type
.compatible (type_reference
)) {
249 Report
.error (source_reference
, "Foreach: Cannot convert from `%s' to `%s'".printf (element_type
.to_string (), type_reference
.to_string ()));
251 } else if (element_type
.is_disposable () && element_type
.value_owned
&& !type_reference
.value_owned
) {
253 Report
.error (source_reference
, "Foreach: Invalid assignment from owned expression to unowned variable");
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
));
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) {
275 type_reference
= element_type
.copy ();
276 } else if (!element_type
.compatible (type_reference
)) {
278 Report
.error (source_reference
, "Foreach: Cannot convert from `%s' to `%s'".printf (element_type
.to_string (), type_reference
.to_string ()));
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;
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 ());
313 public override void get_defined_variables (Collection
<LocalVariable
> collection
) {
314 collection
.add (element_variable
);