Implemented validation for loop and indexing limitations specified by GLSL ES spec 1.0 Appendix A Section 4 and 5.
A couple of things to note:
- This CL only validates the "form" of loop and indexing. It does not detect number-of-iterations or out-of-bound access. This will require more involved analysis/heuristics.
- I haved combined SH_VALIDATE_CONTROL_FLOW and SH_VALIDATE_INDEXING into one flag - SH_VALIDATE_LOOP_INDEXING. Validating both together is much easier.
BUG=48
Review URL: http://codereview.appspot.com/3225041

git-svn-id: http://angleproject.googlecode.com/svn/trunk@491 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/include/GLSLANG/ShaderLang.h b/include/GLSLANG/ShaderLang.h
index 24650cf..d0664e4 100644
--- a/include/GLSLANG/ShaderLang.h
+++ b/include/GLSLANG/ShaderLang.h
@@ -17,7 +17,7 @@
 
 // Version number for shader translation API.
 // It is incremented everytime the API changes.
-#define SH_VERSION 102
+#define SH_VERSION 103
 
 //
 // The names of the following enums have been derived by replacing GL prefix
@@ -67,12 +67,11 @@
 
 // Compile options.
 typedef enum {
-  SH_VALIDATE              = 0,
-  SH_VALIDATE_CONTROL_FLOW = 0x0001,
-  SH_VALIDATE_INDEXING     = 0x0002,
-  SH_INTERMEDIATE_TREE     = 0x0004,
-  SH_OBJECT_CODE           = 0x0008,
-  SH_ATTRIBUTES_UNIFORMS   = 0x0010
+  SH_VALIDATE               = 0,
+  SH_VALIDATE_LOOP_INDEXING = 0x0001,
+  SH_INTERMEDIATE_TREE      = 0x0002,
+  SH_OBJECT_CODE            = 0x0004,
+  SH_ATTRIBUTES_UNIFORMS    = 0x0008
 } ShCompileOptions;
 
 //
@@ -146,16 +145,12 @@
 // compileOptions: A mask containing the following parameters:
 // SH_VALIDATE: Validates shader to ensure that it conforms to the spec
 //              specified during compiler construction.
-// SH_VALIDATE_CONTROL_FLOW: Validates control flow in the shader to ensure
-//                           that they do not exceed the minimum functionality
-//                           mandated in GLSL 1.0 spec, Appendix A, Section 4.
-//                           There is no need to specify this parameter when
-//                           compiling for WebGL - it is implied.
-// SH_VALIDATE_INDEXING: Validates indexing of arrays, vectors, and matrices
-//                       in the shader to ensure that they do not exceed the
-//                       minimum functionality mandated in GLSL 1.0 spec,
-//                       Appendix A, Section 5. There is no need to specify this
-//                       parameter when compiling for WebGL - it is implied.
+// SH_VALIDATE_LOOP_INDEXING: Validates loop and indexing in the shader to
+//                            ensure that they do not exceed the minimum
+//                            functionality mandated in GLSL 1.0 spec,
+//                            Appendix A, Section 4 and 5.
+//                            There is no need to specify this parameter when
+//                            compiling for WebGL - it is implied.
 // SH_INTERMEDIATE_TREE: Writes intermediate tree to info log.
 //                       Can be queried by calling ShGetInfoLog().
 // SH_OBJECT_CODE: Translates intermediate tree to glsl or hlsl shader.
diff --git a/src/build_angle.gyp b/src/build_angle.gyp
index 993e3aa..170d5bc 100644
--- a/src/build_angle.gyp
+++ b/src/build_angle.gyp
@@ -59,6 +59,8 @@
         'compiler/unistd.h',
         'compiler/util.cpp',
         'compiler/util.h',
+        'compiler/ValidateLimitations.cpp',
+        'compiler/ValidateLimitations.h',
         'compiler/VariableInfo.cpp',
         'compiler/VariableInfo.h',
         'compiler/preprocessor/atom.c',
diff --git a/src/compiler/Compiler.cpp b/src/compiler/Compiler.cpp
index 11acfae..f59a097 100644
--- a/src/compiler/Compiler.cpp
+++ b/src/compiler/Compiler.cpp
@@ -7,6 +7,7 @@
 #include "compiler/Initialize.h"
 #include "compiler/ParseHelper.h"
 #include "compiler/ShHandle.h"
