]> git.ipfire.org Git - thirdparty/vala.git/commitdiff
Report use of possibly uninitialized variables, fixes bug 508477 and bug
authorJürg Billeter <j@bitron.ch>
Fri, 14 Nov 2008 18:22:29 +0000 (18:22 +0000)
committerJürg Billeter <juergbi@src.gnome.org>
Fri, 14 Nov 2008 18:22:29 +0000 (18:22 +0000)
2008-11-14  Jürg Billeter  <j@bitron.ch>

* vala/Makefile.am:
* vala/valaassignment.vala:
* vala/valabasicblock.vala:
* vala/valabinaryexpression.vala:
* vala/valacastexpression.vala:
* vala/valacatchclause.vala:
* vala/valacodenode.vala:
* vala/valadeclarationstatement.vala:
* vala/valaelementaccess.vala:
* vala/valaexpressionstatement.vala:
* vala/valaflowanalyzer.vala:
* vala/valaforeachstatement.vala:
* vala/valainvocationexpression.vala:
* vala/valamemberaccess.vala:
* vala/valaobjectcreationexpression.vala:
* vala/valaparenthesizedexpression.vala:
* vala/valaphifunction.vala:
* vala/valapointerindirection.vala:
* vala/valareferencetransferexpression.vala:
* vala/valareturnstatement.vala:
* vala/valathrowstatement.vala:
* vala/valaunaryexpression.vala:
* compiler/valacompiler.vala:

Report use of possibly uninitialized variables, fixes bug 508477
and bug 556861

svn path=/trunk/; revision=2018

24 files changed:
ChangeLog
compiler/valacompiler.vala
vala/Makefile.am
vala/valaassignment.vala
vala/valabasicblock.vala
vala/valabinaryexpression.vala
vala/valacastexpression.vala
vala/valacatchclause.vala
vala/valacodenode.vala
vala/valadeclarationstatement.vala
vala/valaelementaccess.vala
vala/valaexpressionstatement.vala
vala/valaflowanalyzer.vala [moved from vala/valacfgbuilder.vala with 67% similarity]
vala/valaforeachstatement.vala
vala/valainvocationexpression.vala
vala/valamemberaccess.vala
vala/valaobjectcreationexpression.vala
vala/valaparenthesizedexpression.vala
vala/valaphifunction.vala [new file with mode: 0644]
vala/valapointerindirection.vala
vala/valareferencetransferexpression.vala
vala/valareturnstatement.vala
vala/valathrowstatement.vala
vala/valaunaryexpression.vala

index cb527202d8ed098e2457f3ed4b5940803bcd3052..e7b664446fd77e561d979cd6466de155f28ba1c1 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,32 @@
+2008-11-14  Jürg Billeter  <j@bitron.ch>
+
+       * vala/Makefile.am:
+       * vala/valaassignment.vala:
+       * vala/valabasicblock.vala:
+       * vala/valabinaryexpression.vala:
+       * vala/valacastexpression.vala:
+       * vala/valacatchclause.vala:
+       * vala/valacodenode.vala:
+       * vala/valadeclarationstatement.vala:
+       * vala/valaelementaccess.vala:
+       * vala/valaexpressionstatement.vala:
+       * vala/valaflowanalyzer.vala:
+       * vala/valaforeachstatement.vala:
+       * vala/valainvocationexpression.vala:
+       * vala/valamemberaccess.vala:
+       * vala/valaobjectcreationexpression.vala:
+       * vala/valaparenthesizedexpression.vala:
+       * vala/valaphifunction.vala:
+       * vala/valapointerindirection.vala:
+       * vala/valareferencetransferexpression.vala:
+       * vala/valareturnstatement.vala:
+       * vala/valathrowstatement.vala:
+       * vala/valaunaryexpression.vala:
+       * compiler/valacompiler.vala:
+
+       Report use of possibly uninitialized variables, fixes bug 508477
+       and bug 556861
+
 2008-11-14  Jürg Billeter  <j@bitron.ch>
 
        * vala/valagenieparser.vala:
index 2c8ecbb0af4da7c38167bfdd774715d11977d214..4983deddbe7a5d03c3e382a2d6326eeaa6280477 100644 (file)
@@ -256,8 +256,8 @@ class Vala.Compiler {
                        return quit ();
                }
 
