valamethod.vala 32 KB
Newer Older
1 2
/* valamethod.vala
 *
3
 * Copyright (C) 2006-2010  Jürg Billeter
4
 * Copyright (C) 2006-2008  Raffaele Sandrini
5 6 7 8
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * 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 12 13 14 15 16 17 18 19 20 21

 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 *
 * Author:
 * 	Jürg Billeter <j@bitron.ch>
22
 *	Raffaele Sandrini <raffaele@sandrini.ch>
23 24 25 26
 */

using GLib;

27 28 29
/**
 * Represents a type or namespace method.
 */
30
public class Vala.Method : Subroutine, Callable {
31
	List<TypeParameter> type_parameters;
32

33 34 35
	/**
	 * The return type of this method.
	 */
36 37 38
	public DataType return_type {
		get { return _return_type; }
		set {
39
			_return_type = value;
40 41 42
			_return_type.parent_node = this;
		}
	}
43

44 45 46 47
	public override bool has_result {
		get { return !(return_type is VoidType); }
	}

48 49 50 51
	/**
	 * Specifies whether this method may only be called with an instance of
	 * the contained type.
	 */
52
	public MemberBinding binding { get; set; default = MemberBinding.INSTANCE; }
53

54 55 56 57 58 59
	/**
	 * Specifies whether this method is abstract. Abstract methods have no
	 * body, may only be specified within abstract classes, and must be
	 * overriden by derived non-abstract classes.
	 */
	public bool is_abstract { get; set; }
60

61 62 63 64 65
	/**
	 * Specifies whether this method is virtual. Virtual methods may be
	 * overridden by derived classes.
	 */
	public bool is_virtual { get; set; }
66

67 68 69 70 71
	/**
	 * Specifies whether this method overrides a virtual or abstract method
	 * of a base type.
	 */
	public bool overrides { get; set; }
72

73 74 75 76
	/**
	 * Specifies whether this method should be inlined.
	 */
	public bool is_inline { get; set; }
77

78 79 80 81 82 83 84 85
	public bool returns_floating_reference {
		get {
			return get_attribute_bool ("CCode", "returns_floating_reference");
		}
		set {
			set_attribute_bool ("CCode", "returns_floating_reference", value);
		}
	}
86

87
	/*
88 89 90 91
	 * Specifies whether the C method returns a new instance pointer which
	 * may be different from the previous instance pointer. Only valid for
	 * imported methods.
	 */
92 93 94 95 96 97 98 99
	public bool returns_modified_pointer {
		get {
			return get_attribute ("ReturnsModifiedPointer") != null;
		}
		set {
			set_attribute ("ReturnsModifiedPointer", value);
		}
	}
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 107 108 109 110 111
	public Method base_method {
		get {
			find_base_methods ();
			return _base_method;
		}
	}
112

113 114 115
	/**
	 * Specifies the abstract interface method this method implements.
	 */
116 117 118 119 120 121
	public Method base_interface_method {
		get {
			find_base_methods ();
			return _base_interface_method;
		}
	}
122

123 124 125 126 127 128 129 130 131 132 133
	/**
	 * Specifies the explicit interface containing the method this method implements.
	 */
	public DataType base_interface_type {
		get { return _base_interface_type; }
		set {
			_base_interface_type = value;
			_base_interface_type.parent_node = this;
		}
	}

134 135
	public bool entry_point { get; private set; }

136
	/**
Didier 'Ptitjes's avatar
Didier 'Ptitjes committed
137
	 * Specifies the generated `this` parameter for instance methods.
138
	 */
139
	public Parameter this_parameter { get; set; }
140

141 142 143
	/**
	 * Specifies whether this method expects printf-style format arguments.
	 */
144 145 146 147 148 149 150 151
	public bool printf_format {
		get {
			return get_attribute ("PrintfFormat") != null;
		}
		set {
			set_attribute ("PrintfFormat", value);
		}
	}
152

153 154 155
	/**
	 * Specifies whether this method expects scanf-style format arguments.
	 */
156 157 158 159 160 161 162 163
	public bool scanf_format {
		get {
			return get_attribute ("ScanfFormat") != null;
		}
		set {
			set_attribute ("ScanfFormat", value);
		}
	}
164

165 166 167 168
	/**
	 * Specifies whether a construct function with a GType parameter is
	 * available. This is only applicable to creation methods.
	 */
169 170 171 172 173 174 175 176
	public bool has_construct_function {
		get {
			return get_attribute_bool ("CCode", "has_construct_function", true);
		}
		set {
			set_attribute_bool ("CCode", "has_construct_function", value);
		}
	}
177

178 179
	public weak Signal signal_reference { get; set; }

180 181
	public bool closure { get; set; }

Jürg Billeter's avatar
Jürg Billeter committed
182 183
	public bool coroutine { get; set; }

184 185
	public bool is_async_callback { get; set; }

186
	private List<Parameter> parameters = new ArrayList<Parameter> ();
187 188
	private List<Expression> preconditions;
	private List<Expression> postconditions;
189
	private DataType _return_type;
190

191
	private weak Method _base_method;
192
	private weak Method _base_interface_method;
193
	private DataType _base_interface_type;
194 195
	private bool base_methods_valid;

196
	Method? callback_method;
197
	Method? end_method;
198

199
	// only valid for closures
200
	List<LocalVariable> captured_variables;
201

202 203 204
	static List<Expression> _empty_expression_list;
	static List<TypeParameter> _empty_type_parameter_list;

205 206 207
	/**
	 * Creates a new method.
	 *
208 209 210 211
	 * @param name              method name
	 * @param return_type       method return type
	 * @param source_reference  reference to source code
	 * @return                  newly created method
212
	 */
213 214
	public Method (string? name, DataType return_type, SourceReference? source_reference = null, Comment? comment = null) {
		base (name, source_reference, comment);
215
		this.return_type = return_type;
216 217
	}

218 219 220 221 222
	/**
	 * Appends parameter to this method.
	 *
	 * @param param a formal parameter
	 */
223
	public void add_parameter (Parameter param) {
224
		// default C parameter position
225
		parameters.add (param);
226
		scope.add (param.name, param);
227
	}
228

229
	public List<Parameter> get_parameters () {
230
		return parameters;
231
	}
232

233 234 235 236
	/**
	 * Remove all parameters from this method.
	 */
	public void clear_parameters () {
237
		foreach (Parameter param in parameters) {
238 239 240 241 242 243 244
			if (!param.ellipsis) {
				scope.remove (param.name);
			}
		}
		parameters.clear ();
	}

245 246 247 248 249 250 251 252 253
	public bool is_variadic () {
		foreach (Parameter param in parameters) {
			if (param.ellipsis) {
				return true;
			}
		}
		return false;
	}

254
	public override void accept (CodeVisitor visitor) {
255 256 257
		visitor.visit_method (this);
	}

258
	public override void accept_children (CodeVisitor visitor) {
259 260 261 262
		foreach (TypeParameter p in get_type_parameters ()) {
			p.accept (visitor);
		}

263 264 265 266
		if (base_interface_type != null) {
			base_interface_type.accept (visitor);
		}

267 268 269
		if (return_type != null) {
			return_type.accept (visitor);
		}
270

271
		foreach (Parameter param in parameters) {
272
			param.accept (visitor);
273
		}
274

275 276
		foreach (DataType error_type in get_error_types ()) {
			error_type.accept (visitor);
277 278
		}

279
		if (result_var != null) {
280
			result_var.accept (visitor);
281 282
		}

283 284 285 286
		if (preconditions != null) {
			foreach (Expression precondition in preconditions) {
				precondition.accept (visitor);
			}
287 288
		}

289 290 291 292
		if (postconditions != null) {
			foreach (Expression postcondition in postconditions) {
				postcondition.accept (visitor);
			}
293 294
		}

295 296
		if (body != null) {
			body.accept (visitor);
297
		}
298
	}
299

300
	/**
301 302
	 * Checks whether the parameters and return type of this method are
	 * compatible with the specified method
303
	 *
304 305 306
	 * @param base_method a method
	 * @param invalid_match error string about which check failed
	 * @return true if the specified method is compatible to this method
307
	 */
308
	public bool compatible (Method base_method, out string? invalid_match) {
309 310 311 312 313 314
		// method is always compatible to itself
		if (this == base_method) {
			invalid_match = null;
			return true;
		}

315 316 317 318 319
		if (binding != base_method.binding) {
			invalid_match = "incompatible binding";
			return false;
		}

320 321 322 323 324 325 326 327 328 329
		ObjectType object_type = null;
		if (parent_symbol is ObjectTypeSymbol) {
			object_type = new ObjectType ((ObjectTypeSymbol) parent_symbol);
			foreach (TypeParameter type_parameter in object_type.type_symbol.get_type_parameters ()) {
				var type_arg = new GenericType (type_parameter);
				type_arg.value_owned = true;
				object_type.add_type_argument (type_arg);
			}
		}

330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
		if (this.get_type_parameters ().size < base_method.get_type_parameters ().size) {
			invalid_match = "too few type parameters";
			return false;
		} else if (this.get_type_parameters ().size > base_method.get_type_parameters ().size) {
			invalid_match = "too many type parameters";
			return false;
		}

		List<DataType> method_type_args = null;
		if (this.get_type_parameters ().size > 0) {
			method_type_args = new ArrayList<DataType> ();
			foreach (TypeParameter type_parameter in this.get_type_parameters ()) {
				var type_arg = new GenericType (type_parameter);
				type_arg.value_owned = true;
				method_type_args.add (type_arg);
			}
		}

		var actual_base_type = base_method.return_type.get_actual_type (object_type, method_type_args, this);
349
		if (!return_type.equals (actual_base_type)) {
350
			invalid_match = "Base method expected return type `%s', but `%s' was provided".printf (actual_base_type.to_prototype_string (), return_type.to_prototype_string ());
351 352
			return false;
		}
353

354
		Iterator<Parameter> method_params_it = parameters.iterator ();
355
		int param_index = 1;
356
		foreach (Parameter base_param in base_method.parameters) {
357
			/* this method may not expect less arguments */
358
			if (!method_params_it.next ()) {
359
				invalid_match = "too few parameters";
360 361
				return false;
			}
362 363 364 365

			var param = method_params_it.get ();
			if (base_param.ellipsis != param.ellipsis) {
				invalid_match = "ellipsis parameter mismatch";
366 367
				return false;
			}
368
			if (!base_param.ellipsis) {
369 370 371 372 373
				if (base_param.direction != param.direction) {
					invalid_match = "incompatible direction of parameter %d".printf (param_index);
					return false;
				}

374
				actual_base_type = base_param.variable_type.get_actual_type (object_type, method_type_args, this);
375 376 377 378 379
				if (!actual_base_type.equals (param.variable_type)) {
					invalid_match = "incompatible type of parameter %d".printf (param_index);
					return false;
				}
			}
380
			param_index++;
381
		}
382

383
		/* this method may not expect more arguments */
384
		if (method_params_it.next ()) {
385
			invalid_match = "too many parameters";
386 387
			return false;
		}
388

389
		/* this method may throw less but not more errors than the base method */
390
		foreach (DataType method_error_type in get_error_types ()) {
391
			bool match = false;
392 393
			foreach (DataType base_method_error_type in base_method.get_error_types ()) {
				if (method_error_type.compatible (base_method_error_type)) {
394 395 396
					match = true;
					break;
				}
397 398
			}

399
			if (!match) {
400
				invalid_match = "incompatible error type `%s'".printf (method_error_type.to_string ());
401 402 403
				return false;
			}
		}
404 405 406 407
		if (base_method.coroutine != this.coroutine) {
			invalid_match = "async mismatch";
			return false;
		}
408

409
		invalid_match = null;
410 411
		return true;
	}
412

413 414 415 416 417 418
	/**
	 * Appends the specified parameter to the list of type parameters.
	 *
	 * @param p a type parameter
	 */
	public void add_type_parameter (TypeParameter p) {
419 420 421
		if (type_parameters == null) {
			type_parameters = new ArrayList<TypeParameter> ();
		}
422 423 424 425 426 427 428 429 430
		type_parameters.add (p);
		scope.add (p.name, p);
	}

