static void outputStringExpr(CodeGenerator &cg, AnalysisResultPtr ar,
                             ExpressionPtr exp, bool asLitStr) {
  if (asLitStr && exp->isLiteralString()) {
    const std::string &s = exp->getLiteralString();
    std::string enc = string_cplus_escape(s.c_str(), s.size());
    cg_printf("\"%s\", %d", enc.c_str(), (int)s.size());
    return;
  }

  TypePtr et(exp->getExpectedType());
  exp->setExpectedType(Type::String);
  exp->outputCPP(cg, ar);
  exp->setExpectedType(et);
}
Пример #2
0
ExpressionPtr Expression::replaceValue(ExpressionPtr rep) {
  if (hasContext(Expression::RefValue) &&
      isRefable(true) && !rep->isRefable(true)) {
    /*
      An assignment isRefable, but the rhs may not be. Need this to
      prevent "bad pass by reference" errors.
    */
    ExpressionListPtr el(new ExpressionList(getScope(), getRange(),
                                            ExpressionList::ListKindWrapped));
    el->addElement(rep);
    rep->clearContext(AssignmentRHS);
    rep = el;
  }
  if (rep->is(KindOfSimpleVariable) && !is(KindOfSimpleVariable)) {
    static_pointer_cast<SimpleVariable>(rep)->setAlwaysStash();
  }
  rep->copyContext(m_context & ~(DeadStore|AccessContext));
  if (TypePtr t1 = getType()) {
    if (TypePtr t2 = rep->getType()) {
      if (!Type::SameType(t1, t2)) {
        rep->setExpectedType(t1);
      }
    }
  }

  if (rep->getScope() != getScope()) {
    rep->resetScope(getScope());
  }

  return rep;
}
Пример #3
0
TypePtr AssignmentExpression::
inferTypesImpl(AnalysisResultPtr ar, TypePtr type, bool coerce,
               ExpressionPtr variable,
               ExpressionPtr value /* = ExpressionPtr() */) {
  TypePtr ret = type;
  if (value) {
    if (coerce) {
      ret = value->inferAndCheck(ar, type, coerce);
    } else {
      ret = value->inferAndCheck(ar, NEW_TYPE(Some), coerce);
    }
  }

  BlockScopePtr scope = ar->getScope();
  if (variable->is(Expression::KindOfConstantExpression)) {
    // ...as in ClassConstant statement
    ConstantExpressionPtr exp =
      dynamic_pointer_cast<ConstantExpression>(variable);
    bool p;
    scope->getConstants()->check(exp->getName(), ret, true, ar, variable, p);
  } else if (variable->is(Expression::KindOfDynamicVariable)) {
    // simptodo: not too sure about this
    ar->getFileScope()->setAttribute(FileScope::ContainsLDynamicVariable);
  } else if (variable->is(Expression::KindOfSimpleVariable)) {
    SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(variable);
    if (var->getName() == "this" && ar->getClassScope()) {
      if (ar->isFirstPass()) {
        ar->getCodeError()->record(variable, CodeError::ReassignThis,
                                   variable);
      }
    }
    if (ar->getPhase() == AnalysisResult::LastInference && value) {
      if (!value->getExpectedType()) {
        value->setExpectedType(variable->getActualType());
      }
    }
  }
  // if the value may involve object, consider the variable as "referenced"
  // so that objects are not destructed prematurely.
  bool referenced = true;
  if (value && value->isScalar()) referenced = false;
  if (ret && ret->isNoObjectInvolved()) referenced = false;
  if (referenced && variable->is(Expression::KindOfSimpleVariable)) {
    SimpleVariablePtr var =
      dynamic_pointer_cast<SimpleVariable>(variable);
    const std::string &name = var->getName();
    VariableTablePtr variables = ar->getScope()->getVariables();
    variables->addReferenced(name);
  }

  TypePtr vt = variable->inferAndCheck(ar, ret, true);
  if (!coerce && type->is(Type::KindOfAny)) {
    ret = vt;
  }

  return ret;
}
Пример #4
0
void UnaryOpExpression::SetExpTypeForExistsContext(AnalysisResultPtr ar,
                                                   ExpressionPtr e,
                                                   bool allowPrimitives) {
  if (!e) return;
  TypePtr at(e->getActualType());
  if (!allowPrimitives && at &&
      at->isExactType() && at->isPrimitive()) {
    at = e->inferAndCheck(ar, Type::Variant, true);
  }
  TypePtr it(e->getImplementedType());
  TypePtr et(e->getExpectedType());
  if (et && et->is(Type::KindOfVoid)) e->setExpectedType(TypePtr());
  if (at && (!it || Type::IsMappedToVariant(it)) &&
      ((allowPrimitives && Type::HasFastCastMethod(at)) ||
       (!allowPrimitives &&
        (at->is(Type::KindOfObject) ||
         at->is(Type::KindOfArray) ||
         at->is(Type::KindOfString))))) {
    e->setExpectedType(it ? at : TypePtr());
  }
}
Пример #5
0
// foldConst() is callable from the parse phase as well as the analysis phase.
// We take advantage of this during the parse phase to reduce very simple
// expressions down to a single scalar and keep the parse tree smaller,
// especially in cases of long chains of binary operators. However, we limit
// the effectivness of this during parse to ensure that we eliminate only
// very simple scalars that don't require analysis in later phases. For now,
// that's just simply scalar values.
ExpressionPtr BinaryOpExpression::foldConst(AnalysisResultConstPtr ar) {
  ExpressionPtr optExp;
  Variant v1;
  Variant v2;

  if (!m_exp2->getScalarValue(v2)) {
    if ((ar->getPhase() != AnalysisResult::ParseAllFiles) &&
        m_exp1->isScalar() && m_exp1->getScalarValue(v1)) {
      switch (m_op) {
        case T_IS_IDENTICAL:
        case T_IS_NOT_IDENTICAL:
          if (v1.isNull()) {
            return makeIsNull(ar, getLocation(), m_exp2,
                              m_op == T_IS_NOT_IDENTICAL);
          }
          break;
        case T_LOGICAL_AND:
        case T_BOOLEAN_AND:
        case T_LOGICAL_OR:
        case T_BOOLEAN_OR: {
          ExpressionPtr rep =
            v1.toBoolean() == (m_op == T_LOGICAL_AND ||
                               m_op == T_BOOLEAN_AND) ? m_exp2 : m_exp1;
          rep = ExpressionPtr(
              new UnaryOpExpression(
                getScope(), getLocation(),
                rep, T_BOOL_CAST, true));
          rep->setActualType(Type::Boolean);
          return replaceValue(rep);
        }
        case '+':
        case '.':
        case '*':
        case '&':
        case '|':
        case '^':
          if (m_exp2->is(KindOfBinaryOpExpression)) {
            BinaryOpExpressionPtr binOpExp =
              dynamic_pointer_cast<BinaryOpExpression>(m_exp2);
            if (binOpExp->m_op == m_op && binOpExp->m_exp1->isScalar()) {
              ExpressionPtr aExp = m_exp1;
              ExpressionPtr bExp = binOpExp->m_exp1;
              ExpressionPtr cExp = binOpExp->m_exp2;
              m_exp1 = binOpExp = Clone(binOpExp);
              m_exp2 = cExp;
              binOpExp->m_exp1 = aExp;
              binOpExp->m_exp2 = bExp;
              if (ExpressionPtr optExp = binOpExp->foldConst(ar)) {
                m_exp1 = optExp;
              }
              return static_pointer_cast<Expression>(shared_from_this());
            }
          }
        break;
        default:
          break;
      }
    }

    return ExpressionPtr();
  }

  if (m_exp1->isScalar()) {
    if (!m_exp1->getScalarValue(v1)) return ExpressionPtr();
    try {
      ScalarExpressionPtr scalar1 =
        dynamic_pointer_cast<ScalarExpression>(m_exp1);
      ScalarExpressionPtr scalar2 =
        dynamic_pointer_cast<ScalarExpression>(m_exp2);
      // Some data, like the values of __CLASS__ and friends, are not available
      // while we're still in the initial parse phase.
      if (ar->getPhase() == AnalysisResult::ParseAllFiles) {
        if ((scalar1 && scalar1->needsTranslation()) ||
            (scalar2 && scalar2->needsTranslation())) {
          return ExpressionPtr();
        }
      }
      if (!Option::WholeProgram || !Option::ParseTimeOpts) {
        // In the VM, don't optimize __CLASS__ if within a trait, since
        // __CLASS__ is not resolved yet.
        ClassScopeRawPtr clsScope = getOriginalClass();
        if (clsScope && clsScope->isTrait()) {
          if ((scalar1 && scalar1->getType() == T_CLASS_C) ||
              (scalar2 && scalar2->getType() == T_CLASS_C)) {
            return ExpressionPtr();
          }
        }
      }
      Variant result;
      switch (m_op) {
        case T_LOGICAL_XOR:
          result = logical_xor(v1, v2); break;
        case '|':
          result = bitwise_or(v1, v2); break;
        case '&':
          result = bitwise_and(v1, v2); break;
        case '^':
          result = bitwise_xor(v1, v2); break;
        case '.':
          result = concat(v1, v2); break;
        case T_IS_IDENTICAL:
          result = same(v1, v2); break;
        case T_IS_NOT_IDENTICAL:
          result = !same(v1, v2); break;
        case T_IS_EQUAL:
          result = equal(v1, v2); break;
        case T_IS_NOT_EQUAL:
          result = !equal(v1, v2); break;
        case '<':
          result = less(v1, v2); break;
        case T_IS_SMALLER_OR_EQUAL:
          result = less_or_equal(v1, v2); break;
        case '>':
          result = more(v1, v2); break;
        case T_IS_GREATER_OR_EQUAL:
          result = more_or_equal(v1, v2); break;
        case '+':
          result = plus(v1, v2); break;
        case '-':
          result = minus(v1, v2); break;
        case '*':
          result = multiply(v1, v2); break;
        case '/':
          if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) {
            return ExpressionPtr();
          }
          result = divide(v1, v2); break;
        case '%':
          if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) {
            return ExpressionPtr();
          }
          result = modulo(v1, v2); break;
        case T_SL:
          result = shift_left(v1, v2); break;
        case T_SR:
          result = shift_right(v1, v2); break;
        case T_BOOLEAN_OR:
          result = v1 || v2; break;
        case T_BOOLEAN_AND:
          result = v1 && v2; break;
        case T_LOGICAL_OR:
          result = v1 || v2; break;
        case T_LOGICAL_AND:
          result = v1 && v2; break;
        case T_INSTANCEOF:
          result = false; break;
        default:
          return ExpressionPtr();
      }
      return makeScalarExpression(ar, result);
    } catch (...) {
    }
  } else if (ar->getPhase() != AnalysisResult::ParseAllFiles) {
    switch (m_op) {
      case T_LOGICAL_AND:
      case T_BOOLEAN_AND:
      case T_LOGICAL_OR:
      case T_BOOLEAN_OR: {
        bool useFirst = v2.toBoolean() == (m_op == T_LOGICAL_AND ||
                                           m_op == T_BOOLEAN_AND);
        ExpressionPtr rep = useFirst ? m_exp1 : m_exp2;
        rep = ExpressionPtr(
          new UnaryOpExpression(
            getScope(), getLocation(),
            rep, T_BOOL_CAST, true));
        rep->setActualType(Type::Boolean);
        if (!useFirst) {
          ExpressionListPtr l(
            new ExpressionList(
              getScope(), getLocation(),
              ExpressionList::ListKindComma));
          l->addElement(m_exp1);
          l->addElement(rep);
          l->setActualType(Type::Boolean);
          rep = l;
        }
        rep->setExpectedType(getExpectedType());
        return replaceValue(rep);
      }
      case T_LOGICAL_XOR:
      case '|':
      case '&':
      case '^':
      case '.':
      case '+':
      case '*':
        optExp = foldRightAssoc(ar);
        if (optExp) return optExp;
        break;
      case T_IS_IDENTICAL:
      case T_IS_NOT_IDENTICAL:
        if (v2.isNull()) {
          return makeIsNull(ar, getLocation(), m_exp1,
                            m_op == T_IS_NOT_IDENTICAL);
        }
        break;
      default:
        break;
    }
  }
  return ExpressionPtr();
}
Пример #6
0
int FunctionScope::inferParamTypes(AnalysisResultPtr ar, ConstructPtr exp,
                                   ExpressionListPtr params, bool &valid) {
  if (!params) {
    if (m_minParam > 0) {
      if (ar->isFirstPass()) {
        ar->getCodeError()->record(CodeError::TooFewArgument, exp, m_stmt);
      }
      valid = false;
      setDynamic();
    }
    return 0;
  }

  int ret = 0;
  if (params->getCount() < m_minParam) {
    if (ar->isFirstPass()) {
      ar->getCodeError()->record(CodeError::TooFewArgument, exp, m_stmt);
    }
    valid = false;
    setDynamic();
  }
  if (params->getCount() > m_maxParam) {
    if (isVariableArgument()) {
      ret = params->getCount() - m_maxParam;
    } else {
      if (ar->isFirstPass()) {
        ar->getCodeError()->record(CodeError::TooManyArgument, exp, m_stmt);
      }
      valid = false;
      setDynamic();
    }
  }

  bool canSetParamType = isUserFunction() && !m_overriding;
  for (int i = 0; i < params->getCount(); i++) {
    ExpressionPtr param = (*params)[i];
    if (valid && param->hasContext(Expression::InvokeArgument)) {
      param->clearContext(Expression::InvokeArgument);
      param->clearContext(Expression::RefValue);
      param->clearContext(Expression::NoRefWrapper);
    }
    TypePtr expType;
    if (!canSetParamType && i < m_maxParam) {
      expType = param->inferAndCheck(ar, getParamType(i), false);
    } else {
      expType = param->inferAndCheck(ar, NEW_TYPE(Some), false);
    }
    bool isRefVararg = (i >= m_maxParam && isReferenceVariableArgument());
    if ((i < m_maxParam && isRefParam(i)) || isRefVararg) {
      param->setContext(Expression::LValue);
      param->setContext(Expression::RefValue);
      param->inferAndCheck(ar, Type::Variant, true);
    } else if (!(param->getContext() & Expression::RefParameter)) {
      param->clearContext(Expression::LValue);
      param->clearContext(Expression::RefValue);
      param->clearContext(Expression::InvokeArgument);
      param->clearContext(Expression::NoRefWrapper);
    }
    if (i < m_maxParam) {
      if (m_paramTypeSpecs[i] && ar->isFirstPass()) {
        if (!Type::Inferred(ar, expType, m_paramTypeSpecs[i])) {
          const char *file = m_stmt->getLocation()->file;
          Logger::Error("%s: parameter %d of %s requires %s, called with %s",
                        file, i, m_name.c_str(),
                        m_paramTypeSpecs[i]->toString().c_str(),
                        expType->toString().c_str());
          ar->getCodeError()->record(CodeError::BadArgumentType, m_stmt);
        }
      }
      TypePtr paramType = getParamType(i);
      if (canSetParamType) {
        paramType = setParamType(ar, i, expType);
      }
      if (!Type::IsLegalCast(ar, expType, paramType) &&
          paramType->isNonConvertibleType()) {
        param->inferAndCheck(ar, paramType, true);
      }
      param->setExpectedType(paramType);
    }
    // we do a best-effort check for bad pass-by-reference and do not report
    // error for some vararg case (e.g., array_multisort can have either ref
    // or value for the same vararg).
    if (!isRefVararg || !isMixedVariableArgument()) {
      Expression::checkPassByReference(ar, param);
    }
  }
  return ret;
}
Пример #7
0
int FunctionScope::inferParamTypes(AnalysisResultPtr ar, ConstructPtr exp,
                                   ExpressionListPtr params, bool &valid) {
  if (!params) {
    if (m_minParam > 0) {
      if (exp->getScope()->isFirstPass()) {
        Compiler::Error(Compiler::TooFewArgument, exp, m_stmt);
      }
      valid = false;
      if (!Option::AllDynamic) setDynamic();
    }
    return 0;
  }

  int ret = 0;
  if (params->getCount() < m_minParam) {
    if (exp->getScope()->isFirstPass()) {
      Compiler::Error(Compiler::TooFewArgument, exp, m_stmt);
    }
    valid = false;
    if (!Option::AllDynamic) setDynamic();
  }
  if (params->getCount() > m_maxParam) {
    if (isVariableArgument()) {
      ret = params->getCount() - m_maxParam;
    } else {
      if (exp->getScope()->isFirstPass()) {
        Compiler::Error(Compiler::TooManyArgument, exp, m_stmt);
      }
      valid = false;
      if (!Option::AllDynamic) setDynamic();
    }
  }

  bool canSetParamType = isUserFunction() && !m_overriding && !m_perfectVirtual;
  for (int i = 0; i < params->getCount(); i++) {
    ExpressionPtr param = (*params)[i];
    if (i < m_maxParam && param->hasContext(Expression::RefParameter)) {
      /**
       * This should be very un-likely, since call time pass by ref is a
       * deprecated, not very widely used (at least in FB codebase) feature.
       */
      TRY_LOCK_THIS();
      Symbol *sym = getVariables()->addSymbol(m_paramNames[i]);
      sym->setLvalParam();
      sym->setCallTimeRef();
    }
    if (valid && param->hasContext(Expression::InvokeArgument)) {
      param->clearContext(Expression::InvokeArgument);
      param->clearContext(Expression::RefValue);
      param->clearContext(Expression::NoRefWrapper);
    }
    bool isRefVararg = (i >= m_maxParam && isReferenceVariableArgument());
    if ((i < m_maxParam && isRefParam(i)) || isRefVararg) {
      param->setContext(Expression::LValue);
      param->setContext(Expression::RefValue);
      param->inferAndCheck(ar, Type::Variant, true);
    } else if (!(param->getContext() & Expression::RefParameter)) {
      param->clearContext(Expression::LValue);
      param->clearContext(Expression::RefValue);
      param->clearContext(Expression::InvokeArgument);
      param->clearContext(Expression::NoRefWrapper);
    }
    TypePtr expType;
    /**
     * Duplicate the logic of getParamType(i), w/o the mutation
     */
    TypePtr paramType(i < m_maxParam && !isZendParamMode() ?
                      m_paramTypes[i] : TypePtr());
    if (!paramType) paramType = Type::Some;
    if (valid && !canSetParamType && i < m_maxParam &&
        (!Option::HardTypeHints || !m_paramTypeSpecs[i])) {
      /**
       * What is this magic, you might ask?
       *
       * Here, we take advantage of implicit conversion from every type to
       * Variant. Essentially, we don't really care what type comes out of this
       * expression since it'll just get converted anyways. Doing it this way
       * allows us to generate less temporaries along the way.
       */
      TypePtr optParamType(paramType->is(Type::KindOfVariant) ?
                           Type::Some : paramType);
      expType = param->inferAndCheck(ar, optParamType, false);
    } else {
      expType = param->inferAndCheck(ar, Type::Some, false);
    }
    if (i < m_maxParam) {
      if (!Option::HardTypeHints || !m_paramTypeSpecs[i]) {
        if (canSetParamType) {
          if (!Type::SameType(paramType, expType) &&
              !paramType->is(Type::KindOfVariant)) {
            TRY_LOCK_THIS();
            paramType = setParamType(ar, i, expType);
          } else {
            // do nothing - how is this safe?  well, if we ever observe
            // paramType == expType, then this means at some point in the past,
            // somebody called setParamType() with expType.  thus, by calling
            // setParamType() again with expType, we contribute no "new"
            // information. this argument also still applies in the face of
            // concurrency
          }
        }
        // See note above. If we have an implemented type, however, we
        // should set the paramType to the implemented type to avoid an
        // un-necessary cast
        if (paramType->is(Type::KindOfVariant)) {
          TypePtr it(param->getImplementedType());
          paramType = it ? it : expType;
        }
        if (valid) {
          if (!Type::IsLegalCast(ar, expType, paramType) &&
              paramType->isNonConvertibleType()) {
            param->inferAndCheck(ar, paramType, true);
          }
          param->setExpectedType(paramType);
        }
      }
    }
    // we do a best-effort check for bad pass-by-reference and do not report
    // error for some vararg case (e.g., array_multisort can have either ref
    // or value for the same vararg).
    if (!isRefVararg || !isMixedVariableArgument()) {
      Expression::CheckPassByReference(ar, param);
    }
  }
  return ret;
}
Пример #8
0
ExpressionPtr BinaryOpExpression::foldConst(AnalysisResultConstPtr ar) {
    ExpressionPtr optExp;
    Variant v1;
    Variant v2;

    if (!m_exp2->getScalarValue(v2)) {
        if (m_exp1->isScalar() && m_exp1->getScalarValue(v1)) {
            switch (m_op) {
            case T_IS_IDENTICAL:
            case T_IS_NOT_IDENTICAL:
                if (v1.isNull()) {
                    return makeIsNull(ar, getLocation(), m_exp2,
                                      m_op == T_IS_NOT_IDENTICAL);
                }
                break;
            case T_LOGICAL_AND:
            case T_BOOLEAN_AND:
            case T_LOGICAL_OR:
            case T_BOOLEAN_OR: {
                ExpressionPtr rep =
                    v1.toBoolean() == (m_op == T_LOGICAL_AND ||
                                       m_op == T_BOOLEAN_AND) ? m_exp2 : m_exp1;
                rep = ExpressionPtr(
                          new UnaryOpExpression(
                              getScope(), getLocation(),
                              rep, T_BOOL_CAST, true));
                rep->setActualType(Type::Boolean);
                return replaceValue(rep);
            }
            case '+':
            case '.':
            case '*':
            case '&':
            case '|':
            case '^':
                if (m_exp2->is(KindOfBinaryOpExpression)) {
                    BinaryOpExpressionPtr binOpExp =
                        dynamic_pointer_cast<BinaryOpExpression>(m_exp2);
                    if (binOpExp->m_op == m_op && binOpExp->m_exp1->isScalar()) {
                        ExpressionPtr aExp = m_exp1;
                        ExpressionPtr bExp = binOpExp->m_exp1;
                        ExpressionPtr cExp = binOpExp->m_exp2;
                        m_exp1 = binOpExp = Clone(binOpExp);
                        m_exp2 = cExp;
                        binOpExp->m_exp1 = aExp;
                        binOpExp->m_exp2 = bExp;
                        if (ExpressionPtr optExp = binOpExp->foldConst(ar)) {
                            m_exp1 = optExp;
                        }
                        return static_pointer_cast<Expression>(shared_from_this());
                    }
                }
                break;
            default:
                break;
            }
        }

        return ExpressionPtr();
    }

    if (m_exp1->isScalar()) {
        if (!m_exp1->getScalarValue(v1)) return ExpressionPtr();
        try {
            Variant result;
            switch (m_op) {
            case T_LOGICAL_XOR:
                result = logical_xor(v1, v2);
                break;
            case '|':
                result = bitwise_or(v1, v2);
                break;
            case '&':
                result = bitwise_and(v1, v2);
                break;
            case '^':
                result = bitwise_xor(v1, v2);
                break;
            case '.':
                result = concat(v1, v2);
                break;
            case T_IS_IDENTICAL:
                result = same(v1, v2);
                break;
            case T_IS_NOT_IDENTICAL:
                result = !same(v1, v2);
                break;
            case T_IS_EQUAL:
                result = equal(v1, v2);
                break;
            case T_IS_NOT_EQUAL:
                result = !equal(v1, v2);
                break;
            case '<':
                result = less(v1, v2);
                break;
            case T_IS_SMALLER_OR_EQUAL:
                result = not_more(v1, v2);
                break;
            case '>':
                result = more(v1, v2);
                break;
            case T_IS_GREATER_OR_EQUAL:
                result = not_less(v1, v2);
                break;
            case '+':
                result = plus(v1, v2);
                break;
            case '-':
                result = minus(v1, v2);
                break;
            case '*':
                result = multiply(v1, v2);
                break;
            case '/':
                if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) {
                    return ExpressionPtr();
                }
                result = divide(v1, v2);
                break;
            case '%':
                if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) {
                    return ExpressionPtr();
                }
                result = modulo(v1, v2);
                break;
            case T_SL:
                result = shift_left(v1, v2);
                break;
            case T_SR:
                result = shift_right(v1, v2);
                break;
            case T_BOOLEAN_OR:
                result = v1 || v2;
                break;
            case T_BOOLEAN_AND:
                result = v1 && v2;
                break;
            case T_LOGICAL_OR:
                result = v1 || v2;
                break;
            case T_LOGICAL_AND:
                result = v1 && v2;
                break;
            case T_INSTANCEOF:
                result = false;
                break;
            default:
                return ExpressionPtr();
            }
            return makeScalarExpression(ar, result);
        } catch (...) {
        }
    } else {
        switch (m_op) {
        case T_LOGICAL_AND:
        case T_BOOLEAN_AND:
        case T_LOGICAL_OR:
        case T_BOOLEAN_OR: {
            bool useFirst = v2.toBoolean() == (m_op == T_LOGICAL_AND ||
                                               m_op == T_BOOLEAN_AND);
            ExpressionPtr rep = useFirst ? m_exp1 : m_exp2;
            rep = ExpressionPtr(
                      new UnaryOpExpression(
                          getScope(), getLocation(),
                          rep, T_BOOL_CAST, true));
            rep->setActualType(Type::Boolean);
            if (!useFirst) {
                ExpressionListPtr l(
                    new ExpressionList(
                        getScope(), getLocation(),
                        ExpressionList::ListKindComma));
                l->addElement(m_exp1);
                l->addElement(rep);
                l->setActualType(Type::Boolean);
                rep = l;
            }
            rep->setExpectedType(getExpectedType());
            return replaceValue(rep);
        }
        case T_LOGICAL_XOR:
        case '|':
        case '&':
        case '^':
        case '.':
        case '+':
        case '*':
            optExp = foldRightAssoc(ar);
            if (optExp) return optExp;
            break;
        case T_IS_IDENTICAL:
        case T_IS_NOT_IDENTICAL:
            if (v2.isNull()) {
                return makeIsNull(ar, getLocation(), m_exp1,
                                  m_op == T_IS_NOT_IDENTICAL);
            }
            break;
        default:
            break;
        }
    }
    return ExpressionPtr();
}