-               var cfg_builder = new CFGBuilder ();
-               cfg_builder.build_cfg (context);
+               var flow_analyzer = new FlowAnalyzer ();
+               flow_analyzer.analyze (context);
 
                if (Report.get_errors () > 0) {
                        return quit ();
index 1963e31e91a8e55813df44da455a071d41891afa..b53002042b628d6c55b7912d76eefff7a3fff30e 100644 (file)
@@ -30,7 +30,6 @@ libvalacore_la_VALASOURCES = \
        valabreakstatement.vala \
        valacastexpression.vala \
        valacatchclause.vala \
-       valacfgbuilder.vala \
        valacharacterliteral.vala \
        valaclass.vala \
        valaclasstype.vala \
@@ -64,6 +63,7 @@ libvalacore_la_VALASOURCES = \
        valaexpressionstatement.vala \
        valafield.vala \
        valafieldprototype.vala \
+       valaflowanalyzer.vala \
        valaforeachstatement.vala \
        valaformalparameter.vala \
        valaforstatement.vala \
@@ -99,6 +99,7 @@ libvalacore_la_VALASOURCES = \
        valaobjecttypesymbol.vala \
        valaparenthesizedexpression.vala \
        valaparser.vala \
+       valaphifunction.vala \
        valapointerindirection.vala \
        valapointertype.vala \
        valapostfixexpression.vala \
index 04af2b659c9ff4d43bb2d041edf51731688f58af..f45a95e28abc4b785c28575dc7325b3c6edda1ac 100644 (file)
@@ -20,7 +20,7 @@
  *     Jürg Billeter <j@bitron.ch>
  */
 
-using GLib;
+using Gee;
 
 /**
  * Represents an assignment expression in the source code.
@@ -376,6 +376,26 @@ public class Vala.Assignment : Expression {
 
                return !error;
        }
+
+       public override void get_defined_variables (Collection<LocalVariable> collection) {
+               right.get_defined_variables (collection);
+               left.get_defined_variables (collection);
+               var local = left.symbol_reference as LocalVariable;
+               if (local != null) {
+                       collection.add (local);
+               }
+       }
+
+       public override void get_used_variables (Collection<LocalVariable> collection) {
+               var ma = left as MemberAccess;
+               var ea = left as ElementAccess;
+               if (ma != null && ma.inner != null) {
+                       ma.inner.get_used_variables (collection);
+               } else if (ea != null) {
+                       ea.get_used_variables (collection);
+               }
+               right.get_used_variables (collection);
+       }
 }
        
 public enum Vala.AssignmentOperator {
index 4dd9792eefbaa38a13b98b2a5a047e54e7887c0e..2602f5968cf5663f36fd168b5403e83210c31b21 100644 (file)
@@ -30,9 +30,17 @@ using Gee;
 public class Vala.BasicBlock {
        private Gee.List<CodeNode> nodes = new ArrayList<CodeNode> ();
 
+       // control flow graph
        private Gee.List<weak BasicBlock> predecessors = new ArrayList<weak BasicBlock> ();
        private Gee.List<BasicBlock> successors = new ArrayList<BasicBlock> ();
 
+       // dominator tree
+       public BasicBlock parent { get; private set; }
+       Gee.List<BasicBlock> children = new ArrayList<BasicBlock> ();
+       Set<BasicBlock> df = new HashSet<BasicBlock> ();
+
+       Set<PhiFunction> phi_functions = new HashSet<PhiFunction> ();
+
        public BasicBlock () {
        }
 
@@ -46,6 +54,10 @@ public class Vala.BasicBlock {
                nodes.add (node);
        }
 
+       public Gee.List<CodeNode> get_nodes () {
+               return nodes;
+       }
+
        public void connect (BasicBlock target) {
                if (!successors.contains (target)) {
                        successors.add (target);
@@ -62,4 +74,29 @@ public class Vala.BasicBlock {
        public Gee.List<weak BasicBlock> get_successors () {
                return new ReadOnlyList<weak BasicBlock> (successors);
        }
+
+       public void add_child (BasicBlock block) {
+               children.add (block);
+               block.parent = this;
+       }
+
+       public Gee.List<BasicBlock> get_children () {
+               return children;
+       }
+
+       public void add_dominator_frontier (BasicBlock block) {
+               df.add (block);
+       }
+
+       public Gee.Set<BasicBlock> get_dominator_frontier () {
+               return df;
+       }
+
+       public void add_phi_function (PhiFunction phi) {
+               phi_functions.add (phi);
+       }
+
+       public Gee.Set<PhiFunction> get_phi_functions () {
+               return phi_functions;
+       }
 }
index a3743c715ed9c7b2e5a1a326018103e985d480f8..200bb711db6af6de7ccf89003a889814b926d236 100644 (file)
@@ -300,6 +300,16 @@ public class Vala.BinaryExpression : Expression {
 
                return !error;
        }
+
+       public override void get_defined_variables (Collection<LocalVariable> collection) {
+               left.get_defined_variables (collection);
+               right.get_defined_variables (collection);
+       }
+
+       public override void get_used_variables (Collection<LocalVariable> collection) {
+               left.get_used_variables (collection);
+               right.get_used_variables (collection);
+       }
 }
 
 public enum Vala.BinaryOperator {
index d94041ca3d22c2f5423615f68b3db77dbd497119..4f94832ce54ef6dd59f3e0e7dda7e78e2bf8bc47 100644 (file)
@@ -20,7 +20,7 @@
  *     Jürg Billeter <j@bitron.ch>
  */
 
-using GLib;
+using Gee;
 
 /**
  * Represents a type cast in the source code.
@@ -123,4 +123,12 @@ public class Vala.CastExpression : Expression {
 
                return !error;
        }
+
+       public override void get_defined_variables (Collection<LocalVariable> collection) {
+               inner.get_defined_variables (collection);
+       }
+
+       public override void get_used_variables (Collection<LocalVariable> collection) {
+               inner.get_used_variables (collection);
+       }
 }
index 0cb7d018972979db731126d09dca1ae2bdb89613..e1c7e4b925981ad49c76b0552ca474b33c21b2fd 100644 (file)
@@ -20,7 +20,7 @@
  *     Jürg Billeter <j@bitron.ch>
  */
 
-using GLib;
+using Gee;
 
 /**
  * Represents a catch clause in a try statement in the source code.
@@ -119,4 +119,8 @@ public class Vala.CatchClause : CodeNode {
 
                return !error;
        }
+
+       public override void get_defined_variables (Collection<LocalVariable> collection) {
+               collection.add (error_variable);
+       }
 }
index 920eecfe3d843a4f765a33a6025b5da11cfd607a..88e21dde41e3651f29de24584d91c660753ec3dd 100644 (file)
@@ -177,4 +177,10 @@ public abstract class Vala.CodeNode {
 
                return str.append (" */").str;
        }