	/**
	 * Returns a copy of the type parameter list.
	 *
	 * @return list of type parameters
	 */
431
	public List<TypeParameter> get_type_parameters () {
432 433 434 435 436 437 438
		if (type_parameters != null) {
			return type_parameters;
		}
		if (_empty_type_parameter_list == null) {
			_empty_type_parameter_list = new ArrayList<TypeParameter> ();
		}
		return _empty_type_parameter_list;
439 440 441
	}

	public int get_type_parameter_index (string name) {
442 443 444 445
		if (type_parameters == null) {
			return -1;
		}

446 447 448 449 450 451 452 453 454 455
		int i = 0;
		foreach (TypeParameter parameter in type_parameters) {
			if (parameter.name == name) {
				return i;
			}
			i++;
		}
		return -1;
	}

456 457 458 459 460
	/**
	 * Adds a precondition to this method.
	 *
	 * @param precondition a boolean precondition expression
	 */
461
	public void add_precondition (Expression precondition) {
462 463 464
		if (preconditions == null) {
			preconditions = new ArrayList<Expression> ();
		}
465 466 467 468 469 470 471 472 473
		preconditions.add (precondition);
		precondition.parent_node = this;
	}

	/**
	 * Returns a copy of the list of preconditions of this method.
	 *
	 * @return list of preconditions
	 */
474
	public List<Expression> get_preconditions () {
475 476 477 478 479 480 481
		if (preconditions != null) {
			return preconditions;
		}
		if (_empty_expression_list == null) {
			_empty_expression_list = new ArrayList<Expression> ();
		}
		return _empty_expression_list;
482 483 484 485 486 487 488
	}

