From: Jürg Billeter Date: Tue, 2 Jun 2009 15:26:29 +0000 (+0200) Subject: Add initial support for generic methods X-Git-Tag: 0.7.4~62 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=37c82ec971b2b200f3d7ca188d1c91d0f34a3f9f;p=thirdparty%2Fvala.git Add initial support for generic methods Fixes bug 492483. --- diff --git a/codegen/valaccodebasemodule.vala b/codegen/valaccodebasemodule.vala index 79e37e271..bd639cb11 100644 --- a/codegen/valaccodebasemodule.vala +++ b/codegen/valaccodebasemodule.vala @@ -1719,7 +1719,11 @@ internal class Vala.CCodeBaseModule : CCodeModule { private CCodeExpression get_type_id_expression (DataType type) { if (type is GenericType) { string var_name = "%s_type".printf (type.type_parameter.name.down ()); - return new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), var_name); + if (type.type_parameter.parent_symbol is TypeSymbol) { + return new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), var_name); + } else { + return new CCodeIdentifier (var_name); + } } else { string type_id = type.get_type_id (); if (type_id == null) { @@ -1765,7 +1769,11 @@ internal class Vala.CCodeBaseModule : CCodeModule { return new CCodeIdentifier (dup_function); } else if (type.type_parameter != null && current_type_symbol is Class) { string func_name = "%s_dup_func".printf (type.type_parameter.name.down ()); - return new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), func_name); + if (type.type_parameter.parent_symbol is TypeSymbol) { + return new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), func_name); + } else { + return new CCodeIdentifier (func_name); + } } else if (type is PointerType) { var pointer_type = (PointerType) type; return get_dup_func_expression (pointer_type.base_type, source_reference); @@ -1891,7 +1899,11 @@ internal class Vala.CCodeBaseModule : CCodeModule { return new CCodeIdentifier (unref_function); } else if (type.type_parameter != null && current_type_symbol is Class) { string func_name = "%s_destroy_func".printf (type.type_parameter.name.down ()); - return new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), func_name); + if (type.type_parameter.parent_symbol is TypeSymbol) { + return new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), func_name); + } else { + return new CCodeIdentifier (func_name); + } } else if (type is ArrayType) { return new CCodeIdentifier ("g_free"); } else if (type is PointerType) { diff --git a/codegen/valaccodemethodcallmodule.vala b/codegen/valaccodemethodcallmodule.vala index 8e328ca51..adb443454 100644 --- a/codegen/valaccodemethodcallmodule.vala +++ b/codegen/valaccodemethodcallmodule.vala @@ -98,34 +98,12 @@ internal class Vala.CCodeMethodCallModule : CCodeAssignmentModule { foreach (DataType base_type in current_class.get_base_types ()) { if (base_type.data_type is Class) { - foreach (DataType type_arg in base_type.get_type_arguments ()) { - if (type_arg is GenericType) { - // map generic type parameter - string type_param = type_arg.type_parameter.name.down (); - ccall.add_argument (new CCodeIdentifier ("%s_type".printf (type_param))); - ccall.add_argument (new CCodeIdentifier ("%s_dup_func".printf (type_param))); - ccall.add_argument (new CCodeIdentifier ("%s_destroy_func".printf (type_param))); - } else { - ccall.add_argument (new CCodeIdentifier (type_arg.get_type_id ())); - if (requires_copy (type_arg)) { - var dup_func = get_dup_func_expression (type_arg, type_arg.source_reference); - if (dup_func == null) { - // type doesn't contain a copy function - expr.error = true; - return; - } - ccall.add_argument (new CCodeCastExpression (dup_func, "GBoxedCopyFunc")); - ccall.add_argument (get_destroy_func_expression (type_arg)); - } else { - ccall.add_argument (new CCodeConstant ("NULL")); - ccall.add_argument (new CCodeConstant ("NULL")); - } - } - } - + add_generic_type_arguments (ccall, base_type.get_type_arguments (), expr); break; } } + } else if (m != null && m.get_type_parameters ().size > 0) { + add_generic_type_arguments (ccall, ma.get_type_arguments (), expr); } // the complete call expression, might include casts, comma expressions, and/or assignments @@ -668,5 +646,32 @@ internal class Vala.CCodeMethodCallModule : CCodeAssignmentModule { expr.ccodenode = ccomma; } } + + void add_generic_type_arguments (CCodeFunctionCall ccall, Gee.List type_args, CodeNode expr) { + foreach (var type_arg in type_args) { + if (type_arg is GenericType) { + // map generic type parameter + string type_param = type_arg.type_parameter.name.down (); + ccall.add_argument (new CCodeIdentifier ("%s_type".printf (type_param))); + ccall.add_argument (new CCodeIdentifier ("%s_dup_func".printf (type_param))); + ccall.add_argument (new CCodeIdentifier ("%s_destroy_func".printf (type_param))); + } else { + ccall.add_argument (new CCodeIdentifier (type_arg.get_type_id ())); + if (requires_copy (type_arg)) { + var dup_func = get_dup_func_expression (type_arg, type_arg.source_reference); + if (dup_func == null) { + // type doesn't contain a copy function + expr.error = true; + return; + } + ccall.add_argument (new CCodeCastExpression (dup_func, "GBoxedCopyFunc")); + ccall.add_argument (get_destroy_func_expression (type_arg)); + } else { + ccall.add_argument (new CCodeConstant ("NULL")); + ccall.add_argument (new CCodeConstant ("NULL")); + } + } + } + } } diff --git a/codegen/valaccodemethodmodule.vala b/codegen/valaccodemethodmodule.vala index 26a51c34a..0bd87c89c 100644 --- a/codegen/valaccodemethodmodule.vala +++ b/codegen/valaccodemethodmodule.vala @@ -740,6 +740,19 @@ internal class Vala.CCodeMethodModule : CCodeStructModule { } type_param_index++; } + } else { + int type_param_index = 0; + foreach (var type_param in m.get_type_parameters ()) { + cparam_map.set (get_param_pos (0.1 * type_param_index + 0.01), new CCodeFormalParameter ("%s_type".printf (type_param.name.down ()), "GType")); + cparam_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeFormalParameter ("%s_dup_func".printf (type_param.name.down ()), "GBoxedCopyFunc")); + cparam_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeFormalParameter ("%s_destroy_func".printf (type_param.name.down ()), "GDestroyNotify")); + if (carg_map != null) { + carg_map.set (get_param_pos (0.1 * type_param_index + 0.01), new CCodeIdentifier ("%s_type".printf (type_param.name.down ()))); + carg_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeIdentifier ("%s_dup_func".printf (type_param.name.down ()))); + carg_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeIdentifier ("%s_destroy_func".printf (type_param.name.down ()))); + } + type_param_index++; + } } foreach (FormalParameter param in m.get_parameters ()) { diff --git a/vala/valadatatype.vala b/vala/valadatatype.vala index 45af86606..8150fb9c4 100644 --- a/vala/valadatatype.vala +++ b/vala/valadatatype.vala @@ -1,6 +1,7 @@ /* valadatatype.vala * - * Copyright (C) 2006-2008 Jürg Billeter, Raffaele Sandrini + * Copyright (C) 2006-2009 Jürg Billeter + * Copyright (C) 2006-2008 Raffaele Sandrini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -477,22 +478,22 @@ public abstract class Vala.DataType : CodeNode { return false; } - public DataType get_actual_type (DataType? derived_instance_type, CodeNode node_reference) { - if (derived_instance_type == null) { + public DataType get_actual_type (DataType? derived_instance_type, MemberAccess? method_access, CodeNode node_reference) { + if (derived_instance_type == null && method_access == null) { return this; } DataType result = this; if (result is GenericType) { - result = SemanticAnalyzer.get_actual_type (derived_instance_type, (GenericType) result, node_reference); + result = SemanticAnalyzer.get_actual_type (derived_instance_type, method_access, (GenericType) result, node_reference); // don't try to resolve type arguments of returned actual type // they can never be resolved and are not related to the instance type } else if (result.type_argument_list != null) { // recursely get actual types for type arguments result = result.copy (); for (int i = 0; i < result.type_argument_list.size; i++) { - result.type_argument_list[i] = result.type_argument_list[i].get_actual_type (derived_instance_type, node_reference); + result.type_argument_list[i] = result.type_argument_list[i].get_actual_type (derived_instance_type, method_access, node_reference); } } diff --git a/vala/valadelegate.vala b/vala/valadelegate.vala index d141ed0fc..033058db7 100644 --- a/vala/valadelegate.vala +++ b/vala/valadelegate.vala @@ -107,7 +107,6 @@ public class Vala.Delegate : TypeSymbol { */ public void add_type_parameter (TypeParameter p) { type_parameters.add (p); - p.type = this; scope.add (p.name, p); } diff --git a/vala/valaforeachstatement.vala b/vala/valaforeachstatement.vala index 91bbd8580..78836c136 100644 --- a/vala/valaforeachstatement.vala +++ b/vala/valaforeachstatement.vala @@ -199,7 +199,7 @@ public class Vala.ForeachStatement : Block { error = true; return false; } - var iterator_type = iterator_method.return_type.get_actual_type (collection_type, this); + var iterator_type = iterator_method.return_type.get_actual_type (collection_type, null, this); if (iterator_type is VoidType) { Report.error (collection.source_reference, "`%s' must return an iterator".printf (iterator_method.get_full_name ())); error = true; @@ -232,7 +232,7 @@ public class Vala.ForeachStatement : Block { error = true; return false; } - var element_type = get_method.return_type.get_actual_type (iterator_type, this); + var element_type = get_method.return_type.get_actual_type (iterator_type, null, this); if (element_type is VoidType) { Report.error (collection.source_reference, "`%s' must return an element".printf (get_method.get_full_name ())); error = true; diff --git a/vala/valamemberaccess.vala b/vala/valamemberaccess.vala index 320ff50a1..2ef15a475 100644 --- a/vala/valamemberaccess.vala +++ b/vala/valamemberaccess.vala @@ -547,7 +547,7 @@ public class Vala.MemberAccess : Expression { formal_value_type = analyzer.get_value_type_for_symbol (symbol_reference, lvalue); if (inner != null && formal_value_type != null) { - value_type = formal_value_type.get_actual_type (inner.value_type, this); + value_type = formal_value_type.get_actual_type (inner.value_type, null, this); } else { value_type = formal_value_type; } diff --git a/vala/valamethod.vala b/vala/valamethod.vala index 19e6c08b2..5acfc655b 100644 --- a/vala/valamethod.vala +++ b/vala/valamethod.vala @@ -29,6 +29,8 @@ using Gee; * Represents a type or namespace method. */ public class Vala.Method : Member { + Gee.List type_parameters = new ArrayList (); + public const string DEFAULT_SENTINEL = "NULL"; /** @@ -422,7 +424,7 @@ public class Vala.Method : Member { } } - var actual_base_type = base_method.return_type.get_actual_type (object_type, this); + var actual_base_type = base_method.return_type.get_actual_type (object_type, null, this); if (!return_type.equals (actual_base_type)) { invalid_match = "incompatible return type"; return false; @@ -437,7 +439,7 @@ public class Vala.Method : Member { return false; } - actual_base_type = base_param.parameter_type.get_actual_type (object_type, this); + actual_base_type = base_param.parameter_type.get_actual_type (object_type, null, this); if (!actual_base_type.equals (method_params_it.get ().parameter_type)) { invalid_match = "incompatible type of parameter %d".printf (param_index); return false; @@ -470,6 +472,36 @@ public class Vala.Method : Member { return true; } + /** + * Appends the specified parameter to the list of type parameters. + * + * @param p a type parameter + */ + public void add_type_parameter (TypeParameter p) { + type_parameters.add (p); + scope.add (p.name, p); + } + + /** + * Returns a copy of the type parameter list. + * + * @return list of type parameters + */ + public Gee.List get_type_parameters () { + return new ReadOnlyList (type_parameters); + } + + public int get_type_parameter_index (string name) { + int i = 0; + foreach (TypeParameter parameter in type_parameters) { + if (parameter.name == name) { + return i; + } + i++; + } + return -1; + } + /** * Adds a precondition to this method. * diff --git a/vala/valamethodcall.vala b/vala/valamethodcall.vala index 670464d1d..450f187fb 100644 --- a/vala/valamethodcall.vala +++ b/vala/valamethodcall.vala @@ -238,7 +238,7 @@ public class Vala.MethodCall : Expression { /* store expected type for callback parameters */ arg.formal_target_type = param.parameter_type; - arg.target_type = arg.formal_target_type.get_actual_type (target_object_type, this); + arg.target_type = arg.formal_target_type.get_actual_type (target_object_type, call as MemberAccess, this); last_arg = arg; } @@ -399,7 +399,7 @@ public class Vala.MethodCall : Expression { } formal_value_type = ret_type; - value_type = formal_value_type.get_actual_type (target_object_type, this); + value_type = formal_value_type.get_actual_type (target_object_type, call as MemberAccess, this); bool may_throw = false; diff --git a/vala/valaobjectcreationexpression.vala b/vala/valaobjectcreationexpression.vala index 20b829bdb..26d3bca37 100644 --- a/vala/valaobjectcreationexpression.vala +++ b/vala/valaobjectcreationexpression.vala @@ -308,7 +308,7 @@ public class Vala.ObjectCreationExpression : Expression { /* store expected type for callback parameters */ arg.formal_target_type = param.parameter_type; - arg.target_type = arg.formal_target_type.get_actual_type (value_type, this); + arg.target_type = arg.formal_target_type.get_actual_type (value_type, null, this); } } diff --git a/vala/valaobjecttypesymbol.vala b/vala/valaobjecttypesymbol.vala index 6f51e9751..752290c9f 100644 --- a/vala/valaobjecttypesymbol.vala +++ b/vala/valaobjecttypesymbol.vala @@ -1,6 +1,6 @@ /* valaobjecttypesymbol.vala * - * Copyright (C) 2008 Jürg Billeter + * Copyright (C) 2008-2009 Jürg Billeter * Copyright (C) 2008 Philip Van Hoof * * This library is free software; you can redistribute it and/or @@ -47,7 +47,6 @@ public abstract class Vala.ObjectTypeSymbol : TypeSymbol { */ public void add_type_parameter (TypeParameter p) { type_parameters.add (p); - p.type = this; scope.add (p.name, p); } diff --git a/vala/valaparser.vala b/vala/valaparser.vala index 5e7d92c1c..085e9271e 100644 --- a/vala/valaparser.vala +++ b/vala/valaparser.vala @@ -2160,10 +2160,13 @@ public class Vala.Parser : CodeVisitor { var flags = parse_member_declaration_modifiers (); var type = parse_type (); string id = parse_identifier (); - parse_type_parameter_list (); + var type_param_list = parse_type_parameter_list (); var method = new Method (id, type, get_src_com (begin)); method.access = access; set_attributes (method, attrs); + foreach (TypeParameter type_param in type_param_list) { + method.add_type_parameter (type_param); + } if (ModifierFlags.STATIC in flags) { method.binding = MemberBinding.STATIC; } else if (ModifierFlags.CLASS in flags) { diff --git a/vala/valasemanticanalyzer.vala b/vala/valasemanticanalyzer.vala index 47f806a96..1bef37711 100644 --- a/vala/valasemanticanalyzer.vala +++ b/vala/valasemanticanalyzer.vala @@ -551,30 +551,62 @@ public class Vala.SemanticAnalyzer : CodeVisitor { return null; } - public static DataType? get_actual_type (DataType derived_instance_type, GenericType generic_type, CodeNode node_reference) { - // trace type arguments back to the datatype where the method has been declared - var instance_type = get_instance_base_type_for_member (derived_instance_type, (TypeSymbol) generic_type.type_parameter.parent_symbol, node_reference); + public static DataType? get_actual_type (DataType? derived_instance_type, MemberAccess? method_access, GenericType generic_type, CodeNode node_reference) { + if (generic_type.type_parameter.parent_symbol is TypeSymbol) { + if (derived_instance_type == null) { + return generic_type; + } - assert (instance_type != null); + // trace type arguments back to the datatype where the method has been declared + var instance_type = get_instance_base_type_for_member (derived_instance_type, (TypeSymbol) generic_type.type_parameter.parent_symbol, node_reference); - int param_index = instance_type.data_type.get_type_parameter_index (generic_type.type_parameter.name); - if (param_index == -1) { - Report.error (node_reference.source_reference, "internal error: unknown type parameter %s".printf (generic_type.type_parameter.name)); - node_reference.error = true; - return null; - } + assert (instance_type != null); - DataType actual_type = null; - if (param_index < instance_type.get_type_arguments ().size) { - actual_type = (DataType) instance_type.get_type_arguments ().get (param_index); - } - if (actual_type == null) { - // no actual type available - return generic_type; + int param_index = instance_type.data_type.get_type_parameter_index (generic_type.type_parameter.name); + if (param_index == -1) { + Report.error (node_reference.source_reference, "internal error: unknown type parameter %s".printf (generic_type.type_parameter.name)); + node_reference.error = true; + return null; + } + + DataType actual_type = null; + if (param_index < instance_type.get_type_arguments ().size) { + actual_type = (DataType) instance_type.get_type_arguments ().get (param_index); + } + if (actual_type == null) { + // no actual type available + return generic_type; + } + actual_type = actual_type.copy (); + actual_type.value_owned = actual_type.value_owned && generic_type.value_owned; + return actual_type; + } else { + // generic method + var m = (Method) generic_type.type_parameter.parent_symbol; + + if (method_access == null) { + return generic_type; + } + + int param_index = m.get_type_parameter_index (generic_type.type_parameter.name); + if (param_index == -1) { + Report.error (node_reference.source_reference, "internal error: unknown type parameter %s".printf (generic_type.type_parameter.name)); + node_reference.error = true; + return null; + } + + DataType actual_type = null; + if (param_index < method_access.get_type_arguments ().size) { + actual_type = (DataType) method_access.get_type_arguments ().get (param_index); + } + if (actual_type == null) { + // no actual type available + return generic_type; + } + actual_type = actual_type.copy (); + actual_type.value_owned = actual_type.value_owned && generic_type.value_owned; + return actual_type; } - actual_type = actual_type.copy (); - actual_type.value_owned = actual_type.value_owned && generic_type.value_owned; - return actual_type; } public bool is_in_instance_method () { diff --git a/vala/valasignal.vala b/vala/valasignal.vala index 58f54ef2a..9b85e89b9 100644 --- a/vala/valasignal.vala +++ b/vala/valasignal.vala @@ -95,7 +95,7 @@ public class Vala.Signal : Member, Lockable { * @return delegate */ public Delegate get_delegate (DataType sender_type, CodeNode node_reference) { - var actual_return_type = return_type.get_actual_type (sender_type, node_reference); + var actual_return_type = return_type.get_actual_type (sender_type, null, node_reference); var generated_delegate = new Delegate (null, actual_return_type); generated_delegate.has_target = true; @@ -111,7 +111,7 @@ public class Vala.Signal : Member, Lockable { foreach (FormalParameter param in parameters) { var actual_param = param.copy (); - actual_param.parameter_type = actual_param.parameter_type.get_actual_type (sender_type, node_reference); + actual_param.parameter_type = actual_param.parameter_type.get_actual_type (sender_type, null, node_reference); generated_delegate.add_parameter (actual_param); } diff --git a/vala/valastruct.vala b/vala/valastruct.vala index 684425a4c..f715b3b03 100644 --- a/vala/valastruct.vala +++ b/vala/valastruct.vala @@ -113,7 +113,6 @@ public class Vala.Struct : TypeSymbol { */ public void add_type_parameter (TypeParameter p) { type_parameters.add (p); - p.type = this; scope.add (p.name, p); } diff --git a/vala/valatypeparameter.vala b/vala/valatypeparameter.vala index 8985ea8a2..623044a75 100644 --- a/vala/valatypeparameter.vala +++ b/vala/valatypeparameter.vala @@ -1,6 +1,6 @@ /* valatypeparameter.vala * - * Copyright (C) 2006-2008 Jürg Billeter + * Copyright (C) 2006-2009 Jürg Billeter * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -27,11 +27,6 @@ using Gee; * Represents a generic type parameter in the source code. */ public class Vala.TypeParameter : Symbol { - /** - * The generic type declaring this parameter. - */ - public weak TypeSymbol type; - /** * Creates a new generic type parameter. *