1 /* valalambdaexpression.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
20 * Jürg Billeter <j@bitron.ch>
26 * Represents a lambda expression in the source code. Lambda expressions are
27 * anonymous methods with implicitly typed parameters.
29 public class Vala
.LambdaExpression
: Expression
{
30 private static int next_lambda_id
= 0;
33 * The expression body of this lambda expression. Only one of
34 * expression_body or statement_body may be set.
36 public Expression expression_body
{ get; set; }
39 * The statement body of this lambda expression. Only one of
40 * expression_body or statement_body may be set.
42 public Block statement_body
{ get; set; }
45 * The generated method.
47 public Method method
{ get; set; }
49 private List
<Parameter
> parameters
= new ArrayList
<Parameter
> ();
52 * Creates a new lambda expression.
54 * @param expression_body expression body
55 * @param source_reference reference to source code
56 * @return newly created lambda expression
58 public LambdaExpression (Expression expression_body
, SourceReference source_reference
) {
59 this
.source_reference
= source_reference
;
60 this
.expression_body
= expression_body
;
64 * Creates a new lambda expression with statement body.
66 * @param statement_body statement body
67 * @param source_reference reference to source code
68 * @return newly created lambda expression
70 public LambdaExpression
.with_statement_body (Block statement_body
, SourceReference source_reference
) {
71 this
.statement_body
= statement_body
;
72 this
.source_reference
= source_reference
;
76 * Appends implicitly typed parameter.
78 * @param param parameter name
80 public void add_parameter (Parameter param
) {
81 parameters
.add (param
);
85 * Returns copy of parameter list.
87 * @return parameter list
89 public List
<Parameter
> get_parameters () {
93 public override void accept (CodeVisitor visitor
) {
94 visitor
.visit_lambda_expression (this
);
96 visitor
.visit_expression (this
);
99 public override void accept_children (CodeVisitor visitor
) {
100 if (method
== null) {
101 if (expression_body
!= null) {
102 expression_body
.accept (visitor
);
103 visitor
.visit_end_full_expression (expression_body
);
104 } else if (statement_body
!= null) {
105 statement_body
.accept (visitor
);
108 method
.accept (visitor
);
112 public override bool is_pure () {
116 public override bool check (CodeContext context
) {
123 if (!(target_type is DelegateType
)) {
125 if (target_type
!= null) {
126 Report
.error (source_reference
, "Cannot convert lambda expression to `%s'".printf (target_type
.to_string ()));
128 Report
.error (source_reference
, "lambda expression not allowed in this context");
133 var cb
= (Delegate
) ((DelegateType
) target_type
).delegate_symbol
;
134 var return_type
= cb
.return_type
.get_actual_type (target_type
, null, this
);
135 method
= new
Method ("_lambda%d_".printf (next_lambda_id
++), return_type
, source_reference
);
136 // track usage for flow analyzer
138 method
.version
.check (source_reference
);
140 if (!cb
.has_target
|| !context
.analyzer
.is_in_instance_method ()) {
141 method
.binding
= MemberBinding
.STATIC
;
143 var sym
= context
.analyzer
.current_symbol
;
144 while (method
.this_parameter
== null) {
145 if (sym is Property
) {
146 var prop
= (Property
) sym
;
147 method
.this_parameter
= prop
.this_parameter
;
148 } else if (sym is Constructor
) {
149 var c
= (Constructor
) sym
;
150 method
.this_parameter
= c
.this_parameter
;
151 } else if (sym is Destructor
) {
152 var d
= (Destructor
) sym
;
153 method
.this_parameter
= d
.this_parameter
;
154 } else if (sym is Method
) {
155 var m
= (Method
) sym
;
156 method
.this_parameter
= m
.this_parameter
;
159 sym
= sym
.parent_symbol
;
162 method
.owner
= context
.analyzer
.current_symbol
.scope
;
164 var lambda_params
= get_parameters ();
165 Iterator
<Parameter
> lambda_param_it
= lambda_params
.iterator ();
167 if (cb
.sender_type
!= null && lambda_params
.size
== cb
.get_parameters ().size
+ 1) {
168 // lambda expression has sender parameter
169 lambda_param_it
.next ();
171 Parameter lambda_param
= lambda_param_it
.get ();
172 lambda_param
.variable_type
= cb
.sender_type
;
173 method
.add_parameter (lambda_param
);
176 foreach (Parameter cb_param
in cb
.get_parameters ()) {
177 if (!lambda_param_it
.next ()) {
178 /* lambda expressions are allowed to have less parameters */
182 Parameter lambda_param
= lambda_param_it
.get ();
184 if (lambda_param
.direction
!= cb_param
.direction
) {
186 Report
.error (lambda_param
.source_reference
, "direction of parameter `%s' is incompatible with the target delegate".printf (lambda_param
.name
));
189 lambda_param
.variable_type
= cb_param
.variable_type
.get_actual_type (target_type
, null, this
);
190 method
.add_parameter (lambda_param
);
193 if (lambda_param_it
.next ()) {
194 /* lambda expressions may not expect more parameters */
196 Report
.error (source_reference
, "lambda expression: too many parameters");
200 foreach (var error_type
in cb
.get_error_types ()) {
201 method
.add_error_type (error_type
.copy ());
204 if (expression_body
!= null) {
205 var block
= new
Block (source_reference
);
206 block
.scope
.parent_scope
= method
.scope
;
208 if (method
.return_type
.data_type
!= null) {
209 block
.add_statement (new
ReturnStatement (expression_body
, source_reference
));
211 block
.add_statement (new
ExpressionStatement (expression_body
, source_reference
));
216 method
.body
= statement_body
;
218 method
.body
.owner
= method
.scope
;
220 // support use of generics in closures
221 var m
= context
.analyzer
.find_parent_method (context
.analyzer
.current_symbol
);
223 foreach (var type_param
in m
.get_type_parameters ()) {
224 method
.add_type_parameter (new
TypeParameter (type_param
.name
, type_param
.source_reference
));
226 method
.closure
= true;
227 m
.body
.captured
= true;
231 /* lambda expressions should be usable like MemberAccess of a method */
232 symbol_reference
= method
;
234 method
.check (context
);
236 value_type
= new
MethodType (method
);
237 value_type
.value_owned
= target_type
.value_owned
;
242 public override void emit (CodeGenerator codegen
) {
243 codegen
.visit_lambda_expression (this
);
245 codegen
.visit_expression (this
);
248 public override void get_used_variables (Collection
<Variable
> collection
) {
249 // require captured variables to be initialized
250 if (method
.closure
) {
251 method
.get_captured_variables ((Collection
<LocalVariable
>) collection
);