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