[vala/staging] vala: Add support for type narrowing
- From: Rico Tzschichholz <ricotz src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [vala/staging] vala: Add support for type narrowing
- Date: Wed, 11 Nov 2020 17:24:25 +0000 (UTC)
commit 7ae2f115a702439bd94bf09867b38019f39d010f
Author: Rico Tzschichholz <ricotz ubuntu com>
Date: Wed Nov 11 17:04:32 2020 +0100
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 non-null 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 | 3 ++
.../member-access-narrowed-instance.vala | 13 +++++
tests/objects/type-narrowing-fallback.vala | 25 ++++++++++
tests/objects/type-narrowing.vala | 58 ++++++++++++++++++++++
vala/valadatatype.vala | 15 ++++--
vala/valamemberaccess.vala | 49 ++++++++++++++++++
7 files changed, 168 insertions(+), 3 deletions(-)
---
diff --git a/codegen/valaccodememberaccessmodule.vala b/codegen/valaccodememberaccessmodule.vala
index 6d455738b..1f2231c55 100644
--- a/codegen/valaccodememberaccessmodule.vala
+++ b/codegen/valaccodememberaccessmodule.vala
@@ -404,6 +404,14 @@ public abstract class Vala.CCodeMemberAccessModule : CCodeControlFlowModule {
expr.target_value = load_parameter (param, expr);
}
}
+
+ // 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 0c5f059e6..50816d39c 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -475,6 +475,8 @@ TESTS = \
objects/signals-prototype-access-invalid-2.test \
objects/signals-struct-return.vala \
objects/singleton.vala \
+ objects/type-narrowing.vala \
+ objects/type-narrowing-fallback.vala \
objects/test-025.vala \
objects/test-026.vala \
objects/test-029.vala \
@@ -1039,6 +1041,7 @@ TESTS = \
semantic/yield-statement-requires-async-context.test \
nullability/bug611223.vala \
nullability/local-variable-invalid-convert.test \
+ nullability/member-access-narrowed-instance.vala \
nullability/member-access-nullable-instance.test \
nullability/method-parameter-invalid-convert.test \
nullability/method-return-invalid-convert.test \
diff --git a/tests/nullability/member-access-narrowed-instance.vala
b/tests/nullability/member-access-narrowed-instance.vala
new file mode 100644
index 000000000..f83ff5dfd
--- /dev/null
+++ b/tests/nullability/member-access-narrowed-instance.vala
@@ -0,0 +1,13 @@
+class Foo {
+ public int i;
+}
+
+void main () {
+ Foo? foo = new Foo ();
+ if (foo is Foo) {
+ foo.i = 42;
+ assert (foo.i == 42);
+ } else {
+ assert_not_reached ();
+ }
+}
diff --git a/tests/objects/type-narrowing-fallback.vala b/tests/objects/type-narrowing-fallback.vala
new file mode 100644
index 000000000..17c9a23fd
--- /dev/null
+++ b/tests/objects/type-narrowing-fallback.vala
@@ -0,0 +1,25 @@
+interface Foo : Object {
+ public abstract int get_foo ();
+}
+
+class Bar : Object {
+ public int get_bar () {
+ return 23;
+ }
+}
+
+class Manam : Bar, Foo {
+ public virtual int get_foo () {
+ return 42;
+ }
+}
+
+void main () {
+ Foo foo = new Manam ();
+ if (foo is Bar) {
+ assert (foo.get_bar () == 23);
+ assert (foo.get_foo () == 42);
+ } else {
+ assert_not_reached ();
+ }
+}
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/valadatatype.vala b/vala/valadatatype.vala
index ada2c1dd4..04ba29700 100644
--- a/vala/valadatatype.vala
+++ b/vala/valadatatype.vala
@@ -44,6 +44,11 @@ public abstract class Vala.DataType : CodeNode {
*/
public weak Symbol? symbol { get; private set; }
+ /**
+ * The referred symbol in the current context.
+ */
+ public weak Symbol? context_symbol { get; set; }
+
/**
* The referred type symbol.
*/
@@ -428,10 +433,14 @@ public abstract class Vala.DataType : CodeNode {
}
public virtual Symbol? get_member (string member_name) {
- if (type_symbol != null) {
- return SemanticAnalyzer.symbol_lookup_inherited (type_symbol, member_name);
+ Symbol? member = null;
+ if (context_symbol != null) {
+ member = SemanticAnalyzer.symbol_lookup_inherited (context_symbol, member_name);
}
- return null;
+ if (member == null && type_symbol != null) {
+ member = SemanticAnalyzer.symbol_lookup_inherited (type_symbol, member_name);
+ }
+ return member;
}
public virtual Symbol? get_pointer_member (string member_name) {
diff --git a/vala/valamemberaccess.vala b/vala/valamemberaccess.vala
index 54caa3a23..06cf64ce4 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 != null && context.profile == Profile.GOBJECT) {
+ check_narrowed_value_type ();
+ }
}
if (value_type != null) {
@@ -1112,4 +1116,49 @@ public class Vala.MemberAccess : Expression {
return found;
}
+
+ void check_narrowed_value_type () {
+ unowned Variable? variable = symbol_reference as Variable;
+ if (variable == null) {
+ return;
+ }
+
+ if (!(parent_node is MemberAccess)) {
+ return;
+ }
+
+ bool is_negation = false;
+ unowned CodeNode? parent = parent_node;
+ unowned IfStatement? if_statement = null;
+ while (parent != null && !(parent is Method)) {
+ if (parent is TypeCheck) {
+ parent = null;
+ break;
+ }
+ if (parent.parent_node is IfStatement) {
+ if_statement = (IfStatement) parent.parent_node;
+ is_negation = if_statement.false_statement == parent;
+ break;
+ }
+ parent = parent.parent_node;
+ }
+
+ if (if_statement != null) {
+ unowned Expression expr = if_statement.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) {
+ unowned TypeSymbol? narrowed_symnol = type_check.type_reference.type_symbol;
+ if (variable == type_check.expression.symbol_reference) {
+ if (narrowed_symnol != value_type.type_symbol) {
+ value_type.context_symbol = narrowed_symnol;
+ }
+ value_type.nullable = false;
+ }
+ }
+ }
+ }
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]