	/**
	 * Adds a postcondition to this method.
	 *
	 * @param postcondition a boolean postcondition expression
	 */
489
	public void add_postcondition (Expression postcondition) {
490 491 492
		if (postconditions == null) {
			postconditions = new ArrayList<Expression> ();
		}
493 494 495 496 497 498 499 500 501
		postconditions.add (postcondition);
		postcondition.parent_node = this;
	}

	/**
	 * Returns a copy of the list of postconditions of this method.
	 *
	 * @return list of postconditions
	 */
502
	public List<Expression> get_postconditions () {
503 504 505 506 507 508 509
		if (postconditions != null) {
			return postconditions;
		}
		if (_empty_expression_list == null) {
			_empty_expression_list = new ArrayList<Expression> ();
		}
		return _empty_expression_list;
510 511
	}

512
	public override void replace_type (DataType old_type, DataType new_type) {
513 514 515 516
		if (base_interface_type == old_type) {
			base_interface_type = new_type;
			return;
		}
517 518 519 520
		if (return_type == old_type) {
			return_type = new_type;
			return;
		}
521 522 523 524
		var error_types = get_error_types ();
		for (int i = 0; i < error_types.size; i++) {
			if (error_types[i] == old_type) {
				error_types[i] = new_type;
525 526 527 528
				return;
			}
		}
	}
529

530 531 532 533 534 535
	private void find_base_methods () {
		if (base_methods_valid) {
			return;
		}

		if (parent_symbol is Class) {
536 537 538 539
			if (!(this is CreationMethod)) {
				find_base_interface_method ((Class) parent_symbol);
				if (is_virtual || is_abstract || overrides) {
					find_base_class_method ((Class) parent_symbol);
540 541 542 543 544 545 546 547 548 549 550 551 552
				}
			}
		} else if (parent_symbol is Interface) {
			if (is_virtual || is_abstract) {
				_base_interface_method = this;
			}
		}

		base_methods_valid = true;
	}

