From: Jürg Billeter Date: Sat, 6 Jun 2009 13:58:44 +0000 (+0200) Subject: Convert while loops into simple loops X-Git-Tag: 0.7.4~50 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=14cb3893cf381f6b9136eccccdba86bc42786a56;p=thirdparty%2Fvala.git 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. --- diff --git a/codegen/valaccodebasemodule.vala b/codegen/valaccodebasemodule.vala index fa55b8d76..3cc49d639 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 4c2b60112..192ad8aa3 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 52b57d60f..263c9182b 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 8dff9bf5a..1da365d4b 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 ea92234d3..0b1044e24 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 d392c96ea..9c99ec838 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 d3f57e765..65ddb23ea 100644 --- a/vala/valacodevisitor.vala +++ b/vala/valacodevisitor.vala @@ -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. * diff --git a/vala/valacodewriter.vala b/vala/valacodewriter.vala index 047e1ed84..72ba348c2 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 8f37efd7a..ce67db8c5 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 dde54469d..33fe0da6a 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 000000000..a0281e9cd --- /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 + */ + +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 d2ba8245d..0d0bffa82 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 f5be4211f..24612f164 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 64c8e1ce2..71f540594 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); } }