[vala] Convert while loops into simple loops



commit 14cb3893cf381f6b9136eccccdba86bc42786a56
Author: Jürg Billeter <j bitron ch>
Date:   Sat Jun 6 15:58:44 2009 +0200

    Convert while loops into simple loops
    
    Simplifies and fixes bugs in semantic and flow analysis and code
    generation. Based on patch by Levi Bard, fixes bug 570091.
---
 codegen/valaccodebasemodule.vala        |    2 +-
 codegen/valaccodecontrolflowmodule.vala |    6 +--
 codegen/valaccodegenerator.vala         |    4 +-
 codegen/valaccodemodule.vala            |    4 +-
 vala/Makefile.am                        |    1 +
 vala/valablock.vala                     |    2 +
 vala/valacodevisitor.vala               |    8 +++
 vala/valacodewriter.vala                |    7 +++
 vala/valaexpression.vala                |    5 +--
 vala/valaflowanalyzer.vala              |   30 ++++--------
 vala/valaloop.vala                      |   78 +++++++++++++++++++++++++++++++
 vala/valanullchecker.vala               |    6 +--
 vala/valasymbolresolver.vala            |    4 ++
 vala/valawhilestatement.vala            |   60 +++++++-----------------
 14 files changed, 137 insertions(+), 80 deletions(-)