	private void find_base_class_method (Class cl) {
		var sym = cl.scope.lookup (name);
553 554 555 556
		if (sym is Signal) {
			var sig = (Signal) sym;
			sym = sig.default_handler;
		}
557 558 559 560 561 562
		if (sym is Method) {
			var base_method = (Method) sym;
			if (base_method.is_abstract || base_method.is_virtual) {
				string invalid_match;
				if (!compatible (base_method, out invalid_match)) {
					error = true;
563 564
					var base_method_type = new MethodType (base_method);
					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));
565 566 567
					return;
				}

568 569 570 571 572 573 574 575 576 577 578 579 580
				_base_method = base_method;
				return;
			}
		}

		if (cl.base_class != null) {
			find_base_class_method (cl.base_class);
		}
	}

	private void find_base_interface_method (Class cl) {
		foreach (DataType type in cl.get_base_types ()) {
			if (type.data_type is Interface) {
581 582 583 584
				if (base_interface_type != null && base_interface_type.data_type != type.data_type) {
					continue;
				}

585
				var sym = type.data_type.scope.lookup (name);
586 587 588 589
				if (sym is Signal) {
					var sig = (Signal) sym;
					sym = sig.default_handler;
				}
590 591 592
				if (sym is Method) {
					var base_method = (Method) sym;
					if (base_method.is_abstract || base_method.is_virtual) {
593 594 595 596 597 598 599 600 601 602 603 604 605
						if (base_interface_type == null) {
							// check for existing explicit implementation
							var has_explicit_implementation = false;
							foreach (var m in cl.get_methods ()) {
								if (m.base_interface_type != null && base_method == m.base_interface_method) {
									has_explicit_implementation = true;
									break;
								}
							}
							if (has_explicit_implementation) {
								continue;
							}
						}
606

607
						string invalid_match = null;
608 609
						if (!compatible (base_method, out invalid_match)) {
							error = true;
610 611
							var base_method_type = new MethodType (base_method);
							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));
612 613
							return;
						}
614

615 616 617 618 619 620
						_base_interface_method = base_method;
						return;
					}
				}
			}
		}
621 622

		if (base_interface_type != null) {
623
			Report.error (source_reference, "`%s': no suitable interface method found to implement".printf (get_full_name ()));
624
		}
