From e1a3ff9470763e7c6ff5a887036390bd418f4e46 Mon Sep 17 00:00:00 2001 From: Luca Bruno Date: Wed, 8 Jun 2011 14:10:55 +0200 Subject: [PATCH] Support explicit interface methods implementation Fixes bug 652098 --- codegen/valaccodeattribute.vala | 8 ++++- tests/Makefile.am | 1 + tests/methods/bug652098.vala | 55 ++++++++++++++++++++++++++++ vala/valaclass.vala | 29 +++++++++------ vala/valamethod.vala | 63 ++++++++++++++++++++++++++++++--- vala/valaparser.vala | 7 ++-- 6 files changed, 146 insertions(+), 17 deletions(-) create mode 100644 tests/methods/bug652098.vala diff --git a/codegen/valaccodeattribute.vala b/codegen/valaccodeattribute.vala index 7e1c38a8a..03ace0b7b 100644 --- a/codegen/valaccodeattribute.vala +++ b/codegen/valaccodeattribute.vala @@ -1254,7 +1254,13 @@ public class Vala.CCodeAttribute : AttributeCache { } else if (sym is Method) { var m = (Method) sym; if (m.base_method != null || m.base_interface_method != null) { - return "%sreal_%s".printf (CCodeBaseModule.get_ccode_lower_case_prefix (m.parent_symbol), m.name); + if (m.base_interface_type != null) { + return "%sreal_%s%s".printf (CCodeBaseModule.get_ccode_lower_case_prefix (m.parent_symbol), + CCodeBaseModule.get_ccode_lower_case_prefix (m.base_interface_type.data_type), + m.name); + } else { + return "%sreal_%s".printf (CCodeBaseModule.get_ccode_lower_case_prefix (m.parent_symbol), m.name); + } } else { return name; } diff --git a/tests/Makefile.am b/tests/Makefile.am index 4833286d8..bcf82e1af 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -54,6 +54,7 @@ TESTS = \ methods/bug646345.vala \ methods/bug648320.vala \ methods/bug649562.vala \ + methods/bug652098.vala \ methods/bug653391.vala \ methods/bug653908.vala \ methods/bug663210.vala \ diff --git a/tests/methods/bug652098.vala b/tests/methods/bug652098.vala new file mode 100644 index 000000000..d16f27f5d --- /dev/null +++ b/tests/methods/bug652098.vala @@ -0,0 +1,55 @@ +interface Iface1 : Object { + public abstract int foo (); +} + +interface Iface2 : Object { + public abstract int foo (); +} + +class Obj1 : Object, Iface1, Iface2 { + public int Iface1.foo () { + return 1; + } + + public int Iface2.foo () { + return 2; + } +} + +class Obj2 : Object, Iface1, Iface2 { + public int Iface1.foo () { + return 1; + } + + public int foo () { + return 2; + } +} + +class Base : Object { + public void foo () { + } +} + +interface Iface : Object { + public abstract void foo (); +} + +class Concrete : Base, Iface { +} + +void main () { + var obj1 = new Obj1 (); + var iface1 = (Iface1) obj1; + var iface2 = (Iface2) obj1; + + assert (iface1.foo () == 1); + assert (iface2.foo () == 2); + + var obj2 = new Obj2 (); + iface1 = (Iface1) obj2; + iface2 = (Iface2) obj2; + + assert (iface1.foo () == 1); + assert (iface2.foo () == 2); +} \ No newline at end of file diff --git a/vala/valaclass.vala b/vala/valaclass.vala index ba23a508f..12a82afd0 100644 --- a/vala/valaclass.vala +++ b/vala/valaclass.vala @@ -317,7 +317,12 @@ public class Vala.Class : ObjectTypeSymbol { } methods.add (m); - scope.add (m.name, m); + if (m.base_interface_type == null) { + scope.add (m.name, m); + } else { + // explicit interface method implementation + scope.add (null, m); + } } /** @@ -782,18 +787,22 @@ public class Vala.Class : ObjectTypeSymbol { /* check methods */ foreach (Method m in iface.get_methods ()) { if (m.is_abstract) { - Symbol sym = null; + var implemented = false; var base_class = this; - while (base_class != null && !(sym is Method)) { - sym = base_class.scope.lookup (m.name); + while (base_class != null) { + foreach (var impl in base_class.get_methods ()) { + if (impl.name == m.name && (impl.base_interface_type == null || impl.base_interface_type.data_type == iface)) { + // method is used as interface implementation, so it is not unused + impl.check_deprecated (source_reference); + impl.check_experimental (source_reference); + impl.used = true; + implemented = true; + break; + } + } base_class = base_class.base_class; } - if (sym is Method) { - // method is used as interface implementation, so it is not unused - sym.check_deprecated (source_reference); - sym.check_experimental (source_reference); - sym.used = true; - } else { + if (!implemented) { error = true; Report.error (source_reference, "`%s' does not implement interface method `%s'".printf (get_full_name (), m.get_full_name ())); } diff --git a/vala/valamethod.vala b/vala/valamethod.vala index 3e9096a7d..afc705387 100644 --- a/vala/valamethod.vala +++ b/vala/valamethod.vala @@ -109,7 +109,7 @@ public class Vala.Method : Subroutine { return _base_method; } } - + /** * Specifies the abstract interface method this method implements. */ @@ -120,6 +120,17 @@ public class Vala.Method : Subroutine { } } + /** + * 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; + } + } + public bool entry_point { get; private set; } /** @@ -181,6 +192,7 @@ public class Vala.Method : Subroutine { private weak Method _base_method; private weak Method _base_interface_method; + private DataType _base_interface_type; private bool base_methods_valid; Method? callback_method; @@ -249,6 +261,10 @@ public class Vala.Method : Subroutine { p.accept (visitor); } + if (base_interface_type != null) { + base_interface_type.accept (visitor); + } + if (return_type != null) { return_type.accept (visitor); } @@ -471,6 +487,10 @@ public class Vala.Method : Subroutine { } public override void replace_type (DataType old_type, DataType new_type) { + if (base_interface_type == old_type) { + base_interface_type = new_type; + return; + } if (return_type == old_type) { return_type = new_type; return; @@ -532,9 +552,12 @@ public class Vala.Method : Subroutine { } private void find_base_interface_method (Class cl) { - // FIXME report error if multiple possible base methods are found foreach (DataType type in cl.get_base_types ()) { if (type.data_type is Interface) { + if (base_interface_type != null && base_interface_type.data_type != type.data_type) { + continue; + } + var sym = type.data_type.scope.lookup (name); if (sym is Signal) { var sig = (Signal) sym; @@ -543,19 +566,37 @@ public class Vala.Method : Subroutine { if (sym is Method) { var base_method = (Method) sym; if (base_method.is_abstract || base_method.is_virtual) { - string invalid_match; + 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; + } + } + + string invalid_match = null; if (!compatible (base_method, out invalid_match)) { error = true; 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)); return; } - + _base_interface_method = base_method; return; } } } } + + if (base_interface_type != null) { + Report.error (source_reference, "%s: no suitable interface method found to implement".printf (get_full_name ())); + } } public override bool check (CodeContext context) { @@ -716,6 +757,20 @@ public class Vala.Method : Subroutine { return false; } + 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; + } + } + } + context.analyzer.current_source_file = old_source_file; context.analyzer.current_symbol = old_symbol; diff --git a/vala/valaparser.vala b/vala/valaparser.vala index 3a478bc0c..c465a8e62 100644 --- a/vala/valaparser.vala +++ b/vala/valaparser.vala @@ -2619,9 +2619,12 @@ public class Vala.Parser : CodeVisitor { var access = parse_access_modifier (); var flags = parse_member_declaration_modifiers (); var type = parse_type (true, false); - string id = parse_identifier (); + var sym = parse_symbol_name (); var type_param_list = parse_type_parameter_list (); - var method = new Method (id, type, get_src (begin), comment); + var method = new Method (sym.name, type, get_src (begin), comment); + if (sym.inner != null) { + method.base_interface_type = new UnresolvedType.from_symbol (sym.inner, sym.inner.source_reference); + } method.access = access; set_attributes (method, attrs); foreach (TypeParameter type_param in type_param_list) { -- 2.47.2