+#include "compiler/ValidateLimitations.h"
 
 namespace {
 bool InitializeSymbolTable(
@@ -110,10 +111,9 @@
     if (numStrings == 0)
         return true;
 
-    // If compiling for WebGL, validate control-flow and indexing as well.
-    if (shaderSpec == SH_WEBGL_SPEC) {
-        compileOptions |= SH_VALIDATE_CONTROL_FLOW | SH_VALIDATE_INDEXING;
-    }
+    // If compiling for WebGL, validate loop and indexing as well.
+    if (shaderSpec == SH_WEBGL_SPEC)
+        compileOptions |= SH_VALIDATE_LOOP_INDEXING;
 
     TIntermediate intermediate(infoSink);
     TParseContext parseContext(symbolTable, extensionBehavior, intermediate,
@@ -131,16 +131,20 @@
         (PaParseStrings(numStrings, shaderStrings, NULL, &parseContext) == 0) &&
         (parseContext.treeRoot != NULL);
     if (success) {
-        success = intermediate.postProcess(parseContext.treeRoot);
+        TIntermNode* root = parseContext.treeRoot;
+        success = intermediate.postProcess(root);
+
+        if (success && (compileOptions & SH_VALIDATE_LOOP_INDEXING))
+            success = validateLimitations(root);
 
         if (success && (compileOptions & SH_INTERMEDIATE_TREE))
-            intermediate.outputTree(parseContext.treeRoot);
+            intermediate.outputTree(root);
 
         if (success && (compileOptions & SH_OBJECT_CODE))
-            translate(parseContext.treeRoot);
+            translate(root);
 
         if (success && (compileOptions & SH_ATTRIBUTES_UNIFORMS))
-            collectAttribsUniforms(parseContext.treeRoot);
+            collectAttribsUniforms(root);
     }
 
     // Cleanup memory.
@@ -172,6 +176,12 @@
     uniforms.clear();
 }
 
+bool TCompiler::validateLimitations(TIntermNode* root) {
+    ValidateLimitations validate(shaderType, infoSink.info);
+    root->traverse(&validate);
+    return validate.numErrors() == 0;
+}
+
 void TCompiler::collectAttribsUniforms(TIntermNode* root)
 {
     CollectAttribsUniforms collect(attribs, uniforms);
diff --git a/src/compiler/Intermediate.cpp b/src/compiler/Intermediate.cpp
index 4591cfb..ea71234 100644
--- a/src/compiler/Intermediate.cpp
+++ b/src/compiler/Intermediate.cpp
@@ -22,6 +22,101 @@
     return left > right ? left : right;
 }
 
+const char* getOperatorString(TOperator op) {
+    switch (op) {
+      case EOpInitialize: return "=";
+      case EOpAssign: return "=";
+      case EOpAddAssign: return "+=";
+      case EOpSubAssign: return "-=";
+      case EOpDivAssign: return "/=";
+
+      // Fall-through.
+      case EOpMulAssign: 
+      case EOpVectorTimesMatrixAssign:
+      case EOpVectorTimesScalarAssign:
+      case EOpMatrixTimesScalarAssign:
+      case EOpMatrixTimesMatrixAssign: return "*=";
+
+      // Fall-through.
+      case EOpIndexDirect:
+      case EOpIndexIndirect: return "[]";
+
+      case EOpIndexDirectStruct: return ".";
+      case EOpVectorSwizzle: return ".";
+      case EOpAdd: return "+";
+      case EOpSub: return "-";
+      case EOpMul: return "*";
+      case EOpDiv: return "/";
+      case EOpMod: UNIMPLEMENTED(); break;
+      case EOpEqual: return "==";
+      case EOpNotEqual: return "!=";
+      case EOpLessThan: return "<";
+      case EOpGreaterThan: return ">";
+      case EOpLessThanEqual: return "<=";
+      case EOpGreaterThanEqual: return ">=";
+
+      // Fall-through.
+      case EOpVectorTimesScalar:
+      case EOpVectorTimesMatrix:
+      case EOpMatrixTimesVector:
+      case EOpMatrixTimesScalar:
+      case EOpMatrixTimesMatrix: return "*";
+
+      case EOpLogicalOr: return "||";
+      case EOpLogicalXor: return "^^";
+      case EOpLogicalAnd: return "&&";
+      case EOpNegative: return "-";
+      case EOpVectorLogicalNot: return "not";
+      case EOpLogicalNot: return "!";
+      case EOpPostIncrement: return "++";
+      case EOpPostDecrement: return "--";
+      case EOpPreIncrement: return "++";
+      case EOpPreDecrement: return "--";
+
+      // Fall-through.
+      case EOpConvIntToBool:
+      case EOpConvFloatToBool: return "bool";
+ 
+      // Fall-through.
+      case EOpConvBoolToFloat:
+      case EOpConvIntToFloat: return "float";
+ 
+      // Fall-through.
+      case EOpConvFloatToInt:
+      case EOpConvBoolToInt: return "int";
+
+      case EOpRadians: return "radians";
+      case EOpDegrees: return "degrees";
+      case EOpSin: return "sin";
+      case EOpCos: return "cos";
+      case EOpTan: return "tan";
+      case EOpAsin: return "asin";
+      case EOpAcos: return "acos";
+      case EOpAtan: return "atan";
+      case EOpExp: return "exp";
+      case EOpLog: return "log";
+      case EOpExp2: return "exp2";
+      case EOpLog2: return "log2";
+      case EOpSqrt: return "sqrt";
+      case EOpInverseSqrt: return "inversesqrt";
+      case EOpAbs: return "abs";
+      case EOpSign: return "sign";
+      case EOpFloor: return "floor";
+      case EOpCeil: return "ceil";
+      case EOpFract: return "fract";
+      case EOpLength: return "length";
+      case EOpNormalize: return "normalize";
+      case EOpDFdx: return "dFdx";
+      case EOpDFdy: return "dFdy";
+      case EOpFwidth: return "fwidth";
+      case EOpAny: return "any";
+      case EOpAll: return "all";
+
+      default: break;
+    }
+    return "";
+}
+
 ////////////////////////////////////////////////////////////////////////////
 //
 // First set of functions are to help build the intermediate representation.
diff --git a/src/compiler/ShHandle.h b/src/compiler/ShHandle.h
index 4e2affe..d134ea7 100644
--- a/src/compiler/ShHandle.h
+++ b/src/compiler/ShHandle.h
@@ -65,6 +65,9 @@
     bool InitBuiltInSymbolTable(const ShBuiltInResources& resources);
     // Clears the results from the previous compilation.
     void clearResults();
+    // Returns true if the given shader does not exceed the minimum
+    // functionality mandated in GLSL 1.0 spec Appendix A.
+    bool validateLimitations(TIntermNode* root);
     // Collect info for all attribs and uniforms.
     void collectAttribsUniforms(TIntermNode* root);
     // Translate to object code.
diff --git a/src/compiler/ValidateLimitations.cpp b/src/compiler/ValidateLimitations.cpp
new file mode 100644
index 0000000..886f693
--- /dev/null
+++ b/src/compiler/ValidateLimitations.cpp
@@ -0,0 +1,468 @@
+//
+// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/ValidateLimitations.h"
+#include "compiler/InfoSink.h"
+#include "compiler/ParseHelper.h"
+
+namespace {
+bool IsLoopIndex(const TIntermSymbol* symbol, const TLoopStack& stack) {
+    for (TLoopStack::const_iterator i = stack.begin(); i != stack.end(); ++i) {
+        if (i->index.id == symbol->getId())
+            return true;
+    }
+    return false;
+}
+
+// Traverses a node to check if it represents a constant index expression.
+// Definition:
+// constant-index-expressions are a superset of constant-expressions.
+// Constant-index-expressions can include loop indices as defined in
+// GLSL ES 1.0 spec, Appendix A, section 4.
+// The following are constant-index-expressions:
+// - Constant expressions
+// - Loop indices as defined in section 4
+// - Expressions composed of both of the above
+class ValidateConstIndexExpr : public TIntermTraverser {
+public:
+    ValidateConstIndexExpr(const TLoopStack& stack)
+        : mValid(true), mLoopStack(stack) {}
+
+    // Returns true if the parsed node represents a constant index expression.
+    bool isValid() const { return mValid; }
+
+    virtual void visitSymbol(TIntermSymbol* symbol) {
+        // Only constants and loop indices are allowed in a
+        // constant index expression.
+        if (mValid) {
+            mValid = (symbol->getQualifier() == EvqConst) ||
+                     IsLoopIndex(symbol, mLoopStack);
+        }
+    }
+    virtual void visitConstantUnion(TIntermConstantUnion*) {}
+    virtual bool visitBinary(Visit, TIntermBinary*) { return true; }
+    virtual bool visitUnary(Visit, TIntermUnary*) { return true; }
+    virtual bool visitSelection(Visit, TIntermSelection*) { return true; }
+    virtual bool visitAggregate(Visit, TIntermAggregate*) { return true; }
+    virtual bool visitLoop(Visit, TIntermLoop*) { return true; }
+    virtual bool visitBranch(Visit, TIntermBranch*) { return true; }
+
+private:
+    bool mValid;
+    const TLoopStack& mLoopStack;
+};
+}  // namespace
+
+ValidateLimitations::ValidateLimitations(ShShaderType shaderType,
+                                         TInfoSinkBase& sink)
+    : mShaderType(shaderType),
+      mSink(sink),
+      mNumErrors(0)
+{
+}
+
+void ValidateLimitations::visitSymbol(TIntermSymbol*)
+{
+}
+
+void ValidateLimitations::visitConstantUnion(TIntermConstantUnion*)
+{
+}
+
+bool ValidateLimitations::visitBinary(Visit, TIntermBinary* node)
+{
+    // Check if loop index is modified in the loop body.
+    validateOperation(node, node->getLeft());
+
+    // Check indexing.
+    switch (node->getOp()) {
+      case EOpIndexDirect:
+      case EOpIndexIndirect:
+        validateIndexing(node);
+        break;
+      default: break;
+    }
+    return true;
+}
+
+bool ValidateLimitations::visitUnary(Visit, TIntermUnary* node)
+{
+    // Check if loop index is modified in the loop body.
+    validateOperation(node, node->getOperand());
+
+    return true;
+}
+
+bool ValidateLimitations::visitSelection(Visit, TIntermSelection*)
+{
+    return true;
+}
+
+bool ValidateLimitations::visitAggregate(Visit, TIntermAggregate* node)
+{
+    switch (node->getOp()) {
+      case EOpFunctionCall:
+        validateFunctionCall(node);
+        break;
+      default:
+        break;
+    }
+    return true;
+}
+
+bool ValidateLimitations::visitLoop(Visit, TIntermLoop* node)
+{
+    if (!validateLoopType(node))
+        return false;
+
+    TLoopInfo info;
+    memset(&info, 0, sizeof(TLoopInfo));
+    if (!validateForLoopHeader(node, &info))
+        return false;
+
+    TIntermNode* body = node->getBody();
+    if (body != NULL) {
+        mLoopStack.push_back(info);
+        body->traverse(this);
+        mLoopStack.pop_back();
+    }
+
+    // The loop is fully processed - no need to visit children.
+    return false;
+}
+
+bool ValidateLimitations::visitBranch(Visit, TIntermBranch*)
+{
+    return true;
+}
+
+void ValidateLimitations::error(TSourceLoc loc,
+                                const char *reason, const char* token)
+{
+    mSink.prefix(EPrefixError);
+    mSink.location(loc);
+    mSink << "'" << token << "' : " << reason << "\n";
+    ++mNumErrors;
+}
+
+bool ValidateLimitations::withinLoopBody() const
+{
+    return !mLoopStack.empty();
+}
+
+bool ValidateLimitations::isLoopIndex(const TIntermSymbol* symbol) const
+{
+    return IsLoopIndex(symbol, mLoopStack);
+}
+
+bool ValidateLimitations::validateLoopType(TIntermLoop* node) {
+    TLoopType type = node->getType();
+    if (type == ELoopFor)
+        return true;
+
+    // Reject while and do-while loops.
+    error(node->getLine(),
+          "This type of loop is not allowed",
+          type == ELoopWhile ? "while" : "do");
+    return false;
+}
+
+bool ValidateLimitations::validateForLoopHeader(TIntermLoop* node,
+                                                TLoopInfo* info)
+{
+    ASSERT(node->getType() == ELoopFor);
+
+    //
+    // The for statement has the form:
+    //    for ( init-declaration ; condition ; expression ) statement
+    //
+    if (!validateForLoopInit(node, info))
+        return false;
+    if (!validateForLoopCond(node, info))
+        return false;
+    if (!validateForLoopExpr(node, info))
+        return false;
+
+    return true;
+}
+
+bool ValidateLimitations::validateForLoopInit(TIntermLoop* node,
+                                              TLoopInfo* info)
+{
+    TIntermNode* init = node->getInit();
+    if (init == NULL) {
+        error(node->getLine(), "Missing init declaration", "for");
+        return false;
+    }
+
+    //
+    // init-declaration has the form:
+    //     type-specifier identifier = constant-expression
+    //
+    TIntermAggregate* decl = init->getAsAggregate();
+    if ((decl == NULL) || (decl->getOp() != EOpDeclaration)) {
+        error(init->getLine(), "Invalid init declaration", "for");
+        return false;
+    }
+    // To keep things simple do not allow declaration list.
+    TIntermSequence& declSeq = decl->getSequence();
+    if (declSeq.size() != 1) {
+        error(decl->getLine(), "Invalid init declaration", "for");
+        return false;
+    }
+    TIntermBinary* declInit = declSeq[0]->getAsBinaryNode();
+    if ((declInit == NULL) || (declInit->getOp() != EOpInitialize)) {
+        error(decl->getLine(), "Invalid init declaration", "for");
+        return false;
+    }
+    TIntermSymbol* symbol = declInit->getLeft()->getAsSymbolNode();
+    if (symbol == NULL) {
+        error(declInit->getLine(), "Invalid init declaration", "for");
+        return false;
+    }
+    // The loop index has type int or float.
+    TBasicType type = symbol->getBasicType();
+    if ((type != EbtInt) && (type != EbtFloat)) {
+        error(symbol->getLine(),
+              "Invalid type for loop index", getBasicString(type));
+        return false;
+    }
+    // The loop index is initialized with constant expression.
+    if (!isConstExpr(declInit->getRight())) {
+        error(declInit->getLine(),
+              "Loop index cannot be initialized with non-constant expression",
+              symbol->getSymbol().c_str());
+        return false;
+    }
+
+    info->index.id = symbol->getId();
+    return true;
+}
+
+bool ValidateLimitations::validateForLoopCond(TIntermLoop* node,
+                                              TLoopInfo* info)
+{
+    TIntermNode* cond = node->getCondition();
+    if (cond == NULL) {
+        error(node->getLine(), "Missing condition", "for");
+        return false;
+    }
+    //
+    // condition has the form:
+    //     loop_index relational_operator constant_expression
+    //
+    TIntermBinary* binOp = cond->getAsBinaryNode();
+    if (binOp == NULL) {
+        error(node->getLine(), "Invalid condition", "for");
+        return false;
+    }
+    // Loop index should be to the left of relational operator.
+    TIntermSymbol* symbol = binOp->getLeft()->getAsSymbolNode();
+    if (symbol == NULL) {
+        error(binOp->getLine(), "Invalid condition", "for");
+        return false;
+    }
+    if (symbol->getId() != info->index.id) {
+        error(symbol->getLine(),
+              "Expected loop index", symbol->getSymbol().c_str());
+        return false;
+    }
+    // Relational operator is one of: > >= < <= == or !=.
+    switch (binOp->getOp()) {
+      case EOpEqual:
+      case EOpNotEqual:
+      case EOpLessThan:
+      case EOpGreaterThan:
+      case EOpLessThanEqual:
+      case EOpGreaterThanEqual:
+        break;
+      default:
+        error(binOp->getLine(),
+              "Invalid relational operator",
+              getOperatorString(binOp->getOp()));
+        break;
+    }
+    // Loop index must be compared with a constant.
+    if (!isConstExpr(binOp->getRight())) {
+        error(binOp->getLine(),
+              "Loop index cannot be compared with non-constant expression",
+              symbol->getSymbol().c_str());
+        return false;
+    }
+
+    return true;
+}
+
+bool ValidateLimitations::validateForLoopExpr(TIntermLoop* node,
+                                              TLoopInfo* info)
+{
+    TIntermNode* expr = node->getExpression();
+    if (expr == NULL) {
+        error(node->getLine(), "Missing expression", "for");
+        return false;
+    }
+
+    // for expression has one of the following forms:
+    //     loop_index++
+    //     loop_index--
+    //     loop_index += constant_expression
+    //     loop_index -= constant_expression
+    //     ++loop_index
+    //     --loop_index
+    // The last two forms are not specified in the spec, but I am assuming
+    // its an oversight.
+    TIntermUnary* unOp = expr->getAsUnaryNode();
+    TIntermBinary* binOp = unOp ? NULL : expr->getAsBinaryNode();
+
+    TOperator op = EOpNull;
+    TIntermSymbol* symbol = NULL;
+    if (unOp != NULL) {
+        op = unOp->getOp();
+        symbol = unOp->getOperand()->getAsSymbolNode();
+    } else if (binOp != NULL) {
+        op = binOp->getOp();
+        symbol = binOp->getLeft()->getAsSymbolNode();
+    }
+
+    // The operand must be loop index.
+    if (symbol == NULL) {
+        error(expr->getLine(), "Invalid expression", "for");
+        return false;
+    }
+    if (symbol->getId() != info->index.id) {
+        error(symbol->getLine(),
+              "Expected loop index", symbol->getSymbol().c_str());
+        return false;
+    }
+
+    // The operator is one of: ++ -- += -=.
+    switch (op) {
+        case EOpPostIncrement:
+        case EOpPostDecrement:
+        case EOpPreIncrement:
+        case EOpPreDecrement:
+            ASSERT((unOp != NULL) && (binOp == NULL));
+            break;
+        case EOpAddAssign:
+        case EOpSubAssign:
+            ASSERT((unOp == NULL) && (binOp != NULL));
+            break;
+        default:
+            error(expr->getLine(), "Invalid operator", getOperatorString(op));
+            return false;
+    }
+
+    // Loop index must be incremented/decremented with a constant.
+    if (binOp != NULL) {
+        if (!isConstExpr(binOp->getRight())) {
+            error(binOp->getLine(),
+                  "Loop index cannot be modified by non-constant expression",
+                  symbol->getSymbol().c_str());
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool ValidateLimitations::validateFunctionCall(TIntermAggregate* node)
+{
+    ASSERT(node->getOp() == EOpFunctionCall);
+
+    // If not within loop body, there is nothing to check.
+    if (!withinLoopBody())
+        return true;
+
+    // List of param indices for which loop indices are used as argument.
+    typedef std::vector<int> ParamIndex;
+    ParamIndex pIndex;
+    TIntermSequence& params = node->getSequence();
+    for (TIntermSequence::size_type i = 0; i < params.size(); ++i) {
+        TIntermSymbol* symbol = params[i]->getAsSymbolNode();
+        if (symbol && isLoopIndex(symbol))
+            pIndex.push_back(i);
+    }
+    // If none of the loop indices are used as arguments,
+    // there is nothing to check.
+    if (pIndex.empty())
+        return true;
+
+    bool valid = true;
+    TSymbolTable& symbolTable = GlobalParseContext->symbolTable;
+    TSymbol* symbol = symbolTable.find(node->getName());
+    ASSERT(symbol && symbol->isFunction());
+    TFunction* function = static_cast<TFunction*>(symbol);
+    for (ParamIndex::const_iterator i = pIndex.begin();
+         i != pIndex.end(); ++i) {
+        const TParameter& param = function->getParam(*i);
+        TQualifier qual = param.type->getQualifier();
+        if ((qual == EvqOut) || (qual == EvqInOut)) {
+            error(params[*i]->getLine(),
+                  "Loop index cannot be used as argument to a function out or inout parameter",
+                  params[*i]->getAsSymbolNode()->getSymbol().c_str());
+            valid = false;
+        }
+    }
+
+    return valid;
+}
+
+bool ValidateLimitations::validateOperation(TIntermOperator* node,
+                                            TIntermNode* operand) {
+    // Check if loop index is modified in the loop body.
+    if (!withinLoopBody() || !node->modifiesState())
+        return true;
+
+    const TIntermSymbol* symbol = operand->getAsSymbolNode();
+    if (symbol && isLoopIndex(symbol)) {
+        error(node->getLine(),
+              "Loop index cannot be statically assigned to within the body of the loop",
+              symbol->getSymbol().c_str());
+    }
+    return true;
+}
+
+bool ValidateLimitations::isConstExpr(TIntermNode* node)
+{
+    ASSERT(node != NULL);
+    return node->getAsConstantUnion() != NULL;
+}
+
+bool ValidateLimitations::isConstIndexExpr(TIntermNode* node)
+{
+    ASSERT(node != NULL);
+
+    ValidateConstIndexExpr validate(mLoopStack);
+    node->traverse(&validate);
+    return validate.isValid();
+}
+
+bool ValidateLimitations::validateIndexing(TIntermBinary* node)
+{
+    ASSERT((node->getOp() == EOpIndexDirect) ||
+           (node->getOp() == EOpIndexIndirect));
+
+    bool valid = true;
+    TIntermTyped* index = node->getRight();
+    // The index expression must have integral type.
+    if (!index->isScalar() || (index->getBasicType() != EbtInt)) {
+        error(index->getLine(),
+              "Index expression must have integral type",
+              index->getCompleteString().c_str());
+        valid = false;
+    }
+    // The index expession must be a constant-index-expression unless
+    // the operand is a uniform in a vertex shader.
+    TIntermTyped* operand = node->getLeft();
+    bool skip = (mShaderType == SH_VERTEX_SHADER) &&
+                (operand->getQualifier() == EvqUniform);
+    if (!skip && !isConstIndexExpr(index)) {
+        error(index->getLine(), "Index expression must be constant", "[]");
+        valid = false;
+    }
+    return valid;
+}
+
diff --git a/src/compiler/ValidateLimitations.h b/src/compiler/ValidateLimitations.h
new file mode 100644
index 0000000..a4f5a28
--- /dev/null
+++ b/src/compiler/ValidateLimitations.h
@@ -0,0 +1,62 @@
+//
+// Copyright (c) 2010 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "GLSLANG/ShaderLang.h"
+#include "compiler/intermediate.h"
+
+class TInfoSinkBase;
+
+struct TLoopInfo {
+    struct TIndex {
+        int id;  // symbol id.
+    } index;
+};
+typedef TVector<TLoopInfo> TLoopStack;
+
+// Traverses intermediate tree to ensure that the shader does not exceed the
+// minimum functionality mandated in GLSL 1.0 spec, Appendix A.
+class ValidateLimitations : public TIntermTraverser {
+public:
+    ValidateLimitations(ShShaderType shaderType, TInfoSinkBase& sink);
+
+    int numErrors() const { return mNumErrors; }
+
+    virtual void visitSymbol(TIntermSymbol*);
+    virtual void visitConstantUnion(TIntermConstantUnion*);
+    virtual bool visitBinary(Visit, TIntermBinary*);
+    virtual bool visitUnary(Visit, TIntermUnary*);
+    virtual bool visitSelection(Visit, TIntermSelection*);
+    virtual bool visitAggregate(Visit, TIntermAggregate*);
+    virtual bool visitLoop(Visit, TIntermLoop*);
+    virtual bool visitBranch(Visit, TIntermBranch*);
+
+private:
+    void error(TSourceLoc loc, const char *reason, const char* token);
+
+    bool withinLoopBody() const;
+    bool isLoopIndex(const TIntermSymbol* symbol) const;
+    bool validateLoopType(TIntermLoop* node);
+    bool validateForLoopHeader(TIntermLoop* node, TLoopInfo* info);
+    bool validateForLoopInit(TIntermLoop* node, TLoopInfo* info);
+    bool validateForLoopCond(TIntermLoop* node, TLoopInfo* info);
+    bool validateForLoopExpr(TIntermLoop* node, TLoopInfo* info);
+    // Returns true if none of the loop indices is used as the argument to
+    // the given function out or inout parameter.
+    bool validateFunctionCall(TIntermAggregate* node);
+    bool validateOperation(TIntermOperator* node, TIntermNode* operand);
+
+    // Returns true if indexing does not exceed the minimum functionality
+    // mandated in GLSL 1.0 spec, Appendix A, Section 5.
+    bool isConstExpr(TIntermNode* node);
+    bool isConstIndexExpr(TIntermNode* node);
+    bool validateIndexing(TIntermBinary* node);
+
+    ShShaderType mShaderType;
+    TInfoSinkBase& mSink;
+    int mNumErrors;
+    TLoopStack mLoopStack;
+};
+
diff --git a/src/compiler/intermediate.h b/src/compiler/intermediate.h
index 1b08d73..f9fa1de 100644
--- a/src/compiler/intermediate.h
+++ b/src/compiler/intermediate.h
@@ -184,6 +184,8 @@
     EOpDivAssign,
 };
 
+extern const char* getOperatorString(TOperator op);
+
 class TIntermTraverser;
 class TIntermAggregate;
 class TIntermBinary;