From: Timm Bäder Date: Tue, 8 Nov 2016 19:31:56 +0000 (+0100) Subject: checkpoint X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=08849bb74579967db7196d276e31d671a46440c5;p=thirdparty%2Fvala.git checkpoint --- diff --git a/tests/basic-types/bug591552.vala b/tests/basic-types/bug591552.vala index e51a48cbe..9ddd8155a 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 40eebfae6..583b8e170 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 e96d0b173..89d5e504a 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 (); 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 (); 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> 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 4c44b3183..445289ec9 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) {