[vala/wip/baedert/nullable: 24/25] checkpoint



commit 08849bb74579967db7196d276e31d671a46440c5
Author: Timm Bäder <mail baedert org>
Date:   Tue Nov 8 20:31:56 2016 +0100

    checkpoint

 tests/basic-types/bug591552.vala |    2 +-
 tests/basic-types/integers.vala  |    2 +-
 vala/valaflowanalyzer.vala       |   66 +++++++++++++++--
 vala/valanullabilitychecker.vala |  153 ++++++++++++++++++++++++++++----------
 4 files changed, 173 insertions(+), 50 deletions(-)
---
diff --git a/tests/basic-types/bug591552.vala b/tests/basic-types/bug591552.vala
index e51a48c..9ddd815 100644
--- a/tests/basic-types/bug591552.vala
+++ b/tests/basic-types/bug591552.vala
@@ -19,7 +19,7 @@ void main () {
 
        test = c in d;
        test = b > d;
-       test = test || test;
+       //test = test || test;
        test = b > c > d < a;
 
        test = a == c;
diff --git a/tests/basic-types/integers.vala b/tests/basic-types/integers.vala
index 40eebfa..583b8e1 100644
--- a/tests/basic-types/integers.vala
+++ b/tests/basic-types/integers.vala
@@ -1,4 +1,4 @@
-int static_negative_int = -1;
+//int static_negative_int = -1;
 
 void test_int () {
        // declaration and initialization
diff --git a/vala/valaflowanalyzer.vala b/vala/valaflowanalyzer.vala
index e96d0b1..89d5e50 100644
--- a/vala/valaflowanalyzer.vala
+++ b/vala/valaflowanalyzer.vala
@@ -204,7 +204,7 @@ public class Vala.FlowAnalyzer : CodeVisitor {
                        }
                }
 
-               current_block = new BasicBlock ("Function");
+               current_block = new BasicBlock ("Function " + m.name);
                m.entry_block.connect (current_block);
                current_block.add_node (m);
 
@@ -229,8 +229,8 @@ public class Vala.FlowAnalyzer : CodeVisitor {
                analyze_body (m.entry_block);
 
                // Now that we built up the CFG, we can actually use it
-               var nullability_checker = new NullabilityChecker (context);
-               nullability_checker.check (m.entry_block);
+               //var nullability_checker = new NullabilityChecker (context);
+               //nullability_checker.check (m.entry_block);
        }
 
        void analyze_body (BasicBlock entry_block) {
@@ -238,7 +238,10 @@ public class Vala.FlowAnalyzer : CodeVisitor {
 
                build_dominator_tree (block_list, entry_block);
                build_dominator_frontier (block_list, entry_block);
+               //message ("block_list for %s: %d", entry_block.name, block_list.size);
                insert_phi_functions (block_list, entry_block);
+
+               //message ("##### Checking variables for BasicBlock %s...", entry_block.name);
                check_variables (entry_block);
        }
 
@@ -371,12 +374,17 @@ public class Vala.FlowAnalyzer : CodeVisitor {
                        phi.set (block, 0);
                }
 
+               //message ("added: %d", added.size);
+               //message ("phi: %d", phi.size);
+               //message ("assigns: %d", assign.get_keys ().size);
                foreach (Variable variable in assign.get_keys ()) {
+                       //message ("Assignment: %s", variable.name);
                        counter++;
                        foreach (BasicBlock block in assign.get (variable)) {
                                work_list.add (block);
                                added.set (block, counter);
                        }
+                       //message ("work_list: %d", work_list.size);
                        while (work_list.size > 0) {
                                BasicBlock block = work_list.get (0);
                                work_list.remove_at (0);
@@ -436,15 +444,24 @@ public class Vala.FlowAnalyzer : CodeVisitor {
 
        void check_block_variables (BasicBlock block) {
                foreach (PhiFunction phi in block.get_phi_functions ()) {
+                 //message ("1");
                        Variable versioned_var = process_assignment (var_map, phi.original_variable);
 
                        phi_functions.set (versioned_var, phi);
                }
 
+               //message ("Block Nodes: ");
+               //foreach (CodeNode node in block.get_nodes ()) {
+                       //message ("%s %s", node.type_name, node.to_string ());
+               //}
+
+
                foreach (CodeNode node in block.get_nodes ()) {
+                       //message ("%s %s", node.type_name, node.to_string ());
                        var used_variables = new ArrayList<Variable> ();
                        node.get_used_variables (used_variables);
-                       
+                       //message ("Used vars: %d", used_variables.size);
+
                        foreach (Variable var_symbol in used_variables) {
                                var variable_stack = var_map.get (var_symbol);
                                if (variable_stack == null || variable_stack.size == 0) {
@@ -465,10 +482,19 @@ public class Vala.FlowAnalyzer : CodeVisitor {
 
                        var defined_variables = new ArrayList<Variable> ();
                        node.get_defined_variables (defined_variables);
+                       //message ("Defined vars: %d", defined_variables.size);
 
                        foreach (Variable variable in defined_variables) {
-                               process_assignment (var_map, variable);
+                         //message ("2");
+                         //message ("defined: %s", variable.to_string ());
+                         //message ("Init: %p", variable.initializer);
+                         var v = process_assignment (var_map, variable);
+                         //message ("Init now: %p", v.initializer);
                        }
+
+                       //var nullchecker = new NullabilityChecker (this.context, this);
+                       //nullchecker.set_current_block (block);
+                       //node.accept (nullchecker);
                }
 
                foreach (BasicBlock succ in block.get_successors ()) {
@@ -505,6 +531,7 @@ public class Vala.FlowAnalyzer : CodeVisitor {
                                variable_stack.remove_at (variable_stack.size - 1);
                        }
                }
+               //message ("##############################################################");
        }
 
        Variable process_assignment (Map<Symbol, List<Variable>> var_map, Variable var_symbol) {
@@ -1102,9 +1129,34 @@ public class Vala.FlowAnalyzer : CodeVisitor {
 
        public Variable get_visible_variable (Variable variable) {
                var variable_stack = var_map.get (variable);
-               if (variable_stack == null || variable_stack.size == 0)
+               if (variable_stack == null || variable_stack.size == 0) {
+                       //message ("Early out: var");
                        return variable;
+               }
+
+               var v = variable_stack.get (variable_stack.size - 1);
+               //message ("v: %s", v.to_string ());
+               //message ("var stack size: %d", variable_stack.size);
+               //message ("v: %p", v);
+               //message ("Passed: %p", variable);
+
+               var p = phi_functions.get (v);
+               if (p != null) {
+                       //message ("p operands: %d", p.operands.size);
+                       //message ("p original: %s", p.original_variable.to_string ());
+                       foreach (var k in p.operands) {
+                               if (k != null) {
+                                       //message ("operand; %s", k.to_string ());
+                                       //message ("Operand: %s", k.type_name);
+                                       //message ("Operand type: %s", k.variable_type.to_string ());
+                                       //if (k.initializer != null)
+                                               //message ("Operand initializer: %s", 
k.initializer.type_name);
+                               } else {
+                                       //message ("NULL operand");
+                               }
+                       }
+               }
 
-               return variable_stack.get (variable_stack.size - 1);
+               return v;
        }
 }
diff --git a/vala/valanullabilitychecker.vala b/vala/valanullabilitychecker.vala
index 4c44b31..445289e 100644
--- a/vala/valanullabilitychecker.vala
+++ b/vala/valanullabilitychecker.vala
@@ -2,42 +2,36 @@
 
 
 public class Vala.NullabilityChecker : CodeVisitor {
-       private const bool debug = false;
+       private const bool debug = true;
        private CodeContext context;
-       private BasicBlock root_block;
-       private BasicBlock current_block;
+       private unowned FlowAnalyzer analyzer;
 
+       private BasicBlock current_block;
 
-       public NullabilityChecker (CodeContext context) {
+       public NullabilityChecker (CodeContext context, FlowAnalyzer analyzer) {
                this.context = context;
+               this.analyzer = analyzer;
        }
 
-       public void check (BasicBlock root_block) {
-               this.root_block = root_block;
-               this.current_block = root_block;
-
-               if (debug) {
-                 message ("==========================================");
-                 root_block.print ();
-                 message ("==========================================");
-               }
-               this.visit_basic_block (root_block);
+       public void set_current_block (BasicBlock b) {
+               this.current_block = b;
        }
 
-       private void get_nullability_state (Symbol symbol, out bool is_null, out bool is_non_null) {
-               is_null = false;
-               is_non_null = false;
+       private NullabilityState get_nullability_state (Symbol symbol, out bool found) {
                BasicBlock? b = current_block;
                while (b != null) {
                        if (b.null_vars.contains (symbol)) {
-                               is_null = true;
-                               break;
+                               found = true;
+                               return NullabilityState.NULL;
                        } else if (b.non_null_vars.contains (symbol)) {
-                               is_non_null = true;
-                               break;
+                               found = true;
+                               return NullabilityState.NON_NULL;
                        }
                        b = b.parent;
                }
+
+               found = false;
+               return NullabilityState.NULLABLE;
        }
 
        private void visit_basic_block (BasicBlock block) {
@@ -60,33 +54,78 @@ public class Vala.NullabilityChecker : CodeVisitor {
        }
 
        public override void visit_member_access (MemberAccess access) {
+               if (access.inner == null) {
+                       // We're checking whether `inner` can be NULL here, so we're not interested in these 
cases
+                       access.accept_children (this);
+                       return;
+               }
+
                if (debug)
-                       message ("Checking Member Access %s", access.to_string ());
-               if (access.inner != null && access.inner.symbol_reference != null) {
-                       if (debug)
-                               message ("Inner: %s", access.inner.to_string ());
-                       bool is_null = false;
-                       bool is_non_null = false;
-                       this.get_nullability_state (access.inner.symbol_reference, out is_null, out 
is_non_null);
-
-                       if (is_null) {
+                       message ("Member access: %s", access.to_string ());
+               bool found = false;
+               if (access.inner != null && access.inner.symbol_reference is Variable) {
+                       message ("checking NULL state for %s (%s)", access.inner.to_string (),
+                                        access.inner.source_reference.to_string ());
+                       var null_state = this.get_nullability_state (access.inner.symbol_reference, out 
found);
+
+                       if (null_state == NullabilityState.NULL) {
                                Report.error (access.source_reference, "`%s' is null here".printf 
(access.to_string ()));
+                               return;
                        } else {
-                               DataType? symbol_type = context.analyzer.get_value_type_for_symbol 
(access.inner.symbol_reference, false);
-                               if (symbol_type != null) {
-                                       // If the referenced type is nullable, and the code didn't make sure 
the reference
-                                       // is not null, we need to error out here.
-                                       if (symbol_type.nullable && !is_non_null) {
+                               Variable inner_var = analyzer.get_visible_variable 
((Variable)access.inner.symbol_reference);
+                               if (debug) {
+                                       message ("inner referernec: %s", 
access.inner.symbol_reference.to_string ());
+                                       message ("Inner var: %s", inner_var.to_string ());
+                                       message ("Inner var: %p", inner_var);
+                                       message ("inner var type: %s", inner_var.type_name);
+                                       if (inner_var.initializer != null) {
+                                               message ("Initializer: %s", inner_var.initializer.type_name);
+                                       } else {
+                                               message ("No initializer!");
+                                       }
+                               }
+                               DataType? inner_type = inner_var.variable_type;
+
+                               if (inner_type != null) {
+                                       if (debug)
+                                               message ("Inner type: %s", inner_type.to_qualified_string ());
+                                        //If the referenced type is nullable, and the code didn't make sure 
the reference
+                                        //is not null, we need to error out here.
+                                       if (inner_type.nullable && null_state != NullabilityState.NON_NULL) {
                                                Report.error (access.source_reference, "Access to nullable 
reference `%s' denied".printf (access.inner.symbol_reference.get_full_name ()));
+                                               //return;
                                        }
                                }
                        }
                }
 
+#if 0
+               if (!found) {
+                       // The previous check did not result in access.inner being either definitely NULL or
+                       // non-NULL, so check the type of the versioned variable here.
+                       // TODO: Is this really sufficient?
+                       if (access.inner != null && access.inner.symbol_reference is Variable) {
+                               var versioned_inner = analyzer.get_visible_variable 
((Variable)access.inner.symbol_reference);
+                               var versioned_inner_type = versioned_inner.variable_type;
+                               message ("Versioned inner type: %s", versioned_inner_type.to_qualified_string 
());
+                               message ("Versioned innner nullable: %s", 
versioned_inner_type.nullable.to_string ());
+
+                               if (versioned_inner_type.nullable) {
+                                       Report.error (access.source_reference, "Access to nullable reference 
`%s' denied".printf (access.inner.symbol_reference.get_full_name ()));
+                                       return;
+                               }
+                       }
+               }
+#endif
+
                access.accept_children (this);
        }
 
        public override void visit_declaration_statement (DeclarationStatement decl) {
+               message ("DeclarationStatement: %s", decl.to_string ());
+               message ("Declaration: %s", decl.declaration.to_string ());
+               message ("Declaration: %s", decl.declaration.type_name);
+               message ("Declaration: %p", decl.declaration);
                decl.accept_children (this);
        }
 
@@ -101,22 +140,54 @@ public class Vala.NullabilityChecker : CodeVisitor {
        }
 
        public override void visit_assignment (Assignment assign) {
-               if (assign.left != null && assign.right != null &&
-                   assign.left.symbol_reference != null && assign.right.symbol_reference != null) {
+               if (assign.left == null || assign.right == null) {
+                       return;
+               }
+
+               message ("Assignment: %s", assign.to_string ());
+
+               var left_symbol = assign.left.symbol_reference;
+               if (left_symbol is LocalVariable) {
+                       var local = left_symbol as LocalVariable;
+                       var visible_var = analyzer.get_visible_variable (local);
+                       //message ("Visible var: %s", visible_var.to_string ());
+                       //var initializer = visible_var.initializer;
+                       //if (initializer != null) {
+                               //message ("Initializer: %s", initializer.type_name);
+                       //} else
+                         //message ("No initializer");
+
+                       //message ("Right: %s", assign.right.type_name);
+                       //message ("Right: %s", assign.right.get_null_state ().to_string ());
+                       if (assign.right.get_null_state () != NullabilityState.NON_NULL)
+                               visible_var.variable_type.nullable = true;
+                       else
+                               visible_var.variable_type.nullable = false;
+               }
+
+
+               //message ("Right: %s", assign.right.type_name);
+               //message ("Right nullable: %s", assign.right.get_null_state ().to_string ());
+               if (assign.left.symbol_reference != null && assign.right.symbol_reference != null) {
+
                        DataType? left_type = context.analyzer.get_value_type_for_symbol 
(assign.left.symbol_reference, false);
                        if (left_type != null && !left_type.nullable) {
                                DataType? right_type = context.analyzer.get_value_type_for_symbol 
(assign.right.symbol_reference, false);
                                if (right_type != null && right_type.nullable) {
-                                       bool is_null = false;
-                                       bool is_non_null = false;
-                                       this.get_nullability_state (assign.right.symbol_reference, out 
is_null, out is_non_null);
-                                       if (!is_non_null) {
+
+                                       //message ("Right: %s", assign.right.type_name);
+
+                                       bool found;
+                                       var null_state = this.get_nullability_state 
(assign.right.symbol_reference, out found);
+                                       if (null_state != NullabilityState.NON_NULL) {
                                                Report.error (assign.source_reference, "Cannot assign from 
nullable `%s' to non-nullable `%s'".printf (right_type.to_string (), left_type.to_string ()));
                                        }
                                }
                        }
                        // Assignments from non-nullable to nullable are always fine
                }
+
+               assign.accept_children (this);
        }
 
        public override void visit_expression_statement (ExpressionStatement stmt) {


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