625
	}
626

627
	public override bool check (CodeContext context) {
628 629 630 631 632 633
		if (checked) {
			return !error;
		}

		checked = true;

634 635 636 637 638 639
		if (get_attribute ("DestroysInstance") != null) {
			this_parameter.variable_type.value_owned = true;
		}
		if (get_attribute ("NoThrow") != null) {
			get_error_types ().clear ();
		}
640

641 642 643 644 645 646 647 648 649
		if (parent_symbol is Class && (is_abstract || is_virtual)) {
			var cl = (Class) parent_symbol;
			if (cl.is_compact && cl.base_class != null) {
				error = true;
				Report.error (source_reference, "Abstract and virtual methods may not be declared in derived compact classes");
				return false;
			}
		}

650 651 652 653 654 655
		if (is_variadic () && (is_abstract || is_virtual)) {
			error = true;
			Report.error (source_reference, "Abstract and virtual methods may not be variadic. Use a `va_list' parameter instead of `...'.");
			return false;
		}

656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
		if (is_abstract) {
			if (parent_symbol is Class) {
				var cl = (Class) parent_symbol;
				if (!cl.is_abstract) {
					error = true;
					Report.error (source_reference, "Abstract methods may not be declared in non-abstract classes");
					return false;
				}
			} else if (!(parent_symbol is Interface)) {
				error = true;
				Report.error (source_reference, "Abstract methods may not be declared outside of classes and interfaces");
				return false;
			}
		} else if (is_virtual) {
			if (!(parent_symbol is Class) && !(parent_symbol is Interface)) {
				error = true;
				Report.error (source_reference, "Virtual methods may not be declared outside of classes and interfaces");
				return false;
			}
		} else if (overrides) {
			if (!(parent_symbol is Class)) {
				error = true;
				Report.error (source_reference, "Methods may not be overridden outside of classes");
				return false;
			}
681 682 683 684 685 686
		} else if (access == SymbolAccessibility.PROTECTED) {
			if (!(parent_symbol is Class) && !(parent_symbol is Interface)) {
				error = true;
				Report.error (source_reference, "Protected methods may not be declared outside of classes and interfaces");
				return false;
			}
687 688 689 690
		}

		if (is_abstract && body != null) {
			Report.error (source_reference, "Abstract methods cannot have bodies");
691
		} else if ((is_abstract || is_virtual) && external && !external_package && !parent_symbol.external) {
692
			Report.error (source_reference, "Extern methods cannot be abstract or virtual");
693 694
		} else if (external && body != null) {
			Report.error (source_reference, "Extern methods cannot have bodies");
695
		} else if (!is_abstract && !external && source_type == SourceFileType.SOURCE && body == null) {
696 697 698
			Report.error (source_reference, "Non-abstract, non-extern methods must have bodies");
		}

699
		if (coroutine && !external_package && !context.has_package ("gio-2.0")) {
700 701 702 703 704
			error = true;
			Report.error (source_reference, "gio-2.0 package required for async methods");
			return false;
		}

705 706
		var old_source_file = context.analyzer.current_source_file;
		var old_symbol = context.analyzer.current_symbol;
707 708

		if (source_reference != null) {
709
			context.analyzer.current_source_file = source_reference.file;
710
		}
711
		context.analyzer.current_symbol = this;
712

713
		return_type.floating_reference = returns_floating_reference;
714
		return_type.check (context);
715

716 717 718 719 720
		var init_attr = get_attribute ("ModuleInit");
		if (init_attr != null) {
			source_reference.file.context.module_init_method = this;
		}

721
		if (return_type != null) {
722
			return_type.check (context);
723 724
		}

725 726
		if (parameters.size == 1 && parameters[0].ellipsis && body != null && binding != MemberBinding.INSTANCE) {
			// accept just `...' for external methods and instance methods
727 728 729 730
			error = true;
			Report.error (parameters[0].source_reference, "Named parameter required before `...'");
		}

731 732 733 734 735 736 737
		var optional_param = false;
		foreach (Parameter param in parameters) {
			param.check (context);
			if (coroutine && param.direction == ParameterDirection.REF) {
				error = true;
				Report.error (param.source_reference, "Reference parameters are not supported for async methods");
			}
738 739 740 741
			// TODO: begin and end parameters must be checked separately for coroutines
			if (coroutine) {
				continue;
			}
742 743 744 745
			if (optional_param && param.initializer == null && !param.ellipsis) {
				Report.warning (param.source_reference, "parameter without default follows parameter with default");
			} else if (param.initializer != null) {
				optional_param = true;
746
			}
747 748 749
		}

		foreach (DataType error_type in get_error_types ()) {
750
			error_type.check (context);
751 752

			// check whether error type is at least as accessible as the method
753
			if (!context.analyzer.is_type_accessible (this, error_type)) {
754 755 756 757
				error = true;
				Report.error (source_reference, "error type `%s` is less accessible than method `%s`".printf (error_type.to_string (), get_full_name ()));
				return false;
			}
758 759 760
		}

		if (result_var != null) {
761
			result_var.check (context);
762 763
		}

764 765
		if (preconditions != null) {
			foreach (Expression precondition in preconditions) {
766
				precondition.check (context);
767
			}
768 769
		}

770 771
		if (postconditions != null) {
			foreach (Expression postcondition in postconditions) {
772
				postcondition.check (context);
773
			}
774 775 776
		}

		if (body != null) {
777
			body.check (context);
778
		}
779

780
		if (context.analyzer.current_struct != null) {
781
			if (is_abstract || is_virtual || overrides) {
782
				error = true;
783 784 785 786
				Report.error (source_reference, "A struct member `%s' cannot be marked as override, virtual, or abstract".printf (get_full_name ()));
				return false;
			}
		} else if (overrides && base_method == null) {
787
			Report.error (source_reference, "`%s': no suitable method found to override".printf (get_full_name ()));
788 789 790 791
		} else if ((is_abstract || is_virtual || overrides) && access == SymbolAccessibility.PRIVATE) {
			error = true;
			Report.error (source_reference, "Private member `%s' cannot be marked as override, virtual, or abstract".printf (get_full_name ()));
			return false;
792 793
		}

794 795 796 797 798 799 800 801 802 803 804 805 806 807
		if (base_interface_type != null && base_interface_method != null && parent_symbol is Class) {
			var cl = (Class) parent_symbol;
			foreach (var m in cl.get_methods ()) {
				if (m != this && m.base_interface_method == base_interface_method) {
					m.checked = true;
					m.error = true;
					error = true;
					Report.error (source_reference, "`%s' already contains an implementation for `%s'".printf (cl.get_full_name (), base_interface_method.get_full_name ()));
					Report.notice (m.source_reference, "previous implementation of `%s' was here".printf (base_interface_method.get_full_name ()));
					return false;
				}
			}
		}

808 809 810
		context.analyzer.current_source_file = old_source_file;
		context.analyzer.current_symbol = old_symbol;

811 812 813 814
		if (!external_package && !overrides && !hides && get_hidden_member () != null) {
			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 ()));
		}