diff --git a/codegen/valaccodebasemodule.vala b/codegen/valaccodebasemodule.vala
index fa55b8d..3cc49d6 100644
--- a/codegen/valaccodebasemodule.vala
+++ b/codegen/valaccodebasemodule.vala
@@ -2231,7 +2231,7 @@ internal class Vala.CCodeBaseModule : CCodeModule {
 		}
 		
 		if (stop_at_loop) {
-			if (b.parent_node is DoStatement || b.parent_node is WhileStatement ||
+			if (b.parent_node is DoStatement || b.parent_node is Loop ||
 			    b.parent_node is ForStatement || b.parent_node is ForeachStatement ||
 			    b.parent_node is SwitchStatement) {
 				return;
diff --git a/codegen/valaccodecontrolflowmodule.vala b/codegen/valaccodecontrolflowmodule.vala
index 4c2b601..192ad8a 100644
--- a/codegen/valaccodecontrolflowmodule.vala
+++ b/codegen/valaccodecontrolflowmodule.vala
@@ -221,12 +221,10 @@ internal class Vala.CCodeControlFlowModule : CCodeMethodModule {
 		label.accept_children (codegen);
 	}
 
-	public override void visit_while_statement (WhileStatement stmt) {
+	public override void visit_loop (Loop stmt) {
 		stmt.accept_children (codegen);
 
-		stmt.ccodenode = new CCodeWhileStatement ((CCodeExpression) stmt.condition.ccodenode, (CCodeStatement) stmt.body.ccodenode);
-		
-		create_temp_decl (stmt, stmt.condition.temp_vars);
+		stmt.ccodenode = new CCodeWhileStatement (new CCodeConstant ("TRUE"), (CCodeStatement) stmt.body.ccodenode);
 	}
 
 	public override void visit_do_statement (DoStatement stmt) {
diff --git a/codegen/valaccodegenerator.vala b/codegen/valaccodegenerator.vala
index 52b57d6..263c918 100644
--- a/codegen/valaccodegenerator.vala
+++ b/codegen/valaccodegenerator.vala
@@ -189,8 +189,8 @@ public class Vala.CCodeGenerator : CodeGenerator {
 		head.visit_switch_label (label);
 	}
 
-	public override void visit_while_statement (WhileStatement stmt) {
-		head.visit_while_statement (stmt);
+	public override void visit_loop (Loop stmt) {
+		head.visit_loop (stmt);
 	}
 
 	public override void visit_do_statement (DoStatement stmt) {
diff --git a/codegen/valaccodemodule.vala b/codegen/valaccodemodule.vala
index 8dff9bf..1da365d 100644
--- a/codegen/valaccodemodule.vala
+++ b/codegen/valaccodemodule.vala
@@ -168,8 +168,8 @@ public abstract class Vala.CCodeModule {
 		next.visit_switch_label (label);
 	}
 
-	public virtual void visit_while_statement (WhileStatement stmt) {
-		next.visit_while_statement (stmt);
+	public virtual void visit_loop (Loop stmt) {
+		next.visit_loop (stmt);
 	}
 
 	public virtual void visit_do_statement (DoStatement stmt) {
diff --git a/vala/Makefile.am b/vala/Makefile.am
index ea92234..0b1044e 100644
--- a/vala/Makefile.am
+++ b/vala/Makefile.am
@@ -89,6 +89,7 @@ libvalacore_la_VALASOURCES = \
 	valalocalvariable.vala \
 	valalockable.vala \
 	valalockstatement.vala \
+	valaloop.vala \
 	valamember.vala \
 	valamemberaccess.vala \
 	valamemberinitializer.vala \
diff --git a/vala/valablock.vala b/vala/valablock.vala
index d392c96..9c99ec8 100644
--- a/vala/valablock.vala
+++ b/vala/valablock.vala
@@ -51,10 +51,12 @@ public class Vala.Block : Symbol, Statement {
 	 * @param stmt a statement
 	 */
 	public void add_statement (Statement stmt) {
+		stmt.parent_node = this;
 		statement_list.add (stmt);
 	}
 
 	public void insert_statement (int index, Statement stmt) {
+		stmt.parent_node = this;
 		statement_list.insert (index, stmt);
 	}
 
diff --git a/vala/valacodevisitor.vala b/vala/valacodevisitor.vala
index d3f57e7..65ddb23 100644
--- a/vala/valacodevisitor.vala
+++ b/vala/valacodevisitor.vala
@@ -301,6 +301,14 @@ public abstract class Vala.CodeVisitor {
 	}
 
 	/**
+	 * Visit operation called for loops.
+	 *
+	 * @param stmt a loop
+	 */
+	public virtual void visit_loop (Loop stmt) {
+	}
+
+	/**
 	 * Visit operation called for while statements.
 	 *
 	 * @param stmt an while statement
diff --git a/vala/valacodewriter.vala b/vala/valacodewriter.vala
index 047e1ed..72ba348 100644
--- a/vala/valacodewriter.vala
+++ b/vala/valacodewriter.vala
@@ -1098,6 +1098,13 @@ public class Vala.CodeWriter : CodeVisitor {
 		}
 	}
 
+	public override void visit_loop (Loop stmt) {
+		write_indent ();
+		write_string ("loop");
+		stmt.body.accept (this);
+		write_newline ();
+	}
+
 	public override void visit_while_statement (WhileStatement stmt) {
 		write_indent ();
 		write_string ("while (");
diff --git a/vala/valaexpression.vala b/vala/valaexpression.vala
index 8f37efd..ce67db8 100644
--- a/vala/valaexpression.vala
+++ b/vala/valaexpression.vala
@@ -103,13 +103,10 @@ public abstract class Vala.Expression : CodeNode {
 	}
 
 	public Block prepare_condition_split (SemanticAnalyzer analyzer) {
-		var while_stmt = parent_statement as WhileStatement;
 		var do_stmt = parent_statement as DoStatement;
 		var for_stmt = parent_statement as ForStatement;
 
-		if (while_stmt != null) {
-			return while_stmt.prepare_condition_split (analyzer);
-		} else if (do_stmt != null) {
+		if (do_stmt != null) {
 			return do_stmt.prepare_condition_split (analyzer);
 		} else if (for_stmt != null) {
 			return for_stmt.prepare_condition_split (analyzer);
diff --git a/vala/valaflowanalyzer.vala b/vala/valaflowanalyzer.vala
index dde5446..33fe0da 100644
--- a/vala/valaflowanalyzer.vala
+++ b/vala/valaflowanalyzer.vala
@@ -655,45 +655,35 @@ public class Vala.FlowAnalyzer : CodeVisitor {
 		jump_stack.remove_at (jump_stack.size - 1);
 	}
 
-	public override void visit_while_statement (WhileStatement stmt) {
+	public override void visit_loop (Loop stmt) {
 		if (unreachable (stmt)) {
 			return;
 		}
 
-		var condition_block = new BasicBlock ();
-		jump_stack.add (new JumpTarget.continue_target (condition_block));
+		var loop_block = new BasicBlock ();
+		jump_stack.add (new JumpTarget.continue_target (loop_block));
 		var after_loop_block = new BasicBlock ();
 		jump_stack.add (new JumpTarget.break_target (after_loop_block));
 
-		// condition
+		// loop block
 		var last_block = current_block;
-		last_block.connect (condition_block);
-		current_block = condition_block;
-		current_block.add_node (stmt.condition);
-
-		handle_errors (stmt.condition);
+		last_block.connect (loop_block);
+		current_block = loop_block;
 
-		// loop block
-		if (always_false (stmt.condition)) {
-			current_block = null;
-			unreachable_reported = false;
-		} else {
-			current_block = new BasicBlock ();
-			condition_block.connect (current_block);
-		}
 		stmt.body.accept (this);
 		// end of loop block reachable?
 		if (current_block != null) {
-			current_block.connect (condition_block);
+			current_block.connect (loop_block);
 		}
 
 		// after loop
 		// reachable?
-		if (always_true (stmt.condition) && after_loop_block.get_predecessors ().size == 0) {
+		if (after_loop_block.get_predecessors ().size == 0) {
+			// after loop block not reachable
 			current_block = null;
 			unreachable_reported = false;
 		} else {
-			condition_block.connect (after_loop_block);
+			// after loop block reachable
 			current_block = after_loop_block;
 		}
 
diff --git a/vala/valaloop.vala b/vala/valaloop.vala
new file mode 100644
index 0000000..a0281e9
--- /dev/null
+++ b/vala/valaloop.vala
@@ -0,0 +1,78 @@
+/* valaloop.vala
+ *
+ * Copyright (C) 2009  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 GLib;
+
+/**
+ * Represents an endless loop.
+ */
+public class Vala.Loop : CodeNode, Statement {
+	/**
+	 * Specifies the loop body.
+	 */
+	public Block body {
+		get {
+			return _body;
+		}
+		set {
+			_body = value;
+			_body.parent_node = this;
+		}
+	}
+
+	private Block _body;
+
+	/**
+	 * Creates a new loop.
+	 *
+	 * @param body   loop body
+	 * @param source reference to source code
+	 * @return       newly created while statement
+	 */
+	public Loop (Block body, SourceReference? source_reference = null) {
+		this.body = body;
+		this.source_reference = source_reference;
+	}
+
+	public override void accept (CodeVisitor visitor) {
+		visitor.visit_loop (this);
+	}
+
+	public override void accept_children (CodeVisitor visitor) {
+		body.accept (visitor);
+	}
+
+	public override bool check (SemanticAnalyzer analyzer) {
+		if (checked) {
+			return !error;
+		}
+
+		checked = true;
+
+		body.check (analyzer);
+
+		add_error_types (body.get_error_types ());
+
+		return !error;
+	}
+}
+
diff --git a/vala/valanullchecker.vala b/vala/valanullchecker.vala
index d2ba824..0d0bffa 100644
--- a/vala/valanullchecker.vala
+++ b/vala/valanullchecker.vala
@@ -1,6 +1,6 @@
 /* valanullchecker.vala
  *
- * Copyright (C) 2008  Jürg Billeter
+ * Copyright (C) 2008-2009  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
@@ -152,10 +152,8 @@ public class Vala.NullChecker : CodeVisitor {
 		section.accept_children (this);
 	}
 
-	public override void visit_while_statement (WhileStatement stmt) {
+	public override void visit_loop (Loop stmt) {
 		stmt.accept_children (this);
-
-		check_non_null (stmt.condition);
 	}
 
 	public override void visit_do_statement (DoStatement stmt) {
diff --git a/vala/valasymbolresolver.vala b/vala/valasymbolresolver.vala
index f5be421..24612f1 100644
--- a/vala/valasymbolresolver.vala
+++ b/vala/valasymbolresolver.vala
@@ -380,6 +380,10 @@ public class Vala.SymbolResolver : CodeVisitor {
 		label.accept_children (this);
 	}
 
+	public override void visit_loop (Loop stmt) {
+		stmt.accept_children (this);
+	}
+
 	public override void visit_while_statement (WhileStatement stmt) {
 		stmt.accept_children (this);
 	}
diff --git a/vala/valawhilestatement.vala b/vala/valawhilestatement.vala
index 64c8e1c..71f5405 100644
--- a/vala/valawhilestatement.vala
+++ b/vala/valawhilestatement.vala
@@ -1,6 +1,6 @@
 /* valawhilestatement.vala
  *
- * Copyright (C) 2006-2008  Jürg Billeter
+ * Copyright (C) 2006-2009  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
@@ -81,55 +81,29 @@ public class Vala.WhileStatement : CodeNode, Statement {
 		body.accept (visitor);
 	}
 
-	public override void replace_expression (Expression old_node, Expression new_node) {
-		if (condition == old_node) {
-			condition = new_node;
-		}
+	bool always_true (Expression condition) {
+		var literal = condition as BooleanLiteral;
+		return (literal != null && literal.value);
 	}
 
 	public override bool check (SemanticAnalyzer analyzer) {
-		if (checked) {
-			return !error;
-		}
-
-		checked = true;
-
-		condition.check (analyzer);
-		
-		body.check (analyzer);
-
-		if (condition.error) {
-			/* if there was an error in the condition, skip this check */
-			error = true;
-			return false;
+		// convert to simple loop
+
+		// do not generate if block if condition is always true
+		if (!always_true (condition)) {
+			var if_condition = new UnaryExpression (UnaryOperator.LOGICAL_NEGATION, condition, condition.source_reference);
+			var true_block = new Block (condition.source_reference);
+			true_block.add_statement (new BreakStatement (condition.source_reference));
+			var if_stmt = new IfStatement (if_condition, true_block, null, condition.source_reference);
+			body.insert_statement (0, if_stmt);
 		}
 
-		if (!condition.value_type.compatible (analyzer.bool_type)) {
-			error = true;
-			Report.error (condition.source_reference, "Condition must be boolean");
-			return false;
-		}
-
-		add_error_types (condition.get_error_types ());
-		add_error_types (body.get_error_types ());
-
-		return !error;
-	}
-
-	public Block prepare_condition_split (SemanticAnalyzer analyzer) {
-		// move condition into the loop body to allow split
-		// in multiple statements
-
-		var if_condition = new UnaryExpression (UnaryOperator.LOGICAL_NEGATION, condition, condition.source_reference);
-		var true_block = new Block (condition.source_reference);
-		true_block.add_statement (new BreakStatement (condition.source_reference));
-		var if_stmt = new IfStatement (if_condition, true_block, null, condition.source_reference);
-		body.insert_statement (0, if_stmt);
+		var loop = new Loop (body, source_reference);
 
-		condition = new BooleanLiteral (true, source_reference);
-		condition.check (analyzer);
+		var parent_block = (Block) parent_node;
+		parent_block.replace_statement (this, loop);
 
-		return body;
+		return loop.check (analyzer);
 	}
 }
 



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