+
+       public virtual void get_defined_variables (Collection<LocalVariable> collection) {
+       }
+
+       public virtual void get_used_variables (Collection<LocalVariable> collection) {
+       }
 }
index a11449892066ce321bfdd655fcc162188873978f..0cc28fbd7c59a5ef3d3281f414c5bee92dad1c85 100644 (file)
@@ -20,7 +20,7 @@
  *     Jürg Billeter <j@bitron.ch>
  */
 
-using GLib;
+using Gee;
 
 /**
  * Represents a local variable or constant declaration statement in the source code.
@@ -71,4 +71,19 @@ public class Vala.DeclarationStatement : CodeNode, Statement {
 
                return !error;
        }
+
+       public override void get_defined_variables (Collection<LocalVariable> collection) {
+               var local = declaration as LocalVariable;
+               if (local != null && local.initializer != null) {
+                       local.initializer.get_defined_variables (collection);
+                       collection.add (local);
+               }
+       }
+
+       public override void get_used_variables (Collection<LocalVariable> collection) {
+               var local = declaration as LocalVariable;
+               if (local != null && local.initializer != null) {
+                       local.initializer.get_used_variables (collection);
+               }
+       }
 }
index 296103b25a5ef04dae1ff8bdfc3cf160f69b69c8..c18363c96b5e4e44c62bbbb7e129dc0cd90c9687 100644 (file)
@@ -208,4 +208,18 @@ public class Vala.ElementAccess : Expression {
 
                return !error;
        }
+
+       public override void get_defined_variables (Collection<LocalVariable> collection) {
+               container.get_defined_variables (collection);
+               foreach (Expression index in indices) {
+                       index.get_defined_variables (collection);
+               }
+       }
+
+       public override void get_used_variables (Collection<LocalVariable> collection) {
+               container.get_used_variables (collection);
+               foreach (Expression index in indices) {
+                       index.get_used_variables (collection);
+               }
+       }
 }
index 6dc66df31df1111105e39b0553121bae7287b90f..1981624a2243ec194c36f1aead947e6ead778270 100644 (file)
@@ -20,7 +20,7 @@
  *     Jürg Billeter <j@bitron.ch>
  */
 
