1/* valamethod.vala
2 *
3 * Copyright (C) 2006-2010  Jürg Billeter
4 * Copyright (C) 2006-2008  Raffaele Sandrini
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
19 *
20 * Author:
21 * 	Jürg Billeter <j@bitron.ch>
22 *	Raffaele Sandrini <raffaele@sandrini.ch>
23 */
24
25using GLib;
26
27/**
28 * Represents a type or namespace method.
29 */
30public class Vala.Method : Subroutine, Callable {
31	List<TypeParameter> type_parameters;
32
33	/**
34	 * The return type of this method.
35	 */
36	public DataType return_type {
37		get { return _return_type; }
38		set {
39			_return_type = value;
40			_return_type.parent_node = this;
41		}
42	}
43
44	public override bool has_result {
45		get { return !(return_type is VoidType); }
46	}
47
48	/**
49	 * Specifies whether this method may only be called with an instance of
50	 * the contained type.
51	 */
52	public MemberBinding binding { get; set; default = MemberBinding.INSTANCE; }
53
54	/**
55	 * Specifies whether this method is abstract. Abstract methods have no
56	 * body, may only be specified within abstract classes, and must be
57	 * overridden by derived non-abstract classes.
58	 */
59	public bool is_abstract { get; set; }
60
61	/**
62	 * Specifies whether this method is virtual. Virtual methods may be
63	 * overridden by derived classes.
64	 */
65	public bool is_virtual { get; set; }
66
67	/**
68	 * Specifies whether this method overrides a virtual or abstract method
69	 * of a base type.
70	 */
71	public bool overrides { get; set; }
72
73	/**
74	 * Specifies whether this method should be inlined.
75	 */
76	public bool is_inline { get; set; }
77
78	public bool returns_floating_reference {
79		get {
80			return get_attribute_bool ("CCode", "returns_floating_reference");
81		}
82		set {
83			set_attribute_bool ("CCode", "returns_floating_reference", value);
84		}
85	}
86
87	/*
88	 * Specifies whether the C method returns a new instance pointer which
89	 * may be different from the previous instance pointer. Only valid for
90	 * imported methods.
91	 */
92	public bool returns_modified_pointer {
93		get {
94			return get_attribute ("ReturnsModifiedPointer") != null;
95		}
96		set {
97			set_attribute ("ReturnsModifiedPointer", value);
98		}
99	}
100
101	/**
102	 * Specifies the virtual or abstract method this method overrides.
103	 * Reference must be weak as virtual and abstract methods set
104	 * base_method to themselves.
105	 */
106	public Method base_method {
107		get {
108			find_base_methods ();
109			return _base_method;
110		}
111	}
112
113	/**
114	 * Specifies the abstract interface method this method implements.
115	 */
116	public Method base_interface_method {
117		get {
118			find_base_methods ();
119			return _base_interface_method;
120		}
121	}
122
123	/**
124	 * Specifies the explicit interface containing the method this method implements.
125	 */
126	public DataType base_interface_type {
127		get { return _base_interface_type; }
128		set {
129			_base_interface_type = value;
130			_base_interface_type.parent_node = this;
131		}
132	}
133
134	public bool entry_point { get; private set; }
135
136	/**
137	 * Specifies the generated `this` parameter for instance methods.
138	 */
139	public Parameter this_parameter { get; set; }
140
141	/**
142	 * Specifies whether this method expects printf-style format arguments.
143	 */
144	public bool printf_format {
145		get {
146			return get_attribute ("PrintfFormat") != null;
147		}
148		set {
149			set_attribute ("PrintfFormat", value);
150		}
151	}
152
153	/**
154	 * Specifies whether this method expects scanf-style format arguments.
155	 */
156	public bool scanf_format {
157		get {
158			return get_attribute ("ScanfFormat") != null;
159		}
160		set {
161			set_attribute ("ScanfFormat", value);
162		}
163	}
164
165	/**
166	 * Specifies whether a construct function with a GType parameter is
167	 * available. This is only applicable to creation methods.
168	 */
169	public bool has_construct_function {
170		get {
171			return get_attribute_bool ("CCode", "has_construct_function", true);
172		}
173		set {
174			set_attribute_bool ("CCode", "has_construct_function", value);
175		}
176	}
177
178	public LocalVariable? params_array_var { get; protected set; }
179
180	public weak Signal signal_reference { get; set; }
181
182	public bool closure { get; set; }
183
184	public bool coroutine { get; set; }
185
186	public bool is_async_callback { get; set; }
187
188	private List<Parameter> parameters = new ArrayList<Parameter> ();
189	private List<Expression> preconditions;
190	private List<Expression> postconditions;
191	private DataType _return_type;
192
193	protected List<DataType> error_types;
194
195	private weak Method _base_method;
196	private weak Method _base_interface_method;
197	private DataType _base_interface_type;
198	private bool base_methods_valid;
199
200	Method? callback_method;
201	Method? end_method;
202
203	// only valid for closures
204	List<LocalVariable> captured_variables;
205
206	static List<Expression> _empty_expression_list;
207	static List<TypeParameter> _empty_type_parameter_list;
208
209	/**
210	 * Creates a new method.
211	 *
212	 * @param name              method name
213	 * @param return_type       method return type
214	 * @param source_reference  reference to source code
215	 * @return                  newly created method
216	 */
217	public Method (string? name, DataType return_type, SourceReference? source_reference = null, Comment? comment = null) {
218		base (name, source_reference, comment);
219		this.return_type = return_type;
220	}
221
222	/**
223	 * Appends parameter to this method.
224	 *
225	 * @param param a formal parameter
226	 */
227	public void add_parameter (Parameter param) {
228		// default C parameter position
229		parameters.add (param);
230		scope.add (param.name, param);
231	}
232
233	public unowned List<Parameter> get_parameters () {
234		return parameters;
235	}
236
237	/**
238	 * Remove all parameters from this method.
239	 */
240	public void clear_parameters () {
241		foreach (Parameter param in parameters) {
242			if (!param.ellipsis) {
243				scope.remove (param.name);
244			}
245		}
246		parameters.clear ();
247	}
248
249	public bool is_variadic () {
250		foreach (Parameter param in parameters) {
251			if (param.ellipsis || param.params_array) {
252				return true;
253			}
254		}
255		return false;
256	}
257
258	public override void accept (CodeVisitor visitor) {
259		visitor.visit_method (this);
260	}
261
262	public override void accept_children (CodeVisitor visitor) {
263		foreach (TypeParameter p in get_type_parameters ()) {
264			p.accept (visitor);
265		}
266
267		if (base_interface_type != null) {
268			base_interface_type.accept (visitor);
269		}
270
271		if (return_type != null) {
272			return_type.accept (visitor);
273		}
274
275		foreach (Parameter param in parameters) {
276			param.accept (visitor);
277		}
278
279		if (error_types != null) {
280			foreach (DataType error_type in error_types) {
281			error_type.accept (visitor);
282		}
283		}
284
285		if (result_var != null) {
286			result_var.accept (visitor);
287		}
288
289		if (preconditions != null) {
290			foreach (Expression precondition in preconditions) {
291				precondition.accept (visitor);
292			}
293		}
294
295		if (postconditions != null) {
296			foreach (Expression postcondition in postconditions) {
297				postcondition.accept (visitor);
298			}
299		}
300
301		if (body != null) {
302			body.accept (visitor);
303		}
304	}
305
306	/**
307	 * Checks whether the parameters and return type of this method are
308	 * compatible with the specified method
309	 *
310	 * @param base_method a method
311	 * @param invalid_match error string about which check failed
312	 * @return true if the specified method is compatible to this method
313	 */
314	public bool compatible (Method base_method, out string? invalid_match) {
315		return compatible_internal (base_method, out invalid_match, this);
316	}
317
318	/**
319	 * Checks whether the parameters and return type of this method are
320	 * compatible with the specified method
321	 *
322	 * @param base_method a method
323	 * @return true if the specified method is compatible to this method
324	 */
325	public bool compatible_no_error (Method base_method) {
326		return compatible_internal (base_method, null, null);
327	}
328
329	bool compatible_internal (Method base_method, out string? invalid_match, CodeNode? node_reference) {
330		// method is always compatible to itself
331		if (this == base_method) {
332			invalid_match = null;
333			return true;
334		}
335
336		if (binding != base_method.binding) {
337			invalid_match = "incompatible binding";
338			return false;
339		}
340
341		ObjectType object_type = null;
342		if (parent_symbol is ObjectTypeSymbol) {
343			object_type = new ObjectType ((ObjectTypeSymbol) parent_symbol);
344			foreach (TypeParameter type_parameter in object_type.object_type_symbol.get_type_parameters ()) {
345				var type_arg = new GenericType (type_parameter);
346				type_arg.value_owned = true;
347				object_type.add_type_argument (type_arg);
348			}
349		}
350
351		if (this.get_type_parameters ().size < base_method.get_type_parameters ().size) {
352			invalid_match = "too few type parameters";
353			return false;
354		} else if (this.get_type_parameters ().size > base_method.get_type_parameters ().size) {
355			invalid_match = "too many type parameters";
356			return false;
357		}
358
359		List<DataType> method_type_args = null;
360		if (this.has_type_parameters ()) {
361			method_type_args = new ArrayList<DataType> ();
362			foreach (TypeParameter type_parameter in this.get_type_parameters ()) {
363				var type_arg = new GenericType (type_parameter);
364				type_arg.value_owned = true;
365				method_type_args.add (type_arg);
366			}
367		}
368
369		var return_type = this.return_type.copy ();
370		if (has_attribute_argument ("CCode", "returns_floating_reference")) {
371			return_type.floating_reference = returns_floating_reference;
372		} else {
373			return_type.floating_reference = base_method.returns_floating_reference;
374		}
375
376		var actual_base_type = base_method.return_type.get_actual_type (object_type, method_type_args, node_reference);
377		if (!return_type.equals (actual_base_type)) {
378			invalid_match = "Base method expected return type `%s', but `%s' was provided".printf (actual_base_type.to_prototype_string (), return_type.to_prototype_string ());
379			return false;
380		}
381
382		Iterator<Parameter> method_params_it = parameters.iterator ();
383		int param_index = 1;
384		foreach (Parameter base_param in base_method.parameters) {
385			/* this method may not expect less arguments */
386			if (!method_params_it.next ()) {
387				invalid_match = "too few parameters";
388				return false;
389			}
390
391			var param = method_params_it.get ();
392			if (base_param.ellipsis != param.ellipsis) {
393				invalid_match = "ellipsis parameter mismatch";
394				return false;
395			}
396			if (base_param.params_array != param.params_array) {
397				invalid_match = "params array parameter mismatch";
398				return false;
399			}
400			if (!base_param.ellipsis) {
401				if (base_param.direction != param.direction) {
402					invalid_match = "incompatible direction of parameter %d".printf (param_index);
403					return false;
404				}
405
406				actual_base_type = base_param.variable_type.get_actual_type (object_type, method_type_args, node_reference);
407				if (!actual_base_type.equals (param.variable_type)) {
408					invalid_match = "incompatible type of parameter %d".printf (param_index);
409					return false;
410				}
411			}
412			param_index++;
413		}
414
415		/* this method may not expect more arguments */
416		if (method_params_it.next ()) {
417			invalid_match = "too many parameters";
418			return false;
419		}
420
421		/* this method may throw less but not more errors than the base method */
422		var base_method_errors = new ArrayList<DataType> ();
423		base_method.get_error_types (base_method_errors);
424		if (error_types != null) {
425			foreach (DataType method_error_type in error_types) {
426			bool match = false;
427				foreach (DataType base_method_error_type in base_method_errors) {
428				if (method_error_type.compatible (base_method_error_type)) {
429					match = true;
430					break;
431				}
432			}
433
434			if (!match) {
435				invalid_match = "incompatible error type `%s'".printf (method_error_type.to_string ());
436				return false;
437			}
438		}
439		}
440		if (base_method.coroutine != this.coroutine) {
441			invalid_match = "async mismatch";
442			return false;
443		}
444
445		invalid_match = null;
446		return true;
447	}
448
449	/**
450	 * Appends the specified parameter to the list of type parameters.
451	 *
452	 * @param p a type parameter
453	 */
454	public void add_type_parameter (TypeParameter p) {
455		if (type_parameters == null) {
456			type_parameters = new ArrayList<TypeParameter> ();
457		}
458		type_parameters.add (p);
459		scope.add (p.name, p);
460	}
461
462	/**
463	 * Returns the type parameter list.
464	 *
465	 * @return list of type parameters
466	 */
467	public unowned List<TypeParameter> get_type_parameters () {
468		if (type_parameters != null) {
469			return type_parameters;
470		}
471		if (_empty_type_parameter_list == null) {
472			_empty_type_parameter_list = new ArrayList<TypeParameter> ();
473		}
474		return _empty_type_parameter_list;
475	}
476
477	public int get_type_parameter_index (string name) {
478		if (type_parameters == null) {
479			return -1;
480		}
481
482		int i = 0;
483		foreach (TypeParameter parameter in type_parameters) {
484			if (parameter.name == name) {
485				return i;
486			}
487			i++;
488		}
489		return -1;
490	}
491
492	public bool has_type_parameters () {
493		return (type_parameters != null && type_parameters.size > 0);
494	}
495
496	/**
497	 * Adds a precondition to this method.
498	 *
499	 * @param precondition a boolean precondition expression
500	 */
501	public void add_precondition (Expression precondition) {
502		if (preconditions == null) {
503			preconditions = new ArrayList<Expression> ();
504		}
505		preconditions.add (precondition);
506		precondition.parent_node = this;
507	}
508
509	/**
510	 * Returns the list of preconditions of this method.
511	 *
512	 * @return list of preconditions
513	 */
514	public unowned List<Expression> get_preconditions () {
515		if (preconditions != null) {
516			return preconditions;
517		}
518		if (_empty_expression_list == null) {
519			_empty_expression_list = new ArrayList<Expression> ();
520		}
521		return _empty_expression_list;
522	}
523
524	/**
525	 * Adds a postcondition to this method.
526	 *
527	 * @param postcondition a boolean postcondition expression
528	 */
529	public void add_postcondition (Expression postcondition) {
530		if (postconditions == null) {
531			postconditions = new ArrayList<Expression> ();
532		}
533		postconditions.add (postcondition);
534		postcondition.parent_node = this;
535	}
536
537	/**
538	 * Returns the list of postconditions of this method.
539	 *
540	 * @return list of postconditions
541	 */
542	public unowned List<Expression> get_postconditions () {
543		if (postconditions != null) {
544			return postconditions;
545		}
546		if (_empty_expression_list == null) {
547			_empty_expression_list = new ArrayList<Expression> ();
548		}
549		return _empty_expression_list;
550	}
551
552	/**
553	 * Adds an error type to the exceptions that can be
554	 * thrown by this method.
555	 */
556	public void add_error_type (DataType error_type) {
557		if (error_types == null) {
558			error_types = new ArrayList<DataType> ();
559		}
560		error_types.add (error_type);
561		error_type.parent_node = this;
562	}
563
564	public override void get_error_types (Collection<DataType> collection, SourceReference? source_reference = null) {
565		if (error_types != null) {
566			foreach (var error_type in error_types) {
567				if (source_reference != null) {
568					var type = error_type.copy ();
569					type.source_reference = source_reference;
570					collection.add (type);
571				} else {
572					collection.add (error_type);
573				}
574			}
575		}
576	}
577
578	public override void replace_type (DataType old_type, DataType new_type) {
579		if (base_interface_type == old_type) {
580			base_interface_type = new_type;
581			return;
582		}
583		if (return_type == old_type) {
584			return_type = new_type;
585			return;
586		}
587		if (error_types != null) {
588		for (int i = 0; i < error_types.size; i++) {
589			if (error_types[i] == old_type) {
590				error_types[i] = new_type;
591				return;
592			}
593		}
594	}
595	}
596
597	private void find_base_methods () {
598		if (base_methods_valid) {
599			return;
600		}
601
602		if (parent_symbol is Class) {
603			if (!(this is CreationMethod)) {
604				find_base_interface_method ((Class) parent_symbol);
605				if (is_virtual || is_abstract || overrides) {
606					find_base_class_method ((Class) parent_symbol);
607				}
608			}
609		} else if (parent_symbol is Interface) {
610			if (is_virtual || is_abstract) {
611				_base_interface_method = this;
612			}
613		}
614
615		base_methods_valid = true;
616	}
617
618	private void find_base_class_method (Class cl) {
619		var sym = cl.scope.lookup (name);
620		if (sym is Signal) {
621			unowned Signal sig = (Signal) sym;
622			sym = sig.default_handler;
623		}
624		if (sym is Method) {
625			unowned Method base_method = (Method) sym;
626			if (base_method.is_abstract || base_method.is_virtual) {
627				string invalid_match;
628				if (!compatible (base_method, out invalid_match)) {
629					error = true;
630					var base_method_type = new MethodType (base_method);
631					Report.error (source_reference, "overriding method `%s' is incompatible with base method `%s': %s.".printf (get_full_name (), base_method_type.to_prototype_string (), invalid_match));
632					return;
633				}
634
635				_base_method = base_method;
636				copy_attribute_double (base_method, "CCode", "instance_pos");
637				copy_attribute_bool (base_method, "CCode", "returns_floating_reference");
638				return;
639			}
640		}
641
642		if (cl.base_class != null) {
643			find_base_class_method (cl.base_class);
644		}
645	}
646
647	private void find_base_interface_method (Class cl) {
648		Method? base_match = null;
649
650		string? invalid_error = null;
651		Method? invalid_base_match = null;
652
653		foreach (DataType type in cl.get_base_types ()) {
654			if (type.type_symbol is Interface) {
655				if (base_interface_type != null && base_interface_type.type_symbol != type.type_symbol) {
656					continue;
657				}
658
659				var sym = type.type_symbol.scope.lookup (name);
660				if (sym is Signal) {
661					unowned Signal sig = (Signal) sym;
662					sym = sig.default_handler;
663				}
664				if (sym is Method) {
665					unowned Method base_method = (Method) sym;
666					if (base_method.is_abstract || base_method.is_virtual) {
667						if (base_interface_type == null) {
668							// check for existing explicit implementation
669							var has_explicit_implementation = false;
670							foreach (var m in cl.get_methods ()) {
671								if (m.base_interface_type != null && base_method == m.base_interface_method) {
672									has_explicit_implementation = true;
673									break;
674								}
675							}
676							if (has_explicit_implementation) {
677								continue;
678							}
679						}
680
681						string invalid_match = null;
682						if (!compatible (base_method, out invalid_match)) {
683							invalid_error = invalid_match;
684							invalid_base_match = base_method;
685						} else {
686							base_match = base_method;
687							break;
688						}
689					}
690				}
691			}
692		}
693
694		if (base_match != null) {
695			_base_interface_method = base_match;
696			copy_attribute_double (base_match, "CCode", "instance_pos");
697			copy_attribute_bool (base_match, "CCode", "returns_floating_reference");
698			return;
699		} else if (!hides && invalid_base_match != null) {
700			error = true;
701			var base_method_type = new MethodType (invalid_base_match);
702			Report.error (source_reference, "overriding method `%s' is incompatible with base method `%s': %s.".printf (get_full_name (), base_method_type.to_prototype_string (), invalid_error));
703			return;
704		}
705
706		if (base_interface_type != null) {
707			Report.error (source_reference, "`%s': no suitable interface method found to implement".printf (get_full_name ()));
708		}
709	}
710
711	public override bool check (CodeContext context) {
712		if (checked) {
713			return !error;
714		}
715
716		checked = true;
717
718		if (this_parameter != null) {
719			this_parameter.check (context);
720		}
721
722		if (get_attribute ("DestroysInstance") != null) {
723			this_parameter.variable_type.value_owned = true;
724		}
725		if (get_attribute ("NoThrow") != null) {
726			error_types = null;
727		}
728
729		if (parent_symbol is Class && (is_abstract || is_virtual)) {
730			unowned Class cl = (Class) parent_symbol;
731			if (cl.is_compact && cl.base_class != null) {
732				error = true;
733				Report.error (source_reference, "Abstract and virtual methods may not be declared in derived compact classes");
734				return false;
735			}
736		}
737
738		if (is_variadic () && (is_abstract || is_virtual)) {
739			error = true;
740			Report.error (source_reference, "Abstract and virtual methods may not be variadic. Use a `va_list' parameter instead of `...' or params-array.");
741			return false;
742		}
743
744		if (get_attribute ("NoWrapper") != null && !(is_abstract || is_virtual)) {
745			error = true;
746			Report.error (source_reference, "[NoWrapper] methods must be declared abstract or virtual");
747			return false;
748		}
749
750		if (is_abstract) {
751			if (parent_symbol is Class) {
752				unowned Class cl = (Class) parent_symbol;
753				if (!cl.is_abstract) {
754					error = true;
755					Report.error (source_reference, "Abstract methods may not be declared in non-abstract classes");
756					return false;
757				}
758			} else if (!(parent_symbol is Interface)) {
759				error = true;
760				Report.error (source_reference, "Abstract methods may not be declared outside of classes and interfaces");
761				return false;
762			}
763		} else if (is_virtual) {
764			if (!(parent_symbol is Class) && !(parent_symbol is Interface)) {
765				error = true;
766				Report.error (source_reference, "Virtual methods may not be declared outside of classes and interfaces");
767				return false;
768			}
769		} else if (overrides) {
770			if (!(parent_symbol is Class)) {
771				error = true;
772				Report.error (source_reference, "Methods may not be overridden outside of classes");
773				return false;
774			}
775		} else if (access == SymbolAccessibility.PROTECTED) {
776			if (!(parent_symbol is Class) && !(parent_symbol is Interface)) {
777				error = true;
778				Report.error (source_reference, "Protected methods may not be declared outside of classes and interfaces");
779				return false;
780			}
781		}
782
783		if (is_abstract && body != null) {
784			error = true;
785			Report.error (source_reference, "Abstract methods cannot have bodies");
786		} else if ((is_abstract || is_virtual) && is_extern) {
787			error = true;
788			Report.error (source_reference, "Extern methods cannot be abstract or virtual");
789		} else if (is_extern && body != null) {
790			error = true;
791			Report.error (source_reference, "Extern methods cannot have bodies");
792		} else if (!is_abstract && !external && source_type == SourceFileType.SOURCE && body == null) {
793			error = true;
794			Report.error (source_reference, "Non-abstract, non-extern methods must have bodies");
795		}
796
797		if (coroutine && !external_package && !context.has_package ("gio-2.0")) {
798			error = true;
799			Report.error (source_reference, "gio-2.0 package required for async methods");
800			return false;
801		}
802
803		var old_source_file = context.analyzer.current_source_file;
804		var old_symbol = context.analyzer.current_symbol;
805
806		if (source_reference != null) {
807			context.analyzer.current_source_file = source_reference.file;
808		}
809		context.analyzer.current_symbol = this;
810
811		return_type.floating_reference = returns_floating_reference;
812		return_type.check (context);
813		if (!external_package) {
814			context.analyzer.check_type (return_type);
815		}
816
817		if (return_type.type_symbol == context.analyzer.va_list_type.type_symbol) {
818			error = true;
819			Report.error (source_reference, "`%s' not supported as return type".printf (return_type.type_symbol.get_full_name ()));
820			return false;
821		}
822
823		var init_attr = get_attribute ("ModuleInit");
824		if (init_attr != null) {
825			source_reference.file.context.module_init_method = this;
826		}
827
828		if (return_type != null) {
829			return_type.check (context);
830		}
831
832		if (parameters.size == 1 && parameters[0].ellipsis && body != null && binding != MemberBinding.INSTANCE) {
833			// accept just `...' for external methods and instance methods
834			error = true;
835			Report.error (parameters[0].source_reference, "Named parameter required before `...'");
836		}
837
838		if (get_attribute ("Print") != null && (parameters.size != 1 || parameters[0].variable_type.type_symbol != context.analyzer.string_type.type_symbol)) {
839			error = true;
840			Report.error (source_reference, "[Print] methods must have exactly one parameter of type `string'");
841		}
842
843		var optional_param = false;
844		var params_array_param = false;
845		foreach (Parameter param in parameters) {
846			if (!param.check (context)) {
847				error = true;
848				continue;
849			}
850			if (coroutine && param.direction == ParameterDirection.REF) {
851				error = true;
852				Report.error (param.source_reference, "Reference parameters are not supported for async methods");
853			}
854			if (!external_package && coroutine && (param.ellipsis || param.params_array || param.variable_type.type_symbol == context.analyzer.va_list_type.type_symbol)) {
855				error = true;
856				Report.error (param.source_reference, "Variadic parameters are not supported for async methods");
857				return false;
858			}
859			// TODO: begin and end parameters must be checked separately for coroutines
860			if (coroutine) {
861				continue;
862			}
863			if (optional_param && param.initializer == null && !param.ellipsis) {
864				Report.warning (param.source_reference, "parameter without default follows parameter with default");
865			} else if (param.initializer != null) {
866				optional_param = true;
867			}
868
869			if (params_array_param) {
870				Report.error (param.source_reference, "parameter follows params-array parameter");
871			} else if (param.params_array) {
872				params_array_param = true;
873			}
874			// Add local variable to provide access to params arrays which will be constructed out of the given va-args
875			if (param.params_array && body != null) {
876				if (params_array_var != null) {
877					error = true;
878					Report.error (param.source_reference, "Only one params-array parameter is allowed");
879					continue;
880				}
881				if (!context.experimental) {
882					Report.warning (param.source_reference, "Support of params-arrays is experimental");
883				}
884				var type = (ArrayType) param.variable_type.copy ();
885				type.element_type.value_owned = type.value_owned;
886				type.value_owned = true;
887				if (type.element_type.is_real_struct_type () && !type.element_type.nullable) {
888					error = true;
889					Report.error (param.source_reference, "Only nullable struct elements are supported in params-array");
890				}
891				if (type.length != null) {
892					error = true;
893					Report.error (param.source_reference, "Passing length to params-array is not supported yet");
894				}
895				params_array_var = new LocalVariable (type, param.name, null, param.source_reference);
896				body.insert_statement (0, new DeclarationStatement (params_array_var, param.source_reference));
897			}
898		}
899
900		if (coroutine) {
901			// TODO: async methods with out-parameters before in-parameters are not supported
902			bool requires_pointer = false;
903			for (int i = parameters.size - 1; i >= 0; i--) {
904				var param = parameters[i];
905				if (param.direction == ParameterDirection.IN) {
906					requires_pointer = true;
907				} else if (requires_pointer) {
908					error = true;
909					Report.error (param.source_reference, "Synchronous out-parameters are not supported in async methods");
910				}
911			}
912		}
913
914		if (error_types != null) {
915			foreach (DataType error_type in error_types) {
916				error_type.check (context);
917
918				// check whether error type is at least as accessible as the method
919				if (!context.analyzer.is_type_accessible (this, error_type)) {
920					error = true;
921					Report.error (source_reference, "error type `%s' is less accessible than method `%s'".printf (error_type.to_string (), get_full_name ()));
922					return false;
923				}
924			}
925		}
926
927		if (result_var != null) {
928			result_var.check (context);
929		}
930
931		if (preconditions != null) {
932			foreach (Expression precondition in preconditions) {
933				precondition.check (context);
934			}
935		}
936
937		if (postconditions != null) {
938			foreach (Expression postcondition in postconditions) {
939				postcondition.check (context);
940			}
941		}
942
943		if (body != null) {
944			body.check (context);
945		}
946
947		if (context.analyzer.current_struct != null) {
948			if (is_abstract || is_virtual || overrides) {
949				error = true;
950				Report.error (source_reference, "A struct member `%s' cannot be marked as override, virtual, or abstract".printf (get_full_name ()));
951				return false;
952			}
953		} else if (overrides && base_method == null && base_interface_method != null && base_interface_method.is_abstract) {
954			Report.warning (source_reference, "`override' not required to implement `abstract' interface method `%s'".printf (base_interface_method.get_full_name ()));
955			overrides = false;
956		} else if (overrides && base_method == null && base_interface_method == null) {
957			Report.error (source_reference, "`%s': no suitable method found to override".printf (get_full_name ()));
958		} else if ((is_abstract || is_virtual || overrides) && access == SymbolAccessibility.PRIVATE) {
959			error = true;
960			Report.error (source_reference, "Private member `%s' cannot be marked as override, virtual, or abstract".printf (get_full_name ()));
961			return false;
962		}
963
964		if (base_interface_type != null && base_interface_method != null && parent_symbol is Class) {
965			unowned Class cl = (Class) parent_symbol;
966			foreach (var m in cl.get_methods ()) {
967				if (m != this && m.base_interface_method == base_interface_method) {
968					m.checked = true;
969					m.error = true;
970					error = true;
971					Report.error (source_reference, "`%s' already contains an implementation for `%s'".printf (cl.get_full_name (), base_interface_method.get_full_name ()));
972					Report.notice (m.source_reference, "previous implementation of `%s' was here".printf (base_interface_method.get_full_name ()));
973					return false;
974				}
975			}
976		}
977
978		context.analyzer.current_source_file = old_source_file;
979		context.analyzer.current_symbol = old_symbol;
980
981		if (!external_package && !overrides && !hides && get_hidden_member () != null) {
982			Report.warning (source_reference, "%s hides inherited method `%s'. Use the `new' keyword if hiding was intentional".printf (get_full_name (), get_hidden_member ().get_full_name ()));
983		}
984
985		// check whether return type is at least as accessible as the method
986		if (!context.analyzer.is_type_accessible (this, return_type)) {
987			error = true;
988			Report.error (source_reference, "return type `%s' is less accessible than method `%s'".printf (return_type.to_string (), get_full_name ()));
989			return false;
990		}
991
992		foreach (Expression precondition in get_preconditions ()) {
993			if (precondition.error) {
994				// if there was an error in the precondition, skip this check
995				error = true;
996				return false;
997			}
998
999			if (!precondition.value_type.compatible (context.analyzer.bool_type)) {
1000				error = true;
1001				Report.error (precondition.source_reference, "Precondition must be boolean");
1002				return false;
1003			}
1004		}
1005
1006		foreach (Expression postcondition in get_postconditions ()) {
1007			if (postcondition.error) {
1008				// if there was an error in the postcondition, skip this check
1009				error = true;
1010				return false;
1011			}
1012
1013			if (!postcondition.value_type.compatible (context.analyzer.bool_type)) {
1014				error = true;
1015				Report.error (postcondition.source_reference, "Postcondition must be boolean");
1016				return false;
1017			}
1018		}
1019
1020		// check that all errors that can be thrown in the method body are declared
1021		if (body != null && !body.error) {
1022			var body_errors = new ArrayList<DataType> ();
1023			body.get_error_types (body_errors);
1024			foreach (DataType body_error_type in body_errors) {
1025				bool can_propagate_error = false;
1026				if (error_types != null) {
1027					foreach (DataType method_error_type in error_types) {
1028					if (body_error_type.compatible (method_error_type)) {
1029						can_propagate_error = true;
1030					}
1031				}
1032				}
1033				bool is_dynamic_error = body_error_type is ErrorType && ((ErrorType) body_error_type).dynamic_error;
1034				if (!can_propagate_error && !is_dynamic_error) {
1035					Report.warning (body_error_type.source_reference, "unhandled error `%s'".printf (body_error_type.to_string()));
1036				}
1037			}
1038		}
1039
1040		// check that DBus methods at least throw "GLib.Error" or "GLib.DBusError, GLib.IOError"
1041		if (!(this is CreationMethod) && binding == MemberBinding.INSTANCE
1042		    && !overrides && access == SymbolAccessibility.PUBLIC
1043		    && parent_symbol is ObjectTypeSymbol && parent_symbol.get_attribute ("DBus") != null) {
1044			Attribute? dbus_attr = get_attribute ("DBus");
1045			if (dbus_attr == null || dbus_attr.get_bool ("visible", true)) {
1046				bool throws_gerror = false;
1047				bool throws_gioerror = false;
1048				bool throws_gdbuserror = false;
1049				var error_types = new ArrayList<DataType> ();
1050				get_error_types (error_types);
1051				foreach (DataType error_type in error_types) {
1052					if (!(error_type is ErrorType)) {
1053						continue;
1054					}
1055					unowned ErrorDomain? error_domain = ((ErrorType) error_type).error_domain;
1056					if (error_domain == null) {
1057						throws_gerror = true;
1058						break;
1059					}
1060					string? full_error_domain = error_domain.get_full_name ();
1061					if (full_error_domain == "GLib.IOError") {
1062						throws_gioerror = true;
1063					} else if (full_error_domain == "GLib.DBusError") {
1064						throws_gdbuserror = true;
1065					}
1066				}
1067				if (!throws_gerror && !(throws_gioerror && throws_gdbuserror)) {
1068					Report.warning (source_reference, "DBus methods are recommended to throw at least `GLib.Error' or `GLib.DBusError, GLib.IOError'");
1069				}
1070			}
1071		}
1072
1073		if (is_possible_entry_point (context)) {
1074			if (context.entry_point != null) {
1075				error = true;
1076				Report.error (source_reference, "program already has an entry point `%s'".printf (context.entry_point.get_full_name ()));
1077				return false;
1078			}
1079			entry_point = true;
1080			context.entry_point = this;
1081
1082			if (tree_can_fail) {
1083				error = true;
1084				Report.error (source_reference, "\"main\" method cannot throw errors");
1085			}
1086
1087			if (is_inline) {
1088				error = true;
1089				Report.error (source_reference, "\"main\" method cannot be inline");
1090			}
1091
1092			if (coroutine) {
1093				error = true;
1094				Report.error (source_reference, "\"main\" method cannot be async");
1095			}
1096		}
1097
1098		if (get_attribute ("GtkCallback") != null) {
1099			used = true;
1100		}
1101
1102		return !error;
1103	}
1104
1105	bool is_possible_entry_point (CodeContext context) {
1106		if (external_package) {
1107			return false;
1108		}
1109
1110		if (context.entry_point_name == null) {
1111			if (name == null || name != "main") {
1112				// method must be called "main"
1113				return false;
1114			}
1115		} else {
1116			// custom entry point name
1117			if (get_full_name () != context.entry_point_name) {
1118				return false;
1119			}
1120		}
1121
1122		if (binding == MemberBinding.INSTANCE) {
1123			// method must be static
1124			return false;
1125		}
1126
1127		if (return_type is VoidType) {
1128		} else if (return_type.type_symbol == context.analyzer.int_type.type_symbol) {
1129		} else {
1130			// return type must be void or int
1131			return false;
1132		}
1133
1134		var params = get_parameters ();
1135		if (params.size == 0) {
1136			// method may have no parameters
1137			return true;
1138		}
1139
1140		if (params.size > 1) {
1141			// method must not have more than one parameter
1142			return false;
1143		}
1144
1145		Iterator<Parameter> params_it = params.iterator ();
1146		params_it.next ();
1147		var param = params_it.get ();
1148
1149		if (param.direction == ParameterDirection.OUT) {
1150			// parameter must not be an out parameter
1151			return false;
1152		}
1153
1154		if (!(param.variable_type is ArrayType)) {
1155			// parameter must be an array
1156			return false;
1157		}
1158
1159		unowned ArrayType array_type = (ArrayType) param.variable_type;
1160		if (array_type.element_type.type_symbol != context.analyzer.string_type.type_symbol) {
1161			// parameter must be an array of strings
1162			return false;
1163		}
1164
1165		return true;
1166	}
1167
1168	public int get_required_arguments () {
1169		int n = 0;
1170		foreach (var param in parameters) {
1171			if (param.initializer != null || param.ellipsis) {
1172				// optional argument
1173				break;
1174			}
1175			n++;
1176		}
1177		return n;
1178	}
1179
1180	public unowned Method get_end_method () {
1181		assert (this.coroutine);
1182
1183		if (end_method == null) {
1184			end_method = new Method ("end", return_type, source_reference);
1185			end_method.access = SymbolAccessibility.PUBLIC;
1186			end_method.external = true;
1187			end_method.owner = scope;
1188			foreach (var param in get_async_end_parameters ()) {
1189				end_method.add_parameter (param.copy ());
1190			}
1191			foreach (var param in get_type_parameters ()) {
1192				end_method.add_type_parameter (param);
1193			}
1194			end_method.copy_attribute_double (this, "CCode", "async_result_pos");
1195		}
1196		return end_method;
1197	}
1198
1199	public unowned Method get_callback_method () {
1200		assert (this.coroutine);
1201
1202		if (callback_method == null) {
1203			var bool_type = new BooleanType ((Struct) CodeContext.get ().root.scope.lookup ("bool"));
1204			bool_type.value_owned = true;
1205			callback_method = new Method ("callback", bool_type, source_reference);
1206			callback_method.access = SymbolAccessibility.PUBLIC;
1207			callback_method.external = true;
1208			callback_method.binding = MemberBinding.INSTANCE;
1209			callback_method.owner = scope;
1210			callback_method.is_async_callback = true;
1211		}
1212		return callback_method;
1213	}
1214
1215	public List<Parameter> get_async_begin_parameters () {
1216		assert (this.coroutine);
1217
1218		var glib_ns = CodeContext.get ().root.scope.lookup ("GLib");
1219
1220		var params = new ArrayList<Parameter> ();
1221		Parameter ellipsis = null;
1222		foreach (var param in parameters) {
1223			if (param.ellipsis) {
1224				ellipsis = param;
1225			} else if (param.direction == ParameterDirection.IN) {
1226				params.add (param);
1227			}
1228		}
1229
1230		var callback_type = new DelegateType ((Delegate) glib_ns.scope.lookup ("AsyncReadyCallback"));
1231		callback_type.nullable = true;
1232		callback_type.value_owned = true;
1233		callback_type.is_called_once = true;
1234
1235		var callback_param = new Parameter ("_callback_", callback_type);
1236		callback_param.initializer = new NullLiteral (source_reference);
1237		callback_param.initializer.target_type = callback_type.copy ();
1238		callback_param.set_attribute_double ("CCode", "pos", -1);
1239		callback_param.set_attribute_double ("CCode", "delegate_target_pos", -0.9);
1240
1241		params.add (callback_param);
1242
1243		if (ellipsis != null) {
1244			params.add (ellipsis);
1245		}
1246
1247		return params;
1248	}
1249
1250	public List<Parameter> get_async_end_parameters () {
1251		assert (this.coroutine);
1252
1253		var params = new ArrayList<Parameter> ();
1254
1255		var glib_ns = CodeContext.get ().root.scope.lookup ("GLib");
1256		var result_type = new ObjectType ((ObjectTypeSymbol) glib_ns.scope.lookup ("AsyncResult"));
1257
1258		var result_param = new Parameter ("_res_", result_type);
1259		result_param.set_attribute_double ("CCode", "pos", get_attribute_double ("CCode", "async_result_pos", 0.1));
1260		params.add (result_param);
1261
1262		foreach (var param in parameters) {
1263			if (param.direction == ParameterDirection.OUT) {
1264				params.add (param);
1265			}
1266		}
1267
1268		return params;
1269	}
1270
1271	public void add_captured_variable (LocalVariable local) {
1272		assert (this.closure);
1273
1274		if (captured_variables == null) {
1275			captured_variables = new ArrayList<LocalVariable> ();
1276		}
1277		captured_variables.add (local);
1278	}
1279
1280	public void get_captured_variables (Collection<LocalVariable> variables) {
1281		if (captured_variables != null) {
1282			foreach (var local in captured_variables) {
1283				variables.add (local);
1284			}
1285		}
1286	}
1287
1288	public override void get_defined_variables (Collection<Variable> collection) {
1289		if (result_var != null) {
1290			collection.add (result_var);
1291		}
1292		if (params_array_var != null) {
1293			collection.add (params_array_var);
1294		}
1295
1296		// capturing variables is only supported if they are initialized
1297		// therefore assume that captured variables are initialized
1298		if (closure) {
1299			get_captured_variables ((Collection<LocalVariable>) collection);
1300		}
1301	}
1302
1303	public int get_format_arg_index () {
1304		for (int i = 0; i < parameters.size; i++) {
1305			if (parameters[i].format_arg) {
1306				return i;
1307			}
1308		}
1309		return -1;
1310	}
1311
1312	public bool has_error_type_parameter () {
1313		if (tree_can_fail) {
1314			return true;
1315		}
1316		if (base_method != null && base_method != this && base_method.has_error_type_parameter ()) {
1317			return true;
1318		}
1319		if (base_interface_method != null && base_interface_method != this && base_interface_method.has_error_type_parameter ()) {
1320			return true;
1321		}
1322		return false;
1323	}
1324}
1325
1326// vim:sw=8 noet
1327