Fix crash with nested lambda expressions
[vala-lang.git] / vala / valalambdaexpression.vala
blob37c5fb7e2ad4bbf118e611f7332dea09b56606e3
1 /* valalambdaexpression.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>
23 using GLib;
24 using Gee;
26 /**
27 * Represents a lambda expression in the source code. Lambda expressions are
28 * anonymous methods with implicitly typed parameters.
30 public class Vala.LambdaExpression : Expression {
31 /**
32 * The expression body of this lambda expression. Only one of
33 * expression_body or statement_body may be set.
35 public Expression expression_body { get; set; }
37 /**
38 * The statement body of this lambda expression. Only one of
39 * expression_body or statement_body may be set.
41 public Block statement_body { get; set; }
43 /**
44 * The generated method.
46 public Method method { get; set; }
48 private Gee.List<string> parameters = new ArrayList<string> ();
50 /**
51 * Creates a new lambda expression.
53 * @param expression_body expression body
54 * @param source_reference reference to source code
55 * @return newly created lambda expression
57 public LambdaExpression (Expression expression_body, SourceReference source_reference) {
58 this.source_reference = source_reference;
59 this.expression_body = expression_body;
62 /**
63 * Creates a new lambda expression with statement body.
65 * @param statement_body statement body
66 * @param source_reference reference to source code
67 * @return newly created lambda expression
69 public LambdaExpression.with_statement_body (Block statement_body, SourceReference source_reference) {
70 this.statement_body = statement_body;
71 this.source_reference = source_reference;
74 /**
75 * Appends implicitly typed parameter.
77 * @param param parameter name
79 public void add_parameter (string param) {
80 parameters.add (param);
83 /**
84 * Returns copy of parameter list.
86 * @return parameter list
88 public Gee.List<string> get_parameters () {
89 return new ReadOnlyList<string> (parameters);
92 public override void accept (CodeVisitor visitor) {
93 visitor.visit_lambda_expression (this);
95 visitor.visit_expression (this);
98 public override void accept_children (CodeVisitor visitor) {
99 if (method == null) {
100 if (expression_body != null) {
101 expression_body.accept (visitor);
102 visitor.visit_end_full_expression (expression_body);
103 } else if (statement_body != null) {
104 statement_body.accept (visitor);
106 } else {
107 method.accept (visitor);
111 public override bool is_pure () {
112 return false;
115 string get_lambda_name (SemanticAnalyzer analyzer) {
116 var result = "__lambda%d".printf (analyzer.next_lambda_id);
118 analyzer.next_lambda_id++;
120 return result;
123 public override bool check (SemanticAnalyzer analyzer) {
124 if (checked) {
125 return !error;
128 checked = true;
130 if (!(target_type is DelegateType)) {
131 error = true;
132 Report.error (source_reference, "lambda expression not allowed in this context");
133 return false;
136 var cb = (Delegate) ((DelegateType) target_type).delegate_symbol;
137 method = new Method (get_lambda_name (analyzer), cb.return_type);
138 if (!cb.has_target || !analyzer.is_in_instance_method ()) {
139 method.binding = MemberBinding.STATIC;
140 } else {
141 var sym = analyzer.current_symbol;
142 while (method.this_parameter == null) {
143 if (sym is Property) {
144 var prop = (Property) sym;
145 method.this_parameter = prop.this_parameter;
146 } else if (sym is Constructor) {
147 var c = (Constructor) sym;
148 method.this_parameter = c.this_parameter;
149 } else if (sym is Destructor) {
150 var d = (Destructor) sym;
151 method.this_parameter = d.this_parameter;
152 } else if (sym is Method) {
153 var m = (Method) sym;
154 method.this_parameter = m.this_parameter;
157 sym = sym.parent_symbol;
160 method.owner = analyzer.current_symbol.scope;
162 var lambda_params = get_parameters ();
163 Iterator<string> lambda_param_it = lambda_params.iterator ();
164 foreach (FormalParameter cb_param in cb.get_parameters ()) {
165 if (!lambda_param_it.next ()) {
166 /* lambda expressions are allowed to have less parameters */
167 break;
170 string lambda_param = lambda_param_it.get ();
172 var param = new FormalParameter (lambda_param, cb_param.parameter_type);
174 method.add_parameter (param);
177 if (lambda_param_it.next ()) {
178 /* lambda expressions may not expect more parameters */
179 error = true;
180 Report.error (source_reference, "lambda expression: too many parameters");
181 return false;
184 foreach (var error_type in cb.get_error_types ()) {
185 method.add_error_type (error_type.copy ());
188 if (expression_body != null) {
189 var block = new Block (source_reference);
190 block.scope.parent_scope = method.scope;
192 if (method.return_type.data_type != null) {
193 block.add_statement (new ReturnStatement (expression_body, source_reference));
194 } else {
195 block.add_statement (new ExpressionStatement (expression_body, source_reference));
198 method.body = block;
199 } else {
200 method.body = statement_body;
202 method.body.owner = method.scope;
204 /* lambda expressions should be usable like MemberAccess of a method */
205 symbol_reference = method;
207 if (method == null) {
208 if (expression_body != null) {
209 expression_body.check (analyzer);
210 } else if (statement_body != null) {
211 statement_body.check (analyzer);
213 } else {
214 method.check (analyzer);
217 value_type = new MethodType (method);
219 return !error;