Read comments
[vala-lang.git] / vala / valamethod.vala
blob98e8d9da77e62ca284c8a8faff1793b09b42680f
1 /* valamethod.vala
3 * Copyright (C) 2006-2009 Jürg Billeter
4 * Copyright (C) 2006-2008 Raffaele Sandrini
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.
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.
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
20 * Author:
21 * Jürg Billeter <j@bitron.ch>
22 * Raffaele Sandrini <raffaele@sandrini.ch>
25 using GLib;
26 using Gee;
28 /**
29 * Represents a type or namespace method.
31 public class Vala.Method : Member {
32 public const string DEFAULT_SENTINEL = "NULL";
34 /**
35 * The return type of this method.
37 public DataType return_type {
38 get { return _return_type; }
39 set {
40 _return_type = value;
41 _return_type.parent_node = this;
45 public Block body {
46 get { return _body; }
47 set {
48 _body = value;
49 if (_body != null) {
50 _body.owner = scope;
55 public BasicBlock entry_block { get; set; }
57 public BasicBlock exit_block { get; set; }
59 /**
60 * Specifies whether this method may only be called with an instance of
61 * the contained type.
63 public MemberBinding binding { get; set; default = MemberBinding.INSTANCE; }
65 /**
66 * The name of the vfunc of this method as it is used in C code.
68 public string vfunc_name {
69 get {
70 if (_vfunc_name == null) {
71 _vfunc_name = this.name;
73 return _vfunc_name;
75 set {
76 _vfunc_name = value;
80 /**
81 * The sentinel to use for terminating variable length argument lists.
83 public string sentinel {
84 get {
85 if (_sentinel == null) {
86 return DEFAULT_SENTINEL;
89 return _sentinel;
92 set {
93 _sentinel = value;
97 /**
98 * Specifies whether this method is abstract. Abstract methods have no
99 * body, may only be specified within abstract classes, and must be
100 * overriden by derived non-abstract classes.
102 public bool is_abstract { get; set; }
105 * Specifies whether this method is virtual. Virtual methods may be
106 * overridden by derived classes.
108 public bool is_virtual { get; set; }
111 * Specifies whether this method overrides a virtual or abstract method
112 * of a base type.
114 public bool overrides { get; set; }
117 * Specifies whether this method should be inlined.
119 public bool is_inline { get; set; }
122 * Specifies whether the C method returns a new instance pointer which
123 * may be different from the previous instance pointer. Only valid for
124 * imported methods.
126 public bool returns_modified_pointer { get; set; }
129 * Specifies the virtual or abstract method this method overrides.
130 * Reference must be weak as virtual and abstract methods set
131 * base_method to themselves.
133 public Method base_method {
134 get {
135 find_base_methods ();
136 return _base_method;
141 * Specifies the abstract interface method this method implements.
143 public Method base_interface_method {
144 get {
145 find_base_methods ();
146 return _base_interface_method;
150 public bool entry_point { get; private set; }
153 * Specifies the generated `this' parameter for instance methods.
155 public FormalParameter this_parameter { get; set; }
158 * Specifies the generated `result' variable for postconditions.
160 public LocalVariable result_var { get; set; }
163 * Specifies the position of the instance parameter in the C function.
165 public double cinstance_parameter_position { get; set; }
168 * Specifies the position of the array length out parameter in the C
169 * function.
171 public double carray_length_parameter_position { get; set; }
174 * Specifies the position of the delegate target out parameter in the C
175 * function.
177 public double cdelegate_target_parameter_position { get; set; }
180 * Specifies whether the array length should be returned implicitly
181 * if the return type is an array.
183 public bool no_array_length { get; set; }
186 * Specifies whether the array is null terminated.
188 public bool array_null_terminated { get; set; }
191 * Specifies whether this method expects printf-style format arguments.
193 public bool printf_format { get; set; }
196 * Specifies whether a construct function with a GType parameter is
197 * available. This is only applicable to creation methods.
199 public bool has_construct_function { get; set; default = true; }
201 public bool coroutine { get; set; }
203 private Gee.List<FormalParameter> parameters = new ArrayList<FormalParameter> ();
204 private string cname;
205 private string _vfunc_name;
206 private string _sentinel;
207 private Gee.List<Expression> preconditions = new ArrayList<Expression> ();
208 private Gee.List<Expression> postconditions = new ArrayList<Expression> ();
209 private DataType _return_type;
210 private Block _body;
212 private weak Method _base_method;
213 private Method _base_interface_method;
214 private bool base_methods_valid;
217 * Creates a new method.
219 * @param name method name
220 * @param return_type method return type
221 * @param source reference to source code
222 * @return newly created method
224 public Method (string? name, DataType return_type, SourceReference? source_reference = null) {
225 base (name, source_reference);
226 this.return_type = return_type;
228 carray_length_parameter_position = -3;
229 cdelegate_target_parameter_position = -3;
233 * Appends parameter to this method.
235 * @param param a formal parameter
237 public void add_parameter (FormalParameter param) {
238 // default C parameter position
239 param.cparameter_position = parameters.size + 1;
240 param.carray_length_parameter_position = param.cparameter_position + 0.1;
241 param.cdelegate_target_parameter_position = param.cparameter_position + 0.1;
243 parameters.add (param);
244 if (!param.ellipsis) {
245 scope.add (param.name, param);
249 public Gee.List<FormalParameter> get_parameters () {
250 return new ReadOnlyList<FormalParameter> (parameters);
254 * Remove all parameters from this method.
256 public void clear_parameters () {
257 foreach (FormalParameter param in parameters) {
258 if (!param.ellipsis) {
259 scope.remove (param.name);
262 parameters.clear ();
265 public override void accept (CodeVisitor visitor) {
266 visitor.visit_method (this);
269 public override void accept_children (CodeVisitor visitor) {
270 if (return_type != null) {
271 return_type.accept (visitor);
274 foreach (FormalParameter param in parameters) {
275 param.accept (visitor);
278 foreach (DataType error_type in get_error_types ()) {
279 error_type.accept (visitor);
282 if (result_var != null) {
283 result_var.accept (visitor);
286 foreach (Expression precondition in preconditions) {
287 precondition.accept (visitor);
290 foreach (Expression postcondition in postconditions) {
291 postcondition.accept (visitor);
294 if (body != null) {
295 body.accept (visitor);
300 * Returns the interface name of this method as it is used in C code.
302 * @return the name to be used in C code
304 public string get_cname () {
305 if (cname == null) {
306 cname = get_default_cname ();
308 return cname;
312 * Returns the default interface name of this method as it is used in C
313 * code.
315 * @return the name to be used in C code by default
317 public virtual string get_default_cname () {
318 if (name == "main" && parent_symbol.name == null) {
319 // avoid conflict with generated main function
320 return "_main";
321 } else if (name.has_prefix ("_")) {
322 return "_%s%s".printf (parent_symbol.get_lower_case_cprefix (), name.offset (1));
323 } else {
324 return "%s%s".printf (parent_symbol.get_lower_case_cprefix (), name);
329 * Returns the implementation name of this data type as it is used in C
330 * code.
332 * @return the name to be used in C code
334 public virtual string get_real_cname () {
335 if (base_method != null || base_interface_method != null) {
336 return "%sreal_%s".printf (parent_symbol.get_lower_case_cprefix (), name);
337 } else {
338 return get_cname ();
343 * Sets the name of this method as it is used in C code.
345 * @param cname the name to be used in C code
347 public void set_cname (string cname) {
348 this.cname = cname;
351 private void process_ccode_attribute (Attribute a) {
352 if (a.has_argument ("cname")) {
353 set_cname (a.get_string ("cname"));
355 if (a.has_argument ("cheader_filename")) {
356 var val = a.get_string ("cheader_filename");
357 foreach (string filename in val.split (",")) {
358 add_cheader_filename (filename);
361 if (a.has_argument ("vfunc_name")) {
362 this.vfunc_name = a.get_string ("vfunc_name");
364 if (a.has_argument ("sentinel")) {
365 this.sentinel = a.get_string ("sentinel");
367 if (a.has_argument ("instance_pos")) {
368 cinstance_parameter_position = a.get_double ("instance_pos");
370 if (a.has_argument ("array_length")) {
371 no_array_length = !a.get_bool ("array_length");
373 if (a.has_argument ("array_null_terminated")) {
374 array_null_terminated = a.get_bool ("array_null_terminated");
376 if (a.has_argument ("array_length_pos")) {
377 carray_length_parameter_position = a.get_double ("array_length_pos");
379 if (a.has_argument ("delegate_target_pos")) {
380 cdelegate_target_parameter_position = a.get_double ("delegate_target_pos");
382 if (a.has_argument ("has_construct_function")) {
383 has_construct_function = a.get_bool ("has_construct_function");
388 * Process all associated attributes.
390 public void process_attributes () {
391 foreach (Attribute a in attributes) {
392 if (a.name == "CCode") {
393 process_ccode_attribute (a);
394 } else if (a.name == "ReturnsModifiedPointer") {
395 returns_modified_pointer = true;
396 } else if (a.name == "FloatingReference") {
397 return_type.floating_reference = true;
398 } else if (a.name == "PrintfFormat") {
399 printf_format = true;
405 * Checks whether the parameters and return type of this method are
406 * compatible with the specified method
408 * @param base_method a method
409 * @param invalid_match error string about which check failed
410 * @return true if the specified method is compatible to this method
412 public bool compatible (Method base_method, out string? invalid_match) {
413 ObjectType object_type = null;
414 if (parent_symbol is ObjectTypeSymbol) {
415 object_type = new ObjectType ((ObjectTypeSymbol) parent_symbol);
416 foreach (TypeParameter type_parameter in object_type.type_symbol.get_type_parameters ()) {
417 var type_arg = new GenericType (type_parameter);
418 type_arg.value_owned = true;
419 object_type.add_type_argument (type_arg);
423 var actual_base_type = base_method.return_type.get_actual_type (object_type, this);
424 if (!return_type.equals (actual_base_type)) {
425 invalid_match = "incompatible return type";
426 return false;
429 Iterator<FormalParameter> method_params_it = parameters.iterator ();
430 int param_index = 1;
431 foreach (FormalParameter base_param in base_method.parameters) {
432 /* this method may not expect less arguments */
433 if (!method_params_it.next ()) {
434 invalid_match = "too few parameters";
435 return false;
438 actual_base_type = base_param.parameter_type.get_actual_type (object_type, this);
439 if (!actual_base_type.equals (method_params_it.get ().parameter_type)) {
440 invalid_match = "incompatible type of parameter %d".printf (param_index);
441 return false;
443 param_index++;
446 /* this method may not expect more arguments */
447 if (method_params_it.next ()) {
448 invalid_match = "too many parameters";
449 return false;
452 /* this method may throw more but not less errors than the base method */
453 foreach (DataType method_error_type in get_error_types ()) {
454 bool match = false;
455 foreach (DataType base_method_error_type in base_method.get_error_types ()) {
456 if (method_error_type.compatible (base_method_error_type)) {
457 match = true;
458 break;
462 if (!match) {
463 invalid_match = "incompatible error type `%s'".printf (method_error_type.to_string ());
464 return false;
468 return true;
472 * Adds a precondition to this method.
474 * @param precondition a boolean precondition expression
476 public void add_precondition (Expression precondition) {
477 preconditions.add (precondition);
478 precondition.parent_node = this;
482 * Returns a copy of the list of preconditions of this method.
484 * @return list of preconditions
486 public Gee.List<Expression> get_preconditions () {
487 return new ReadOnlyList<Expression> (preconditions);
491 * Adds a postcondition to this method.
493 * @param postcondition a boolean postcondition expression
495 public void add_postcondition (Expression postcondition) {
496 postconditions.add (postcondition);
497 postcondition.parent_node = this;
501 * Returns a copy of the list of postconditions of this method.
503 * @return list of postconditions
505 public Gee.List<Expression> get_postconditions () {
506 return new ReadOnlyList<Expression> (postconditions);
509 public override void replace_type (DataType old_type, DataType new_type) {
510 if (return_type == old_type) {
511 return_type = new_type;
512 return;
514 var error_types = get_error_types ();
515 for (int i = 0; i < error_types.size; i++) {
516 if (error_types[i] == old_type) {
517 error_types[i] = new_type;
518 return;
523 private void find_base_methods () {
524 if (base_methods_valid) {
525 return;
528 if (parent_symbol is Class) {
529 /* VAPI classes don't specify overridden methods */
530 if (!parent_symbol.external_package) {
531 if (!(this is CreationMethod)) {
532 find_base_interface_method ((Class) parent_symbol);
533 if (is_virtual || is_abstract || overrides) {
534 find_base_class_method ((Class) parent_symbol);
537 } else if (is_virtual || is_abstract) {
538 _base_method = this;
540 } else if (parent_symbol is Interface) {
541 if (is_virtual || is_abstract) {
542 _base_interface_method = this;
546 base_methods_valid = true;
549 private void find_base_class_method (Class cl) {
550 var sym = cl.scope.lookup (name);
551 if (sym is Method) {
552 var base_method = (Method) sym;
553 if (base_method.is_abstract || base_method.is_virtual) {
554 string invalid_match;
555 if (!compatible (base_method, out invalid_match)) {
556 error = true;
557 Report.error (source_reference, "overriding method `%s' is incompatible with base method `%s': %s.".printf (get_full_name (), base_method.get_full_name (), invalid_match));
558 return;
561 _base_method = base_method;
562 return;
564 } else if (sym is Signal) {
565 var sig = (Signal) sym;
566 if (sig.is_virtual) {
567 var base_method = sig.get_method_handler ();
568 string invalid_match;
569 if (!compatible (base_method, out invalid_match)) {
570 error = true;
571 Report.error (source_reference, "overriding method `%s' is incompatible with base method `%s': %s.".printf (get_full_name (), base_method.get_full_name (), invalid_match));
572 return;
575 _base_method = base_method;
576 return;
580 if (cl.base_class != null) {
581 find_base_class_method (cl.base_class);
585 private void find_base_interface_method (Class cl) {
586 // FIXME report error if multiple possible base methods are found
587 foreach (DataType type in cl.get_base_types ()) {
588 if (type.data_type is Interface) {
589 var sym = type.data_type.scope.lookup (name);
590 if (sym is Method) {
591 var base_method = (Method) sym;
592 if (base_method.is_abstract || base_method.is_virtual) {
593 string invalid_match;
594 if (!compatible (base_method, out invalid_match)) {
595 error = true;
596 Report.error (source_reference, "overriding method `%s' is incompatible with base method `%s': %s.".printf (get_full_name (), base_method.get_full_name (), invalid_match));
597 return;
600 _base_interface_method = base_method;
601 return;
608 public override bool check (SemanticAnalyzer analyzer) {
609 if (checked) {
610 return !error;
613 checked = true;
615 process_attributes ();
617 if (is_abstract) {
618 if (parent_symbol is Class) {
619 var cl = (Class) parent_symbol;
620 if (!cl.is_abstract) {
621 error = true;
622 Report.error (source_reference, "Abstract methods may not be declared in non-abstract classes");
623 return false;
625 } else if (!(parent_symbol is Interface)) {
626 error = true;
627 Report.error (source_reference, "Abstract methods may not be declared outside of classes and interfaces");
628 return false;
630 } else if (is_virtual) {
631 if (!(parent_symbol is Class) && !(parent_symbol is Interface)) {
632 error = true;
633 Report.error (source_reference, "Virtual methods may not be declared outside of classes and interfaces");
634 return false;
637 if (parent_symbol is Class) {
638 var cl = (Class) parent_symbol;
639 if (cl.is_compact) {
640 Report.error (source_reference, "Virtual methods may not be declared in compact classes");
641 return false;
644 } else if (overrides) {
645 if (!(parent_symbol is Class)) {
646 error = true;
647 Report.error (source_reference, "Methods may not be overridden outside of classes");
648 return false;
652 if (is_abstract && body != null) {
653 Report.error (source_reference, "Abstract methods cannot have bodies");
654 } else if (external && body != null) {
655 Report.error (source_reference, "Extern methods cannot have bodies");
656 } else if (!is_abstract && !external && !external_package && body == null) {
657 Report.error (source_reference, "Non-abstract, non-extern methods must have bodies");
660 var old_source_file = analyzer.current_source_file;
661 var old_symbol = analyzer.current_symbol;
662 var old_class = analyzer.current_class;
663 var old_struct = analyzer.current_struct;
664 var old_return_type = analyzer.current_return_type;
666 if (source_reference != null) {
667 analyzer.current_source_file = source_reference.file;
669 analyzer.current_symbol = this;
670 if (parent_symbol is Class) {
671 analyzer.current_class = (Class) parent_symbol;
672 } else if (parent_symbol is Struct) {
673 analyzer.current_struct = (Struct) parent_symbol;
675 analyzer.current_return_type = return_type;
677 return_type.check (analyzer);
679 var init_attr = get_attribute ("ModuleInit");
680 if (init_attr != null) {
681 source_reference.file.context.module_init_method = this;
684 if (!is_internal_symbol ()) {
685 if (return_type is ValueType) {
686 analyzer.current_source_file.add_type_dependency (return_type, SourceFileDependencyType.HEADER_FULL);
687 } else {
688 analyzer.current_source_file.add_type_dependency (return_type, SourceFileDependencyType.HEADER_SHALLOW);
691 analyzer.current_source_file.add_type_dependency (return_type, SourceFileDependencyType.SOURCE);
693 if (return_type != null) {
694 return_type.check (analyzer);
697 foreach (FormalParameter param in parameters) {
698 param.check (analyzer);
701 foreach (DataType error_type in get_error_types ()) {
702 error_type.check (analyzer);
705 if (result_var != null) {
706 result_var.check (analyzer);
709 foreach (Expression precondition in preconditions) {
710 precondition.check (analyzer);
713 foreach (Expression postcondition in postconditions) {
714 postcondition.check (analyzer);
717 if (body != null) {
718 body.check (analyzer);
721 analyzer.current_source_file = old_source_file;
722 analyzer.current_symbol = old_symbol;
723 analyzer.current_class = old_class;
724 analyzer.current_struct = old_struct;
725 analyzer.current_return_type = old_return_type;
727 if (analyzer.current_symbol.parent_symbol is Method) {
728 /* lambda expressions produce nested methods */
729 var up_method = (Method) analyzer.current_symbol.parent_symbol;
730 analyzer.current_return_type = up_method.return_type;
733 if (analyzer.current_symbol is Struct) {
734 if (is_abstract || is_virtual || overrides) {
735 Report.error (source_reference, "A struct member `%s' cannot be marked as override, virtual, or abstract".printf (get_full_name ()));
736 return false;
738 } else if (overrides && base_method == null) {
739 Report.error (source_reference, "%s: no suitable method found to override".printf (get_full_name ()));
742 // check whether return type is at least as accessible as the method
743 if (!analyzer.is_type_accessible (this, return_type)) {
744 error = true;
745 Report.error (source_reference, "return type `%s` is less accessible than method `%s`".printf (return_type.to_string (), get_full_name ()));
746 return false;
749 foreach (Expression precondition in get_preconditions ()) {
750 if (precondition.error) {
751 // if there was an error in the precondition, skip this check
752 error = true;
753 return false;
756 if (!precondition.value_type.compatible (analyzer.bool_type)) {
757 error = true;
758 Report.error (precondition.source_reference, "Precondition must be boolean");
759 return false;
763 foreach (Expression postcondition in get_postconditions ()) {
764 if (postcondition.error) {
765 // if there was an error in the postcondition, skip this check
766 error = true;
767 return false;
770 if (!postcondition.value_type.compatible (analyzer.bool_type)) {
771 error = true;
772 Report.error (postcondition.source_reference, "Postcondition must be boolean");
773 return false;
777 if (tree_can_fail && name == "main") {
778 Report.error (source_reference, "\"main\" method cannot throw errors");
781 // check that all errors that can be thrown in the method body are declared
782 if (body != null) {
783 foreach (DataType body_error_type in body.get_error_types ()) {
784 bool can_propagate_error = false;
785 foreach (DataType method_error_type in get_error_types ()) {
786 if (body_error_type.compatible (method_error_type)) {
787 can_propagate_error = true;
790 if (!can_propagate_error) {
791 Report.warning (body_error_type.source_reference, "unhandled error `%s'".printf (body_error_type.to_string()));
796 if (is_possible_entry_point (analyzer)) {
797 entry_point = true;
800 return !error;
803 bool is_possible_entry_point (SemanticAnalyzer analyzer) {
804 if (name == null || name != "main") {
805 // method must be called "main"
806 return false;
809 if (binding == MemberBinding.INSTANCE) {
810 // method must be static
811 return false;
814 if (return_type is VoidType) {
815 } else if (return_type.data_type == analyzer.int_type.data_type) {
816 } else {
817 // return type must be void or int
818 return false;
821 var params = get_parameters ();
822 if (params.size == 0) {
823 // method may have no parameters
824 return true;
827 if (params.size > 1) {
828 // method must not have more than one parameter
829 return false;
832 Iterator<FormalParameter> params_it = params.iterator ();
833 params_it.next ();
834 var param = params_it.get ();
836 if (param.direction == ParameterDirection.OUT) {
837 // parameter must not be an out parameter
838 return false;
841 if (!(param.parameter_type is ArrayType)) {
842 // parameter must be an array
843 return false;
846 var array_type = (ArrayType) param.parameter_type;
847 if (array_type.element_type.data_type != analyzer.string_type.data_type) {
848 // parameter must be an array of strings
849 return false;
852 return true;