diff options
Diffstat (limited to 'gfx/angle/checkout/src/compiler/translator/util.cpp')
-rw-r--r-- | gfx/angle/checkout/src/compiler/translator/util.cpp | 936 |
1 files changed, 936 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/compiler/translator/util.cpp b/gfx/angle/checkout/src/compiler/translator/util.cpp new file mode 100644 index 0000000000..7781c1bc7c --- /dev/null +++ b/gfx/angle/checkout/src/compiler/translator/util.cpp @@ -0,0 +1,936 @@ +// +// 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 "compiler/translator/util.h" + +#include <limits> + +#include "common/utilities.h" +#include "compiler/preprocessor/numeric_lex.h" +#include "compiler/translator/ImmutableStringBuilder.h" +#include "compiler/translator/SymbolTable.h" + +bool atoi_clamp(const char *str, unsigned int *value) +{ + bool success = angle::pp::numeric_lex_int(str, value); + if (!success) + *value = std::numeric_limits<unsigned int>::max(); + return success; +} + +namespace sh +{ + +namespace +{ + +bool IsInterpolationIn(TQualifier qualifier) +{ + switch (qualifier) + { + case EvqSmoothIn: + case EvqFlatIn: + case EvqCentroidIn: + return true; + default: + return false; + } +} + +} // anonymous namespace + +float NumericLexFloat32OutOfRangeToInfinity(const std::string &str) +{ + // Parses a decimal string using scientific notation into a floating point number. + // Out-of-range values are converted to infinity. Values that are too small to be + // represented are converted to zero. + + // The mantissa in decimal scientific notation. The magnitude of the mantissa integer does not + // matter. + unsigned int decimalMantissa = 0; + size_t i = 0; + bool decimalPointSeen = false; + bool nonZeroSeenInMantissa = false; + + // The exponent offset reflects the position of the decimal point. + int exponentOffset = -1; + + // This is just a counter for how many decimal digits are written to decimalMantissa. + int mantissaDecimalDigits = 0; + + while (i < str.length()) + { + const char c = str[i]; + if (c == 'e' || c == 'E') + { + break; + } + if (c == '.') + { + decimalPointSeen = true; + ++i; + continue; + } + + unsigned int digit = static_cast<unsigned int>(c - '0'); + ASSERT(digit < 10u); + if (digit != 0u) + { + nonZeroSeenInMantissa = true; + } + if (nonZeroSeenInMantissa) + { + // Add bits to the mantissa until space runs out in 32-bit int. This should be + // enough precision to make the resulting binary mantissa accurate to 1 ULP. + if (decimalMantissa <= (std::numeric_limits<unsigned int>::max() - 9u) / 10u) + { + decimalMantissa = decimalMantissa * 10u + digit; + ++mantissaDecimalDigits; + } + if (!decimalPointSeen) + { + ++exponentOffset; + } + } + else if (decimalPointSeen) + { + --exponentOffset; + } + ++i; + } + if (decimalMantissa == 0) + { + return 0.0f; + } + int exponent = 0; + if (i < str.length()) + { + ASSERT(str[i] == 'e' || str[i] == 'E'); + ++i; + bool exponentOutOfRange = false; + bool negativeExponent = false; + if (str[i] == '-') + { + negativeExponent = true; + ++i; + } + else if (str[i] == '+') + { + ++i; + } + while (i < str.length()) + { + const char c = str[i]; + unsigned int digit = static_cast<unsigned int>(c - '0'); + ASSERT(digit < 10u); + if (exponent <= (std::numeric_limits<int>::max() - 9) / 10) + { + exponent = exponent * 10 + digit; + } + else + { + exponentOutOfRange = true; + } + ++i; + } + if (negativeExponent) + { + exponent = -exponent; + } + if (exponentOutOfRange) + { + if (negativeExponent) + { + return 0.0f; + } + else + { + return std::numeric_limits<float>::infinity(); + } + } + } + // Do the calculation in 64-bit to avoid overflow. + long long exponentLong = + static_cast<long long>(exponent) + static_cast<long long>(exponentOffset); + if (exponentLong > std::numeric_limits<float>::max_exponent10) + { + return std::numeric_limits<float>::infinity(); + } + else if (exponentLong < std::numeric_limits<float>::min_exponent10) + { + return 0.0f; + } + // The exponent is in range, so we need to actually evaluate the float. + exponent = static_cast<int>(exponentLong); + double value = decimalMantissa; + + // Calculate the exponent offset to normalize the mantissa. + int normalizationExponentOffset = 1 - mantissaDecimalDigits; + // Apply the exponent. + value *= std::pow(10.0, static_cast<double>(exponent + normalizationExponentOffset)); + if (value > static_cast<double>(std::numeric_limits<float>::max())) + { + return std::numeric_limits<float>::infinity(); + } + if (value < static_cast<double>(std::numeric_limits<float>::min())) + { + return 0.0f; + } + return static_cast<float>(value); +} + +bool strtof_clamp(const std::string &str, float *value) +{ + // Custom float parsing that can handle the following corner cases: + // 1. The decimal mantissa is very small but the exponent is very large, putting the resulting + // number inside the float range. + // 2. The decimal mantissa is very large but the exponent is very small, putting the resulting + // number inside the float range. + // 3. The value is out-of-range and should be evaluated as infinity. + // 4. The value is too small and should be evaluated as zero. + // See ESSL 3.00.6 section 4.1.4 for the relevant specification. + *value = NumericLexFloat32OutOfRangeToInfinity(str); + return !gl::isInf(*value); +} + +GLenum GLVariableType(const TType &type) +{ + if (type.getBasicType() == EbtFloat) + { + if (type.isVector()) + { + switch (type.getNominalSize()) + { + case 2: + return GL_FLOAT_VEC2; + case 3: + return GL_FLOAT_VEC3; + case 4: + return GL_FLOAT_VEC4; + default: + UNREACHABLE(); +#if !UNREACHABLE_IS_NORETURN + return GL_NONE; +#endif + } + } + else if (type.isMatrix()) + { + switch (type.getCols()) + { + case 2: + switch (type.getRows()) + { + case 2: + return GL_FLOAT_MAT2; + case 3: + return GL_FLOAT_MAT2x3; + case 4: + return GL_FLOAT_MAT2x4; + default: + UNREACHABLE(); +#if !UNREACHABLE_IS_NORETURN + return GL_NONE; +#endif + } + + case 3: + switch (type.getRows()) + { + case 2: + return GL_FLOAT_MAT3x2; + case 3: + return GL_FLOAT_MAT3; + case 4: + return GL_FLOAT_MAT3x4; + default: + UNREACHABLE(); +#if !UNREACHABLE_IS_NORETURN + return GL_NONE; +#endif + } + + case 4: + switch (type.getRows()) + { + case 2: + return GL_FLOAT_MAT4x2; + case 3: + return GL_FLOAT_MAT4x3; + case 4: + return GL_FLOAT_MAT4; + default: + UNREACHABLE(); +#if !UNREACHABLE_IS_NORETURN + return GL_NONE; +#endif + } + + default: + UNREACHABLE(); +#if !UNREACHABLE_IS_NORETURN + return GL_NONE; +#endif + } + } + else + { + return GL_FLOAT; + } + } + else if (type.getBasicType() == EbtInt) + { + if (type.isVector()) + { + switch (type.getNominalSize()) + { + case 2: + return GL_INT_VEC2; + case 3: + return GL_INT_VEC3; + case 4: + return GL_INT_VEC4; + default: + UNREACHABLE(); +#if !UNREACHABLE_IS_NORETURN + return GL_NONE; +#endif + } + } + else + { + ASSERT(!type.isMatrix()); + return GL_INT; + } + } + else if (type.getBasicType() == EbtUInt) + { + if (type.isVector()) + { + switch (type.getNominalSize()) + { + case 2: + return GL_UNSIGNED_INT_VEC2; + case 3: + return GL_UNSIGNED_INT_VEC3; + case 4: + return GL_UNSIGNED_INT_VEC4; + default: + UNREACHABLE(); +#if !UNREACHABLE_IS_NORETURN + return GL_NONE; +#endif + } + } + else + { + ASSERT(!type.isMatrix()); + return GL_UNSIGNED_INT; + } + } + else if (type.getBasicType() == EbtBool) + { + if (type.isVector()) + { + switch (type.getNominalSize()) + { + case 2: + return GL_BOOL_VEC2; + case 3: + return GL_BOOL_VEC3; + case 4: + return GL_BOOL_VEC4; + default: + UNREACHABLE(); +#if !UNREACHABLE_IS_NORETURN + return GL_NONE; +#endif + } + } + else + { + ASSERT(!type.isMatrix()); + return GL_BOOL; + } + } + + switch (type.getBasicType()) + { + case EbtSampler2D: + return GL_SAMPLER_2D; + case EbtSampler3D: + return GL_SAMPLER_3D; + case EbtSamplerCube: + return GL_SAMPLER_CUBE; + case EbtSamplerExternalOES: + return GL_SAMPLER_EXTERNAL_OES; + case EbtSamplerExternal2DY2YEXT: + return GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT; + case EbtSampler2DRect: + return GL_SAMPLER_2D_RECT_ANGLE; + case EbtSampler2DArray: + return GL_SAMPLER_2D_ARRAY; + case EbtSampler2DMS: + return GL_SAMPLER_2D_MULTISAMPLE; + case EbtSampler2DMSArray: + return GL_SAMPLER_2D_MULTISAMPLE_ARRAY; + case EbtISampler2D: + return GL_INT_SAMPLER_2D; + case EbtISampler3D: + return GL_INT_SAMPLER_3D; + case EbtISamplerCube: + return GL_INT_SAMPLER_CUBE; + case EbtISampler2DArray: + return GL_INT_SAMPLER_2D_ARRAY; + case EbtISampler2DMS: + return GL_INT_SAMPLER_2D_MULTISAMPLE; + case EbtISampler2DMSArray: + return GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY; + case EbtUSampler2D: + return GL_UNSIGNED_INT_SAMPLER_2D; + case EbtUSampler3D: + return GL_UNSIGNED_INT_SAMPLER_3D; + case EbtUSamplerCube: + return GL_UNSIGNED_INT_SAMPLER_CUBE; + case EbtUSampler2DArray: + return GL_UNSIGNED_INT_SAMPLER_2D_ARRAY; + case EbtUSampler2DMS: + return GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE; + case EbtUSampler2DMSArray: + return GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY; + case EbtSampler2DShadow: + return GL_SAMPLER_2D_SHADOW; + case EbtSamplerCubeShadow: + return GL_SAMPLER_CUBE_SHADOW; + case EbtSampler2DArrayShadow: + return GL_SAMPLER_2D_ARRAY_SHADOW; + case EbtImage2D: + return GL_IMAGE_2D; + case EbtIImage2D: + return GL_INT_IMAGE_2D; + case EbtUImage2D: + return GL_UNSIGNED_INT_IMAGE_2D; + case EbtImage2DArray: + return GL_IMAGE_2D_ARRAY; + case EbtIImage2DArray: + return GL_INT_IMAGE_2D_ARRAY; + case EbtUImage2DArray: + return GL_UNSIGNED_INT_IMAGE_2D_ARRAY; + case EbtImage3D: + return GL_IMAGE_3D; + case EbtIImage3D: + return GL_INT_IMAGE_3D; + case EbtUImage3D: + return GL_UNSIGNED_INT_IMAGE_3D; + case EbtImageCube: + return GL_IMAGE_CUBE; + case EbtIImageCube: + return GL_INT_IMAGE_CUBE; + case EbtUImageCube: + return GL_UNSIGNED_INT_IMAGE_CUBE; + case EbtAtomicCounter: + return GL_UNSIGNED_INT_ATOMIC_COUNTER; + default: + UNREACHABLE(); + } + + return GL_NONE; +} + +GLenum GLVariablePrecision(const TType &type) +{ + if (type.getBasicType() == EbtFloat) + { + switch (type.getPrecision()) + { + case EbpHigh: + return GL_HIGH_FLOAT; + case EbpMedium: + return GL_MEDIUM_FLOAT; + case EbpLow: + return GL_LOW_FLOAT; + case EbpUndefined: + // Desktop specs do not use precision + return GL_NONE; + default: + UNREACHABLE(); + } + } + else if (type.getBasicType() == EbtInt || type.getBasicType() == EbtUInt) + { + switch (type.getPrecision()) + { + case EbpHigh: + return GL_HIGH_INT; + case EbpMedium: + return GL_MEDIUM_INT; + case EbpLow: + return GL_LOW_INT; + case EbpUndefined: + // Desktop specs do not use precision + return GL_NONE; + default: + UNREACHABLE(); + } + } + + // Other types (boolean, sampler) don't have a precision + return GL_NONE; +} + +ImmutableString ArrayString(const TType &type) +{ + if (!type.isArray()) + return ImmutableString(""); + + const TVector<unsigned int> &arraySizes = *type.getArraySizes(); + constexpr const size_t kMaxDecimalDigitsPerSize = 10u; + ImmutableStringBuilder arrayString(arraySizes.size() * (kMaxDecimalDigitsPerSize + 2u)); + for (auto arraySizeIter = arraySizes.rbegin(); arraySizeIter != arraySizes.rend(); + ++arraySizeIter) + { + arrayString << "["; + if (*arraySizeIter > 0) + { + arrayString.appendDecimal(*arraySizeIter); + } + arrayString << "]"; + } + return arrayString; +} + +ImmutableString GetTypeName(const TType &type, ShHashFunction64 hashFunction, NameMap *nameMap) +{ + if (type.getBasicType() == EbtStruct) + return HashName(type.getStruct(), hashFunction, nameMap); + else + return ImmutableString(type.getBuiltInTypeNameString()); +} + +bool IsVaryingOut(TQualifier qualifier) +{ + switch (qualifier) + { + case EvqVaryingOut: + case EvqSmoothOut: + case EvqFlatOut: + case EvqCentroidOut: + case EvqVertexOut: + case EvqGeometryOut: + return true; + + default: + break; + } + + return false; +} + +bool IsVaryingIn(TQualifier qualifier) +{ + switch (qualifier) + { + case EvqVaryingIn: + case EvqSmoothIn: + case EvqFlatIn: + case EvqCentroidIn: + case EvqFragmentIn: + case EvqGeometryIn: + return true; + + default: + break; + } + + return false; +} + +bool IsVarying(TQualifier qualifier) +{ + return IsVaryingIn(qualifier) || IsVaryingOut(qualifier); +} + +bool IsGeometryShaderInput(GLenum shaderType, TQualifier qualifier) +{ + return (qualifier == EvqGeometryIn) || + ((shaderType == GL_GEOMETRY_SHADER_EXT) && IsInterpolationIn(qualifier)); +} + +InterpolationType GetInterpolationType(TQualifier qualifier) +{ + switch (qualifier) + { + case EvqFlatIn: + case EvqFlatOut: + return INTERPOLATION_FLAT; + + case EvqSmoothIn: + case EvqSmoothOut: + case EvqVertexOut: + case EvqFragmentIn: + case EvqVaryingIn: + case EvqVaryingOut: + case EvqGeometryIn: + case EvqGeometryOut: + return INTERPOLATION_SMOOTH; + + case EvqCentroidIn: + case EvqCentroidOut: + return INTERPOLATION_CENTROID; + + default: + UNREACHABLE(); +#if !UNREACHABLE_IS_NORETURN + return INTERPOLATION_SMOOTH; +#endif + } +} + +TType GetShaderVariableBasicType(const sh::ShaderVariable &var) +{ + switch (var.type) + { + case GL_BOOL: + return TType(EbtBool); + case GL_BOOL_VEC2: + return TType(EbtBool, 2); + case GL_BOOL_VEC3: + return TType(EbtBool, 3); + case GL_BOOL_VEC4: + return TType(EbtBool, 4); + case GL_FLOAT: + return TType(EbtFloat); + case GL_FLOAT_VEC2: + return TType(EbtFloat, 2); + case GL_FLOAT_VEC3: + return TType(EbtFloat, 3); + case GL_FLOAT_VEC4: + return TType(EbtFloat, 4); + case GL_FLOAT_MAT2: + return TType(EbtFloat, 2, 2); + case GL_FLOAT_MAT3: + return TType(EbtFloat, 3, 3); + case GL_FLOAT_MAT4: + return TType(EbtFloat, 4, 4); + case GL_FLOAT_MAT2x3: + return TType(EbtFloat, 2, 3); + case GL_FLOAT_MAT2x4: + return TType(EbtFloat, 2, 4); + case GL_FLOAT_MAT3x2: + return TType(EbtFloat, 3, 2); + case GL_FLOAT_MAT3x4: + return TType(EbtFloat, 3, 4); + case GL_FLOAT_MAT4x2: + return TType(EbtFloat, 4, 2); + case GL_FLOAT_MAT4x3: + return TType(EbtFloat, 4, 3); + case GL_INT: + return TType(EbtInt); + case GL_INT_VEC2: + return TType(EbtInt, 2); + case GL_INT_VEC3: + return TType(EbtInt, 3); + case GL_INT_VEC4: + return TType(EbtInt, 4); + case GL_UNSIGNED_INT: + return TType(EbtUInt); + case GL_UNSIGNED_INT_VEC2: + return TType(EbtUInt, 2); + case GL_UNSIGNED_INT_VEC3: + return TType(EbtUInt, 3); + case GL_UNSIGNED_INT_VEC4: + return TType(EbtUInt, 4); + default: + UNREACHABLE(); +#if !UNREACHABLE_IS_NORETURN + return TType(); +#endif + } +} + +void DeclareGlobalVariable(TIntermBlock *root, const TVariable *variable) +{ + TIntermDeclaration *declaration = new TIntermDeclaration(); + declaration->appendDeclarator(new TIntermSymbol(variable)); + + TIntermSequence *globalSequence = root->getSequence(); + globalSequence->insert(globalSequence->begin(), declaration); +} + +// GLSL ES 1.0.17 4.6.1 The Invariant Qualifier +bool CanBeInvariantESSL1(TQualifier qualifier) +{ + return IsVaryingIn(qualifier) || IsVaryingOut(qualifier) || + IsBuiltinOutputVariable(qualifier) || + (IsBuiltinFragmentInputVariable(qualifier) && qualifier != EvqFrontFacing); +} + +// GLSL ES 3.00 Revision 6, 4.6.1 The Invariant Qualifier +// GLSL ES 3.10 Revision 4, 4.8.1 The Invariant Qualifier +bool CanBeInvariantESSL3OrGreater(TQualifier qualifier) +{ + return IsVaryingOut(qualifier) || qualifier == EvqFragmentOut || + IsBuiltinOutputVariable(qualifier); +} + +bool IsBuiltinOutputVariable(TQualifier qualifier) +{ + switch (qualifier) + { + case EvqPosition: + case EvqPointSize: + case EvqFragDepth: + case EvqFragDepthEXT: + case EvqFragColor: + case EvqSecondaryFragColorEXT: + case EvqFragData: + case EvqSecondaryFragDataEXT: + return true; + default: + break; + } + return false; +} + +bool IsBuiltinFragmentInputVariable(TQualifier qualifier) +{ + switch (qualifier) + { + case EvqFragCoord: + case EvqPointCoord: + case EvqFrontFacing: + return true; + default: + break; + } + return false; +} + +bool IsShaderOutput(TQualifier qualifier) +{ + return IsVaryingOut(qualifier) || IsBuiltinOutputVariable(qualifier); +} + +bool IsOutputESSL(ShShaderOutput output) +{ + return output == SH_ESSL_OUTPUT; +} + +bool IsOutputGLSL(ShShaderOutput output) +{ + switch (output) + { + case SH_GLSL_130_OUTPUT: + case SH_GLSL_140_OUTPUT: + case SH_GLSL_150_CORE_OUTPUT: + case SH_GLSL_330_CORE_OUTPUT: + case SH_GLSL_400_CORE_OUTPUT: + case SH_GLSL_410_CORE_OUTPUT: + case SH_GLSL_420_CORE_OUTPUT: + case SH_GLSL_430_CORE_OUTPUT: + case SH_GLSL_440_CORE_OUTPUT: + case SH_GLSL_450_CORE_OUTPUT: + case SH_GLSL_COMPATIBILITY_OUTPUT: + return true; + default: + break; + } + return false; +} +bool IsOutputHLSL(ShShaderOutput output) +{ + switch (output) + { + case SH_HLSL_3_0_OUTPUT: + case SH_HLSL_4_1_OUTPUT: + case SH_HLSL_4_0_FL9_3_OUTPUT: + return true; + default: + break; + } + return false; +} +bool IsOutputVulkan(ShShaderOutput output) +{ + return output == SH_GLSL_VULKAN_OUTPUT; +} + +bool IsInShaderStorageBlock(TIntermTyped *node) +{ + TIntermSwizzle *swizzleNode = node->getAsSwizzleNode(); + if (swizzleNode) + { + return IsInShaderStorageBlock(swizzleNode->getOperand()); + } + + TIntermBinary *binaryNode = node->getAsBinaryNode(); + if (binaryNode) + { + switch (binaryNode->getOp()) + { + case EOpIndexDirectInterfaceBlock: + case EOpIndexIndirect: + case EOpIndexDirect: + case EOpIndexDirectStruct: + return IsInShaderStorageBlock(binaryNode->getLeft()); + default: + return false; + } + } + + const TType &type = node->getType(); + return type.getQualifier() == EvqBuffer; +} + +GLenum GetImageInternalFormatType(TLayoutImageInternalFormat iifq) +{ + switch (iifq) + { + case EiifRGBA32F: + return GL_RGBA32F; + case EiifRGBA16F: + return GL_RGBA16F; + case EiifR32F: + return GL_R32F; + case EiifRGBA32UI: + return GL_RGBA32UI; + case EiifRGBA16UI: + return GL_RGBA16UI; + case EiifRGBA8UI: + return GL_RGBA8UI; + case EiifR32UI: + return GL_R32UI; + case EiifRGBA32I: + return GL_RGBA32I; + case EiifRGBA16I: + return GL_RGBA16I; + case EiifRGBA8I: + return GL_RGBA8I; + case EiifR32I: + return GL_R32I; + case EiifRGBA8: + return GL_RGBA8; + case EiifRGBA8_SNORM: + return GL_RGBA8_SNORM; + default: + return GL_NONE; + } +} + +bool IsSpecWithFunctionBodyNewScope(ShShaderSpec shaderSpec, int shaderVersion) +{ + return (shaderVersion == 100 && !sh::IsWebGLBasedSpec(shaderSpec)); +} + +ImplicitTypeConversion GetConversion(TBasicType t1, TBasicType t2) +{ + if (t1 == t2) + return ImplicitTypeConversion::Same; + + switch (t1) + { + case EbtInt: + switch (t2) + { + case EbtInt: + UNREACHABLE(); + break; + case EbtUInt: + return ImplicitTypeConversion::Invalid; + case EbtFloat: + return ImplicitTypeConversion::Left; + default: + return ImplicitTypeConversion::Invalid; + } + break; + case EbtUInt: + switch (t2) + { + case EbtInt: + return ImplicitTypeConversion::Invalid; + case EbtUInt: + UNREACHABLE(); + break; + case EbtFloat: + return ImplicitTypeConversion::Left; + default: + return ImplicitTypeConversion::Invalid; + } + break; + case EbtFloat: + switch (t2) + { + case EbtInt: + case EbtUInt: + return ImplicitTypeConversion::Right; + case EbtFloat: + UNREACHABLE(); + break; + default: + return ImplicitTypeConversion::Invalid; + } + break; + default: + return ImplicitTypeConversion::Invalid; + } + return ImplicitTypeConversion::Invalid; +} + +bool IsValidImplicitConversion(sh::ImplicitTypeConversion conversion, TOperator op) +{ + switch (conversion) + { + case sh::ImplicitTypeConversion::Same: + return true; + case sh::ImplicitTypeConversion::Left: + switch (op) + { + case EOpEqual: + case EOpNotEqual: + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + case EOpAdd: + case EOpSub: + case EOpMul: + case EOpDiv: + return true; + default: + break; + } + break; + case sh::ImplicitTypeConversion::Right: + switch (op) + { + case EOpAssign: + case EOpInitialize: + case EOpEqual: + case EOpNotEqual: + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + case EOpAdd: + case EOpSub: + case EOpMul: + case EOpDiv: + case EOpAddAssign: + case EOpSubAssign: + case EOpMulAssign: + case EOpDivAssign: + return true; + default: + break; + } + break; + case sh::ImplicitTypeConversion::Invalid: + break; + } + return false; +} + +} // namespace sh |