-using GLib;
+using Gee;
 
 /**
  * A code statement that evaluates a given expression. The value computed by the
@@ -103,4 +103,12 @@ public class Vala.ExpressionStatement : CodeNode, Statement {
 
                return !error;
        }
+
+       public override void get_defined_variables (Collection<LocalVariable> collection) {
+               expression.get_defined_variables (collection);
+       }
+
+       public override void get_used_variables (Collection<LocalVariable> collection) {
+               expression.get_used_variables (collection);
+       }
 }
similarity index 67%
rename from vala/valacfgbuilder.vala
rename to vala/valaflowanalyzer.vala
index fe65091e7ef7e3f0cc5bf4f9600e96152c8d8435..84026cb75a74eb302bc857fa8bec4fe621cda1da 100644 (file)
@@ -1,4 +1,4 @@
-/* valacfgbuilder.vala
+/* valaflowanalyzer.vala
  *
  * Copyright (C) 2008  Jürg Billeter
  *
@@ -26,7 +26,7 @@ using Gee;
 /**
  * Code visitor building the control flow graph.
  */
-public class Vala.CFGBuilder : CodeVisitor {
+public class Vala.FlowAnalyzer : CodeVisitor {
        private class JumpTarget {
                public bool is_break_target { get; set; }
                public bool is_continue_target { get; set; }
@@ -74,7 +74,11 @@ public class Vala.CFGBuilder : CodeVisitor {
        private bool unreachable_reported;
        private Gee.List<JumpTarget> jump_stack = new ArrayList<JumpTarget> ();
 
-       public CFGBuilder () {
+       Map<Symbol, Gee.List<LocalVariable>> var_map;
+       Set<LocalVariable> used_vars;
+       Map<LocalVariable, PhiFunction> phi_functions;
+
+       public FlowAnalyzer () {
        }
 
        /**
@@ -82,7 +86,7 @@ public class Vala.CFGBuilder : CodeVisitor {
         *
         * @param context a code context
         */
-       public void build_cfg (CodeContext context) {
+       public void analyze (CodeContext context) {
                this.context = context;
 
                /* we're only interested in non-pkg source files */
@@ -159,6 +163,308 @@ public class Vala.CFGBuilder : CodeVisitor {
 
                        current_block.connect (m.exit_block);
                }
+
+               build_dominator_tree (m);
+               build_dominator_frontier (m);
+               insert_phi_functions (m);
+               check_variables (m);
+       }
+
+       Gee.List<BasicBlock> get_depth_first_list (Method m) {
+               var list = new ArrayList<BasicBlock> ();
+               depth_first_traverse (m.entry_block, list);
+               return list;
+       }
+
+       void depth_first_traverse (BasicBlock current, Gee.List<BasicBlock> list) {
+               if (current in list) {
+                       return;
+               }
+               list.add (current);
+               foreach (BasicBlock succ in current.get_successors ()) {
+                       depth_first_traverse (succ, list);
+               }
+       }
+
+       void build_dominator_tree (Method m) {
+               // set dom(n) = {E,1,2...,N,X} forall n
+               var dom = new HashMap<BasicBlock, Set<BasicBlock>> ();
+               var block_list = get_depth_first_list (m);
+               foreach (BasicBlock block in block_list) {
+                       var block_set = new HashSet<BasicBlock> ();
+                       foreach (BasicBlock b in block_list) {
+                               block_set.add (b);
+                       }
+                       dom.set (block, block_set);
+               }
+
+               // set dom(E) = {E}
+               var entry_dom_set = new HashSet<BasicBlock> ();
+               entry_dom_set.add (m.entry_block);
+               dom.set (m.entry_block, entry_dom_set);
+
+               bool changes = true;
+               while (changes) {
+                       changes = false;
+                       foreach (BasicBlock block in block_list) {
+                               // intersect dom(pred) forall pred: pred = predecessor(s)
+                               var dom_set = new HashSet<BasicBlock> ();
+                               bool first = true;
+                               foreach (BasicBlock pred in block.get_predecessors ()) {
+                                       var pred_dom_set = dom.get (pred);
+                                       if (first) {
+                                               foreach (BasicBlock dom_block in pred_dom_set) {
+                                                       dom_set.add (dom_block);
+                                               }
+                                               first = false;
+                                       } else {
+                                               var remove_queue = new ArrayList<BasicBlock> ();
+                                               foreach (BasicBlock dom_block in dom_set) {
+                                                       if (!(dom_block in pred_dom_set)) {
+                                                               remove_queue.add (dom_block);
+                                                       }
+                                               }
+                                               foreach (BasicBlock dom_block in remove_queue) {
+                                                       dom_set.remove (dom_block);
+                                               }
+                                       }
+                               }
+                               // unite with s
+                               dom_set.add (block);
+
+                               // check for changes
+                               if (dom.get (block).size != dom_set.size) {
+                                       changes = true;
+                               } else {
+                                       foreach (BasicBlock dom_block in dom.get (block)) {
+                                               if (!(dom_block in dom_set)) {
+                                                       changes = true;
+                                               }
+                                       }
+                               }
+                               // update set in map
+                               dom.set (block, dom_set);
+                       }
+               }
+
+               // build tree
+               foreach (BasicBlock block in block_list) {
+                       if (block == m.entry_block) {
+                               continue;
+                       }
+
+                       BasicBlock immediate_dominator = null;
+                       foreach (BasicBlock dominator in dom.get (block)) {
+                               if (dominator == block) {
+                                       continue;
+                               }
+
+                               if (immediate_dominator == null) {
+                                       immediate_dominator = dominator;
+                               } else {
+                                       // if immediate_dominator dominates dominator,
+                                       // update immediate_dominator
+                                       if (immediate_dominator in dom.get (dominator)) {
+                                               immediate_dominator = dominator;
+                                       }
+                               }
+                       }
+
+                       immediate_dominator.add_child (block);
+               }
+       }
+
+       void build_dominator_frontier (Method m) {
+               var block_list = get_depth_first_list (m);
+               for (int i = block_list.size - 1; i >= 0; i--) {
+                       var block = block_list[i];
+
+                       foreach (BasicBlock succ in block.get_successors ()) {
+                               // if idom(succ) != block
+                               if (succ.parent != block) {
+                                       block.add_dominator_frontier (succ);
+                               }
+                       }
+
+                       foreach (BasicBlock child in block.get_children ()) {
+                               foreach (BasicBlock child_frontier in child.get_dominator_frontier ()) {
+                                       // if idom(child_frontier) != block
+                                       if (child_frontier.parent != block) {
+                                               block.add_dominator_frontier (child_frontier);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       Map<LocalVariable, Set<BasicBlock>> get_assignment_map (Method m) {
+               var map = new HashMap<LocalVariable, Set<BasicBlock>> ();
+               foreach (BasicBlock block in get_depth_first_list (m)) {
+                       var defined_variables = new ArrayList<LocalVariable> ();
+                       foreach (CodeNode node in block.get_nodes ()) {
+                               node.get_defined_variables (defined_variables);
+                       }
+
+                       foreach (LocalVariable local in defined_variables) {
+                               var block_set = map.get (local);
+                               if (block_set == null) {
+                                       block_set = new HashSet<BasicBlock> ();
+                                       map.set (local, block_set);
+                               }
+                               block_set.add (block);
+                       }
+               }
+               return map;
+       }
+
+       void insert_phi_functions (Method m) {
+               var assign = get_assignment_map (m);
+
+               int counter = 0;
+               var work_list = new ArrayList<BasicBlock> ();
+
+               var added = new HashMap<BasicBlock, int> ();
+               var phi = new HashMap<BasicBlock, int> ();
+               foreach (BasicBlock block in get_depth_first_list (m)) {
+                       added.set (block, 0);
+                       phi.set (block, 0);
+               }
+
+               foreach (LocalVariable local in assign.get_keys ()) {
+                       counter++;
+                       foreach (BasicBlock block in assign.get (local)) {
+                               work_list.add (block);
+                               added.set (block, counter);
+                       }
+                       while (work_list.size > 0) {
+                               BasicBlock block = work_list.get (0);
+                               work_list.remove_at (0);
+                               foreach (BasicBlock frontier in block.get_dominator_frontier ()) {
+                                       int blockPhi = phi.get (frontier);
+                                       if (blockPhi < counter) {
+                                               frontier.add_phi_function (new PhiFunction (local, frontier.get_predecessors ().size));
+                                               phi.set (frontier, counter);
+                                               int block_added = added.get (frontier);
+                                               if (block_added < counter) {
+                                                       added.set (frontier, counter);
+                                                       work_list.add (frontier);
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       void check_variables (Method m) {
+               var_map = new HashMap<Symbol, Gee.List<LocalVariable>>();
+               used_vars = new HashSet<LocalVariable> ();
+               phi_functions = new HashMap<LocalVariable, PhiFunction> ();
+
+               check_block_variables (m, m.entry_block);
+
+               // check for variables used before initialization
+               var used_vars_queue = new ArrayList<LocalVariable> ();
+               foreach (LocalVariable local in used_vars) {
+                       used_vars_queue.add (local);
+               }
+               while (used_vars_queue.size > 0) {
+                       LocalVariable used_var = used_vars_queue[0];
+                       used_vars_queue.remove_at (0);
+
+                       PhiFunction phi = phi_functions.get (used_var);
+                       if (phi != null) {
+                               foreach (LocalVariable local in phi.operands) {
+                                       if (local == null) {
+                                               Report.error (used_var.source_reference, "use of possibly unassigned local variable `%s'".printf (used_var.name));
+                                               continue;
+                                       }
+                                       if (!(local in used_vars)) {
+                                               local.source_reference = used_var.source_reference;
+                                               used_vars.add (local);
+                                               used_vars_queue.add (local);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       void check_block_variables (Method m, BasicBlock block) {
+               foreach (PhiFunction phi in block.get_phi_functions ()) {
+                       LocalVariable versioned_var = process_assignment (m, var_map, phi.original_variable);
+
+                       phi_functions.set (versioned_var, phi);
+               }
+
+               foreach (CodeNode node in block.get_nodes ()) {
+                       var used_variables = new ArrayList<LocalVariable> ();
+                       node.get_used_variables (used_variables);
+                       
+                       foreach (LocalVariable var_symbol in used_variables) {
+                               var variable_stack = var_map.get (var_symbol);
+                               if (variable_stack == null || variable_stack.size == 0) {
+                                       Report.error (node.source_reference, "use of possibly unassigned local variable `%s'".printf (var_symbol.name));
+                                       continue;
+                               }
+                               var versioned_local = variable_stack.get (variable_stack.size - 1);
+                               if (!(versioned_local in used_vars)) {
+                                       versioned_local.source_reference = node.source_reference;
+                               }
+                               used_vars.add (versioned_local);
+                       }
+
+                       var defined_variables = new ArrayList<LocalVariable> ();
+                       node.get_defined_variables (defined_variables);
+
+                       foreach (LocalVariable local in defined_variables) {
+                               process_assignment (m, var_map, local);
+                       }
+               }
+
+               foreach (BasicBlock succ in block.get_successors ()) {
+                       int j = 0;
+                       foreach (BasicBlock pred in succ.get_predecessors ()) {
+                               if (pred == block) {
+                                       break;
+                               }
+                               j++;
+                       }
+
+                       foreach (PhiFunction phi in succ.get_phi_functions ()) {
+                               var variable_stack = var_map.get (phi.original_variable);
+                               if (variable_stack != null && variable_stack.size > 0) {
+                                       phi.operands.set (j, variable_stack.get (variable_stack.size - 1));
+                               }
+                       }
+               }
+
+               foreach (BasicBlock child in block.get_children ()) {
+                       check_block_variables (m, child);
+               }
+
+               foreach (PhiFunction phi in block.get_phi_functions ()) {
+                       var variable_stack = var_map.get (phi.original_variable);
+                       variable_stack.remove_at (variable_stack.size - 1);
+               }
+               foreach (CodeNode node in block.get_nodes ()) {
+                       var defined_variables = new ArrayList<LocalVariable> ();
+                       node.get_defined_variables (defined_variables);
+
+                       foreach (LocalVariable local in defined_variables) {
+                               var variable_stack = var_map.get (local);
+                               variable_stack.remove_at (variable_stack.size - 1);
+                       }
+               }
+       }
+
+       LocalVariable process_assignment (Method m, Map<Symbol, Gee.List<LocalVariable>> var_map, LocalVariable var_symbol) {
+               var variable_stack = var_map.get (var_symbol);
+               if (variable_stack == null) {
+                       variable_stack = new ArrayList<LocalVariable> ();
+                       var_map.set (var_symbol, variable_stack);
+               }
+               LocalVariable versioned_var = new LocalVariable (var_symbol.variable_type, var_symbol.name, null, var_symbol.source_reference);
+               variable_stack.add (versioned_var);
+               return versioned_var;
        }
 
        public override void visit_property (Property prop) {
@@ -311,9 +617,13 @@ public class Vala.CFGBuilder : CodeVisitor {
                        }
                }
 
+               if (!has_default_label) {
+                       condition_block.connect (after_switch_block);
+               }
+
                // after switch
                // reachable?
-               if (!has_default_label || after_switch_block.get_predecessors ().size > 0) {
+               if (after_switch_block.get_predecessors ().size > 0) {
                        current_block = after_switch_block;
                } else {
                        current_block = null;
@@ -476,6 +786,7 @@ public class Vala.CFGBuilder : CodeVisitor {
                var last_block = current_block;
                last_block.connect (loop_block);
                current_block = loop_block;
+               current_block.add_node (stmt);
                stmt.body.accept (this);
                if (current_block != null) {
                        current_block.connect (loop_block);
@@ -695,6 +1006,7 @@ public class Vala.CFGBuilder : CodeVisitor {
                                Report.warning (jump_target.catch_clause.source_reference, "unreachable catch clause detected");
                        } else {
                                current_block = jump_target.basic_block;
+                               current_block.add_node (jump_target.catch_clause);
                                jump_target.catch_clause.body.accept (this);
                                if (current_block != null) {
                                        if (finally_block != null) {
index 949445bfdaef24d1b05817d4c38b23587059b91f..efd2480f2de6da0d41377026c75539e174360b77 100644 (file)
@@ -1,6 +1,6 @@
 /* valaforeachstatement.vala
  *
- * Copyright (C) 2006-2007  Jürg Billeter
+ * Copyright (C) 2006-2008  Jürg Billeter
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -20,7 +20,7 @@
  *     Jürg Billeter <j@bitron.ch>
  */
 
-using GLib;
+using Gee;
 
 /**
  * Represents a foreach statement in the source code. Foreach statements iterate
@@ -247,4 +247,8 @@ public class Vala.ForeachStatement : Block {
 
                return !error;
        }
+
+       public override void get_defined_variables (Collection<LocalVariable> collection) {
+               collection.add (element_variable);
+       }
 }
index 834ea46c044b4bea8409e3c0444194db54d67cb3..3a2de5c9a1ba463b63d68825188273ef97919a44 100644 (file)
@@ -420,4 +420,20 @@ public class Vala.InvocationExpression : Expression {
 
                return !error;
        }
+
+       public override void get_defined_variables (Collection<LocalVariable> collection) {
+               call.get_defined_variables (collection);
+
+               foreach (Expression arg in argument_list) {
+                       arg.get_defined_variables (collection);
+               }
+       }
+
+       public override void get_used_variables (Collection<LocalVariable> collection) {
+               call.get_used_variables (collection);
+
+               foreach (Expression arg in argument_list) {
+                       arg.get_used_variables (collection);
+               }
+       }
 }
index 6ced16f4c494ed7e93e6379998315a62fb2a86c1..01d6f13bf4601178a64c3b7f662dc770ad4573f1 100644 (file)
@@ -535,4 +535,20 @@ public class Vala.MemberAccess : Expression {
 
                return !error;
        }
+
+       public override void get_defined_variables (Collection<LocalVariable> collection) {
+               if (inner != null) {
+                       inner.get_defined_variables (collection);
+               }
+       }
+
+       public override void get_used_variables (Collection<LocalVariable> collection) {
+               if (inner != null) {
+                       inner.get_used_variables (collection);
+               }
+               var local = symbol_reference as LocalVariable;
+               if (local != null) {
+                       collection.add (local);
+               }
+       }
 }
index 0e42c10981f22ada2dd8a72aae5849df1818bbce..9fc43fd10ab4556f113eb40786fdafd85b70b3f8 100644 (file)
@@ -360,4 +360,16 @@ public class Vala.ObjectCreationExpression : Expression {
 
                return !error;
        }
+
+       public override void get_defined_variables (Collection<LocalVariable> collection) {
+               foreach (Expression arg in argument_list) {
+                       arg.get_defined_variables (collection);
+               }
+       }
+
+       public override void get_used_variables (Collection<LocalVariable> collection) {
+               foreach (Expression arg in argument_list) {
+                       arg.get_used_variables (collection);
+               }
+       }
 }
index e6d0a2ebd601a12c5655b7f5e16a6caf3111c8ec..2a29d933273a481037c73699f0a0437f4be234a2 100644 (file)
@@ -20,7 +20,7 @@
  *     Jürg Billeter <j@bitron.ch>
  */
 
-using GLib;
+using Gee;
 
 /**
  * Represents a parenthesized expression in the source code.
@@ -104,4 +104,12 @@ public class Vala.ParenthesizedExpression : Expression {
 
                return !error;
        }
+
+       public override void get_defined_variables (Collection<LocalVariable> collection) {
+               inner.get_defined_variables (collection);
+       }
+
+       public override void get_used_variables (Collection<LocalVariable> collection) {
+               inner.get_used_variables (collection);
+       }
 }
diff --git a/vala/valaphifunction.vala b/vala/valaphifunction.vala
new file mode 100644 (file)
index 0000000..b95cc9f
--- /dev/null
@@ -0,0 +1,37 @@
+/* valaphifunction.vala
+ *
+ * Copyright (C) 2008  Jürg Billeter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+ *
+ * Author:
+ *     Jürg Billeter <j@bitron.ch>
+ */
+
+using Gee;
+
+public class Vala.PhiFunction {
+       public LocalVariable original_variable { get; private set; }
+
+       public Gee.List<LocalVariable?> operands { get; private set; }
+
+       public PhiFunction (LocalVariable variable, int num_of_ops) {
+               this.original_variable = variable;
+               this.operands = new ArrayList<LocalVariable?> ();
+               for (int i = 0; i < num_of_ops; i++) {
+                       this.operands.add ((LocalVariable) null);
+               }
+       }
+}
index 01a27fa9418778dc61bcfe55e555aa6064fea81b..84f1b6e6f81915fa0fc7b78022895684a0f8cab7 100644 (file)
@@ -20,7 +20,7 @@
  *     Jürg Billeter <j@bitron.ch>
  */
 
-using GLib;
+using Gee;
 
 /**
  * Represents a pointer indirection in the source code, e.g. `*pointer'.
@@ -101,4 +101,12 @@ public class Vala.PointerIndirection : Expression {
 
                return !error;
        }
+
+       public override void get_defined_variables (Collection<LocalVariable> collection) {
+               inner.get_defined_variables (collection);
+       }
+
+       public override void get_used_variables (Collection<LocalVariable> collection) {
+               inner.get_used_variables (collection);
+       }
 }
index e30815ae28be00142da9249245787e18f4607642..dcf2e5369e7306f1aff9836de788bf72ca551573 100644 (file)
@@ -20,7 +20,7 @@
  *     Jürg Billeter <j@bitron.ch>
  */
 
-using GLib;
+using Gee;
 
 /**
  * Represents a reference transfer expression in the source code, e.g. `#foo'.
@@ -107,4 +107,12 @@ public class Vala.ReferenceTransferExpression : Expression {
 
                return !error;
        }
+
+       public override void get_defined_variables (Collection<LocalVariable> collection) {
+               inner.get_defined_variables (collection);
+       }
+
+       public override void get_used_variables (Collection<LocalVariable> collection) {
+               inner.get_used_variables (collection);
+       }
 }
index acea142917b4c2754d779f55514c0bc013894261..c9c99ea8a963c9942727c6b3136f8afa14036582 100644 (file)
@@ -20,7 +20,7 @@
  *     Jürg Billeter <j@bitron.ch>
  */
 
-using GLib;
+using Gee;
 
 /**
  * Represents a return statement in the source code.
@@ -141,4 +141,16 @@ public class Vala.ReturnStatement : CodeNode, Statement {
 
                return !error;
        }
+
+       public override void get_defined_variables (Collection<LocalVariable> collection) {
+               if (return_expression != null) {
+                       return_expression.get_defined_variables (collection);
+               }
+       }
+
+       public override void get_used_variables (Collection<LocalVariable> collection) {
+               if (return_expression != null) {
+                       return_expression.get_used_variables (collection);
+               }
+       }
 }
index 3f27778db14b9c7c91c866278801a8c3e63b9927..3732b113558d4ff1baf74a8a2757667b03171975 100644 (file)
@@ -20,7 +20,7 @@
  *     Jürg Billeter <j@bitron.ch>
  */
 
-using GLib;
+using Gee;
 
 /**
  * Represents a throw statement in the source code.
@@ -94,4 +94,12 @@ public class Vala.ThrowStatement : CodeNode, Statement {
 
                return !error;
        }
+
+       public override void get_defined_variables (Collection<LocalVariable> collection) {
+               error_expression.get_defined_variables (collection);
+       }
+
+       public override void get_used_variables (Collection<LocalVariable> collection) {
+               error_expression.get_used_variables (collection);
+       }
 }
index 3ddd3467835de034cc43a6395d33f74cf53b15cf..9f478d8e94113d3856145b35816b08c9bf9d84cd 100644 (file)
@@ -20,7 +20,7 @@
  *     Jürg Billeter <j@bitron.ch>
  */
 
-using GLib;
+using Gee;
 
 /**
  * Represents an expression with one operand in the source code.
@@ -225,6 +225,22 @@ public class Vala.UnaryExpression : Expression {
 
                return !error;
        }
+
+       public override void get_defined_variables (Collection<LocalVariable> collection) {
+               inner.get_defined_variables (collection);
+               if (operator == UnaryOperator.OUT || operator == UnaryOperator.REF) {
+                       var local = inner.symbol_reference as LocalVariable;
+                       if (local != null) {
+                               collection.add (local);
+                       }
+               }
+       }
+
+       public override void get_used_variables (Collection<LocalVariable> collection) {
+               if (operator != UnaryOperator.OUT) {
+                       inner.get_used_variables (collection);
+               }
+       }
 }
 
 public enum Vala.UnaryOperator {