815
		// check whether return type is at least as accessible as the method
816
		if (!context.analyzer.is_type_accessible (this, return_type)) {
817 818 819 820 821 822 823 824 825 826 827 828
			error = true;
			Report.error (source_reference, "return type `%s` is less accessible than method `%s`".printf (return_type.to_string (), get_full_name ()));
			return false;
		}

		foreach (Expression precondition in get_preconditions ()) {
			if (precondition.error) {
				// if there was an error in the precondition, skip this check
				error = true;
				return false;
			}

829
			if (!precondition.value_type.compatible (context.analyzer.bool_type)) {
830 831 832 833 834 835 836 837 838 839 840 841 842
				error = true;
				Report.error (precondition.source_reference, "Precondition must be boolean");
				return false;
			}
		}

		foreach (Expression postcondition in get_postconditions ()) {
			if (postcondition.error) {
				// if there was an error in the postcondition, skip this check
				error = true;
				return false;
			}

843
			if (!postcondition.value_type.compatible (context.analyzer.bool_type)) {
844 845 846 847 848 849 850
				error = true;
				Report.error (postcondition.source_reference, "Postcondition must be boolean");
				return false;
			}
		}

		// check that all errors that can be thrown in the method body are declared
851
		if (body != null) {
852 853 854 855 856 857 858
			foreach (DataType body_error_type in body.get_error_types ()) {
				bool can_propagate_error = false;
				foreach (DataType method_error_type in get_error_types ()) {
					if (body_error_type.compatible (method_error_type)) {
						can_propagate_error = true;
					}
				}
859 860
				bool is_dynamic_error = body_error_type is ErrorType && ((ErrorType) body_error_type).dynamic_error;
				if (!can_propagate_error && !is_dynamic_error) {
861 862 863 864 865
					Report.warning (body_error_type.source_reference, "unhandled error `%s'".printf (body_error_type.to_string()));
				}
			}
		}

