[vala/wip/issue/894] vala: Add support for type narrowing



commit 57391837b85c652d9c2da59ebf3374fe81180828
Author: Rico Tzschichholz <ricotz ubuntu com>
Date:   Thu Apr 2 14:30:10 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

 tests/Makefile.am                 |  1 +
 tests/objects/type-narrowing.vala | 42 +++++++++++++++++++++++++++++++++++++++
 vala/valablock.vala               |  5 +++++
 vala/valaifstatement.vala         | 27 +++++++++++++++++++++++++
 4 files changed, 75 insertions(+)
---
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 9f956d793..851051951 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -413,6 +413,7 @@ TESTS = \
        objects/signals-delegate-parameter.vala \
        objects/signals-lambda-delegate.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..978c0dcf9
--- /dev/null
+++ b/tests/objects/type-narrowing.vala
@@ -0,0 +1,42 @@
+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;
+       }
+}
+
+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);
+}
diff --git a/vala/valablock.vala b/vala/valablock.vala
index a702ea011..b0b1f64eb 100644
--- a/vala/valablock.vala
+++ b/vala/valablock.vala
@@ -99,6 +99,11 @@ public class Vala.Block : Symbol, Statement {
                local_variables.add (local);
        }
 
+       public void add_variable_override (Variable local, LocalVariable replacement) {
+               local_variables.add (replacement);
+               scope.add (local.name, replacement);
+       }
+
        public void remove_local_variable (LocalVariable local) {
                local_variables.remove (local);
        }
diff --git a/vala/valaifstatement.vala b/vala/valaifstatement.vala
index e6f602de1..16929c442 100644
--- a/vala/valaifstatement.vala
+++ b/vala/valaifstatement.vala
@@ -121,6 +121,33 @@ public class Vala.IfStatement : CodeNode, Statement {
 
                condition.check (context);
 
+               // chech if this statement is used for "type narrowing"
+               unowned TypeCheck? type_check = null;
+               unowned Block? narrowed_block = null;
+               unowned Variable? narrowed_var = null;
+               if (condition is UnaryExpression && ((UnaryExpression) condition).operator == 
UnaryOperator.LOGICAL_NEGATION) {
+                       unowned Expression inner = ((UnaryExpression) condition).inner;
+                       if (inner is TypeCheck) {
+                               type_check = (TypeCheck) inner;
+                               narrowed_var = type_check.expression.symbol_reference as Variable;
+                               narrowed_block = false_statement;
+                       }
+               } else if (condition is TypeCheck) {
+                       type_check = (TypeCheck) condition;
+                       narrowed_var = type_check.expression.symbol_reference as Variable;
+                       narrowed_block = true_statement;
+               }
+               if (narrowed_var != null) {
+                       var narrowed_type = type_check.type_reference.copy ();
+                       narrowed_type.value_owned = false;
+                       narrowed_type.nullable = false;
+                       var initializer = new CastExpression (new MemberAccess.simple (narrowed_var.name, 
type_check.source_reference), narrowed_type, type_check.source_reference);
+                       var local = new LocalVariable (narrowed_type, "_%s_as_%s".printf (narrowed_var.name, 
narrowed_type.to_string ()), initializer, type_check.source_reference);
+                       narrowed_block.insert_statement (0, new DeclarationStatement (local, 
source_reference));
+                       narrowed_block.add_variable_override (narrowed_var, local);
+                       local.check (context);
+               }
+
                true_statement.check (context);
                if (false_statement != null) {
                        false_statement.check (context);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]