void SwitchStatement::inferTypes(AnalysisResultPtr ar) {
  // we optimize the most two common cases of switch statements
  bool allInteger = true;
  bool allString = true;
  if (m_cases && m_cases->getCount()) {
    for (int i = 0; i < m_cases->getCount(); i++) {
      CaseStatementPtr stmt =
        dynamic_pointer_cast<CaseStatement>((*m_cases)[i]);
      if (!stmt->getCondition()) {
        if (m_cases->getCount() == 1) allInteger = allString = false;
      } else {
        if (!stmt->isLiteralInteger()) allInteger = false;
        if (!stmt->isLiteralString()) allString = false;
      }
    }
  }
  if (allInteger && allString) {
    allInteger = allString = false;
  }

  TypePtr ret;
  if (allInteger) {
    ret = m_exp->inferAndCheck(ar, Type::Int64, false);
  } else if (allString) {
    // We're not able to do this, because switch($obj) may work on an object
    // that didn't implement __toString(), throwing an exception, which isn't
    // consistent with PHP.
    //ret = m_exp->inferAndCheck(ar, Type::String, false);
    ret = m_exp->inferAndCheck(ar, NEW_TYPE(Some), false);
  } else {
    ret = m_exp->inferAndCheck(ar, NEW_TYPE(Some), false);
  }
  if (ret->is(Type::KindOfObject) && ret->isSpecificObject()) {
    m_exp->setExpectedType(NEW_TYPE(Object));
  }
  ConstructPtr self = shared_from_this();
  if (m_cases && m_cases->getCount()) {
    bool checking = false;
    vector<int> defaults;
    int defaultCount = 0;
    for (int i = 0; i < m_cases->getCount(); i++) {
      CaseStatementPtr stmt =
        dynamic_pointer_cast<CaseStatement>((*m_cases)[i]);
      stmt->inferAndCheck(ar, NEW_TYPE(Some), false);
      ExpressionPtr cond = stmt->getCondition();
      if (!cond) {
        checking = true;
        defaultCount++;
      } else if (checking && cond && ar->isFirstPass()) {
        defaults.push_back(i);
        ar->getCodeError()->record(self, CodeError::CaseAfterDefault, stmt);
      }
    }
    if (defaultCount > 1 && ar->isFirstPass()) {
      ar->getCodeError()->record(self, CodeError::MoreThanOneDefault, m_cases);
    }
  }
}
void SwitchStatement::inferTypes(AnalysisResultPtr ar) {
  // we optimize the most two common cases of switch statements
  bool allInteger = true;
  bool allString = true;
  if (m_cases && m_cases->getCount()) {
    for (int i = 0; i < m_cases->getCount(); i++) {
      CaseStatementPtr stmt =
        dynamic_pointer_cast<CaseStatement>((*m_cases)[i]);
      if (!stmt->getCondition()) {
        if (m_cases->getCount() == 1) allInteger = allString = false;
      } else {
        if (!stmt->isLiteralInteger()) allInteger = false;
        if (!stmt->isLiteralString()) allString = false;
      }
    }
  }
  if (allInteger && allString) {
    allInteger = allString = false;
  }

  TypePtr ret = m_exp->inferAndCheck(ar, Type::Some, false);
  // these are the cases where toInt64(x) is OK for the switch statement
  if (allInteger && (ret->is(Type::KindOfInt32) || ret->is(Type::KindOfInt64))) {
    m_exp->setExpectedType(Type::Int64);
  }
  if (ret->is(Type::KindOfObject) && ret->isSpecificObject()) {
    m_exp->setExpectedType(Type::Object);
  }
  ConstructPtr self = shared_from_this();
  if (m_cases && m_cases->getCount()) {
    int defaultCount = 0;
    for (int i = 0; i < m_cases->getCount(); i++) {
      CaseStatementPtr stmt =
        dynamic_pointer_cast<CaseStatement>((*m_cases)[i]);
      stmt->inferAndCheck(ar, Type::Some, false);
      ExpressionPtr cond = stmt->getCondition();
      if (!cond) {
        defaultCount++;
      }
    }
    // TODO: this really belongs in analyzeProgram()
    if (defaultCount > 1 && getScope()->isFirstPass()) {
      Compiler::Error(Compiler::MoreThanOneDefault, m_cases);
    }
  }
}
void SwitchStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) {
  int labelId = cg.createNewLocalId(shared_from_this());

  bool staticCases = true;
  if (!m_exp->getType()->isInteger()) {
    staticCases = false;
  } else if (m_cases) {
    bool seenDefault = false;
    set<int64> seenNums;
    for (int i = 0; i < m_cases->getCount(); i++) {
      CaseStatementPtr stmt =
        dynamic_pointer_cast<CaseStatement>((*m_cases)[i]);
      if (stmt->getCondition()) {
        if (!stmt->isLiteralInteger() || seenDefault) {
          staticCases = false;
          break;
        }

        // detecting duplicate case value
        int64 num = stmt->getLiteralInteger();
        if (seenNums.find(num) != seenNums.end()) {
          staticCases = false;
          break;
        }
        seenNums.insert(num);
      } else {
        seenDefault = true;
      }
    }
  }

  labelId |= CodeGenerator::InsideSwitch;
  if (staticCases) labelId |= CodeGenerator::StaticCases;
  cg.pushBreakScope(labelId, false);
  labelId &= ~CodeGenerator::BreakScopeBitMask;
  cg_indentBegin("{\n");

  string var;
  int varId = -1;

  if (m_exp->preOutputCPP(cg, ar, 0)) {
    varId = cg.createNewLocalId(shared_from_this());
    var = string(Option::SwitchPrefix) + lexical_cast<string>(varId);
    m_exp->getType()->outputCPPDecl(cg, ar, getScope());
    cg_printf(" %s;\n", var.c_str());

    m_exp->outputCPPBegin(cg, ar);
    cg_printf("%s = (", var.c_str());
    m_exp->outputCPP(cg, ar);
    cg_printf(");\n");
    m_exp->outputCPPEnd(cg, ar);
  }

  if (staticCases) {
    cg_printf("switch (");
    if (!var.empty()) {
      cg_printf("%s", var.c_str());
    } else {
      m_exp->outputCPP(cg, ar);
    }
    cg_printf(") {\n");
    if (m_cases) m_cases->outputCPP(cg, ar);
    cg_printf("}\n");
  } else {
    if (var.empty()) {
      varId = cg.createNewLocalId(shared_from_this());
      if (m_exp->hasContext(Expression::LValue) &&
          m_exp->is(Expression::KindOfSimpleVariable)) {
        var = getScope()->getVariables()->getVariableName(
          cg, ar, static_pointer_cast<SimpleVariable>(m_exp)->getName());
      } else {
        var = string(Option::SwitchPrefix) + lexical_cast<string>(varId);
        m_exp->getType()->outputCPPDecl(cg, ar, getScope());
        cg_printf(" %s = (", var.c_str());
        m_exp->outputCPP(cg, ar);
        cg_printf(");\n");
      }
    }

    if (m_cases && m_cases->getCount()) {
      CaseStatementPtr defaultCase;
      int defaultCaseNum = -1;
      for (int i = 0; i < m_cases->getCount(); i++) {
        CaseStatementPtr stmt =
          dynamic_pointer_cast<CaseStatement>((*m_cases)[i]);
        if (stmt->getCondition()) {
          stmt->outputCPPAsIf(cg, ar, varId, var.c_str(), i);
        } else {
          defaultCase = stmt;
          defaultCaseNum = i;
        }
      }
      if (defaultCaseNum != -1) {
        defaultCase->outputCPPAsIf(cg, ar, varId, var.c_str(), defaultCaseNum);
      } else {
        cg_printf("goto break%d;\n", labelId);
        cg.addLabelId("break", labelId);
      }
      cg_printf("\n");
      for (int i = 0; i < m_cases->getCount(); i++) {
        CaseStatementPtr stmt =
          dynamic_pointer_cast<CaseStatement>((*m_cases)[i]);
        stmt->outputCPPByNumber(cg, ar, varId,
                                !stmt->getCondition() && defaultCaseNum != i ?
                                -1 : i);
      }
    }
  }

  // Even though switch's break/continue will never goto these labels, we need
  // them for "break/continue n" inside switches.
  if (cg.findLabelId("continue", labelId)) {
    cg_printf("continue%d:;\n", labelId);
  }
  if (cg.findLabelId("break", labelId)) {
    cg_printf("break%d:;\n", labelId);
  }
  cg_indentEnd("}\n");
  cg.popBreakScope();
}