866
		// check that DBus methods at least throw "GLib.Error" or "GLib.DBusError, GLib.IOError"
867 868 869
		if (!(this is CreationMethod) && binding == MemberBinding.INSTANCE
		    && !overrides && access == SymbolAccessibility.PUBLIC
		    && parent_symbol is ObjectTypeSymbol && parent_symbol.get_attribute ("DBus") != null) {
870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896
			Attribute? dbus_attr = get_attribute ("DBus");
			if (dbus_attr == null || dbus_attr.get_bool ("visible", true)) {
				bool throws_gerror = false;
				bool throws_gioerror = false;
				bool throws_gdbuserror = false;
				foreach (DataType error_type in get_error_types ()) {
					if (!(error_type is ErrorType)) {
						continue;
					}
					unowned ErrorDomain? error_domain = ((ErrorType) error_type).error_domain;
					if (error_domain == null) {
						throws_gerror = true;
						break;
					}
					string? full_error_domain = error_domain.get_full_name ();
					if (full_error_domain == "GLib.IOError") {
						throws_gioerror = true;
					} else if (full_error_domain == "GLib.DBusError") {
						throws_gdbuserror = true;
					}
				}
				if (!throws_gerror && !(throws_gioerror && throws_gdbuserror)) {
					Report.warning (source_reference, "DBus methods are recommended to throw at least `GLib.Error' or `GLib.DBusError, GLib.IOError'");
				}
			}
		}

897 898
		if (is_possible_entry_point (context)) {
			if (context.entry_point != null) {
899
				error = true;
900
				Report.error (source_reference, "program already has an entry point `%s'".printf (context.entry_point.get_full_name ()));
901 902
				return false;
			}
903
			entry_point = true;
904
			context.entry_point = this;
905

Jürg Billeter's avatar
Jürg Billeter committed
906
			if (tree_can_fail) {
907 908
				Report.error (source_reference, "\"main\" method cannot throw errors");
			}
909 910 911 912 913 914 915 916

			if (is_inline) {
				Report.error (source_reference, "\"main\" method cannot be inline");
			}

			if (coroutine) {
				Report.error (source_reference, "\"main\" method cannot be async");
			}
917 918
		}

919 920 921 922
		if (get_attribute ("GtkCallback") != null) {
			used = true;
		}

923 924
		return !error;
	}
925

926
	bool is_possible_entry_point (CodeContext context) {
927 928 929 930
		if (external_package) {
			return false;
		}

931
		if (context.entry_point_name == null) {
932 933 934 935 936 937
			if (name == null || name != "main") {
				// method must be called "main"
				return false;
			}
		} else {
			// custom entry point name
938
			if (get_full_name () != context.entry_point_name) {
939 940
				return false;
			}
941
		}
942

943 944 945 946
		if (binding == MemberBinding.INSTANCE) {
			// method must be static
			return false;
		}
947

948
		if (return_type is VoidType) {
949
		} else if (return_type.data_type == context.analyzer.int_type.data_type) {
950 951 952 953
		} else {
			// return type must be void or int
			return false;
		}
954

955 956 957 958 959 960 961 962 963 964
		var params = get_parameters ();
		if (params.size == 0) {
			// method may have no parameters
			return true;
		}

		if (params.size > 1) {
			// method must not have more than one parameter
			return false;
		}
965

966
		Iterator<Parameter> params_it = params.iterator ();
967 968 969 970 971 972 973
		params_it.next ();
		var param = params_it.get ();

		if (param.direction == ParameterDirection.OUT) {
			// parameter must not be an out parameter
			return false;
		}
974

Jürg Billeter's avatar
Jürg Billeter committed
975
		if (!(param.variable_type is ArrayType)) {
976 977 978
			// parameter must be an array
			return false;
		}
979

Jürg Billeter's avatar
Jürg Billeter committed
980
		var array_type = (ArrayType) param.variable_type;
981
		if (array_type.element_type.data_type != context.analyzer.string_type.data_type) {
982 983 984
			// parameter must be an array of strings
			return false;
		}
985

986 987
		return true;
	}
988 989 990 991

	public int get_required_arguments () {
		int n = 0;
		foreach (var param in parameters) {
Jürg Billeter's avatar
Jürg Billeter committed
992
			if (param.initializer != null || param.ellipsis) {
993 994 995 996 997 998 999
				// optional argument
				break;
			}
			n++;
		}
		return n;
	}
1000

