private ConstantValue evaluateLogicalExpr(
        Expression lhs, Expression rhs, Type resultType, LogicalBinaryOperation operation) {
      // Evaluate the first subexpression
      final ConstantValue valueLhs = lhs.accept(this, null);

      // Get the factory for the result type
      final ConstantType resultConstantType = typeFactory.newConstantType(resultType);
      checkArgument(
          resultConstantType.getType() == ConstantType.Type.SIGNED_INTEGER,
          "result of logical AND or logical OR expression has not a signed integer type");
      final boolean resultValue;

      /* Determine the result - we use the fact the Java '&&' and '||'
      operators are evaluated lazily. */
      switch (operation) {
        case LOGICAL_AND:
          resultValue = valueLhs.logicalValue() && rhs.accept(this, null).logicalValue();
          break;
        case LOGICAL_OR:
          resultValue = valueLhs.logicalValue() || rhs.accept(this, null).logicalValue();
          break;
        default:
          throw new RuntimeException("unexpected logical binary operation '" + operation + "'");
      }

      return UnsignedIntegerConstantValue.getLogicalValue(resultValue).castTo(resultConstantType);
    }
    @Override
    public ConstantValue visitConditional(Conditional expr, Void arg) {
      final ConstantValue conditionValue = expr.getCondition().accept(this, null);
      final ConstantType resultConstantType = typeFactory.newConstantType(expr.getType().get());
      final ConstantValue resultValue;

      if (conditionValue.logicalValue()) {
        resultValue =
            expr.getOnTrueExp().isPresent()
                ? expr.getOnTrueExp().get().accept(this, null)
                : conditionValue;
      } else {
        resultValue = expr.getOnFalseExp().accept(this, null);
      }

      return resultValue.castTo(resultConstantType);
    }
    private ConstantValue evaluateUnaryExpr(
        Expression opParam, Type resultType, UnaryArithmeticOperation operation) {
      // Evaluate the subexpression
      ConstantValue paramValue = opParam.accept(this, null);

      // Make the integer promotion
      final ConstantType resultConstantType = typeFactory.newConstantType(resultType);
      paramValue = paramValue.castTo(resultConstantType);

      // Perform the operation
      switch (operation) {
        case IDENTITY:
          return paramValue;
        case NEGATION:
          return paramValue.negate();
        case BITWISE_NOT:
          return paramValue.bitwiseNot();
        default:
          throw new RuntimeException("unexpected unary arithmetic operation '" + operation + "'");
      }
    }
    private ConstantValue evaluateShiftExpr(
        Expression lhs, Expression rhs, Type resultType, ShiftOperation operation) {
      // Evaluate subexpressions
      ConstantValue valueLhs = lhs.accept(this, null);
      ConstantValue valueRhs = rhs.accept(this, null);

      // Make integer promotions
      final ConstantType leftPromotedType = typeFactory.newConstantType(resultType);
      final ConstantType rightPromotedType =
          typeFactory.newConstantType(rhs.getType().get().promote());
      valueLhs = valueLhs.castTo(leftPromotedType);
      valueRhs = valueRhs.castTo(rightPromotedType);

      // Perform the operation
      switch (operation) {
        case LEFT_SHIFT:
          return valueLhs.shiftLeft(valueRhs);
        case RIGHT_SHIFT:
          return valueLhs.shiftRight(valueRhs);
        default:
          throw new RuntimeException("unexpected shift operation kind '" + operation + "'");
      }
    }
    private ConstantValue evaluateBinaryExpr(
        Expression lhs, Expression rhs, Type resultType, ArithmeticBinaryOperation operation) {
      // Evaluate subexpressions
      ConstantValue valueLhs = lhs.accept(this, null);
      ConstantValue valueRhs = rhs.accept(this, null);

      // Convert to type implied by usual arithmetic conversions
      final ArithmeticType commonType =
          TypeUtils.doUsualArithmeticConversions(
              (ArithmeticType) lhs.getType().get(), (ArithmeticType) rhs.getType().get());
      final ConstantType commonConstantType = typeFactory.newConstantType(commonType);
      final ConstantType resultConstantType = typeFactory.newConstantType(resultType);
      valueLhs = valueLhs.castTo(commonConstantType);
      valueRhs = valueRhs.castTo(commonConstantType);

      // Perform the operation
      switch (operation) {
        case ADDITION:
          return valueLhs.add(valueRhs);
        case SUBTRACTION:
          return valueLhs.subtract(valueRhs);
        case MULTIPLICATION:
          return valueLhs.multiply(valueRhs);
        case DIVISION:
          return valueLhs.divide(valueRhs);
        case REMAINDER:
          return valueLhs.remainder(valueRhs);
        case BITWISE_AND:
          return valueLhs.bitwiseAnd(valueRhs);
        case BITWISE_OR:
          return valueLhs.bitwiseOr(valueRhs);
        case BITWISE_XOR:
          return valueLhs.bitwiseXor(valueRhs);
        case LESS:
          return valueLhs.less(valueRhs).castTo(resultConstantType);
        case LESS_OR_EQUAL:
          return valueLhs.lessOrEqual(valueRhs).castTo(resultConstantType);
        case GREATER:
          return valueLhs.greater(valueRhs).castTo(resultConstantType);
        case GREATER_OR_EQUAL:
          return valueLhs.greaterOrEqual(valueRhs).castTo(resultConstantType);
        case EQUAL:
          return valueLhs.equalTo(valueRhs).castTo(resultConstantType);
        case NOT_EQUAL:
          return valueLhs.notEqualTo(valueRhs).castTo(resultConstantType);
        default:
          throw new RuntimeException("unexpected arithmetic binary operation '" + operation + "'");
      }
    }