From: Jürg Billeter Date: Sat, 29 Nov 2008 12:20:30 +0000 (+0000) Subject: Convert ternary conditionals into if statements, fixes bug 543870 and bug X-Git-Tag: VALA_0_5_2~28 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9c5443a1041e28145f8b68c0c58d8d73e5857fce;p=thirdparty%2Fvala.git Convert ternary conditionals into if statements, fixes bug 543870 and bug 2008-11-29 Jürg Billeter * vala/Makefile.am: * vala/valablock.vala: * vala/valacodenode.vala: * vala/valaconditionalexpression.vala: * vala/valadeclarationstatement.vala: * vala/valaexpression.vala: * vala/valanullchecker.vala: * vala/valastatementlist.vala: * gobject/valaccodebasemodule.vala: * gobject/valaccodegenerator.vala: * gobject/valaccodemodule.vala: Convert ternary conditionals into if statements, fixes bug 543870 and bug 554594 svn path=/trunk/; revision=2083 --- diff --git a/ChangeLog b/ChangeLog index 6ac82d2f8..33543fd2e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2008-11-29 Jürg Billeter + + * vala/Makefile.am: + * vala/valablock.vala: + * vala/valacodenode.vala: + * vala/valaconditionalexpression.vala: + * vala/valadeclarationstatement.vala: + * vala/valaexpression.vala: + * vala/valanullchecker.vala: + * vala/valastatementlist.vala: + * gobject/valaccodebasemodule.vala: + * gobject/valaccodegenerator.vala: + * gobject/valaccodemodule.vala: + + Convert ternary conditionals into if statements, + fixes bug 543870 and bug 554594 + 2008-11-28 Étienne BERSAC * vapi/gobject-2.0.vapi: diff --git a/gobject/valaccodebasemodule.vala b/gobject/valaccodebasemodule.vala index 88662c3d6..a7ac3d63d 100644 --- a/gobject/valaccodebasemodule.vala +++ b/gobject/valaccodebasemodule.vala @@ -135,6 +135,8 @@ public class Vala.CCodeBaseModule : CCodeModule { public Set wrappers; + Map variable_name_map = new HashMap (str_hash, str_equal); + public CCodeBaseModule (CCodeGenerator codegen, CCodeModule? next) { base (codegen, next); @@ -1276,7 +1278,14 @@ public class Vala.CCodeBaseModule : CCodeModule { } public string get_variable_cname (string name) { - if (c_keywords.contains (name)) { + if (name[0] == '.') { + // compiler-internal variable + if (!variable_name_map.contains (name)) { + variable_name_map.set (name, "_tmp%d".printf (next_temp_var_id)); + next_temp_var_id++; + } + return variable_name_map.get (name); + } else if (c_keywords.contains (name)) { return name + "_"; } else { return name; @@ -2886,10 +2895,6 @@ public class Vala.CCodeBaseModule : CCodeModule { expr.ccodenode = create_type_check (expr.expression.ccodenode, expr.type_reference); } - public override void visit_conditional_expression (ConditionalExpression expr) { - expr.ccodenode = new CCodeConditionalExpression ((CCodeExpression) expr.condition.ccodenode, (CCodeExpression) expr.true_expression.ccodenode, (CCodeExpression) expr.false_expression.ccodenode); - } - public override void visit_lambda_expression (LambdaExpression l) { // use instance position from delegate var dt = (DelegateType) l.target_type; diff --git a/gobject/valaccodegenerator.vala b/gobject/valaccodegenerator.vala index 8913ac522..ca9f8a1b1 100644 --- a/gobject/valaccodegenerator.vala +++ b/gobject/valaccodegenerator.vala @@ -323,10 +323,6 @@ public class Vala.CCodeGenerator : CodeGenerator { head.visit_type_check (expr); } - public override void visit_conditional_expression (ConditionalExpression expr) { - head.visit_conditional_expression (expr); - } - public override void visit_lambda_expression (LambdaExpression l) { head.visit_lambda_expression (l); } diff --git a/gobject/valaccodemodule.vala b/gobject/valaccodemodule.vala index efd33e60d..bc49962d4 100644 --- a/gobject/valaccodemodule.vala +++ b/gobject/valaccodemodule.vala @@ -320,10 +320,6 @@ public abstract class Vala.CCodeModule { next.visit_type_check (expr); } - public virtual void visit_conditional_expression (ConditionalExpression expr) { - next.visit_conditional_expression (expr); - } - public virtual void visit_lambda_expression (LambdaExpression l) { next.visit_lambda_expression (l); } diff --git a/vala/Makefile.am b/vala/Makefile.am index 74b65d0eb..029b7d831 100644 --- a/vala/Makefile.am +++ b/vala/Makefile.am @@ -122,6 +122,7 @@ libvalacore_la_VALASOURCES = \ valasourcelocation.vala \ valasourcereference.vala \ valastatement.vala \ + valastatementlist.vala \ valastringliteral.vala \ valastruct.vala \ valaswitchlabel.vala \ diff --git a/vala/valablock.vala b/vala/valablock.vala index 330daa7f0..8eb3516c0 100644 --- a/vala/valablock.vala +++ b/vala/valablock.vala @@ -64,7 +64,18 @@ public class Vala.Block : Symbol, Statement { * @return statement list */ public Gee.List get_statements () { - return new ReadOnlyList (statement_list); + var list = new ArrayList (); + foreach (Statement stmt in statement_list) { + var stmt_list = stmt as StatementList; + if (stmt_list != null) { + for (int i = 0; i < stmt_list.length; i++) { + list.add (stmt_list.get (i)); + } + } else { + list.add (stmt); + } + } + return list; } /** @@ -75,7 +86,11 @@ public class Vala.Block : Symbol, Statement { public void add_local_variable (LocalVariable local) { local_variables.add (local); } - + + public void remove_local_variable (LocalVariable local) { + local_variables.remove (local); + } + /** * Returns a copy of the list of local variables. * @@ -103,22 +118,60 @@ public class Vala.Block : Symbol, Statement { checked = true; owner = analyzer.current_symbol.scope; + + var old_symbol = analyzer.current_symbol; analyzer.current_symbol = this; - foreach (Statement stmt in statement_list) { - stmt.check (analyzer); + for (int i = 0; i < statement_list.size; i++) { + statement_list[i].check (analyzer); } foreach (LocalVariable local in get_local_variables ()) { local.active = false; } - foreach (Statement stmt in get_statements ()) { + foreach (Statement stmt in statement_list) { add_error_types (stmt.get_error_types ()); } - analyzer.current_symbol = analyzer.current_symbol.parent_symbol; + analyzer.current_symbol = old_symbol; return !error; } + + public void insert_before (Statement stmt, Statement new_stmt) { + for (int i = 0; i < statement_list.size; i++) { + var stmt_list = statement_list[i] as StatementList; + if (stmt_list != null) { + for (int j = 0; j < stmt_list.length; j++) { + if (stmt_list.get (j) == stmt) { + stmt_list.insert (j, new_stmt); + break; + } + } + } else if (statement_list[i] == stmt) { + stmt_list = new StatementList (source_reference); + stmt_list.add (new_stmt); + stmt_list.add (stmt); + statement_list[i] = stmt_list; + } + } + } + + public void replace_statement (Statement old_stmt, Statement new_stmt) { + for (int i = 0; i < statement_list.size; i++) { + var stmt_list = statement_list[i] as StatementList; + if (stmt_list != null) { + for (int j = 0; j < stmt_list.length; j++) { + if (stmt_list.get (j) == old_stmt) { + stmt_list.set (j, new_stmt); + break; + } + } + } else if (statement_list[i] == old_stmt) { + statement_list[i] = new_stmt; + break; + } + } + } } diff --git a/vala/valacodenode.vala b/vala/valacodenode.vala index 88e21dde4..4596e45c7 100644 --- a/vala/valacodenode.vala +++ b/vala/valacodenode.vala @@ -83,6 +83,8 @@ public abstract class Vala.CodeNode { private CCodeNode? _ccodenode; + static int last_temp_nr = 0; + /** * Specifies the exceptions that can be thrown by this node or a child node */ @@ -183,4 +185,8 @@ public abstract class Vala.CodeNode { public virtual void get_used_variables (Collection collection) { } + + public string get_temp_name () { + return "." + (++last_temp_nr).to_string (); + } } diff --git a/vala/valaconditionalexpression.vala b/vala/valaconditionalexpression.vala index 0ba4a859a..f53cd1a15 100644 --- a/vala/valaconditionalexpression.vala +++ b/vala/valaconditionalexpression.vala @@ -29,18 +29,46 @@ public class Vala.ConditionalExpression : Expression { /** * The condition. */ - public Expression condition { get; set; } - + public Expression condition { + get { + return _condition; + } + set { + _condition = value; + _condition.parent_node = this; + } + } + /** * The expression to be evaluated if the condition holds. */ - public Expression true_expression { get; set; } + public Expression true_expression { + get { + return _true_expression; + } + set { + _true_expression = value; + _true_expression.parent_node = this; + } + } /** * The expression to be evaluated if the condition doesn't hold. */ - public Expression false_expression { get; set; } - + public Expression false_expression { + get { + return _false_expression; + } + set { + _false_expression = value; + _false_expression.parent_node = this; + } + } + + Expression _condition; + Expression _true_expression; + Expression _false_expression; + /** * Creates a new conditional expression. * @@ -77,17 +105,35 @@ public class Vala.ConditionalExpression : Expression { checked = true; - if (!condition.check (analyzer) || !false_expression.check (analyzer) || !true_expression.check (analyzer)) { - return false; - } + // convert ternary expression into if statement + // required for flow analysis and exception handling - if (!condition.value_type.compatible (analyzer.bool_type)) { - error = true; - Report.error (condition.source_reference, "Condition must be boolean"); + string temp_name = get_temp_name (); + + true_expression.target_type = target_type; + false_expression.target_type = target_type; + + var true_local = new LocalVariable (null, temp_name, true_expression, true_expression.source_reference); + var true_block = new Block (true_expression.source_reference); + var true_decl = new DeclarationStatement (true_local, true_expression.source_reference); + true_block.add_statement (true_decl); + + var false_local = new LocalVariable (null, temp_name, false_expression, false_expression.source_reference); + var false_block = new Block (false_expression.source_reference); + var false_decl = new DeclarationStatement (false_local, false_expression.source_reference); + false_block.add_statement (false_decl); + + var if_stmt = new IfStatement (condition, true_block, false_block, source_reference); + if (!if_stmt.check (analyzer)) { return false; } - /* FIXME: support memory management */ + true_expression = true_local.initializer; + false_expression = false_local.initializer; + + true_block.remove_local_variable (true_local); + false_block.remove_local_variable (false_local); + if (false_expression.value_type.compatible (true_expression.value_type)) { value_type = true_expression.value_type.copy (); } else if (true_expression.value_type.compatible (false_expression.value_type)) { @@ -98,6 +144,33 @@ public class Vala.ConditionalExpression : Expression { return false; } - return !error; + value_type.value_owned = (true_expression.value_type.value_owned || false_expression.value_type.value_owned); + + var local = new LocalVariable (value_type, temp_name, null, source_reference); + var decl = new DeclarationStatement (local, source_reference); + decl.check (analyzer); + + true_expression.target_type = value_type; + false_expression.target_type = value_type; + + var true_stmt = new ExpressionStatement (new Assignment (new MemberAccess.simple (local.name, true_expression.source_reference), true_expression, AssignmentOperator.SIMPLE, true_expression.source_reference), true_expression.source_reference); + true_stmt.check (analyzer); + + var false_stmt = new ExpressionStatement (new Assignment (new MemberAccess.simple (local.name, false_expression.source_reference), false_expression, AssignmentOperator.SIMPLE, false_expression.source_reference), false_expression.source_reference); + false_stmt.check (analyzer); + + true_block.replace_statement (true_decl, true_stmt); + false_block.replace_statement (false_decl, false_stmt); + + insert_statement ((Block) analyzer.current_symbol, decl); + insert_statement ((Block) analyzer.current_symbol, if_stmt); + + var ma = new MemberAccess.simple (local.name, source_reference); + ma.target_type = target_type; + ma.check (analyzer); + + parent_node.replace_expression (this, ma); + + return true; } } diff --git a/vala/valadeclarationstatement.vala b/vala/valadeclarationstatement.vala index 0cc28fbd7..9e3cc0377 100644 --- a/vala/valadeclarationstatement.vala +++ b/vala/valadeclarationstatement.vala @@ -29,7 +29,19 @@ public class Vala.DeclarationStatement : CodeNode, Statement { /** * The local variable or constant declaration. */ - public Symbol declaration { get; set; } + public Symbol declaration { + get { + return _declaration; + } + set { + _declaration = value; + if (_declaration != null) { + _declaration.parent_node = this; + } + } + } + + Symbol _declaration; /** * Creates a new declaration statement. diff --git a/vala/valaexpression.vala b/vala/valaexpression.vala index 79ca8b13c..11d43950a 100644 --- a/vala/valaexpression.vala +++ b/vala/valaexpression.vala @@ -84,4 +84,25 @@ public abstract class Vala.Expression : CodeNode { public virtual bool is_non_null () { return false; } + + public Statement? parent_statement { + get { + var expr = parent_node as Expression; + var stmt = parent_node as Statement; + var local = parent_node as LocalVariable; + if (stmt != null) { + return stmt; + } else if (expr != null) { + return expr.parent_statement; + } else if (local != null) { + return (Statement) local.parent_node; + } else { + return null; + } + } + } + + public void insert_statement (Block block, Statement stmt) { + block.insert_before (parent_statement, stmt); + } } diff --git a/vala/valanullchecker.vala b/vala/valanullchecker.vala index 7cd940d75..3263c2550 100644 --- a/vala/valanullchecker.vala +++ b/vala/valanullchecker.vala @@ -235,10 +235,6 @@ public class Vala.NullChecker : CodeVisitor { expr.accept_children (this); } - public override void visit_conditional_expression (ConditionalExpression expr) { - check_non_null (expr.condition); - } - public override void visit_lambda_expression (LambdaExpression l) { l.accept_children (this); } diff --git a/vala/valastatementlist.vala b/vala/valastatementlist.vala new file mode 100644 index 000000000..a653f02e5 --- /dev/null +++ b/vala/valastatementlist.vala @@ -0,0 +1,57 @@ +/* valastatementlist.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 + */ + +using Gee; + +public class Vala.StatementList : CodeNode, Statement { + private Gee.List list = new ArrayList (); + + public int length { + get { return list.size; } + } + + public StatementList (SourceReference source_reference) { + this.source_reference = source_reference; + } + + public Statement get (int index) { + return list.get (index); + } + + public void set (int index, Statement stmt) { + list.set (index, stmt); + } + + public void add (Statement stmt) { + list.add (stmt); + } + + public void insert (int index, Statement stmt) { + list.insert (index, stmt); + } + + public override void accept (CodeVisitor visitor) { + foreach (Statement stmt in list) { + stmt.accept (visitor); + } + } +}