[vala/wip/issue/894: 234/235] vala: Add support for type narrowing
- From: Rico Tzschichholz <ricotz src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [vala/wip/issue/894: 234/235] vala: Add support for type narrowing
- Date: Tue, 11 Aug 2020 13:30:53 +0000 (UTC)
commit 46e5f19063df40d646fd6035a04846c3d14f82f7
Author: Rico Tzschichholz <ricotz ubuntu com>
Date: Tue Aug 11 15:22:21 2020 +0200
vala: Add support for type narrowing
This causes type of given variable to be narrowed for the correspoding
child block of an if-statement.
Foo foo = ...;
if (foo is Bar) {
// foo represents a Bar instance inside this block
}
This makes conditional-expressions behaving similar.
... = (foo is Bar) ? "foo is instance of Bar here" : "...";
Fixes https://gitlab.gnome.org/GNOME/vala/issues/894
codegen/valaccodememberaccessmodule.vala | 8 +++++
tests/Makefile.am | 1 +
tests/objects/type-narrowing.vala | 58 ++++++++++++++++++++++++++++++++
vala/valamemberaccess.vala | 4 +++
vala/valasemanticanalyzer.vala | 45 +++++++++++++++++++++++++
5 files changed, 116 insertions(+)
---
diff --git a/codegen/valaccodememberaccessmodule.vala b/codegen/valaccodememberaccessmodule.vala
index 1cec90ac6..6e6d28f92 100644
--- a/codegen/valaccodememberaccessmodule.vala
+++ b/codegen/valaccodememberaccessmodule.vala
@@ -400,6 +400,14 @@ public abstract class Vala.CCodeMemberAccessModule : CCodeControlFlowModule {
expr.target_value = load_parameter (param);
}
}
+
+ // Add cast for narrowed type access of variables if needed
+ if (expr.symbol_reference is Variable) {
+ unowned GLibValue cvalue = (GLibValue) expr.target_value;
+ if (cvalue.value_type.type_symbol != expr.value_type.type_symbol) {
+ cvalue.cvalue = new CCodeCastExpression (cvalue.cvalue, get_ccode_name
(expr.value_type));
+ }
+ }
}
/* Returns lvalue access to the given local variable */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index c2505dcba..7eff0c2ea 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -450,6 +450,7 @@ TESTS = \
objects/signals-prototype-access-invalid-2.test \
objects/signals-struct-return.vala \
objects/singleton.vala \
+ objects/type-narrowing.vala \
objects/test-025.vala \
objects/test-026.vala \
objects/test-029.vala \
diff --git a/tests/objects/type-narrowing.vala b/tests/objects/type-narrowing.vala
new file mode 100644
index 000000000..47997d141
--- /dev/null
+++ b/tests/objects/type-narrowing.vala
@@ -0,0 +1,58 @@
+class Foo {
+ public void manam () {
+ if (this is Bar) {
+ assert (this.str == "bar");
+ }
+ assert (((this is Bar) ? this.str : "foo") == "bar");
+
+ if (!(this is Bar)) {
+ assert_not_reached ();
+ } else {
+ assert (this.str == "bar");
+ }
+ assert ((!(this is Bar) ? "foo" : this.str) == "bar");
+ }
+}
+
+class Bar : Foo {
+ public string str;
+ public Bar (string s) {
+ str = s;
+ }
+}
+
+class Manam : Bar {
+ public Manam (string s) {
+ base (s);
+ }
+}
+
+void manam (Foo foo) {
+ if (foo is Bar) {
+ assert (foo.str == "bar");
+ }
+ assert (((foo is Bar) ? foo.str : "foo") == "bar");
+
+ if (!(foo is Bar)) {
+ assert_not_reached ();
+ } else {
+ assert (foo.str == "bar");
+ }
+ assert ((!(foo is Bar) ? "foo" : foo.str) == "bar");
+}
+
+void main() {
+ {
+ var bar = new Bar ("bar");
+ bar.manam ();
+ manam (bar);
+ }
+ {
+ Bar bar = new Manam ("manam");
+ if (bar is Manam) {
+ assert (bar.str == "manam");
+ bar = new Bar ("bar");
+ }
+ assert (bar.str == "bar");
+ }
+}
diff --git a/vala/valamemberaccess.vala b/vala/valamemberaccess.vala
index d61e2f3fe..09072e1fa 100644
--- a/vala/valamemberaccess.vala
+++ b/vala/valamemberaccess.vala
@@ -986,6 +986,10 @@ public class Vala.MemberAccess : Expression {
var parent_type = SemanticAnalyzer.get_data_type_for_symbol
(symbol_reference.parent_symbol);
inner.target_type = parent_type.get_actual_type (inner.value_type, null,
this);
}
+
+ if (inner == null) {
+ value_type = SemanticAnalyzer.get_narrowed_type (this);
+ }
}
if (value_type != null) {
diff --git a/vala/valasemanticanalyzer.vala b/vala/valasemanticanalyzer.vala
index 8242efd35..ddd43b3f8 100644
--- a/vala/valasemanticanalyzer.vala
+++ b/vala/valasemanticanalyzer.vala
@@ -942,6 +942,51 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
return null;
}
+ public static DataType get_narrowed_type (MemberAccess member_access) {
+ unowned Variable? variable = member_access.symbol_reference as Variable;
+ if (variable == null) {
+ return member_access.value_type;
+ }
+
+ if (!(member_access.parent_node is MemberAccess)) {
+ return member_access.value_type;
+ }
+
+ bool is_negation = false;
+ unowned CodeNode? parent = member_access.parent_node;
+ while (parent != null && !(parent is Method)) {
+ if (parent is TypeCheck) {
+ parent = null;
+ break;
+ }
+ if (parent.parent_node is IfStatement) {
+ is_negation = ((IfStatement) parent.parent_node).false_statement == parent;
+ break;
+ }
+ parent = parent.parent_node;
+ }
+
+ if (parent != null && parent.parent_node is IfStatement) {
+ unowned Expression expr = ((IfStatement) parent.parent_node).condition;
+ if (expr is UnaryExpression && ((UnaryExpression) expr).operator ==
UnaryOperator.LOGICAL_NEGATION) {
+ expr = ((UnaryExpression) expr).inner;
+ is_negation = !is_negation;
+ }
+ unowned TypeCheck? type_check = expr as TypeCheck;
+ if (!is_negation && type_check != null) {
+ var narrowed_type = type_check.type_reference.copy ();
+ narrowed_type.value_owned = member_access.value_type.value_owned;
+ if (variable == type_check.expression.symbol_reference
+ && narrowed_type.type_symbol != member_access.value_type.type_symbol) {
+ Report.notice (member_access.source_reference,
@"`$(variable.variable_type) $variable' narrowed by `$expr' to `$narrowed_type'");
+ return narrowed_type;
+ }
+ }
+ }
+
+ return member_access.value_type;
+ }
+
public static DataType get_actual_type (DataType? derived_instance_type, List<DataType>?
method_type_arguments, GenericType generic_type, CodeNode? node_reference) {
DataType actual_type = null;
if (generic_type.type_parameter.parent_symbol is TypeSymbol) {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]