Commit d0d59aa4 by Olli Etuaho

Add deep copying support for typed AST nodes

Removing dynamic indexing of vectors and matrices will require copying the indexed nodes in case they are written. Any type of l-value node that doesn't have side effects may need to be copied. Add a copying function for all typed node classes so that this copying can be performed. Private copy constructors are used to implement the deepCopy function in order to make maintenance easier. With copy constructors, each subclass only needs to take care of copying its own members, and not the base class members, which reduces the possibility of errors. Copy constructors are disabled for all node classes that don't support deep copying by inheriting TIntermNode from angle::NonCopyable. Assignment operator is disabled for all node classes through inheriting angle::NonCopyable. This applies also to classes that now get the private copy constructor. Explicit copy constructor and assignment operator declarations are added to some classes which show up in node member variables to make code clearer. BUG=angleproject:1116 TEST=angle_unittests Change-Id: Ia757b69397837f8309f0e7511c0cd24ca2c7a721 Reviewed-on: https://chromium-review.googlesource.com/293931Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Tested-by: 's avatarOlli Etuaho <oetuaho@nvidia.com>
parent 5c42266e
...@@ -395,6 +395,77 @@ bool TIntermCase::replaceChildNode( ...@@ -395,6 +395,77 @@ bool TIntermCase::replaceChildNode(
return false; return false;
} }
TIntermTyped::TIntermTyped(const TIntermTyped &node) : TIntermNode(), mType(node.mType)
{
// Copy constructor is disallowed for TIntermNode in order to disallow it for subclasses that
// don't explicitly allow it, so normal TIntermNode constructor is used to construct the copy.
// We need to manually copy any fields of TIntermNode besides handling fields in TIntermTyped.
mLine = node.mLine;
}
TIntermConstantUnion::TIntermConstantUnion(const TIntermConstantUnion &node) : TIntermTyped(node)
{
size_t arraySize = mType.getObjectSize();
mUnionArrayPointer = new TConstantUnion[arraySize];
for (size_t i = 0u; i < arraySize; ++i)
{
mUnionArrayPointer[i] = node.mUnionArrayPointer[i];
}
}
TIntermAggregate::TIntermAggregate(const TIntermAggregate &node)
: TIntermOperator(node),
mName(node.mName),
mUserDefined(node.mUserDefined),
mFunctionId(node.mFunctionId),
mOptimize(node.mOptimize),
mDebug(node.mDebug),
mUseEmulatedFunction(node.mUseEmulatedFunction),
mGotPrecisionFromChildren(node.mGotPrecisionFromChildren)
{
for (TIntermNode *child : node.mSequence)
{
TIntermTyped *typedChild = child->getAsTyped();
ASSERT(typedChild != nullptr);
TIntermTyped *childCopy = typedChild->deepCopy();
mSequence.push_back(childCopy);
}
}
TIntermBinary::TIntermBinary(const TIntermBinary &node)
: TIntermOperator(node), mAddIndexClamp(node.mAddIndexClamp)
{
TIntermTyped *leftCopy = node.mLeft->deepCopy();
TIntermTyped *rightCopy = node.mRight->deepCopy();
ASSERT(leftCopy != nullptr && rightCopy != nullptr);
mLeft = leftCopy;
mRight = rightCopy;
}
TIntermUnary::TIntermUnary(const TIntermUnary &node)
: TIntermOperator(node), mUseEmulatedFunction(node.mUseEmulatedFunction)
{
TIntermTyped *operandCopy = node.mOperand->deepCopy();
ASSERT(operandCopy != nullptr);
mOperand = operandCopy;
}
TIntermSelection::TIntermSelection(const TIntermSelection &node) : TIntermTyped(node)
{
// Only supported for ternary nodes, not if statements.
TIntermTyped *trueTyped = node.mTrueBlock->getAsTyped();
TIntermTyped *falseTyped = node.mFalseBlock->getAsTyped();
ASSERT(trueTyped != nullptr);
ASSERT(falseTyped != nullptr);
TIntermTyped *conditionCopy = node.mCondition->deepCopy();
TIntermTyped *trueCopy = trueTyped->deepCopy();
TIntermTyped *falseCopy = falseTyped->deepCopy();
ASSERT(conditionCopy != nullptr && trueCopy != nullptr && falseCopy != nullptr);
mCondition = conditionCopy;
mTrueBlock = trueCopy;
mFalseBlock = falseCopy;
}
// //
// Say whether or not an operation node changes the value of a variable. // Say whether or not an operation node changes the value of a variable.
// //
......
...@@ -52,6 +52,8 @@ class TName ...@@ -52,6 +52,8 @@ class TName
POOL_ALLOCATOR_NEW_DELETE(); POOL_ALLOCATOR_NEW_DELETE();
explicit TName(const TString &name) : mName(name), mIsInternal(false) {} explicit TName(const TString &name) : mName(name), mIsInternal(false) {}
TName() : mName(), mIsInternal(false) {} TName() : mName(), mIsInternal(false) {}
TName(const TName &) = default;
TName &operator=(const TName &) = default;
const TString &getString() const { return mName; } const TString &getString() const { return mName; }
void setString(const TString &string) { mName = string; } void setString(const TString &string) { mName = string; }
...@@ -66,7 +68,7 @@ class TName ...@@ -66,7 +68,7 @@ class TName
// //
// Base class for the tree nodes // Base class for the tree nodes
// //
class TIntermNode class TIntermNode : angle::NonCopyable
{ {
public: public:
POOL_ALLOCATOR_NEW_DELETE(); POOL_ALLOCATOR_NEW_DELETE();
...@@ -120,6 +122,9 @@ class TIntermTyped : public TIntermNode ...@@ -120,6 +122,9 @@ class TIntermTyped : public TIntermNode
{ {
public: public:
TIntermTyped(const TType &t) : mType(t) { } TIntermTyped(const TType &t) : mType(t) { }
virtual TIntermTyped *deepCopy() const = 0;
TIntermTyped *getAsTyped() override { return this; } TIntermTyped *getAsTyped() override { return this; }
virtual bool hasSideEffects() const = 0; virtual bool hasSideEffects() const = 0;
...@@ -150,6 +155,8 @@ class TIntermTyped : public TIntermNode ...@@ -150,6 +155,8 @@ class TIntermTyped : public TIntermNode
protected: protected:
TType mType; TType mType;
TIntermTyped(const TIntermTyped &node);
}; };
// //
...@@ -235,6 +242,8 @@ class TIntermSymbol : public TIntermTyped ...@@ -235,6 +242,8 @@ class TIntermSymbol : public TIntermTyped
{ {
} }
TIntermTyped *deepCopy() const override { return new TIntermSymbol(*this); }
bool hasSideEffects() const override { return false; } bool hasSideEffects() const override { return false; }
int getId() const { return mId; } int getId() const { return mId; }
...@@ -252,6 +261,9 @@ class TIntermSymbol : public TIntermTyped ...@@ -252,6 +261,9 @@ class TIntermSymbol : public TIntermTyped
protected: protected:
int mId; int mId;
TName mSymbol; TName mSymbol;
private:
TIntermSymbol(const TIntermSymbol &) = default; // Note: not deleted, just private!
}; };
// A Raw node stores raw code, that the translator will insert verbatim // A Raw node stores raw code, that the translator will insert verbatim
...@@ -263,6 +275,13 @@ class TIntermRaw : public TIntermTyped ...@@ -263,6 +275,13 @@ class TIntermRaw : public TIntermTyped
TIntermRaw(const TType &type, const TString &rawText) TIntermRaw(const TType &type, const TString &rawText)
: TIntermTyped(type), : TIntermTyped(type),
mRawText(rawText) { } mRawText(rawText) { }
TIntermRaw(const TIntermRaw &) = delete;
TIntermTyped *deepCopy() const
{
UNREACHABLE();
return nullptr;
}
bool hasSideEffects() const override { return false; } bool hasSideEffects() const override { return false; }
...@@ -284,6 +303,8 @@ class TIntermConstantUnion : public TIntermTyped ...@@ -284,6 +303,8 @@ class TIntermConstantUnion : public TIntermTyped
: TIntermTyped(type), : TIntermTyped(type),
mUnionArrayPointer(unionPointer) { } mUnionArrayPointer(unionPointer) { }
TIntermTyped *deepCopy() const override { return new TIntermConstantUnion(*this); }
bool hasSideEffects() const override { return false; } bool hasSideEffects() const override { return false; }
const TConstantUnion *getUnionArrayPointer() const { return mUnionArrayPointer; } const TConstantUnion *getUnionArrayPointer() const { return mUnionArrayPointer; }
...@@ -328,6 +349,8 @@ class TIntermConstantUnion : public TIntermTyped ...@@ -328,6 +349,8 @@ class TIntermConstantUnion : public TIntermTyped
private: private:
typedef float(*FloatTypeUnaryFunc) (float); typedef float(*FloatTypeUnaryFunc) (float);
bool foldFloatTypeUnary(const TConstantUnion &parameter, FloatTypeUnaryFunc builtinFunc, TInfoSink &infoSink, TConstantUnion *result) const; bool foldFloatTypeUnary(const TConstantUnion &parameter, FloatTypeUnaryFunc builtinFunc, TInfoSink &infoSink, TConstantUnion *result) const;
TIntermConstantUnion(const TIntermConstantUnion &node); // Note: not deleted, just private!
}; };
// //
...@@ -353,6 +376,8 @@ class TIntermOperator : public TIntermTyped ...@@ -353,6 +376,8 @@ class TIntermOperator : public TIntermTyped
: TIntermTyped(type), : TIntermTyped(type),
mOp(op) {} mOp(op) {}
TIntermOperator(const TIntermOperator &) = default;
TOperator mOp; TOperator mOp;
}; };
...@@ -366,6 +391,8 @@ class TIntermBinary : public TIntermOperator ...@@ -366,6 +391,8 @@ class TIntermBinary : public TIntermOperator
: TIntermOperator(op), : TIntermOperator(op),
mAddIndexClamp(false) {} mAddIndexClamp(false) {}
TIntermTyped *deepCopy() const override { return new TIntermBinary(*this); }
TIntermBinary *getAsBinaryNode() override { return this; }; TIntermBinary *getAsBinaryNode() override { return this; };
void traverse(TIntermTraverser *it) override; void traverse(TIntermTraverser *it) override;
bool replaceChildNode( bool replaceChildNode(
...@@ -392,6 +419,9 @@ class TIntermBinary : public TIntermOperator ...@@ -392,6 +419,9 @@ class TIntermBinary : public TIntermOperator
// If set to true, wrap any EOpIndexIndirect with a clamp to bounds. // If set to true, wrap any EOpIndexIndirect with a clamp to bounds.
bool mAddIndexClamp; bool mAddIndexClamp;
private:
TIntermBinary(const TIntermBinary &node); // Note: not deleted, just private!
}; };
// //
...@@ -409,6 +439,8 @@ class TIntermUnary : public TIntermOperator ...@@ -409,6 +439,8 @@ class TIntermUnary : public TIntermOperator
mOperand(NULL), mOperand(NULL),
mUseEmulatedFunction(false) {} mUseEmulatedFunction(false) {}
TIntermTyped *deepCopy() const override { return new TIntermUnary(*this); }
void traverse(TIntermTraverser *it) override; void traverse(TIntermTraverser *it) override;
TIntermUnary *getAsUnaryNode() override { return this; } TIntermUnary *getAsUnaryNode() override { return this; }
bool replaceChildNode( bool replaceChildNode(
...@@ -433,6 +465,9 @@ class TIntermUnary : public TIntermOperator ...@@ -433,6 +465,9 @@ class TIntermUnary : public TIntermOperator
// If set to true, replace the built-in function call with an emulated one // If set to true, replace the built-in function call with an emulated one
// to work around driver bugs. // to work around driver bugs.
bool mUseEmulatedFunction; bool mUseEmulatedFunction;
private:
TIntermUnary(const TIntermUnary &node); // note: not deleted, just private!
}; };
typedef TVector<TIntermNode *> TIntermSequence; typedef TVector<TIntermNode *> TIntermSequence;
...@@ -459,6 +494,9 @@ class TIntermAggregate : public TIntermOperator ...@@ -459,6 +494,9 @@ class TIntermAggregate : public TIntermOperator
} }
~TIntermAggregate() { } ~TIntermAggregate() { }
// Note: only supported for nodes that can be a part of an expression.
TIntermTyped *deepCopy() const override { return new TIntermAggregate(*this); }
TIntermAggregate *getAsAggregate() override { return this; } TIntermAggregate *getAsAggregate() override { return this; }
void traverse(TIntermTraverser *it) override; void traverse(TIntermTraverser *it) override;
bool replaceChildNode( bool replaceChildNode(
...@@ -498,8 +536,6 @@ class TIntermAggregate : public TIntermOperator ...@@ -498,8 +536,6 @@ class TIntermAggregate : public TIntermOperator
bool gotPrecisionFromChildren() const { return mGotPrecisionFromChildren; } bool gotPrecisionFromChildren() const { return mGotPrecisionFromChildren; }
protected: protected:
TIntermAggregate(const TIntermAggregate &); // disallow copy constructor
TIntermAggregate &operator=(const TIntermAggregate &); // disallow assignment operator
TIntermSequence mSequence; TIntermSequence mSequence;
TName mName; TName mName;
bool mUserDefined; // used for user defined function names bool mUserDefined; // used for user defined function names
...@@ -513,6 +549,9 @@ class TIntermAggregate : public TIntermOperator ...@@ -513,6 +549,9 @@ class TIntermAggregate : public TIntermOperator
bool mUseEmulatedFunction; bool mUseEmulatedFunction;
bool mGotPrecisionFromChildren; bool mGotPrecisionFromChildren;
private:
TIntermAggregate(const TIntermAggregate &node); // note: not deleted, just private!
}; };
// //
...@@ -533,6 +572,9 @@ class TIntermSelection : public TIntermTyped ...@@ -533,6 +572,9 @@ class TIntermSelection : public TIntermTyped
mTrueBlock(trueB), mTrueBlock(trueB),
mFalseBlock(falseB) {} mFalseBlock(falseB) {}
// Note: only supported for ternary operator nodes.
TIntermTyped *deepCopy() const override { return new TIntermSelection(*this); }
void traverse(TIntermTraverser *it) override; void traverse(TIntermTraverser *it) override;
bool replaceChildNode( bool replaceChildNode(
TIntermNode *original, TIntermNode *replacement) override; TIntermNode *original, TIntermNode *replacement) override;
...@@ -546,10 +588,13 @@ class TIntermSelection : public TIntermTyped ...@@ -546,10 +588,13 @@ class TIntermSelection : public TIntermTyped
TIntermNode *getFalseBlock() const { return mFalseBlock; } TIntermNode *getFalseBlock() const { return mFalseBlock; }
TIntermSelection *getAsSelectionNode() override { return this; } TIntermSelection *getAsSelectionNode() override { return this; }
protected: protected:
TIntermTyped *mCondition; TIntermTyped *mCondition;
TIntermNode *mTrueBlock; TIntermNode *mTrueBlock;
TIntermNode *mFalseBlock; TIntermNode *mFalseBlock;
private:
TIntermSelection(const TIntermSelection &node); // Note: not deleted, just private!
}; };
// //
......
...@@ -269,6 +269,9 @@ class TType ...@@ -269,6 +269,9 @@ class TType
{ {
} }
TType(const TType &) = default;
TType &operator=(const TType &) = default;
TBasicType getBasicType() const TBasicType getBasicType() const
{ {
return type; return type;
......
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
'<(angle_path)/src/tests/compiler_tests/DebugShaderPrecision_test.cpp', '<(angle_path)/src/tests/compiler_tests/DebugShaderPrecision_test.cpp',
'<(angle_path)/src/tests/compiler_tests/ExpressionLimit_test.cpp', '<(angle_path)/src/tests/compiler_tests/ExpressionLimit_test.cpp',
'<(angle_path)/src/tests/compiler_tests/EXT_blend_func_extended_test.cpp', '<(angle_path)/src/tests/compiler_tests/EXT_blend_func_extended_test.cpp',
'<(angle_path)/src/tests/compiler_tests/IntermNode_test.cpp',
'<(angle_path)/src/tests/compiler_tests/MalformedShader_test.cpp', '<(angle_path)/src/tests/compiler_tests/MalformedShader_test.cpp',
'<(angle_path)/src/tests/compiler_tests/NV_draw_buffers_test.cpp', '<(angle_path)/src/tests/compiler_tests/NV_draw_buffers_test.cpp',
'<(angle_path)/src/tests/compiler_tests/Pack_Unpack_test.cpp', '<(angle_path)/src/tests/compiler_tests/Pack_Unpack_test.cpp',
......
//
// Copyright (c) 2015 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.
//
// IntermNode_test.cpp:
// Unit tests for the AST node classes.
//
#include "angle_gl.h"
#include "gtest/gtest.h"
#include "compiler/translator/InfoSink.h"
#include "compiler/translator/IntermNode.h"
#include "compiler/translator/PoolAlloc.h"
class IntermNodeTest : public testing::Test
{
public:
IntermNodeTest() : mUniqueIndex(0) {}
protected:
void SetUp() override
{
allocator.push();
SetGlobalPoolAllocator(&allocator);
}
void TearDown() override
{
SetGlobalPoolAllocator(nullptr);
allocator.pop();
}
TIntermSymbol *createTestSymbol(const TType &type)
{
TInfoSinkBase symbolNameOut;
symbolNameOut << "test" << mUniqueIndex;
TString symbolName = symbolNameOut.c_str();
++mUniqueIndex;
TIntermSymbol *node = new TIntermSymbol(0, symbolName, type);
node->setLine(createUniqueSourceLoc());
node->setInternal(true);
node->getTypePointer()->setQualifier(EvqTemporary);
return node;
}
TIntermSymbol *createTestSymbol()
{
TType type(EbtFloat, EbpHigh);
return createTestSymbol(type);
}
void checkTypeEqualWithQualifiers(const TType &original, const TType &copy)
{
ASSERT_EQ(original, copy);
ASSERT_EQ(original.getPrecision(), copy.getPrecision());
ASSERT_EQ(original.getQualifier(), copy.getQualifier());
}
void checkSymbolCopy(TIntermNode *aOriginal, TIntermNode *aCopy)
{
ASSERT_NE(aOriginal, aCopy);
TIntermSymbol *copy = aCopy->getAsSymbolNode();
TIntermSymbol *original = aOriginal->getAsSymbolNode();
ASSERT_NE(nullptr, copy);
ASSERT_NE(nullptr, original);
ASSERT_NE(original, copy);
ASSERT_EQ(original->getId(), copy->getId());
ASSERT_EQ(original->getName().getString(), copy->getName().getString());
ASSERT_EQ(original->getName().isInternal(), copy->getName().isInternal());
checkTypeEqualWithQualifiers(original->getType(), copy->getType());
ASSERT_EQ(original->getLine().first_file, copy->getLine().first_file);
ASSERT_EQ(original->getLine().first_line, copy->getLine().first_line);
ASSERT_EQ(original->getLine().last_file, copy->getLine().last_file);
ASSERT_EQ(original->getLine().last_line, copy->getLine().last_line);
}
TSourceLoc createUniqueSourceLoc()
{
TSourceLoc loc;
loc.first_file = mUniqueIndex;
loc.first_line = mUniqueIndex + 1;
loc.last_file = mUniqueIndex + 2;
loc.last_line = mUniqueIndex + 3;
++mUniqueIndex;
return loc;
}
static TSourceLoc getTestSourceLoc()
{
TSourceLoc loc;
loc.first_file = 1;
loc.first_line = 2;
loc.last_file = 3;
loc.last_line = 4;
return loc;
}
static void checkTestSourceLoc(const TSourceLoc &loc)
{
ASSERT_EQ(1, loc.first_file);
ASSERT_EQ(2, loc.first_line);
ASSERT_EQ(3, loc.last_file);
ASSERT_EQ(4, loc.last_line);
}
private:
TPoolAllocator allocator;
int mUniqueIndex;
};
// Check that the deep copy of a symbol node is an actual copy with the same attributes as the
// original.
TEST_F(IntermNodeTest, DeepCopySymbolNode)
{
TType type(EbtInt, EbpHigh);
TIntermSymbol *original = new TIntermSymbol(0, TString("name"), type);
original->setLine(getTestSourceLoc());
original->setInternal(true);
TIntermTyped *copy = original->deepCopy();
checkSymbolCopy(original, copy);
checkTestSourceLoc(copy->getLine());
}
// Check that the deep copy of a constant union node is an actual copy with the same attributes as
// the original.
TEST_F(IntermNodeTest, DeepCopyConstantUnionNode)
{
TType type(EbtInt, EbpHigh);
TConstantUnion *constValue = new TConstantUnion[1];
constValue[0].setIConst(101);
TIntermConstantUnion *original = new TIntermConstantUnion(constValue, type);
original->setLine(getTestSourceLoc());
TIntermTyped *copyTyped = original->deepCopy();
TIntermConstantUnion *copy = copyTyped->getAsConstantUnion();
ASSERT_NE(nullptr, copy);
ASSERT_NE(original, copy);
checkTestSourceLoc(copy->getLine());
checkTypeEqualWithQualifiers(original->getType(), copy->getType());
ASSERT_EQ(101, copy->getIConst(0));
}
// Check that the deep copy of a binary node is an actual copy with the same attributes as the
// original. Child nodes also need to be copies with the same attributes as the original children.
TEST_F(IntermNodeTest, DeepCopyBinaryNode)
{
TType type(EbtFloat, EbpHigh);
TIntermBinary *original = new TIntermBinary(EOpAdd);
original->setLine(getTestSourceLoc());
original->setLeft(createTestSymbol());
original->setRight(createTestSymbol());
TIntermTyped *copyTyped = original->deepCopy();
TIntermBinary *copy = copyTyped->getAsBinaryNode();
ASSERT_NE(nullptr, copy);
ASSERT_NE(original, copy);
checkTestSourceLoc(copy->getLine());
checkTypeEqualWithQualifiers(original->getType(), copy->getType());
checkSymbolCopy(original->getLeft(), copy->getLeft());
checkSymbolCopy(original->getRight(), copy->getRight());
}
// Check that the deep copy of a unary node is an actual copy with the same attributes as the
// original. The child node also needs to be a copy with the same attributes as the original child.
TEST_F(IntermNodeTest, DeepCopyUnaryNode)
{
TType type(EbtFloat, EbpHigh);
TIntermUnary *original = new TIntermUnary(EOpPreIncrement);
original->setLine(getTestSourceLoc());
original->setOperand(createTestSymbol());
TIntermTyped *copyTyped = original->deepCopy();
TIntermUnary *copy = copyTyped->getAsUnaryNode();
ASSERT_NE(nullptr, copy);
ASSERT_NE(original, copy);
checkTestSourceLoc(copy->getLine());
checkTypeEqualWithQualifiers(original->getType(), copy->getType());
checkSymbolCopy(original->getOperand(), copy->getOperand());
}
// Check that the deep copy of an aggregate node is an actual copy with the same attributes as the
// original. Child nodes also need to be copies with the same attributes as the original children.
TEST_F(IntermNodeTest, DeepCopyAggregateNode)
{
TType type(EbtFloat, EbpHigh);
TIntermAggregate *original = new TIntermAggregate(EOpMix);
original->setLine(getTestSourceLoc());
TIntermSequence *originalSeq = original->getSequence();
originalSeq->push_back(createTestSymbol());
originalSeq->push_back(createTestSymbol());
originalSeq->push_back(createTestSymbol());
TIntermTyped *copyTyped = original->deepCopy();
TIntermAggregate *copy = copyTyped->getAsAggregate();
ASSERT_NE(nullptr, copy);
ASSERT_NE(original, copy);
checkTestSourceLoc(copy->getLine());
checkTypeEqualWithQualifiers(original->getType(), copy->getType());
ASSERT_EQ(original->getSequence()->size(), copy->getSequence()->size());
TIntermSequence::size_type i = 0;
for (auto *copyChild : *copy->getSequence())
{
TIntermNode *originalChild = originalSeq->at(i);
checkSymbolCopy(originalChild, copyChild);
++i;
}
}
// Check that the deep copy of a selection node is an actual copy with the same attributes as the
// original. Child nodes also need to be copies with the same attributes as the original children.
TEST_F(IntermNodeTest, DeepCopySelectionNode)
{
TType type(EbtFloat, EbpHigh);
TIntermSelection *original = new TIntermSelection(
createTestSymbol(TType(EbtBool, EbpUndefined)), createTestSymbol(), createTestSymbol());
original->setLine(getTestSourceLoc());
TIntermTyped *copyTyped = original->deepCopy();
TIntermSelection *copy = copyTyped->getAsSelectionNode();
ASSERT_NE(nullptr, copy);
ASSERT_NE(original, copy);
checkTestSourceLoc(copy->getLine());
checkTypeEqualWithQualifiers(original->getType(), copy->getType());
checkSymbolCopy(original->getCondition(), copy->getCondition());
checkSymbolCopy(original->getTrueBlock(), copy->getTrueBlock());
checkSymbolCopy(original->getFalseBlock(), copy->getFalseBlock());
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment