[vala/wip/baedert/nullable: 24/25] checkpoint
- From: Timm Bäder <baedert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [vala/wip/baedert/nullable: 24/25] checkpoint
- Date: Tue, 8 Nov 2016 21:20:04 +0000 (UTC)
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]