void GlobalStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) {
  BlockScopePtr scope = getScope();
  if (m_exp->getCount() > 1) cg_indentBegin("{\n");
  for (int i = 0; i < m_exp->getCount(); i++) {
    ExpressionPtr exp = (*m_exp)[i];
    if (exp->is(Expression::KindOfSimpleVariable)) {
      SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(exp);
      const string &name = var->getName();
      VariableTablePtr variables = scope->getVariables();
      if (variables->needLocalCopy(name)) {
        cg_printf("%s%s = ref(g->%s);\n",
                  Option::VariablePrefix, name.c_str(),
                  variables->getGlobalVariableName(cg, ar, name).c_str());
      }
    } else if (exp->is(Expression::KindOfDynamicVariable)) {
      DynamicVariablePtr var = dynamic_pointer_cast<DynamicVariable>(exp);
      ExpressionPtr exp = var->getSubExpression();
      exp->outputCPPBegin(cg, ar);
      int id = cg.createNewLocalId(shared_from_this());
      cg_printf("CStrRef dgv_%d((", id);
      exp->outputCPP(cg, ar);
      cg_printf("));\n");
      cg_printf("variables->get(dgv_%d) = ref(g->get(dgv_%d));\n", id, id);
      exp->outputCPPEnd(cg, ar);
    } else {
      assert(false);
    }
  }
  if (m_exp->getCount() > 1) cg_indentEnd("}\n");
}
void GlobalStatement::outputCPP(CodeGenerator &cg, AnalysisResultPtr ar) {
  if (m_dynamicGlobal) {
    cg.printf("throw_fatal(\"dynamic global\");\n");
  } else if (!ar->getScope()->inPseudoMain() || !isTopLevel()) {
    BlockScopePtr scope = ar->getScope();
    if (m_exp->getCount() > 1) cg.indentBegin("{\n");
    for (int i = 0; i < m_exp->getCount(); i++) {
      ExpressionPtr exp = (*m_exp)[i];
      if (exp->is(Expression::KindOfSimpleVariable)) {
        SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(exp);
        const string &name = var->getName();
        VariableTablePtr variables = scope->getVariables();
        if (variables->needLocalCopy(name)) {
          cg.printf("%s%s = ref(g->%s);\n",
                    Option::VariablePrefix, name.c_str(),
                    variables->getGlobalVariableName(ar, name).c_str());
        }
      }
      else {
        // type inference should have set m_dynamicGlobal to true.
        ASSERT(false);
      }
    }
    if (m_exp->getCount() > 1) cg.indentEnd("}\n");
  }
}
void StaticStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) {
  BlockScopePtr scope = getScope();
  if (scope->inPseudoMain()) {
    if (m_exp->getCount() > 1) cg_indentBegin("{\n");
    for (int i = 0; i < m_exp->getCount(); i++) {
      ExpressionPtr exp = (*m_exp)[i];
      if (exp->is(Expression::KindOfAssignmentExpression)) {
        exp->outputCPP(cg, ar);
        cg_printf(";\n");
      } else {
        ASSERT(false);
      }
    }
    if (m_exp->getCount() > 1) cg_indentEnd("}\n");
    return;
  }

  VariableTablePtr variables = scope->getVariables();
  if (m_exp->getCount() > 1) cg_indentBegin("{\n");
  for (int i = 0; i < m_exp->getCount(); i++) {
    ExpressionPtr exp = (*m_exp)[i];
    if (exp->is(Expression::KindOfAssignmentExpression)) {
      AssignmentExpressionPtr assignment_exp =
        dynamic_pointer_cast<AssignmentExpression>(exp);
      ExpressionPtr variable = assignment_exp->getVariable();
      SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(variable);
      assert(var->hasContext(Expression::Declaration));
      const std::string &name = var->getName();

      if (variables->needLocalCopy(name)) {
        ASSERT(var->hasAssignableCPPVariable());
        cg_printf("%s.assignRef(%s%s);\n",
                  var->getAssignableCPPVariable(ar).c_str(),
                  Option::StaticVariablePrefix, name.c_str());
      }

      cg_indentBegin("if (!%s%s%s) {\n", Option::InitPrefix,
                     Option::StaticVariablePrefix, name.c_str());
      exp->outputCPP(cg, ar);
      cg_printf(";\n");
      cg_printf("%s%s%s = true;\n", Option::InitPrefix,
                Option::StaticVariablePrefix, name.c_str());
      cg_indentEnd("}\n");
    } else {
      ASSERT(false);
    }
  }
  if (m_exp->getCount() > 1) cg_indentEnd("}\n");
}
void GlobalStatement::inferTypes(AnalysisResultPtr ar) {
  BlockScopePtr scope = ar->getScope();
  scope->getVariables()->setAttribute(VariableTable::InsideGlobalStatement);
  for (int i = 0; i < m_exp->getCount(); i++) {
    ExpressionPtr exp = (*m_exp)[i];
    if (exp->is(Expression::KindOfSimpleVariable)) {
      SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(exp);
      VariableTablePtr variables = scope->getVariables();
      const std::string &name = var->getName();
      /* If we have already seen this variable in the current scope and
         it is not a global variable, record this variable as "redeclared"
         which will force Variant type.
       */
      variables->checkRedeclared(name, KindOfGlobalStatement);

      /* If this is not a top-level global statement, the variable also
         needs to be Variant type. This should not be a common use case in
         php code.
       */
      if (!isTopLevel()) {
        variables->addNestedGlobal(name);
      }
      var->setContext(Expression::Declaration);
      var->inferAndCheck(ar, NEW_TYPE(Any), true);

      if (variables->needLocalCopy(name)) {
        variables->forceVariant(ar, name);
        variables->setAttribute(VariableTable::NeedGlobalPointer);
      }

      ConstructPtr decl =
        ar->getVariables()->getDeclaration(var->getName());
      if (decl) {
        ar->getDependencyGraph()->add(DependencyGraph::KindOfGlobalVariable,
                                      ar->getName(),
                                      var->getName(), var,
                                      var->getName(), decl);
      }
    } else {
      if (ar->isFirstPass()) {
        ar->getCodeError()->record(shared_from_this(), CodeError::UseDynamicGlobal, exp);
      }
      m_dynamicGlobal = true;
    }
  }
  FunctionScopePtr func = ar->getFunctionScope();
  scope->getVariables()->clearAttribute(VariableTable::InsideGlobalStatement);
}
void StaticStatement::inferTypes(AnalysisResultPtr ar) {
  BlockScopePtr scope = ar->getScope();
  if (scope->inPseudoMain()) { // static just means to unset at global level
    for (int i = 0; i < m_exp->getCount(); i++) {
      ExpressionPtr exp = (*m_exp)[i];
      if (exp->is(Expression::KindOfAssignmentExpression)) {
        AssignmentExpressionPtr assignment_exp =
          dynamic_pointer_cast<AssignmentExpression>(exp);
        ExpressionPtr variable = assignment_exp->getVariable();
        if (variable->is(Expression::KindOfSimpleVariable)) {
          SimpleVariablePtr var =
            dynamic_pointer_cast<SimpleVariable>(variable);
          var->setContext(Expression::Declaration);
          scope->getVariables()->forceVariant(ar, var->getName(),
                                              VariableTable::AnyStaticVars);
        } else {
          ASSERT(false);
        }
      } else {
        // Expression was optimized away; remove it
        m_exp->removeElement(i--);
      }
    }
    m_exp->inferTypes(ar, Type::Any, true);
    return;
  }
  scope->getVariables()->setAttribute(VariableTable::InsideStaticStatement);
  for (int i = 0; i < m_exp->getCount(); i++) {
    ExpressionPtr exp = (*m_exp)[i];
    VariableTablePtr variables = scope->getVariables();
    if (exp->is(Expression::KindOfAssignmentExpression)) {
      AssignmentExpressionPtr assignment_exp =
        dynamic_pointer_cast<AssignmentExpression>(exp);
      ExpressionPtr variable = assignment_exp->getVariable();
      if (variable->is(Expression::KindOfSimpleVariable)) {
        SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(variable);
        var->setContext(Expression::Declaration);
        const std::string &name = var->getName();
        /* If we have already seen this variable in the current scope and
           it is not a static variable, record this variable as "redeclared"
           to force Variant type.
         */
        if (ar->isFirstPass()) {
          variables->checkRedeclared(name, KindOfStaticStatement);
        }
        /* If this is not a top-level static statement, the variable also
           needs to be Variant type. This should not be a common use case in
           php code.
         */
        if (!isTopLevel()) {
          variables->addNestedStatic(name);
        }

        if (variables->needLocalCopy(name)) {
          variables->forceVariant(ar, name, VariableTable::AnyStaticVars);
        }
      } else {
        ASSERT(false);
      }
    } else {
      // Expression was optimized away; remove it
      m_exp->removeElement(i--);
      continue;
    }
    exp->inferAndCheck(ar, Type::Any, false);
  }
  scope->getVariables()->clearAttribute(VariableTable::InsideStaticStatement);
}