tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

IntermTraverse.cpp (21320B)


      1 //
      2 // Copyright 2002 The ANGLE Project Authors. All rights reserved.
      3 // Use of this source code is governed by a BSD-style license that can be
      4 // found in the LICENSE file.
      5 //
      6 
      7 #include "compiler/translator/tree_util/IntermTraverse.h"
      8 
      9 #include "compiler/translator/Compiler.h"
     10 #include "compiler/translator/InfoSink.h"
     11 #include "compiler/translator/SymbolTable.h"
     12 #include "compiler/translator/tree_util/IntermNode_util.h"
     13 #include "compiler/translator/util.h"
     14 
     15 namespace sh
     16 {
     17 
     18 // Traverse the intermediate representation tree, and call a node type specific visit function for
     19 // each node. Traversal is done recursively through the node member function traverse(). Nodes with
     20 // children can have their whole subtree skipped if preVisit is turned on and the type specific
     21 // function returns false.
     22 template <typename T>
     23 void TIntermTraverser::traverse(T *node)
     24 {
     25    ScopedNodeInTraversalPath addToPath(this, node);
     26    if (!addToPath.isWithinDepthLimit())
     27        return;
     28 
     29    bool visit = true;
     30 
     31    // Visit the node before children if pre-visiting.
     32    if (preVisit)
     33        visit = node->visit(PreVisit, this);
     34 
     35    if (visit)
     36    {
     37        size_t childIndex = 0;
     38        size_t childCount = node->getChildCount();
     39 
     40        while (childIndex < childCount && visit)
     41        {
     42            mCurrentChildIndex = childIndex;
     43            node->getChildNode(childIndex)->traverse(this);
     44            mCurrentChildIndex = childIndex;
     45 
     46            if (inVisit && childIndex != childCount - 1)
     47            {
     48                visit = node->visit(InVisit, this);
     49            }
     50            ++childIndex;
     51        }
     52 
     53        if (visit && postVisit)
     54            node->visit(PostVisit, this);
     55    }
     56 }
     57 
     58 // Instantiate template for RewriteAtomicFunctionExpressions, in case this gets inlined thus not
     59 // exported from the TU.
     60 template void TIntermTraverser::traverse(TIntermNode *);
     61 
     62 void TIntermNode::traverse(TIntermTraverser *it)
     63 {
     64    it->traverse(this);
     65 }
     66 
     67 void TIntermSymbol::traverse(TIntermTraverser *it)
     68 {
     69    TIntermTraverser::ScopedNodeInTraversalPath addToPath(it, this);
     70    it->visitSymbol(this);
     71 }
     72 
     73 void TIntermConstantUnion::traverse(TIntermTraverser *it)
     74 {
     75    TIntermTraverser::ScopedNodeInTraversalPath addToPath(it, this);
     76    it->visitConstantUnion(this);
     77 }
     78 
     79 void TIntermFunctionPrototype::traverse(TIntermTraverser *it)
     80 {
     81    TIntermTraverser::ScopedNodeInTraversalPath addToPath(it, this);
     82    it->visitFunctionPrototype(this);
     83 }
     84 
     85 void TIntermBinary::traverse(TIntermTraverser *it)
     86 {
     87    it->traverseBinary(this);
     88 }
     89 
     90 void TIntermUnary::traverse(TIntermTraverser *it)
     91 {
     92    it->traverseUnary(this);
     93 }
     94 
     95 void TIntermFunctionDefinition::traverse(TIntermTraverser *it)
     96 {
     97    it->traverseFunctionDefinition(this);
     98 }
     99 
    100 void TIntermBlock::traverse(TIntermTraverser *it)
    101 {
    102    it->traverseBlock(this);
    103 }
    104 
    105 void TIntermAggregate::traverse(TIntermTraverser *it)
    106 {
    107    it->traverseAggregate(this);
    108 }
    109 
    110 void TIntermLoop::traverse(TIntermTraverser *it)
    111 {
    112    it->traverseLoop(this);
    113 }
    114 
    115 void TIntermPreprocessorDirective::traverse(TIntermTraverser *it)
    116 {
    117    it->visitPreprocessorDirective(this);
    118 }
    119 
    120 bool TIntermSymbol::visit(Visit visit, TIntermTraverser *it)
    121 {
    122    it->visitSymbol(this);
    123    return false;
    124 }
    125 
    126 bool TIntermConstantUnion::visit(Visit visit, TIntermTraverser *it)
    127 {
    128    it->visitConstantUnion(this);
    129    return false;
    130 }
    131 
    132 bool TIntermFunctionPrototype::visit(Visit visit, TIntermTraverser *it)
    133 {
    134    it->visitFunctionPrototype(this);
    135    return false;
    136 }
    137 
    138 bool TIntermFunctionDefinition::visit(Visit visit, TIntermTraverser *it)
    139 {
    140    return it->visitFunctionDefinition(visit, this);
    141 }
    142 
    143 bool TIntermUnary::visit(Visit visit, TIntermTraverser *it)
    144 {
    145    return it->visitUnary(visit, this);
    146 }
    147 
    148 bool TIntermSwizzle::visit(Visit visit, TIntermTraverser *it)
    149 {
    150    return it->visitSwizzle(visit, this);
    151 }
    152 
    153 bool TIntermBinary::visit(Visit visit, TIntermTraverser *it)
    154 {
    155    return it->visitBinary(visit, this);
    156 }
    157 
    158 bool TIntermTernary::visit(Visit visit, TIntermTraverser *it)
    159 {
    160    return it->visitTernary(visit, this);
    161 }
    162 
    163 bool TIntermAggregate::visit(Visit visit, TIntermTraverser *it)
    164 {
    165    return it->visitAggregate(visit, this);
    166 }
    167 
    168 bool TIntermDeclaration::visit(Visit visit, TIntermTraverser *it)
    169 {
    170    return it->visitDeclaration(visit, this);
    171 }
    172 
    173 bool TIntermGlobalQualifierDeclaration::visit(Visit visit, TIntermTraverser *it)
    174 {
    175    return it->visitGlobalQualifierDeclaration(visit, this);
    176 }
    177 
    178 bool TIntermBlock::visit(Visit visit, TIntermTraverser *it)
    179 {
    180    return it->visitBlock(visit, this);
    181 }
    182 
    183 bool TIntermIfElse::visit(Visit visit, TIntermTraverser *it)
    184 {
    185    return it->visitIfElse(visit, this);
    186 }
    187 
    188 bool TIntermLoop::visit(Visit visit, TIntermTraverser *it)
    189 {
    190    return it->visitLoop(visit, this);
    191 }
    192 
    193 bool TIntermBranch::visit(Visit visit, TIntermTraverser *it)
    194 {
    195    return it->visitBranch(visit, this);
    196 }
    197 
    198 bool TIntermSwitch::visit(Visit visit, TIntermTraverser *it)
    199 {
    200    return it->visitSwitch(visit, this);
    201 }
    202 
    203 bool TIntermCase::visit(Visit visit, TIntermTraverser *it)
    204 {
    205    return it->visitCase(visit, this);
    206 }
    207 
    208 bool TIntermPreprocessorDirective::visit(Visit visit, TIntermTraverser *it)
    209 {
    210    it->visitPreprocessorDirective(this);
    211    return false;
    212 }
    213 
    214 TIntermTraverser::TIntermTraverser(bool preVisit,
    215                                   bool inVisit,
    216                                   bool postVisit,
    217                                   TSymbolTable *symbolTable)
    218    : preVisit(preVisit),
    219      inVisit(inVisit),
    220      postVisit(postVisit),
    221      mMaxDepth(0),
    222      mMaxAllowedDepth(std::numeric_limits<int>::max()),
    223      mInGlobalScope(true),
    224      mSymbolTable(symbolTable),
    225      mCurrentChildIndex(0)
    226 {
    227    // Only enabling inVisit is not supported.
    228    ASSERT(!(inVisit && !preVisit && !postVisit));
    229 }
    230 
    231 TIntermTraverser::~TIntermTraverser() {}
    232 
    233 void TIntermTraverser::setMaxAllowedDepth(int depth)
    234 {
    235    mMaxAllowedDepth = depth;
    236 }
    237 
    238 const TIntermBlock *TIntermTraverser::getParentBlock() const
    239 {
    240    if (!mParentBlockStack.empty())
    241    {
    242        return mParentBlockStack.back().node;
    243    }
    244    return nullptr;
    245 }
    246 
    247 void TIntermTraverser::pushParentBlock(TIntermBlock *node)
    248 {
    249    mParentBlockStack.push_back(ParentBlock(node, 0));
    250 }
    251 
    252 void TIntermTraverser::incrementParentBlockPos()
    253 {
    254    ++mParentBlockStack.back().pos;
    255 }
    256 
    257 void TIntermTraverser::popParentBlock()
    258 {
    259    ASSERT(!mParentBlockStack.empty());
    260    mParentBlockStack.pop_back();
    261 }
    262 
    263 void TIntermTraverser::insertStatementsInParentBlock(const TIntermSequence &insertions)
    264 {
    265    TIntermSequence emptyInsertionsAfter;
    266    insertStatementsInParentBlock(insertions, emptyInsertionsAfter);
    267 }
    268 
    269 void TIntermTraverser::insertStatementsInParentBlock(const TIntermSequence &insertionsBefore,
    270                                                     const TIntermSequence &insertionsAfter)
    271 {
    272    ASSERT(!mParentBlockStack.empty());
    273    ParentBlock &parentBlock = mParentBlockStack.back();
    274    if (mPath.back() == parentBlock.node)
    275    {
    276        ASSERT(mParentBlockStack.size() >= 2u);
    277        // The current node is a block node, so the parent block is not the topmost one in the block
    278        // stack, but the one below that.
    279        parentBlock = mParentBlockStack.at(mParentBlockStack.size() - 2u);
    280    }
    281    NodeInsertMultipleEntry insert(parentBlock.node, parentBlock.pos, insertionsBefore,
    282                                   insertionsAfter);
    283    mInsertions.push_back(insert);
    284 }
    285 
    286 void TIntermTraverser::insertStatementInParentBlock(TIntermNode *statement)
    287 {
    288    TIntermSequence insertions;
    289    insertions.push_back(statement);
    290    insertStatementsInParentBlock(insertions);
    291 }
    292 
    293 void TIntermTraverser::insertStatementsInBlockAtPosition(TIntermBlock *parent,
    294                                                         size_t position,
    295                                                         const TIntermSequence &insertionsBefore,
    296                                                         const TIntermSequence &insertionsAfter)
    297 {
    298    ASSERT(parent);
    299    ASSERT(position >= 0);
    300    ASSERT(position < parent->getChildCount());
    301 
    302    mInsertions.emplace_back(parent, position, insertionsBefore, insertionsAfter);
    303 }
    304 
    305 void TLValueTrackingTraverser::setInFunctionCallOutParameter(bool inOutParameter)
    306 {
    307    mInFunctionCallOutParameter = inOutParameter;
    308 }
    309 
    310 bool TLValueTrackingTraverser::isInFunctionCallOutParameter() const
    311 {
    312    return mInFunctionCallOutParameter;
    313 }
    314 
    315 void TIntermTraverser::traverseBinary(TIntermBinary *node)
    316 {
    317    traverse(node);
    318 }
    319 
    320 void TLValueTrackingTraverser::traverseBinary(TIntermBinary *node)
    321 {
    322    ScopedNodeInTraversalPath addToPath(this, node);
    323    if (!addToPath.isWithinDepthLimit())
    324        return;
    325 
    326    bool visit = true;
    327 
    328    // visit the node before children if pre-visiting.
    329    if (preVisit)
    330        visit = node->visit(PreVisit, this);
    331 
    332    // Visit the children, in the right order.
    333    if (visit)
    334    {
    335        if (node->isAssignment())
    336        {
    337            ASSERT(!isLValueRequiredHere());
    338            setOperatorRequiresLValue(true);
    339        }
    340 
    341        node->getLeft()->traverse(this);
    342 
    343        if (node->isAssignment())
    344            setOperatorRequiresLValue(false);
    345 
    346        if (inVisit)
    347            visit = node->visit(InVisit, this);
    348 
    349        if (visit)
    350        {
    351            // Some binary operations like indexing can be inside an expression which must be an
    352            // l-value.
    353            bool parentOperatorRequiresLValue     = operatorRequiresLValue();
    354            bool parentInFunctionCallOutParameter = isInFunctionCallOutParameter();
    355 
    356            // Index is not required to be an l-value even when the surrounding expression is
    357            // required to be an l-value.
    358            TOperator op = node->getOp();
    359            if (op == EOpIndexDirect || op == EOpIndexDirectInterfaceBlock ||
    360                op == EOpIndexDirectStruct || op == EOpIndexIndirect)
    361            {
    362                setOperatorRequiresLValue(false);
    363                setInFunctionCallOutParameter(false);
    364            }
    365 
    366            node->getRight()->traverse(this);
    367 
    368            setOperatorRequiresLValue(parentOperatorRequiresLValue);
    369            setInFunctionCallOutParameter(parentInFunctionCallOutParameter);
    370 
    371            // Visit the node after the children, if requested and the traversal
    372            // hasn't been cancelled yet.
    373            if (postVisit)
    374                visit = node->visit(PostVisit, this);
    375        }
    376    }
    377 }
    378 
    379 void TIntermTraverser::traverseUnary(TIntermUnary *node)
    380 {
    381    traverse(node);
    382 }
    383 
    384 void TLValueTrackingTraverser::traverseUnary(TIntermUnary *node)
    385 {
    386    ScopedNodeInTraversalPath addToPath(this, node);
    387    if (!addToPath.isWithinDepthLimit())
    388        return;
    389 
    390    bool visit = true;
    391 
    392    if (preVisit)
    393        visit = node->visit(PreVisit, this);
    394 
    395    if (visit)
    396    {
    397        ASSERT(!operatorRequiresLValue());
    398        switch (node->getOp())
    399        {
    400            case EOpPostIncrement:
    401            case EOpPostDecrement:
    402            case EOpPreIncrement:
    403            case EOpPreDecrement:
    404                setOperatorRequiresLValue(true);
    405                break;
    406            default:
    407                break;
    408        }
    409 
    410        node->getOperand()->traverse(this);
    411 
    412        setOperatorRequiresLValue(false);
    413 
    414        if (postVisit)
    415            visit = node->visit(PostVisit, this);
    416    }
    417 }
    418 
    419 // Traverse a function definition node. This keeps track of global scope.
    420 void TIntermTraverser::traverseFunctionDefinition(TIntermFunctionDefinition *node)
    421 {
    422    ScopedNodeInTraversalPath addToPath(this, node);
    423    if (!addToPath.isWithinDepthLimit())
    424        return;
    425 
    426    bool visit = true;
    427 
    428    if (preVisit)
    429        visit = node->visit(PreVisit, this);
    430 
    431    if (visit)
    432    {
    433        mCurrentChildIndex = 0;
    434        node->getFunctionPrototype()->traverse(this);
    435        mCurrentChildIndex = 0;
    436 
    437        if (inVisit)
    438            visit = node->visit(InVisit, this);
    439        if (visit)
    440        {
    441            mInGlobalScope     = false;
    442            mCurrentChildIndex = 1;
    443            node->getBody()->traverse(this);
    444            mCurrentChildIndex = 1;
    445            mInGlobalScope     = true;
    446            if (postVisit)
    447                visit = node->visit(PostVisit, this);
    448        }
    449    }
    450 }
    451 
    452 // Traverse a block node. This keeps track of the position of traversed child nodes within the block
    453 // so that nodes may be inserted before or after them.
    454 void TIntermTraverser::traverseBlock(TIntermBlock *node)
    455 {
    456    ScopedNodeInTraversalPath addToPath(this, node);
    457    if (!addToPath.isWithinDepthLimit())
    458        return;
    459 
    460    pushParentBlock(node);
    461 
    462    bool visit = true;
    463 
    464    TIntermSequence *sequence = node->getSequence();
    465 
    466    if (preVisit)
    467        visit = node->visit(PreVisit, this);
    468 
    469    if (visit)
    470    {
    471        for (size_t childIndex = 0; childIndex < sequence->size(); ++childIndex)
    472        {
    473            TIntermNode *child = (*sequence)[childIndex];
    474            if (visit)
    475            {
    476                mCurrentChildIndex = childIndex;
    477                child->traverse(this);
    478                mCurrentChildIndex = childIndex;
    479 
    480                if (inVisit)
    481                {
    482                    if (child != sequence->back())
    483                        visit = node->visit(InVisit, this);
    484                }
    485 
    486                incrementParentBlockPos();
    487            }
    488        }
    489 
    490        if (visit && postVisit)
    491            visit = node->visit(PostVisit, this);
    492    }
    493 
    494    popParentBlock();
    495 }
    496 
    497 void TIntermTraverser::traverseAggregate(TIntermAggregate *node)
    498 {
    499    traverse(node);
    500 }
    501 
    502 bool TIntermTraverser::CompareInsertion(const NodeInsertMultipleEntry &a,
    503                                        const NodeInsertMultipleEntry &b)
    504 {
    505    if (a.parent != b.parent)
    506    {
    507        return a.parent < b.parent;
    508    }
    509    return a.position < b.position;
    510 }
    511 
    512 bool TIntermTraverser::updateTree(TCompiler *compiler, TIntermNode *node)
    513 {
    514    // Sort the insertions so that insertion position is increasing and same position insertions are
    515    // not reordered. The insertions are processed in reverse order so that multiple insertions to
    516    // the same parent node are handled correctly.
    517    std::stable_sort(mInsertions.begin(), mInsertions.end(), CompareInsertion);
    518    for (size_t ii = 0; ii < mInsertions.size(); ++ii)
    519    {
    520        // If two insertions are to the same position, insert them in the order they were specified.
    521        // The std::stable_sort call above will automatically guarantee this.
    522        const NodeInsertMultipleEntry &insertion = mInsertions[mInsertions.size() - ii - 1];
    523        ASSERT(insertion.parent);
    524        if (!insertion.insertionsAfter.empty())
    525        {
    526            bool inserted = insertion.parent->insertChildNodes(insertion.position + 1,
    527                                                               insertion.insertionsAfter);
    528            ASSERT(inserted);
    529        }
    530        if (!insertion.insertionsBefore.empty())
    531        {
    532            bool inserted =
    533                insertion.parent->insertChildNodes(insertion.position, insertion.insertionsBefore);
    534            ASSERT(inserted);
    535        }
    536    }
    537    for (size_t ii = 0; ii < mReplacements.size(); ++ii)
    538    {
    539        const NodeUpdateEntry &replacement = mReplacements[ii];
    540        ASSERT(replacement.parent);
    541        bool replaced =
    542            replacement.parent->replaceChildNode(replacement.original, replacement.replacement);
    543        ASSERT(replaced);
    544 
    545        // Make sure the precision is not accidentally dropped.  It's ok if the precision is not the
    546        // same, as the transformations are allowed to replace an expression with one that is
    547        // temporarily evaluated at a different (likely higher) precision.
    548        TIntermTyped *originalAsTyped = replacement.original->getAsTyped();
    549        TIntermTyped *replacementAsTyped =
    550            replacement.replacement ? replacement.replacement->getAsTyped() : nullptr;
    551        if (originalAsTyped != nullptr && replacementAsTyped != nullptr)
    552        {
    553            const TType &originalType    = originalAsTyped->getType();
    554            const TType &replacementType = replacementAsTyped->getType();
    555            ASSERT(!IsPrecisionApplicableToType(originalType.getBasicType()) ||
    556                   !IsPrecisionApplicableToType(replacementType.getBasicType()) ||
    557                   originalType.getPrecision() == EbpUndefined ||
    558                   replacementType.getPrecision() != EbpUndefined);
    559        }
    560 
    561        if (!replacement.originalBecomesChildOfReplacement)
    562        {
    563            // In AST traversing, a parent is visited before its children.
    564            // After we replace a node, if its immediate child is to
    565            // be replaced, we need to make sure we don't update the replaced
    566            // node; instead, we update the replacement node.
    567            for (size_t jj = ii + 1; jj < mReplacements.size(); ++jj)
    568            {
    569                NodeUpdateEntry &replacement2 = mReplacements[jj];
    570                if (replacement2.parent == replacement.original)
    571                    replacement2.parent = replacement.replacement;
    572            }
    573        }
    574    }
    575    for (size_t ii = 0; ii < mMultiReplacements.size(); ++ii)
    576    {
    577        const NodeReplaceWithMultipleEntry &replacement = mMultiReplacements[ii];
    578        ASSERT(replacement.parent);
    579        bool replaced = replacement.parent->replaceChildNodeWithMultiple(replacement.original,
    580                                                                         replacement.replacements);
    581        ASSERT(replaced);
    582    }
    583 
    584    clearReplacementQueue();
    585 
    586    return compiler->validateAST(node);
    587 }
    588 
    589 void TIntermTraverser::clearReplacementQueue()
    590 {
    591    mReplacements.clear();
    592    mMultiReplacements.clear();
    593    mInsertions.clear();
    594 }
    595 
    596 void TIntermTraverser::queueReplacement(TIntermNode *replacement, OriginalNode originalStatus)
    597 {
    598    queueReplacementWithParent(getParentNode(), mPath.back(), replacement, originalStatus);
    599 }
    600 
    601 void TIntermTraverser::queueReplacementWithParent(TIntermNode *parent,
    602                                                  TIntermNode *original,
    603                                                  TIntermNode *replacement,
    604                                                  OriginalNode originalStatus)
    605 {
    606    bool originalBecomesChild = (originalStatus == OriginalNode::BECOMES_CHILD);
    607    mReplacements.push_back(NodeUpdateEntry(parent, original, replacement, originalBecomesChild));
    608 }
    609 
    610 void TIntermTraverser::queueAccessChainReplacement(TIntermTyped *replacement)
    611 {
    612    uint32_t ancestorIndex  = 0;
    613    TIntermTyped *toReplace = nullptr;
    614    while (true)
    615    {
    616        TIntermNode *ancestor = getAncestorNode(ancestorIndex);
    617        ASSERT(ancestor != nullptr);
    618 
    619        TIntermBinary *asBinary = ancestor->getAsBinaryNode();
    620        if (asBinary == nullptr ||
    621            (asBinary->getOp() != EOpIndexDirect && asBinary->getOp() != EOpIndexIndirect))
    622        {
    623            break;
    624        }
    625 
    626        replacement = new TIntermBinary(asBinary->getOp(), replacement, asBinary->getRight());
    627        toReplace   = asBinary;
    628 
    629        ++ancestorIndex;
    630    }
    631 
    632    if (toReplace == nullptr)
    633    {
    634        queueReplacement(replacement, OriginalNode::IS_DROPPED);
    635    }
    636    else
    637    {
    638        queueReplacementWithParent(getAncestorNode(ancestorIndex), toReplace, replacement,
    639                                   OriginalNode::IS_DROPPED);
    640    }
    641 }
    642 
    643 TLValueTrackingTraverser::TLValueTrackingTraverser(bool preVisitIn,
    644                                                   bool inVisitIn,
    645                                                   bool postVisitIn,
    646                                                   TSymbolTable *symbolTable)
    647    : TIntermTraverser(preVisitIn, inVisitIn, postVisitIn, symbolTable),
    648      mOperatorRequiresLValue(false),
    649      mInFunctionCallOutParameter(false)
    650 {
    651    ASSERT(symbolTable);
    652 }
    653 
    654 void TLValueTrackingTraverser::traverseAggregate(TIntermAggregate *node)
    655 {
    656    ScopedNodeInTraversalPath addToPath(this, node);
    657    if (!addToPath.isWithinDepthLimit())
    658        return;
    659 
    660    bool visit = true;
    661 
    662    TIntermSequence *sequence = node->getSequence();
    663 
    664    if (preVisit)
    665        visit = node->visit(PreVisit, this);
    666 
    667    if (visit)
    668    {
    669        size_t paramIndex = 0u;
    670        for (auto *child : *sequence)
    671        {
    672            if (visit)
    673            {
    674                if (node->getFunction())
    675                {
    676                    // Both built-ins and user defined functions should have the function symbol
    677                    // set.
    678                    ASSERT(paramIndex < node->getFunction()->getParamCount());
    679                    TQualifier qualifier =
    680                        node->getFunction()->getParam(paramIndex)->getType().getQualifier();
    681                    setInFunctionCallOutParameter(qualifier == EvqParamOut ||
    682                                                  qualifier == EvqParamInOut);
    683                    ++paramIndex;
    684                }
    685                else
    686                {
    687                    ASSERT(node->isConstructor());
    688                }
    689                child->traverse(this);
    690                if (inVisit)
    691                {
    692                    if (child != sequence->back())
    693                        visit = node->visit(InVisit, this);
    694                }
    695            }
    696        }
    697        setInFunctionCallOutParameter(false);
    698 
    699        if (visit && postVisit)
    700            visit = node->visit(PostVisit, this);
    701    }
    702 }
    703 
    704 void TIntermTraverser::traverseLoop(TIntermLoop *node)
    705 {
    706    traverse(node);
    707 }
    708 }  // namespace sh