LCOV - code coverage report
Current view: top level - vala - valabinaryexpression.vala (source / functions) Coverage Total Hit
Test: vala 0.57.0.298-a8cae1 Lines: 78.1 % 389 304
Test Date: 2024-04-25 11:34:36 Functions: - 0 0

            Line data    Source code
       1              : /* valabinaryexpression.vala
       2              :  *
       3              :  * Copyright (C) 2006-2010  Jürg Billeter
       4              :  *
       5              :  * This library is free software; you can redistribute it and/or
       6              :  * modify it under the terms of the GNU Lesser General Public
       7              :  * License as published by the Free Software Foundation; either
       8              :  * version 2.1 of the License, or (at your option) any later version.
       9              : 
      10              :  * This library is distributed in the hope that it will be useful,
      11              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13              :  * Lesser General Public License for more details.
      14              : 
      15              :  * You should have received a copy of the GNU Lesser General Public
      16              :  * License along with this library; if not, write to the Free Software
      17              :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
      18              :  *
      19              :  * Author:
      20              :  *      Jürg Billeter <j@bitron.ch>
      21              :  */
      22              : 
      23              : 
      24              : /**
      25              :  * Represents an expression with two operands in the source code.
      26              :  *
      27              :  * Supports +, -, *, /, %, <<, >>, <, >, <=, >=, ==, !=, &, |, ^, &&, ||, ??.
      28              :  */
      29       322468 : public class Vala.BinaryExpression : Expression {
      30              :         /**
      31              :          * The binary operator.
      32              :          */
      33       353421 :         public BinaryOperator operator { get; private set; }
      34              : 
      35              :         /**
      36              :          * The left operand.
      37              :          */
      38              :         public Expression left {
      39      6492729 :                 get {
      40      6492729 :                         return _left;
      41              :                 }
      42       321187 :                 private set {
      43       918133 :                         _left = value;
      44       321187 :                         _left.parent_node = this;
      45              :                 }
      46              :         }
      47              : 
      48              :         /**
      49              :          * The right operand.
      50              :          */
      51              :         public Expression right {
      52      6025480 :                 get {
      53      6025480 :                         return _right;
      54              :                 }
      55       321006 :                 private set {
      56       642012 :                         _right = value;
      57       321006 :                         _right.parent_node = this;
      58              :                 }
      59              :         }
      60              : 
      61       333278 :         public bool is_chained { get; private set; }
      62              : 
      63       320931 :         private Expression _left;
      64       320931 :         private Expression _right;
      65              : 
      66              :         /**
      67              :          * Creates a new binary expression.
      68              :          *
      69              :          * @param op      binary operator
      70              :          * @param _left   left operand
      71              :          * @param _right  right operand
      72              :          * @param source  reference to source code
      73              :          * @return        newly created binary expression
      74              :          */
      75       953544 :         public BinaryExpression (BinaryOperator op, Expression _left, Expression _right, SourceReference? source = null) {
      76       317848 :                 operator = op;
      77       317848 :                 left = _left;
      78       317848 :                 right = _right;
      79       317848 :                 is_chained = false;
      80       317848 :                 source_reference = source;
      81              :         }
      82              : 
      83         9249 :         public BinaryExpression.chained (BinaryOperator op, Expression _left, Expression _right, SourceReference? source = null) {
      84         3083 :                 operator = op;
      85         3083 :                 left = _left;
      86         3083 :                 right = _right;
      87         3083 :                 is_chained = true;
      88         3083 :                 source_reference = source;
      89              :         }
      90              : 
      91      1039413 :         public override void accept (CodeVisitor visitor) {
      92      1039413 :                 visitor.visit_binary_expression (this);
      93              : 
      94      1039413 :                 visitor.visit_expression (this);
      95              :         }
      96              : 
      97      1039386 :         public override void accept_children (CodeVisitor visitor) {
      98      1039386 :                 left.accept (visitor);
      99      1039386 :                 right.accept (visitor);
     100              :         }
     101              : 
     102          269 :         public override void replace_expression (Expression old_node, Expression new_node) {
     103          269 :                 if (left == old_node) {
     104          256 :                         left = new_node;
     105              :                 }
     106          269 :                 if (right == old_node) {
     107           13 :                         right = new_node;
     108              :                 }
     109              :         }
     110              : 
     111            0 :         public override string to_string () {
     112            0 :                 return "(%s %s %s)".printf (_left.to_string (), operator.to_string (), _right.to_string ());
     113              :         }
     114              : 
     115          380 :         public override bool is_constant () {
     116          380 :                 return left.is_constant () && right.is_constant ();
     117              :         }
     118              : 
     119            0 :         public override bool is_pure () {
     120            0 :                 return left.is_pure () && right.is_pure ();
     121              :         }
     122              : 
     123         1083 :         public override bool is_non_null () {
     124         1083 :                 return left.is_non_null () && right.is_non_null ();
     125              :         }
     126              : 
     127        12745 :         public override bool is_accessible (Symbol sym) {
     128        12745 :                 return left.is_accessible (sym) && right.is_accessible (sym);
     129              :         }
     130              : 
     131       525046 :         public override void get_error_types (Collection<DataType> collection, SourceReference? source_reference = null) {
     132       525046 :                 left.get_error_types (collection, source_reference);
     133       525046 :                 right.get_error_types (collection, source_reference);
     134              :         }
     135              : 
     136       321316 :         public override bool check (CodeContext context) {
     137       321316 :                 if (checked) {
     138        23039 :                         return !error;
     139              :                 }
     140              : 
     141       298277 :                 checked = true;
     142              : 
     143              :                 // some expressions are not in a block,
     144              :                 // for example, expressions in method contracts
     145       298277 :                 if (context.analyzer.current_symbol is Block
     146       285495 :                     && (operator == BinaryOperator.AND || operator == BinaryOperator.OR)) {
     147              :                         // convert conditional expression into if statement
     148              :                         // required for flow analysis and exception handling
     149              : 
     150        39207 :                         var local = new LocalVariable (context.analyzer.bool_type.copy (), get_temp_name (), null, source_reference);
     151        39207 :                         var decl = new DeclarationStatement (local, source_reference);
     152              : 
     153        39207 :                         var right_stmt = new ExpressionStatement (new Assignment (new MemberAccess.simple (local.name, right.source_reference), right, AssignmentOperator.SIMPLE, right.source_reference), right.source_reference);
     154              : 
     155        39207 :                         var stmt = new ExpressionStatement (new Assignment (new MemberAccess.simple (local.name, left.source_reference), new BooleanLiteral ((operator == BinaryOperator.OR), left.source_reference), AssignmentOperator.SIMPLE, left.source_reference), left.source_reference);
     156              : 
     157        39207 :                         var true_block = new Block (source_reference);
     158        39207 :                         var false_block = new Block (source_reference);
     159              : 
     160        39207 :                         if (operator == BinaryOperator.AND) {
     161        28560 :                                 true_block.add_statement (right_stmt);
     162        28560 :                                 false_block.add_statement (stmt);
     163              :                         } else {
     164        10647 :                                 true_block.add_statement (stmt);
     165        10647 :                                 false_block.add_statement (right_stmt);
     166              :                         }
     167              : 
     168        39207 :                         var if_stmt = new IfStatement (left, true_block, false_block, source_reference);
     169              : 
     170        39207 :                         insert_statement (context.analyzer.insert_block, decl);
     171        39207 :                         insert_statement (context.analyzer.insert_block, if_stmt);
     172              : 
     173        39207 :                         decl.check (context);
     174              : 
     175        39207 :                         if (!if_stmt.check (context)) {
     176            0 :                                 error = true;
     177            0 :                                 return false;
     178              :                         }
     179              : 
     180        39207 :                         var ma = new MemberAccess.simple (local.name, source_reference);
     181        39207 :                         ma.target_type = target_type;
     182        39207 :                         ma.formal_target_type = formal_target_type;
     183              : 
     184        39207 :                         parent_node.replace_expression (this, ma);
     185              : 
     186        39207 :                         ma.check (context);
     187              : 
     188        39207 :                         return true;
     189              :                 }
     190              : 
     191       259070 :                 if (operator == BinaryOperator.COALESCE) {
     192           62 :                         if (target_type != null) {
     193           62 :                                 left.target_type = target_type.copy ();
     194           62 :                                 right.target_type = target_type.copy ();
     195              :                         }
     196              : 
     197           62 :                         if (!left.check (context)) {
     198            0 :                                 error = true;
     199            0 :                                 return false;
     200              :                         }
     201              : 
     202           62 :                         string temp_name = get_temp_name ();
     203              : 
     204              :                         // right expression is checked under a block (required for short-circuiting)
     205           62 :                         var right_local = new LocalVariable (null, temp_name, right, right.source_reference);
     206           62 :                         var right_decl = new DeclarationStatement (right_local, right.source_reference);
     207           62 :                         var true_block = new Block (source_reference);
     208           62 :                         true_block.add_statement (right_decl);
     209              : 
     210           62 :                         if (!true_block.check (context)) {
     211            0 :                                 error = true;
     212            0 :                                 return false;
     213              :                         }
     214              : 
     215              :                         // right expression may have been replaced by the check
     216           62 :                         right = right_local.initializer;
     217              : 
     218           62 :                         DataType local_type = null;
     219           62 :                         bool cast_non_null = false;
     220           62 :                         if (left.value_type is NullType && right.value_type != null) {
     221            1 :                                 Report.warning (left.source_reference, "left operand is always null");
     222            1 :                                 local_type = right.value_type.copy ();
     223            1 :                                 local_type.nullable = true;
     224            1 :                                 if (!right.value_type.nullable) {
     225            1 :                                         cast_non_null = true;
     226              :                                 }
     227           61 :                         } else if (left.value_type != null) {
     228           61 :                                 local_type = left.value_type.copy ();
     229           61 :                                 if (right.value_type != null && right.value_type.value_owned) {
     230              :                                         // value owned if either left or right is owned
     231           18 :                                         local_type.value_owned = true;
     232              :                                 }
     233           61 :                                 if (context.experimental_non_null) {
     234            3 :                                         if (!local_type.nullable) {
     235            1 :                                                 Report.warning (left.source_reference, "left operand is never null");
     236            1 :                                                 if (right.value_type != null && right.value_type.nullable) {
     237            1 :                                                         local_type.nullable = true;
     238            1 :                                                         cast_non_null = true;
     239              :                                                 }
     240            2 :                                         } else if (right.value_type != null && !right.value_type.nullable) {
     241           62 :                                                 cast_non_null = true;
     242              :                                         }
     243              :                                 }
     244            0 :                         } else if (right.value_type != null) {
     245            0 :                                 local_type = right.value_type.copy ();
     246              :                         }
     247              : 
     248           62 :                         if (local_type != null && right.value_type is ValueType && !right.value_type.nullable) {
     249              :                                 // immediate values in the right expression must always be boxed,
     250              :                                 // otherwise the local variable may receive a stale pointer to the stack
     251            8 :                                 local_type.value_owned = true;
     252              :                         }
     253              : 
     254           62 :                         var local = new LocalVariable (local_type, temp_name, left, source_reference);
     255           62 :                         var decl = new DeclarationStatement (local, source_reference);
     256              : 
     257           62 :                         insert_statement (context.analyzer.insert_block, decl);
     258              : 
     259           62 :                         if (!decl.check (context)) {
     260            0 :                                 error = true;
     261            0 :                                 return false;
     262              :                         }
     263              : 
     264              :                         // replace the temporary local variable used to compute the type of the right expression by an assignment
     265           62 :                         var right_stmt = new ExpressionStatement (new Assignment (new MemberAccess.simple (local.name, right.source_reference), right, AssignmentOperator.SIMPLE, right.source_reference), right.source_reference);
     266              : 
     267           62 :                         true_block.remove_local_variable (right_local);
     268           62 :                         true_block.replace_statement (right_decl, right_stmt);
     269              : 
     270           62 :                         if (!right_stmt.check (context)) {
     271            0 :                                 error = true;
     272            0 :                                 return false;
     273              :                         }
     274              : 
     275           62 :                         var cond = new BinaryExpression (BinaryOperator.EQUALITY, new MemberAccess.simple (local.name, left.source_reference), new NullLiteral (source_reference), source_reference);
     276              : 
     277           62 :                         var if_stmt = new IfStatement (cond, true_block, null, source_reference);
     278              : 
     279           62 :                         insert_statement (context.analyzer.insert_block, if_stmt);
     280              : 
     281           62 :                         if (!if_stmt.check (context)) {
     282            0 :                                 error = true;
     283            0 :                                 return false;
     284              :                         }
     285              : 
     286           62 :                         var replace_expr = SemanticAnalyzer.create_temp_access (local, target_type);
     287           65 :                         if (cast_non_null && replace_expr.target_type != null) {
     288            3 :                                 var cast = new CastExpression.non_null (replace_expr, source_reference);
     289            3 :                                 cast.target_type = replace_expr.target_type.copy ();
     290            3 :                                 cast.target_type.nullable = false;
     291            6 :                                 replace_expr = cast;
     292              :                         }
     293              : 
     294           62 :                         parent_node.replace_expression (this, replace_expr);
     295              : 
     296           62 :                         replace_expr.check (context);
     297              : 
     298           62 :                         return true;
     299              :                 }
     300              : 
     301              :                 // enum-type inference
     302       259008 :                 if (target_type != null && target_type.type_symbol is Enum
     303          141 :                     && (operator == BinaryOperator.BITWISE_AND || operator == BinaryOperator.BITWISE_OR)) {
     304          138 :                         left.target_type = target_type.copy ();
     305          138 :                         right.target_type = target_type.copy ();
     306       258870 :                 } else if (operator == BinaryOperator.IN) {
     307          112 :                         right.check (context);
     308          112 :                         if (right.value_type.type_symbol is Enum) {
     309           36 :                                 left.target_type = right.value_type.copy ();
     310              :                         }
     311              :                 }
     312       259008 :                 left.check (context);
     313       259008 :                 if (left.value_type != null && left.value_type.type_symbol is Enum
     314          884 :                     && (operator == BinaryOperator.EQUALITY || operator == BinaryOperator.INEQUALITY)) {
     315          696 :                         right.target_type = left.value_type.copy ();
     316              :                 }
     317       259008 :                 right.check (context);
     318       259008 :                 if (right.value_type != null && right.value_type.type_symbol is Enum
     319          857 :                     && (operator == BinaryOperator.EQUALITY || operator == BinaryOperator.INEQUALITY)) {
     320          669 :                         left.target_type = right.value_type.copy ();
     321              :                         //TODO bug 666035 -- re-check left how?
     322              :                 }
     323              : 
     324       259008 :                 if (!left.check (context) || !right.check (context)) {
     325              :                         /* if there were any errors in inner expressions, skip type check */
     326            1 :                         error = true;
     327            1 :                         return false;
     328              :                 }
     329              : 
     330       259007 :                 if (left.value_type == null) {
     331            0 :                         Report.error (left.source_reference, "invalid left operand");
     332            0 :                         error = true;
     333            0 :                         return false;
     334              :                 }
     335              : 
     336       259007 :                 if (operator != BinaryOperator.IN && right.value_type == null) {
     337            0 :                         Report.error (right.source_reference, "invalid right operand");
     338            0 :                         error = true;
     339            0 :                         return false;
     340              :                 }
     341              : 
     342       259007 :                 if (left.value_type is FieldPrototype || left.value_type is PropertyPrototype) {
     343            2 :                         error = true;
     344            2 :                         Report.error (left.source_reference, "Access to instance member `%s' denied", left.symbol_reference.get_full_name ());
     345            2 :                         return false;
     346              :                 }
     347       259005 :                 if (right.value_type is FieldPrototype || right.value_type is PropertyPrototype) {
     348            0 :                         error = true;
     349            0 :                         Report.error (right.source_reference, "Access to instance member `%s' denied", right.symbol_reference.get_full_name ());
     350            0 :                         return false;
     351              :                 }
     352              : 
     353       259005 :                 left.target_type = left.value_type.copy ();
     354       259005 :                 left.target_type.value_owned = false;
     355       259005 :                 right.target_type = right.value_type.copy ();
     356       259005 :                 right.target_type.value_owned = false;
     357              : 
     358       293882 :                 if (operator == BinaryOperator.PLUS && !(left.value_type is PointerType)
     359        34877 :                     && left.value_type.compatible (context.analyzer.string_type)) {
     360              :                         // string concatenation
     361              : 
     362        13329 :                         if (right.value_type == null || !right.value_type.compatible (context.analyzer.string_type)
     363        13329 :                             || left is NullLiteral || right is NullLiteral) {
     364              :                                 // operands cannot be null
     365            1 :                                 error = true;
     366            1 :                                 Report.error (source_reference, "Operands must be strings");
     367            1 :                                 return false;
     368              :                         }
     369              : 
     370        13328 :                         value_type = context.analyzer.string_type.copy ();
     371        13328 :                         if (left.is_constant () && right.is_constant ()) {
     372        12804 :                                 value_type.value_owned = false;
     373              :                         } else {
     374          524 :                                 value_type.value_owned = true;
     375              :                         }
     376              : 
     377        13328 :                         value_type.check (context);
     378        13328 :                         return !error;
     379       245676 :                 } else if (operator == BinaryOperator.PLUS && left.value_type is ArrayType) {
     380              :                         // array concatenation
     381              : 
     382           66 :                         unowned ArrayType array_type = (ArrayType) left.value_type;
     383              : 
     384           66 :                         if (array_type.inline_allocated) {
     385            1 :                                 error = true;
     386            1 :                                 Report.error (source_reference, "Array concatenation not supported for fixed length arrays");
     387              :                         }
     388           66 :                         if (right.value_type == null || !right.value_type.compatible (array_type.element_type)) {
     389            0 :                                 error = true;
     390            0 :                                 Report.error (source_reference, "Incompatible operand");
     391            0 :                                 return false;
     392              :                         }
     393              : 
     394           66 :                         right.target_type = array_type.element_type.copy ();
     395              : 
     396           66 :                         value_type = array_type.copy ();
     397           66 :                         value_type.value_owned = true;
     398              : 
     399           66 :                         value_type.check (context);
     400           66 :                         return !error;
     401              :                 }
     402              : 
     403       245610 :                 switch (operator) {
     404              :                 case BinaryOperator.PLUS:
     405              :                 case BinaryOperator.MINUS:
     406              :                 case BinaryOperator.MUL:
     407              :                 case BinaryOperator.DIV:
     408              :                         // check for pointer arithmetic
     409        79842 :                         if (left.value_type is PointerType) {
     410        45360 :                                 unowned PointerType pointer_type = (PointerType) left.value_type;
     411        45360 :                                 if (pointer_type.base_type is VoidType) {
     412            0 :                                         error = true;
     413            0 :                                         Report.error (source_reference, "Pointer arithmetic not supported for `void*'");
     414            0 :                                         return false;
     415              :                                 }
     416              : 
     417        45360 :                                 unowned Struct? offset_type = right.value_type.type_symbol as Struct;
     418        45360 :                                 if (offset_type != null && offset_type.is_integer_type ()) {
     419        32589 :                                         if (operator == BinaryOperator.PLUS
     420            2 :                                             || operator == BinaryOperator.MINUS) {
     421              :                                                 // pointer arithmetic: pointer +/- offset
     422        32589 :                                                 value_type = left.value_type.copy ();
     423              :                                         }
     424        12771 :                                 } else if (right.value_type is PointerType) {
     425              :                                         // pointer arithmetic: pointer - pointer
     426        12771 :                                         value_type = context.analyzer.size_t_type;
     427              :                                 }
     428              :                         } else {
     429        34482 :                                 left.target_type.nullable = false;
     430        34482 :                                 right.target_type.nullable = false;
     431              :                         }
     432              : 
     433        79842 :                         if (value_type == null) {
     434        34482 :                                 value_type = context.analyzer.get_arithmetic_result_type (left.target_type, right.target_type);
     435              :                         }
     436              : 
     437        79842 :                         if (value_type == null) {
     438            1 :                                 error = true;
     439            1 :                                 Report.error (source_reference, "Arithmetic operation not supported for types `%s' and `%s'", left.value_type.to_string (), right.value_type.to_string ());
     440            1 :                                 return false;
     441              :                         }
     442              :                         break;
     443              :                 case BinaryOperator.MOD:
     444              :                 case BinaryOperator.SHIFT_LEFT:
     445              :                 case BinaryOperator.SHIFT_RIGHT:
     446           39 :                         left.target_type.nullable = false;
     447           39 :                         right.target_type.nullable = false;
     448              : 
     449           39 :                         value_type = context.analyzer.get_arithmetic_result_type (left.target_type, right.target_type);
     450              : 
     451           39 :                         if (value_type == null) {
     452            0 :                                 error = true;
     453            0 :                                 Report.error (source_reference, "Arithmetic operation not supported for types `%s' and `%s'", left.value_type.to_string (), right.value_type.to_string ());
     454            0 :                                 return false;
     455              :                         }
     456              :                         break;
     457              :                 case BinaryOperator.LESS_THAN:
     458              :                 case BinaryOperator.GREATER_THAN:
     459              :                 case BinaryOperator.LESS_THAN_OR_EQUAL:
     460              :                 case BinaryOperator.GREATER_THAN_OR_EQUAL:
     461        54748 :                         if (left.value_type.compatible (context.analyzer.string_type)
     462           17 :                             && right.value_type.compatible (context.analyzer.string_type)) {
     463              :                                 // string comparison
     464       109419 :                         } else if (left.value_type is PointerType && right.value_type is PointerType) {
     465              :                                 // pointer arithmetic
     466              :                         } else {
     467              :                                 DataType resulting_type;
     468              : 
     469        54708 :                                 if (is_chained) {
     470         2839 :                                         unowned BinaryExpression lbe = (BinaryExpression) left;
     471         2842 :                                         if (lbe.right.value_type.compatible (context.analyzer.string_type)
     472            3 :                                             && right.value_type.compatible (context.analyzer.string_type)) {
     473            3 :                                                 value_type = context.analyzer.bool_type;
     474            3 :                                                 break;
     475              :                                         }
     476         2836 :                                         resulting_type = context.analyzer.get_arithmetic_result_type (lbe.right.target_type, right.target_type);
     477              :                                 } else {
     478        51869 :                                         resulting_type = context.analyzer.get_arithmetic_result_type (left.target_type, right.target_type);
     479              :                                 }
     480              : 
     481        54705 :                                 if (resulting_type == null) {
     482            0 :                                         error = true;
     483              :                                         unowned DataType left_type;
     484            0 :                                         if (is_chained) {
     485            0 :                                                 unowned BinaryExpression lbe = (BinaryExpression) left;
     486            0 :                                                 left_type = lbe.right.value_type;
     487              :                                         } else {
     488            0 :                                                 left_type = left.value_type;
     489              :                                         }
     490            0 :                                         Report.error (source_reference, "Relational operation not supported for types `%s' and `%s'", left_type.to_string (), right.value_type.to_string ());
     491            0 :                                         return false;
     492              :                                 }
     493              : 
     494        54705 :                                 if (!is_chained) {
     495        51869 :                                         left.target_type = resulting_type.copy ();
     496              :                                 }
     497        54705 :                                 right.target_type = resulting_type.copy ();
     498        54705 :                                 left.target_type.nullable = false;
     499        54705 :                                 right.target_type.nullable = false;
     500              :                         }
     501              : 
     502        54728 :                         value_type = context.analyzer.bool_type;
     503        54728 :                         break;
     504              :                 case BinaryOperator.EQUALITY:
     505              :                 case BinaryOperator.INEQUALITY:
     506              :                         /* relational operation */
     507              : 
     508       221154 :                         if (context.profile == Profile.GOBJECT) {
     509              :                                 // Implicit cast for comparison expression of GValue with other type
     510       110535 :                                 var gvalue_type = context.analyzer.gvalue_type.type_symbol;
     511       110535 :                                 if ((left.target_type.type_symbol == gvalue_type && right.target_type.type_symbol != gvalue_type)
     512       110504 :                                         || (left.target_type.type_symbol != gvalue_type && right.target_type.type_symbol == gvalue_type)) {
     513              :                                         Expression gvalue_expr;
     514              :                                         DataType target_type;
     515           39 :                                         if (left.target_type.type_symbol == gvalue_type) {
     516           31 :                                                 gvalue_expr = left;
     517           31 :                                                 target_type = right.target_type;
     518              :                                         } else {
     519            8 :                                                 gvalue_expr = right;
     520            8 :                                                 target_type = left.target_type;
     521              :                                         }
     522              : 
     523           39 :                                         var cast_expr = new CastExpression (gvalue_expr, target_type, gvalue_expr.source_reference);
     524           39 :                                         replace_expression (gvalue_expr, cast_expr);
     525           39 :                                         checked = false;
     526           39 :                                         return check (context);
     527              :                                 }
     528              :                         }
     529              : 
     530              :                         DataType resulting_type;
     531       110619 :                         if (is_chained) {
     532           16 :                                 unowned BinaryExpression lbe = (BinaryExpression) left;
     533           16 :                                 resulting_type = context.analyzer.get_arithmetic_result_type (lbe.right.target_type, right.target_type);
     534           16 :                                 if (!right.value_type.compatible (lbe.right.value_type)
     535            1 :                                     && !lbe.right.value_type.compatible (right.value_type)) {
     536            1 :                                         Report.error (source_reference, "Equality operation: `%s' and `%s' are incompatible", right.value_type.to_string (), lbe.right.value_type.to_string ());
     537            1 :                                         error = true;
     538            1 :                                         return false;
     539              :                                 }
     540              :                         } else {
     541       110603 :                                 resulting_type = context.analyzer.get_arithmetic_result_type (left.target_type, right.target_type);
     542       110603 :                                 if (!right.value_type.compatible (left.value_type)
     543         2863 :                                     && !left.value_type.compatible (right.value_type)) {
     544            0 :                                         Report.error (source_reference, "Equality operation: `%s' and `%s' are incompatible", right.value_type.to_string (), left.value_type.to_string ());
     545            0 :                                         error = true;
     546            0 :                                         return false;
     547              :                                 }
     548              :                         }
     549              : 
     550       110618 :                         if (resulting_type != null) {
     551              :                                 // numeric operation
     552        38446 :                                 if (!is_chained) {
     553        38438 :                                         left.target_type = resulting_type.copy ();
     554              :                                 }
     555        38446 :                                 right.target_type = resulting_type.copy ();
     556              :                         }
     557              : 
     558       110618 :                         left.target_type.value_owned = false;
     559       110618 :                         right.target_type.value_owned = false;
     560              : 
     561       110618 :                         if (left.value_type.nullable != right.value_type.nullable) {
     562              :                                 // if only one operand is nullable, make sure the other
     563              :                                 // operand is promoted to nullable as well,
     564              :                                 // reassign both, as get_arithmetic_result_type doesn't
     565              :                                 // take nullability into account
     566         4382 :                                 left.target_type.nullable = true;
     567         4382 :                                 right.target_type.nullable = true;
     568              :                         }
     569              : 
     570       110618 :                         value_type = context.analyzer.bool_type;
     571       110618 :                         break;
     572              :                 case BinaryOperator.BITWISE_AND:
     573              :                 case BinaryOperator.BITWISE_OR:
     574              :                 case BinaryOperator.BITWISE_XOR:
     575              :                         // integer type or flags type
     576          223 :                         left.target_type.nullable = false;
     577          223 :                         right.target_type.nullable = false;
     578              : 
     579              :                         // Don't falsely resolve to bool
     580          223 :                         if (left.value_type.compatible (context.analyzer.bool_type)
     581            6 :                             && !right.value_type.compatible (context.analyzer.bool_type)) {
     582            2 :                                 value_type = right.target_type.copy ();
     583              :                         } else {
     584          221 :                                 value_type = left.target_type.copy ();
     585              :                         }
     586              :                         break;
     587              :                 case BinaryOperator.AND:
     588              :                 case BinaryOperator.OR:
     589            5 :                         if (!left.value_type.compatible (context.analyzer.bool_type) || !right.value_type.compatible (context.analyzer.bool_type)) {
     590            0 :                                 error = true;
     591            0 :                                 Report.error (source_reference, "Operands must be boolean");
     592              :                         }
     593            5 :                         left.target_type.nullable = false;
     594            5 :                         right.target_type.nullable = false;
     595              : 
     596            5 :                         value_type = context.analyzer.bool_type;
     597            5 :                         break;
     598              :                 case BinaryOperator.IN:
     599          191 :                         if (left.value_type.compatible (context.analyzer.int_type)
     600           79 :                             && right.value_type.compatible (context.analyzer.int_type)) {
     601              :                                 // integers or enums
     602           69 :                                 left.target_type.nullable = false;
     603           69 :                                 right.target_type.nullable = false;
     604           69 :                                 if (left.value_type.type_symbol is Enum && right.value_type.type_symbol is Enum
     605           36 :                                     && left.value_type.type_symbol != right.value_type.type_symbol) {
     606            1 :                                         error = true;
     607            1 :                                         Report.error (source_reference, "Cannot look for `%s' in `%s'", left.value_type.to_string (), right.value_type.to_string ());
     608              :                                 }
     609           43 :                         } else if (right.value_type is ArrayType) {
     610           32 :                                 if (!left.value_type.compatible (((ArrayType) right.value_type).element_type)) {
     611            0 :                                         error = true;
     612            0 :                                         Report.error (source_reference, "Cannot look for `%s' in `%s'", left.value_type.to_string (), right.value_type.to_string ());
     613              :                                 }
     614              :                         } else {
     615              :                                 // otherwise require a bool contains () method
     616           11 :                                 var contains_method = right.value_type.get_member ("contains") as Method;
     617            0 :                                 if (contains_method == null) {
     618            0 :                                         Report.error (source_reference, "`%s' does not have a `contains' method", right.value_type.to_string ());
     619            0 :                                         error = true;
     620            0 :                                         return false;
     621              :                                 }
     622           11 :                                 if (contains_method.get_parameters ().size != 1) {
     623            0 :                                         Report.error (source_reference, "`%s' must have one parameter", contains_method.get_full_name ());
     624            0 :                                         error = true;
     625            0 :                                         return false;
     626              :                                 }
     627           11 :                                 if (!contains_method.return_type.compatible (context.analyzer.bool_type)) {
     628            0 :                                         Report.error (source_reference, "`%s' must return a boolean value", contains_method.get_full_name ());
     629            0 :                                         error = true;
     630            0 :                                         return false;
     631              :                                 }
     632              : 
     633           11 :                                 var contains_call = new MethodCall (new MemberAccess (right, "contains", source_reference), source_reference);
     634           11 :                                 contains_call.add_argument (left);
     635           11 :                                 parent_node.replace_expression (this, contains_call);
     636           11 :                                 return contains_call.check (context);
     637              :                         }
     638              : 
     639          101 :                         value_type = context.analyzer.bool_type;
     640          101 :                         break;
     641              :                 default:
     642            0 :                         error = true;
     643            0 :                         Report.error (source_reference, "internal error: unsupported binary operator");
     644            0 :                         return false;
     645              :                 }
     646              : 
     647       245558 :                 value_type.check (context);
     648              : 
     649       245558 :                 return !error;
     650              :         }
     651              : 
     652        12251 :         public override void emit (CodeGenerator codegen) {
     653        12251 :                 left.emit (codegen);
     654        12251 :                 right.emit (codegen);
     655              : 
     656        12251 :                 codegen.visit_binary_expression (this);
     657              : 
     658        12251 :                 codegen.visit_expression (this);
     659              :         }
     660              : 
     661       510303 :         public override void get_defined_variables (Collection<Variable> collection) {
     662       510303 :                 left.get_defined_variables (collection);
     663       510303 :                 right.get_defined_variables (collection);
     664              :         }
     665              : 
     666       171085 :         public override void get_used_variables (Collection<Variable> collection) {
     667       171085 :                 left.get_used_variables (collection);
     668       171085 :                 right.get_used_variables (collection);
     669              :         }
     670              : }
     671              : 
     672              : public enum Vala.BinaryOperator {
     673              :         NONE,
     674              :         PLUS,
     675              :         MINUS,
     676              :         MUL,
     677              :         DIV,
     678              :         MOD,
     679              :         SHIFT_LEFT,
     680              :         SHIFT_RIGHT,
     681              :         LESS_THAN,
     682              :         GREATER_THAN,
     683              :         LESS_THAN_OR_EQUAL,
     684              :         GREATER_THAN_OR_EQUAL,
     685              :         EQUALITY,
     686              :         INEQUALITY,
     687              :         BITWISE_AND,
     688              :         BITWISE_OR,
     689              :         BITWISE_XOR,
     690              :         AND,
     691              :         OR,
     692              :         IN,
     693              :         COALESCE;
     694              : 
     695              :         public unowned string to_string () {
     696            0 :                 switch (this) {
     697            0 :                 case PLUS: return "+";
     698            0 :                 case MINUS: return "-";
     699            0 :                 case MUL: return "*";
     700            0 :                 case DIV: return "/";
     701            0 :                 case MOD: return "%";
     702            0 :                 case SHIFT_LEFT: return "<<";
     703            0 :                 case SHIFT_RIGHT: return ">>";
     704            0 :                 case LESS_THAN: return "<";
     705            0 :                 case GREATER_THAN: return ">";
     706            0 :                 case LESS_THAN_OR_EQUAL: return "<=";
     707            0 :                 case GREATER_THAN_OR_EQUAL: return ">=";
     708            0 :                 case EQUALITY: return "==";
     709            0 :                 case INEQUALITY: return "!=";
     710            0 :                 case BITWISE_AND: return "&";
     711            0 :                 case BITWISE_OR: return "|";
     712            0 :                 case BITWISE_XOR: return "^";
     713            0 :                 case AND: return "&&";
     714            0 :                 case OR: return "||";
     715            0 :                 case IN: return "in";
     716            0 :                 case COALESCE: return "??";
     717            0 :                 default: assert_not_reached ();
     718              :                 }
     719              :         }
     720              : }
        

Generated by: LCOV version 2.0-1