Commit 14cb3893 authored by Jürg Billeter's avatar Jürg Billeter

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.
parent 6ee07f87
......@@ -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;
......
......@@ -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) {
......
......@@ -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) {
......
......@@ -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) {
......
......@@ -89,6 +89,7 @@ libvalacore_la_VALASOURCES = \
valalocalvariable.vala \
valalockable.vala \
valalockstatement.vala \
valaloop.vala \
valamember.vala \
valamemberaccess.vala \
valamemberinitializer.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);
}
......
......@@ -300,6 +300,14 @@ public abstract class Vala.CodeVisitor {
public virtual void visit_switch_label (SwitchLabel label) {
}
/**
* Visit operation called for loops.
*
* @param stmt a loop
*/
public virtual void visit_loop (Loop stmt) {
}
/**
* Visit operation called for while statements.
*
......
......@@ -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 (");
......
......@@ -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);
......
......@@ -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;
}
......
/* 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;
}
}
/* 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) {
......
......@@ -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);
}
......
/* 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);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment