From d14248fdfa5ba2a39dfb8ce8a6a6a4a083c4f081 Mon Sep 17 00:00:00 2001 From: Rico Tzschichholz Date: Fri, 1 Nov 2019 21:31:47 +0100 Subject: [PATCH] vala: Add GenericType.dup/destroy fields This provides direct access to the according values of generic-types and type-parameters. Fixes https://gitlab.gnome.org/GNOME/vala/issues/190 --- codegen/valaccodememberaccessmodule.vala | 4 ++ tests/Makefile.am | 1 + tests/generics/member-dup-destroy.vala | 88 ++++++++++++++++++++++++ vala/Makefile.am | 2 + vala/valagenericdestroyfield.vala | 36 ++++++++++ vala/valagenericdupfield.vala | 36 ++++++++++ vala/valagenerictype.vala | 24 +++++++ vala/valamemberaccess.vala | 5 ++ vala/valasemanticanalyzer.vala | 3 + 9 files changed, 199 insertions(+) create mode 100644 tests/generics/member-dup-destroy.vala create mode 100644 vala/valagenericdestroyfield.vala create mode 100644 vala/valagenericdupfield.vala diff --git a/codegen/valaccodememberaccessmodule.vala b/codegen/valaccodememberaccessmodule.vala index 7674402ca..e520ede93 100644 --- a/codegen/valaccodememberaccessmodule.vala +++ b/codegen/valaccodememberaccessmodule.vala @@ -125,6 +125,10 @@ public abstract class Vala.CCodeMemberAccessModule : CCodeControlFlowModule { CCodeExpression delegate_target_destroy_notify; get_delegate_target_cexpression (expr.inner, out delegate_target_destroy_notify); set_cvalue (expr, delegate_target_destroy_notify); + } else if (expr.symbol_reference is GenericDupField) { + set_cvalue (expr, get_dup_func_expression (expr.inner.value_type, expr.source_reference)); + } else if (expr.symbol_reference is GenericDestroyField) { + set_cvalue (expr, get_destroy_func_expression (expr.inner.value_type)); } else if (expr.symbol_reference is Field) { var field = (Field) expr.symbol_reference; if (expr.lvalue) { diff --git a/tests/Makefile.am b/tests/Makefile.am index 0265bc99c..daf29e9dc 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -502,6 +502,7 @@ TESTS = \ generics/constructor-chain-up.vala \ generics/inference-static-function.vala \ generics/parameter-sizeof-initializer.vala \ + generics/member-dup-destroy.vala \ generics/type-parameter-properties.vala \ generics/bug640330.test \ generics/bug640330.vala \ diff --git a/tests/generics/member-dup-destroy.vala b/tests/generics/member-dup-destroy.vala new file mode 100644 index 000000000..defe162dc --- /dev/null +++ b/tests/generics/member-dup-destroy.vala @@ -0,0 +1,88 @@ +delegate void FooFunc (); + +class Foo : Object { + public Foo () { + assert (typeof (G) == typeof (string)); + assert (G.dup == (BoxedCopyFunc) string.dup); + assert (G.destroy == (DestroyNotify) free); + + G g = null; + assert (g.dup == (BoxedCopyFunc) string.dup); + assert (g.destroy == (DestroyNotify) free); + } + + public void foo () { + assert (typeof (G) == typeof (string)); + assert (G.dup == (BoxedCopyFunc) string.dup); + assert (G.destroy == (DestroyNotify) free); + + G g = null; + assert (g.dup == (BoxedCopyFunc) string.dup); + assert (g.destroy == (DestroyNotify) free); + } + + public async void foo_async () { + assert (typeof (G) == typeof (string)); + assert (G.dup == (BoxedCopyFunc) string.dup); + assert (G.destroy == (DestroyNotify) free); + + G g = null; + assert (g.dup == (BoxedCopyFunc) string.dup); + assert (g.destroy == (DestroyNotify) free); + } + + public void foo_captured () { + FooFunc f = () => { + assert (typeof (G) == typeof (string)); + assert (G.dup == (BoxedCopyFunc) string.dup); + assert (G.destroy == (DestroyNotify) free); + + G g = null; + assert (g.dup == (BoxedCopyFunc) string.dup); + assert (g.destroy == (DestroyNotify) free); + }; + f (); + } +} + +void bar (T t) { + assert (typeof (T) == typeof (Foo)); + assert (T.dup == (BoxedCopyFunc) Object.@ref); + assert (T.destroy == (DestroyNotify) Object.unref); + + assert (t.dup == (BoxedCopyFunc) Object.@ref); + assert (t.destroy == (DestroyNotify) Object.unref); +} + +async void bar_async (T t) { + assert (typeof (T) == typeof (Foo)); + assert (T.dup == (BoxedCopyFunc) Object.@ref); + assert (T.destroy == (DestroyNotify) Object.unref); + + assert (t.dup == (BoxedCopyFunc) Object.@ref); + assert (t.destroy == (DestroyNotify) Object.unref); +} + +void bar_captured (T t) { + FooFunc f = () => { + assert (typeof (T) == typeof (Foo)); + assert (T.dup == (BoxedCopyFunc) Object.@ref); + assert (T.destroy == (DestroyNotify) Object.unref); + + assert (t.dup == (BoxedCopyFunc) Object.@ref); + assert (t.destroy == (DestroyNotify) Object.unref); + }; + f (); +} + +void main () { + var foo = new Foo (); + + foo.foo (); + foo.foo_async.begin (); + foo.foo_captured (); + + bar (foo); + bar_async.begin (foo); + bar_captured (foo); +} diff --git a/vala/Makefile.am b/vala/Makefile.am index 57c758fdc..c6aed1c54 100644 --- a/vala/Makefile.am +++ b/vala/Makefile.am @@ -90,6 +90,8 @@ libvala_la_VALASOURCES = \ valaforeachstatement.vala \ valaforstatement.vala \ valagirparser.vala \ + valagenericdestroyfield.vala \ + valagenericdupfield.vala \ valagenerictype.vala \ valagenieparser.vala \ valageniescanner.vala \ diff --git a/vala/valagenericdestroyfield.vala b/vala/valagenericdestroyfield.vala new file mode 100644 index 000000000..6627bcca2 --- /dev/null +++ b/vala/valagenericdestroyfield.vala @@ -0,0 +1,36 @@ +/* valagenericdestroyfield.vala + * + * Copyright (C) 2019 Rico Tzschichholz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Rico Tzschichholz + */ + +/** + * Represents the Generic.destroy field. + */ +public class Vala.GenericDestroyField : Field { + /** + * Creates a new generic destroy field. + * + * @return newly created field + */ + public GenericDestroyField (SourceReference source_reference) { + base ("destroy", CodeContext.get ().analyzer.delegate_target_destroy_type, null, source_reference); + external = true; + } +} diff --git a/vala/valagenericdupfield.vala b/vala/valagenericdupfield.vala new file mode 100644 index 000000000..12ab9ecea --- /dev/null +++ b/vala/valagenericdupfield.vala @@ -0,0 +1,36 @@ +/* valagenericdupfield.vala + * + * Copyright (C) 2019 Rico Tzschichholz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Rico Tzschichholz + */ + +/** + * Represents the Generic.dup field. + */ +public class Vala.GenericDupField : Field { + /** + * Creates a new generic dup field. + * + * @return newly created field + */ + public GenericDupField (SourceReference source_reference) { + base ("dup", CodeContext.get ().analyzer.generics_dup_func_type, null, source_reference); + external = true; + } +} diff --git a/vala/valagenerictype.vala b/vala/valagenerictype.vala index e5e1a50a8..1f7a2d1ee 100644 --- a/vala/valagenerictype.vala +++ b/vala/valagenerictype.vala @@ -31,6 +31,9 @@ public class Vala.GenericType : DataType { */ public weak TypeParameter type_parameter { get; set; } + GenericDupField? dup_field; + GenericDestroyField? destroy_field; + public GenericType (TypeParameter type_parameter) { this.type_parameter = type_parameter; // type parameters are always considered nullable @@ -74,6 +77,27 @@ public class Vala.GenericType : DataType { } public override Symbol? get_member (string member_name) { + if (member_name == "dup") { + return get_dup_field (); + } else if (member_name == "destroy") { + return get_destroy_field (); + } return null; } + + unowned GenericDupField get_dup_field () { + if (dup_field == null) { + dup_field = new GenericDupField (source_reference); + dup_field.access = SymbolAccessibility.PUBLIC; + } + return dup_field; + } + + unowned GenericDestroyField get_destroy_field () { + if (destroy_field == null) { + destroy_field = new GenericDestroyField (source_reference); + destroy_field.access = SymbolAccessibility.PUBLIC; + } + return destroy_field; + } } diff --git a/vala/valamemberaccess.vala b/vala/valamemberaccess.vala index 9316e5644..421ee8a9f 100644 --- a/vala/valamemberaccess.vala +++ b/vala/valamemberaccess.vala @@ -362,6 +362,11 @@ public class Vala.MemberAccess : Expression { } } + if (inner is MemberAccess && inner.symbol_reference is TypeParameter) { + inner.value_type = new GenericType ((TypeParameter) inner.symbol_reference); + inner.value_type.source_reference = source_reference; + } + if (symbol_reference == null && inner.value_type != null) { if (pointer_member_access) { symbol_reference = inner.value_type.get_pointer_member (member_name); diff --git a/vala/valasemanticanalyzer.vala b/vala/valasemanticanalyzer.vala index 25c812dc4..c83d58b53 100644 --- a/vala/valasemanticanalyzer.vala +++ b/vala/valasemanticanalyzer.vala @@ -169,6 +169,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor { public Class gsource_type; public DataType delegate_target_type; public DelegateType delegate_target_destroy_type; + public DelegateType generics_dup_func_type; Delegate destroy_notify; @@ -236,6 +237,8 @@ public class Vala.SemanticAnalyzer : CodeVisitor { delegate_target_type = new StructValueType ((Struct) glib_ns.scope.lookup ("pointer")); destroy_notify = (Delegate) glib_ns.scope.lookup ("DestroyNotify"); delegate_target_destroy_type = new DelegateType (destroy_notify); + + generics_dup_func_type = new DelegateType ((Delegate) glib_ns.scope.lookup ("BoxedCopyFunc")); } else { delegate_target_type = new PointerType (new VoidType ()); destroy_notify = new Delegate ("ValaDestroyNotify", new VoidType ()); -- 2.47.2