1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018
	public Method get_end_method () {
		assert (this.coroutine);

		if (end_method == null) {
			end_method = new Method ("end", return_type, source_reference);
			end_method.access = SymbolAccessibility.PUBLIC;
			end_method.external = true;
			end_method.owner = scope;
			foreach (var param in get_async_end_parameters ()) {
				end_method.add_parameter (param.copy ());
			}
			foreach (var param in get_type_parameters ()) {
				end_method.add_type_parameter (param);
			}
		}
		return end_method;
	}

1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033
	public Method get_callback_method () {
		assert (this.coroutine);

		if (callback_method == null) {
			var bool_type = new BooleanType ((Struct) CodeContext.get ().root.scope.lookup ("bool"));
			bool_type.value_owned = true;
			callback_method = new Method ("callback", bool_type, source_reference);
			callback_method.access = SymbolAccessibility.PUBLIC;
			callback_method.external = true;
			callback_method.binding = MemberBinding.INSTANCE;
			callback_method.owner = scope;
			callback_method.is_async_callback = true;
		}
		return callback_method;
	}
1034

1035
	public List<Parameter> get_async_begin_parameters () {
1036 1037 1038 1039
		assert (this.coroutine);

		var glib_ns = CodeContext.get ().root.scope.lookup ("GLib");

1040
		var params = new ArrayList<Parameter> ();
1041
		Parameter ellipsis = null;
1042
		foreach (var param in parameters) {
1043 1044 1045
			if (param.ellipsis) {
				ellipsis = param;
			} else if (param.direction == ParameterDirection.IN) {
1046 1047 1048 1049 1050 1051
				params.add (param);
			}
		}

		var callback_type = new DelegateType ((Delegate) glib_ns.scope.lookup ("AsyncReadyCallback"));
		callback_type.nullable = true;
1052
		callback_type.value_owned = true;
1053
		callback_type.is_called_once = true;
1054

1055
		var callback_param = new Parameter ("_callback_", callback_type);
Jürg Billeter's avatar
Jürg Billeter committed
1056
		callback_param.initializer = new NullLiteral (source_reference);
1057
		callback_param.initializer.target_type = callback_type.copy ();
1058 1059
		callback_param.set_attribute_double ("CCode", "pos", -1);
		callback_param.set_attribute_double ("CCode", "delegate_target_pos", -0.9);
1060 1061 1062

		params.add (callback_param);

1063 1064 1065 1066
		if (ellipsis != null) {
			params.add (ellipsis);
		}

1067 1068 1069
		return params;
	}

1070
	public List<Parameter> get_async_end_parameters () {
1071 1072
		assert (this.coroutine);

1073
		var params = new ArrayList<Parameter> ();
1074

1075
		var glib_ns = CodeContext.get ().root.scope.lookup ("GLib");
1076 1077
		var result_type = new ObjectType ((ObjectTypeSymbol) glib_ns.scope.lookup ("AsyncResult"));

1078
		var result_param = new Parameter ("_res_", result_type);
1079
		result_param.set_attribute_double ("CCode", "pos", 0.1);
1080 1081
		params.add (result_param);

1082 1083 1084 1085 1086 1087
		foreach (var param in parameters) {
			if (param.direction == ParameterDirection.OUT) {
				params.add (param);
			}
		}

1088 1089
		return params;
	}
1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106

	public void add_captured_variable (LocalVariable local) {
		assert (this.closure);

		if (captured_variables == null) {
			captured_variables = new ArrayList<LocalVariable> ();
		}
		captured_variables.add (local);
	}

	public void get_captured_variables (Collection<LocalVariable> variables) {
		if (captured_variables != null) {
			foreach (var local in captured_variables) {
				variables.add (local);
			}
		}
	}
1107

1108
	public override void get_defined_variables (Collection<Variable> collection) {
1109 1110 1111
		// capturing variables is only supported if they are initialized
		// therefore assume that captured variables are initialized
		if (closure) {
1112
			get_captured_variables ((Collection<LocalVariable>) collection);
1113 1114
		}
	}
1115 1116 1117 1118 1119 1120 1121 1122 1123

	public int get_format_arg_index () {
		for (int i = 0; i < parameters.size; i++) {
			if (parameters[i].format_arg) {
				return i;
			}
		}
		return -1;
	}
1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136

	public bool has_error_type_parameter () {
		if (get_error_types ().size > 0) {
			return true;
		}
		if (base_method != null && base_method != this && base_method.has_error_type_parameter ()) {
			return true;
		}
		if (base_interface_method != null && base_interface_method != this && base_interface_method.has_error_type_parameter ()) {
			return true;
		}
		return false;
	}
1137
}
1138 1139

// vim:sw=8 noet