void GlobalStatement::inferTypes(AnalysisResultPtr ar) {
  IMPLEMENT_INFER_AND_CHECK_ASSERT(getScope());

  BlockScopePtr scope = getScope();
  for (int i = 0; i < m_exp->getCount(); i++) {
    ExpressionPtr exp = (*m_exp)[i];
    VariableTablePtr variables = scope->getVariables();
    variables->setAttribute(VariableTable::NeedGlobalPointer);
    if (exp->is(Expression::KindOfSimpleVariable)) {
      SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(exp);
      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->setAttribute(VariableTable::InsideGlobalStatement);
      variables->checkRedeclared(name, KindOfGlobalStatement);
      variables->addLocalGlobal(name);
      var->setContext(Expression::Declaration);
      var->inferAndCheck(ar, Type::Any, true);
      variables->forceVariant(ar, name, VariableTable::AnyVars);
      variables->clearAttribute(VariableTable::InsideGlobalStatement);
    } else {
      variables->forceVariants(ar, VariableTable::AnyVars);
      variables->setAttribute(VariableTable::ContainsLDynamicVariable);
      assert(exp->is(Expression::KindOfDynamicVariable));
      exp->inferAndCheck(ar, Type::Any, true);
    }
  }
}
void IncludeExpression::analyzeProgram(AnalysisResultPtr ar) {
  if (!m_include.empty()) {
    if (ar->getPhase() == AnalysisResult::AnalyzeInclude) {
      ar->parseOnDemand(m_include);
    } else if (ar->getPhase() == AnalysisResult::AnalyzeAll) {
      analyzeInclude(ar, m_include);
    }
  }
  if (!getScope()->inPseudoMain() && !m_privateScope) {
    VariableTablePtr var = getScope()->getVariables();
    var->setAttribute(VariableTable::ContainsLDynamicVariable);
    var->forceVariants(ar, VariableTable::AnyVars);
  }

  UnaryOpExpression::analyzeProgram(ar);
}
void IncludeExpression::analyzeProgram(AnalysisResultPtr ar) {
  if (!m_include.empty()) {
    if (ar->getPhase() == AnalysisResult::AnalyzeAll ||
        ar->getPhase() == AnalysisResult::AnalyzeFinal) {
      if (analyzeInclude(ar, m_include)) {
        FunctionScopePtr func = getFunctionScope();
        getFileScope()->addIncludeDependency(ar, m_include,
                                             func && func->isInlined());
      }
    }
  }

  VariableTablePtr var = getScope()->getVariables();
  var->setAttribute(VariableTable::ContainsLDynamicVariable);
  var->forceVariants(ar, VariableTable::AnyVars);

  UnaryOpExpression::analyzeProgram(ar);
}
void IncludeExpression::analyzeProgram(AnalysisResultPtr ar) {
  if (ar->isFirstPass()) {
    ConstructPtr self = shared_from_this();
    if (m_op == T_INCLUDE || m_op == T_REQUIRE) {
      ar->getCodeError()->record(self, CodeError::UseInclude, self);
    }
  }

  string include = getCurrentInclude(ar);
  if (!include.empty()) {
    if (ar->getPhase() == AnalysisResult::AnalyzeInclude) {
      analyzeInclude(ar, include);
    }
  }
  if (!ar->getScope()->inPseudoMain()) {
    VariableTablePtr var = ar->getScope()->getVariables();
    var->setAttribute(VariableTable::ContainsLDynamicVariable);
    var->forceVariants(ar);
  }

  UnaryOpExpression::analyzeProgram(ar);
}
/**
 * ArrayElementExpression comes from:
 *
 * reference_variable[|expr]
 * ->object_dim_list[|expr]
 * encaps T_VARIABLE[expr]
 * encaps ${T_STRING[expr]}
 */
TypePtr ArrayElementExpression::inferTypes(AnalysisResultPtr ar,
                                           TypePtr type, bool coerce) {
  ConstructPtr self = shared_from_this();

  if (m_offset &&
      !(m_context & (UnsetContext | ExistContext |
                     InvokeArgument | LValue | RefValue))) {
    setLocalEffect(DiagnosticEffect);
  }
  if (m_context & (AssignmentLHS|OprLValue)) {
    clearLocalEffect(AccessorEffect);
  } else if (m_context & (LValue | RefValue)) {
    setLocalEffect(CreateEffect);
  }

  // handling $GLOBALS[...]
  if (m_variable->is(Expression::KindOfSimpleVariable)) {
    SimpleVariablePtr var =
      dynamic_pointer_cast<SimpleVariable>(m_variable);
    if (var->getName() == "GLOBALS") {
      clearLocalEffect(AccessorEffect);
      m_global = true;
      m_dynamicGlobal = true;
      getScope()->getVariables()->
        setAttribute(VariableTable::NeedGlobalPointer);
      VariableTablePtr vars = ar->getVariables();

      Lock l(ar->getMutex());
      if (m_offset && m_offset->is(Expression::KindOfScalarExpression)) {
        ScalarExpressionPtr offset =
          dynamic_pointer_cast<ScalarExpression>(m_offset);

        if (offset->isLiteralString()) {
          m_globalName = offset->getIdentifier();
          if (!m_globalName.empty()) {
            m_dynamicGlobal = false;
            clearLocalEffect(DiagnosticEffect);
            getScope()->getVariables()->
              setAttribute(VariableTable::NeedGlobalPointer);
            TypePtr ret;
            if (coerce) {
              ret = vars->add(m_globalName, type, true, ar, self,
                              ModifierExpressionPtr());
            } else {
              ret = vars->checkVariable(m_globalName, type, coerce, ar, self);
            }
            getScope()->getVariables()->addSuperGlobal(m_globalName);
            return ret;
          }
        }
      } else {
        vars->setAttribute(VariableTable::ContainsDynamicVariable);
      }

      if (hasContext(LValue) || hasContext(RefValue)) {
        vars->forceVariants(ar, VariableTable::AnyVars);
        vars->setAttribute(VariableTable::ContainsLDynamicVariable);
      }
      if (m_offset) {
        m_offset->inferAndCheck(ar, Type::Primitive, false);
      }
      return m_implementedType = Type::Variant; // so not to lose values
    }
  }
  if ((hasContext(LValue) || hasContext(RefValue)) &&
      !hasContext(UnsetContext)) {
    m_variable->setContext(LValue);
  }

  TypePtr varType;
  if (m_offset) {
    varType = m_variable->inferAndCheck(ar, coerce ? Type::AutoSequence :
                                        Type::Sequence, coerce);
    m_offset->inferAndCheck(ar, Type::Some, false);
  } else {
    if (hasContext(ExistContext) || hasContext(UnsetContext)) {
      if (getScope()->isFirstPass()) {
        Compiler::Error(Compiler::InvalidArrayElement, self);
      }
    }
    m_variable->inferAndCheck(ar, Type::Array, true);
  }

  if (varType && Type::SameType(varType, Type::String)) {
    m_implementedType.reset();
    return Type::String;
  }

  TypePtr ret = propagateTypes(ar, Type::Variant);
  m_implementedType = Type::Variant;
  return ret; // so not to lose values
}