More ground work for Uniform blocks
Moved some of the struct / indexing code
from glslang.y to the ParserHelper class
and prepared it for uniform blocks.
Change-Id: I2d5d380f662f36f04d74783fd542c4b258d3f3a5
Reviewed-on: https://swiftshader-review.googlesource.com/3441
Tested-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <capn@google.com>
diff --git a/src/OpenGL/compiler/ParseHelper.cpp b/src/OpenGL/compiler/ParseHelper.cpp
index 22e38a7..2904cfd 100644
--- a/src/OpenGL/compiler/ParseHelper.cpp
+++ b/src/OpenGL/compiler/ParseHelper.cpp
@@ -981,6 +981,26 @@
return false;
}
+bool TParseContext::functionCallLValueErrorCheck(const TFunction *fnCandidate, TIntermAggregate *aggregate)
+{
+ for(size_t i = 0; i < fnCandidate->getParamCount(); ++i)
+ {
+ TQualifier qual = fnCandidate->getParam(i).type->getQualifier();
+ if(qual == EvqOut || qual == EvqInOut)
+ {
+ TIntermTyped *node = (aggregate->getSequence())[i]->getAsTyped();
+ if(lValueErrorCheck(node->getLine(), "assign", node))
+ {
+ error(node->getLine(),
+ "Constant value cannot be passed for 'out' or 'inout' parameters.", "Error");
+ recover();
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
bool TParseContext::supportsExtension(const char* extension)
{
const TExtensionBehavior& extbehavior = extensionBehavior();
@@ -1403,7 +1423,7 @@
// If there is an embedded/nested struct, it appropriately calls addConstStructNested or addConstStructFromAggr
// function and returns the parse-tree with the values of the embedded/nested struct.
//
-TIntermTyped* TParseContext::addConstStruct(TString& identifier, TIntermTyped* node, TSourceLoc line)
+TIntermTyped* TParseContext::addConstStruct(const TString& identifier, TIntermTyped* node, TSourceLoc line)
{
const TFieldList &fields = node->getType().getStruct()->fields();
TIntermTyped *typedNode;
@@ -1433,13 +1453,394 @@
return typedNode;
}
+//
+// Parse an array index expression
+//
+TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression, const TSourceLoc &location, TIntermTyped *indexExpression)
+{
+ TIntermTyped *indexedExpression = NULL;
+
+ if(!baseExpression->isArray() && !baseExpression->isMatrix() && !baseExpression->isVector())
+ {
+ if(baseExpression->getAsSymbolNode())
+ {
+ error(location, " left of '[' is not of type array, matrix, or vector ",
+ baseExpression->getAsSymbolNode()->getSymbol().c_str());
+ }
+ else
+ {
+ error(location, " left of '[' is not of type array, matrix, or vector ", "expression");
+ }
+ recover();
+ }
+
+ TIntermConstantUnion *indexConstantUnion = indexExpression->getAsConstantUnion();
+
+ if(indexExpression->getQualifier() == EvqConstExpr && indexConstantUnion)
+ {
+ int index = indexConstantUnion->getIConst(0);
+ if(index < 0)
+ {
+ std::stringstream infoStream;
+ infoStream << index;
+ std::string info = infoStream.str();
+ error(location, "negative index", info.c_str());
+ recover();
+ index = 0;
+ }
+ if(baseExpression->getType().getQualifier() == EvqConstExpr)
+ {
+ if(baseExpression->isArray())
+ {
+ // constant folding for arrays
+ indexedExpression = addConstArrayNode(index, baseExpression, location);
+ }
+ else if(baseExpression->isVector())
+ {
+ // constant folding for vectors
+ TVectorFields fields;
+ fields.num = 1;
+ fields.offsets[0] = index; // need to do it this way because v.xy sends fields integer array
+ indexedExpression = addConstVectorNode(fields, baseExpression, location);
+ }
+ else if(baseExpression->isMatrix())
+ {
+ // constant folding for matrices
+ indexedExpression = addConstMatrixNode(index, baseExpression, location);
+ }
+ }
+ else
+ {
+ int safeIndex = -1;
+
+ if(baseExpression->isArray())
+ {
+ if(index >= baseExpression->getType().getArraySize())
+ {
+ std::stringstream extraInfoStream;
+ extraInfoStream << "array index out of range '" << index << "'";
+ std::string extraInfo = extraInfoStream.str();
+ error(location, "", "[", extraInfo.c_str());
+ recover();
+ safeIndex = baseExpression->getType().getArraySize() - 1;
+ }
+ }
+ else if((baseExpression->isVector() || baseExpression->isMatrix()) &&
+ baseExpression->getType().getNominalSize() <= index)
+ {
+ std::stringstream extraInfoStream;
+ extraInfoStream << "field selection out of range '" << index << "'";
+ std::string extraInfo = extraInfoStream.str();
+ error(location, "", "[", extraInfo.c_str());
+ recover();
+ safeIndex = baseExpression->getType().getNominalSize() - 1;
+ }
+
+ // Don't modify the data of the previous constant union, because it can point
+ // to builtins, like gl_MaxDrawBuffers. Instead use a new sanitized object.
+ if(safeIndex != -1)
+ {
+ ConstantUnion *safeConstantUnion = new ConstantUnion();
+ safeConstantUnion->setIConst(safeIndex);
+ indexConstantUnion->replaceConstantUnion(safeConstantUnion);
+ }
+
+ indexedExpression = intermediate.addIndex(EOpIndexDirect, baseExpression, indexExpression, location);
+ }
+ }
+ else
+ {
+ if(baseExpression->isInterfaceBlock())
+ {
+ error(location, "",
+ "[", "array indexes for interface blocks arrays must be constant integral expressions");
+ recover();
+ }
+ // FIXME
+ /*
+ else if(baseExpression->getQualifier() == EvqFragmentOut)
+ {
+ error(location, "", "[", "array indexes for fragment outputs must be constant integral expressions");
+ recover();
+ }
+ */
+
+ indexedExpression = intermediate.addIndex(EOpIndexIndirect, baseExpression, indexExpression, location);
+ }
+
+ if(indexedExpression == 0)
+ {
+ ConstantUnion *unionArray = new ConstantUnion[1];
+ unionArray->setFConst(0.0f);
+ indexedExpression = intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpHigh, EvqConstExpr), location);
+ }
+ else if(baseExpression->isArray())
+ {
+ const TType &baseType = baseExpression->getType();
+ if(baseType.getStruct())
+ {
+ TType copyOfType(baseType.getStruct());
+ indexedExpression->setType(copyOfType);
+ }
+ else if(baseType.isInterfaceBlock())
+ {
+ TType copyOfType(baseType.getInterfaceBlock(), baseType.getQualifier(), baseType.getLayoutQualifier(), 0);
+ indexedExpression->setType(copyOfType);
+ }
+ else
+ {
+ indexedExpression->setType(TType(baseExpression->getBasicType(), baseExpression->getPrecision(),
+ EvqTemporary, static_cast<unsigned char>(baseExpression->getNominalSize()),
+ static_cast<unsigned char>(baseExpression->getSecondarySize())));
+ }
+
+ if(baseExpression->getType().getQualifier() == EvqConstExpr)
+ {
+ indexedExpression->getTypePointer()->setQualifier(EvqConstExpr);
+ }
+ }
+ else if(baseExpression->isMatrix())
+ {
+ TQualifier qualifier = baseExpression->getType().getQualifier() == EvqConstExpr ? EvqConstExpr : EvqTemporary;
+ indexedExpression->setType(TType(baseExpression->getBasicType(), baseExpression->getPrecision(),
+ qualifier, static_cast<unsigned char>(baseExpression->getSecondarySize())));
+ }
+ else if(baseExpression->isVector())
+ {
+ TQualifier qualifier = baseExpression->getType().getQualifier() == EvqConstExpr ? EvqConstExpr : EvqTemporary;
+ indexedExpression->setType(TType(baseExpression->getBasicType(), baseExpression->getPrecision(), qualifier));
+ }
+ else
+ {
+ indexedExpression->setType(baseExpression->getType());
+ }
+
+ return indexedExpression;
+}
+
+TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpression, const TSourceLoc &dotLocation,
+ const TString &fieldString, const TSourceLoc &fieldLocation)
+{
+ TIntermTyped *indexedExpression = NULL;
+
+ if(baseExpression->isArray())
+ {
+ error(fieldLocation, "cannot apply dot operator to an array", ".");
+ recover();
+ }
+
+ if(baseExpression->isVector())
+ {
+ TVectorFields fields;
+ if(!parseVectorFields(fieldString, baseExpression->getNominalSize(), fields, fieldLocation))
+ {
+ fields.num = 1;
+ fields.offsets[0] = 0;
+ recover();
+ }
+
+ if(baseExpression->getType().getQualifier() == EvqConstExpr)
+ {
+ // constant folding for vector fields
+ indexedExpression = addConstVectorNode(fields, baseExpression, fieldLocation);
+ if(indexedExpression == 0)
+ {
+ recover();
+ indexedExpression = baseExpression;
+ }
+ else
+ {
+ indexedExpression->setType(TType(baseExpression->getBasicType(), baseExpression->getPrecision(),
+ EvqConstExpr, (unsigned char)(fieldString).size()));
+ }
+ }
+ else
+ {
+ TString vectorString = fieldString;
+ TIntermTyped *index = intermediate.addSwizzle(fields, fieldLocation);
+ indexedExpression = intermediate.addIndex(EOpVectorSwizzle, baseExpression, index, dotLocation);
+ indexedExpression->setType(TType(baseExpression->getBasicType(), baseExpression->getPrecision(),
+ EvqTemporary, (unsigned char)vectorString.size()));
+ }
+ }
+ else if(baseExpression->isMatrix())
+ {
+ TMatrixFields fields;
+ if(!parseMatrixFields(fieldString, baseExpression->getNominalSize(), baseExpression->getSecondarySize(), fields, fieldLocation))
+ {
+ fields.wholeRow = false;
+ fields.wholeCol = false;
+ fields.row = 0;
+ fields.col = 0;
+ recover();
+ }
+
+ if(fields.wholeRow || fields.wholeCol)
+ {
+ error(dotLocation, " non-scalar fields not implemented yet", ".");
+ recover();
+ ConstantUnion *unionArray = new ConstantUnion[1];
+ unionArray->setIConst(0);
+ TIntermTyped *index = intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConstExpr),
+ fieldLocation);
+ indexedExpression = intermediate.addIndex(EOpIndexDirect, baseExpression, index, dotLocation);
+ indexedExpression->setType(TType(baseExpression->getBasicType(), baseExpression->getPrecision(),
+ EvqTemporary, static_cast<unsigned char>(baseExpression->getNominalSize()),
+ static_cast<unsigned char>(baseExpression->getSecondarySize())));
+ }
+ else
+ {
+ ConstantUnion *unionArray = new ConstantUnion[1];
+ unionArray->setIConst(fields.col * baseExpression->getSecondarySize() + fields.row);
+ TIntermTyped *index = intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConstExpr),
+ fieldLocation);
+ indexedExpression = intermediate.addIndex(EOpIndexDirect, baseExpression, index, dotLocation);
+ indexedExpression->setType(TType(baseExpression->getBasicType(), baseExpression->getPrecision()));
+ }
+ }
+ else if(baseExpression->getBasicType() == EbtStruct)
+ {
+ bool fieldFound = false;
+ const TFieldList &fields = baseExpression->getType().getStruct()->fields();
+ if(fields.empty())
+ {
+ error(dotLocation, "structure has no fields", "Internal Error");
+ recover();
+ indexedExpression = baseExpression;
+ }
+ else
+ {
+ unsigned int i;
+ for(i = 0; i < fields.size(); ++i)
+ {
+ if(fields[i]->name() == fieldString)
+ {
+ fieldFound = true;
+ break;
+ }
+ }
+ if(fieldFound)
+ {
+ if(baseExpression->getType().getQualifier() == EvqConstExpr)
+ {
+ indexedExpression = addConstStruct(fieldString, baseExpression, dotLocation);
+ if(indexedExpression == 0)
+ {
+ recover();
+ indexedExpression = baseExpression;
+ }
+ else
+ {
+ indexedExpression->setType(*fields[i]->type());
+ // change the qualifier of the return type, not of the structure field
+ // as the structure definition is shared between various structures.
+ indexedExpression->getTypePointer()->setQualifier(EvqConstExpr);
+ }
+ }
+ else
+ {
+ ConstantUnion *unionArray = new ConstantUnion[1];
+ unionArray->setIConst(i);
+ TIntermTyped *index = intermediate.addConstantUnion(unionArray, *fields[i]->type(), fieldLocation);
+ indexedExpression = intermediate.addIndex(EOpIndexDirectStruct, baseExpression, index, dotLocation);
+ indexedExpression->setType(*fields[i]->type());
+ }
+ }
+ else
+ {
+ error(dotLocation, " no such field in structure", fieldString.c_str());
+ recover();
+ indexedExpression = baseExpression;
+ }
+ }
+ }
+ else if(baseExpression->isInterfaceBlock())
+ {
+ bool fieldFound = false;
+ const TFieldList &fields = baseExpression->getType().getInterfaceBlock()->fields();
+ if(fields.empty())
+ {
+ error(dotLocation, "interface block has no fields", "Internal Error");
+ recover();
+ indexedExpression = baseExpression;
+ }
+ else
+ {
+ unsigned int i;
+ for(i = 0; i < fields.size(); ++i)
+ {
+ if(fields[i]->name() == fieldString)
+ {
+ fieldFound = true;
+ break;
+ }
+ }
+ if(fieldFound)
+ {
+ ConstantUnion *unionArray = new ConstantUnion[1];
+ unionArray->setIConst(i);
+ TIntermTyped *index = intermediate.addConstantUnion(unionArray, *fields[i]->type(), fieldLocation);
+ indexedExpression = intermediate.addIndex(EOpIndexDirectInterfaceBlock, baseExpression, index,
+ dotLocation);
+ indexedExpression->setType(*fields[i]->type());
+ }
+ else
+ {
+ error(dotLocation, " no such field in interface block", fieldString.c_str());
+ recover();
+ indexedExpression = baseExpression;
+ }
+ }
+ }
+ else
+ {
+ if(shaderVersion < 300)
+ {
+ error(dotLocation, " field selection requires structure, vector, or matrix on left hand side",
+ fieldString.c_str());
+ }
+ else
+ {
+ error(dotLocation,
+ " field selection requires structure, vector, matrix, or interface block on left hand side",
+ fieldString.c_str());
+ }
+ recover();
+ indexedExpression = baseExpression;
+ }
+
+ return indexedExpression;
+}
+
TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierType, const TSourceLoc& qualifierTypeLine)
{
TLayoutQualifier qualifier;
qualifier.location = -1;
+ qualifier.matrixPacking = EmpUnspecified;
+ qualifier.blockStorage = EbsUnspecified;
- if (qualifierType == "location")
+ if(qualifierType == "shared")
+ {
+ qualifier.blockStorage = EbsShared;
+ }
+ else if(qualifierType == "packed")
+ {
+ qualifier.blockStorage = EbsPacked;
+ }
+ else if(qualifierType == "std140")
+ {
+ qualifier.blockStorage = EbsStd140;
+ }
+ else if(qualifierType == "row_major")
+ {
+ qualifier.matrixPacking = EmpRowMajor;
+ }
+ else if(qualifierType == "column_major")
+ {
+ qualifier.matrixPacking = EmpColumnMajor;
+ }
+ else if(qualifierType == "location")
{
error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str(), "location requires an argument");
recover();
@@ -1458,6 +1859,8 @@
TLayoutQualifier qualifier;
qualifier.location = -1;
+ qualifier.matrixPacking = EmpUnspecified;
+ qualifier.blockStorage = EbsUnspecified;
if (qualifierType != "location")
{
@@ -1489,6 +1892,14 @@
{
joinedQualifier.location = rightQualifier.location;
}
+ if(rightQualifier.matrixPacking != EmpUnspecified)
+ {
+ joinedQualifier.matrixPacking = rightQualifier.matrixPacking;
+ }
+ if(rightQualifier.blockStorage != EbsUnspecified)
+ {
+ joinedQualifier.blockStorage = rightQualifier.blockStorage;
+ }
return joinedQualifier;
}
@@ -1539,6 +1950,98 @@
return type;
}
+TFieldList *TParseContext::addStructDeclaratorList(const TPublicType &typeSpecifier, TFieldList *fieldList)
+{
+ if(voidErrorCheck(typeSpecifier.line, (*fieldList)[0]->name(), typeSpecifier))
+ {
+ recover();
+ }
+
+ for(unsigned int i = 0; i < fieldList->size(); ++i)
+ {
+ //
+ // Careful not to replace already known aspects of type, like array-ness
+ //
+ TType *type = (*fieldList)[i]->type();
+ type->setBasicType(typeSpecifier.type);
+ type->setNominalSize(typeSpecifier.primarySize);
+ type->setSecondarySize(typeSpecifier.secondarySize);
+ type->setPrecision(typeSpecifier.precision);
+ type->setQualifier(typeSpecifier.qualifier);
+ type->setLayoutQualifier(typeSpecifier.layoutQualifier);
+
+ // don't allow arrays of arrays
+ if(type->isArray())
+ {
+ if(arrayTypeErrorCheck(typeSpecifier.line, typeSpecifier))
+ recover();
+ }
+ if(typeSpecifier.array)
+ type->setArraySize(typeSpecifier.arraySize);
+ if(typeSpecifier.userDef)
+ {
+ type->setStruct(typeSpecifier.userDef->getStruct());
+ }
+
+ if(structNestingErrorCheck(typeSpecifier.line, *(*fieldList)[i]))
+ {
+ recover();
+ }
+ }
+
+ return fieldList;
+}
+
+TPublicType TParseContext::addStructure(const TSourceLoc &structLine, const TSourceLoc &nameLine,
+ const TString *structName, TFieldList *fieldList)
+{
+ TStructure *structure = new TStructure(structName, fieldList);
+ TType *structureType = new TType(structure);
+
+ // Store a bool in the struct if we're at global scope, to allow us to
+ // skip the local struct scoping workaround in HLSL.
+ structure->setUniqueId(TSymbolTableLevel::nextUniqueId());
+ structure->setAtGlobalScope(symbolTable.atGlobalLevel());
+
+ if(!structName->empty())
+ {
+ if(reservedErrorCheck(nameLine, *structName))
+ {
+ recover();
+ }
+ TVariable *userTypeDef = new TVariable(structName, *structureType, true);
+ if(!symbolTable.declare(*userTypeDef))
+ {
+ error(nameLine, "redefinition", structName->c_str(), "struct");
+ recover();
+ }
+ }
+
+ // ensure we do not specify any storage qualifiers on the struct members
+ for(unsigned int typeListIndex = 0; typeListIndex < fieldList->size(); typeListIndex++)
+ {
+ const TField &field = *(*fieldList)[typeListIndex];
+ const TQualifier qualifier = field.type()->getQualifier();
+ switch(qualifier)
+ {
+ case EvqGlobal:
+ case EvqTemporary:
+ break;
+ default:
+ error(field.line(), "invalid qualifier on struct member", getQualifierString(qualifier));
+ recover();
+ break;
+ }
+ }
+
+ TPublicType publicType;
+ publicType.setBasic(EbtStruct, EvqTemporary, structLine);
+ publicType.userDef = structureType;
+ exitStructDeclaration();
+
+ return publicType;
+}
+
bool TParseContext::enterStructDeclaration(int line, const TString& identifier)
{
++structNestingLevel;
@@ -1559,6 +2062,210 @@
--structNestingLevel;
}
+bool TParseContext::structNestingErrorCheck(const TSourceLoc &line, const TField &field)
+{
+ static const int kWebGLMaxStructNesting = 4;
+
+ if(field.type()->getBasicType() != EbtStruct)
+ {
+ return false;
+ }
+
+ // We're already inside a structure definition at this point, so add
+ // one to the field's struct nesting.
+ if(1 + field.type()->getDeepestStructNesting() > kWebGLMaxStructNesting)
+ {
+ std::stringstream reasonStream;
+ reasonStream << "Reference of struct type "
+ << field.type()->getStruct()->name().c_str()
+ << " exceeds maximum allowed nesting level of "
+ << kWebGLMaxStructNesting;
+ std::string reason = reasonStream.str();
+ error(line, reason.c_str(), field.name().c_str(), "");
+ return true;
+ }
+
+ return false;
+}
+
+TIntermTyped *TParseContext::createUnaryMath(TOperator op, TIntermTyped *child, const TSourceLoc &loc, const TType *funcReturnType)
+{
+ if(child == nullptr)
+ {
+ return nullptr;
+ }
+
+ switch(op)
+ {
+ case EOpLogicalNot:
+ if(child->getBasicType() != EbtBool ||
+ child->isMatrix() ||
+ child->isArray() ||
+ child->isVector())
+ {
+ return nullptr;
+ }
+ break;
+ case EOpBitwiseNot:
+ if((child->getBasicType() != EbtInt && child->getBasicType() != EbtUInt) ||
+ child->isMatrix() ||
+ child->isArray())
+ {
+ return nullptr;
+ }
+ break;
+ case EOpPostIncrement:
+ case EOpPreIncrement:
+ case EOpPostDecrement:
+ case EOpPreDecrement:
+ case EOpNegative:
+ if(child->getBasicType() == EbtStruct ||
+ child->getBasicType() == EbtBool ||
+ child->isArray())
+ {
+ return nullptr;
+ }
+ // Operators for built-ins are already type checked against their prototype.
+ default:
+ break;
+ }
+
+ return intermediate.addUnaryMath(op, child, loc); // FIXME , funcReturnType);
+}
+
+TIntermTyped *TParseContext::addUnaryMath(TOperator op, TIntermTyped *child, const TSourceLoc &loc)
+{
+ TIntermTyped *node = createUnaryMath(op, child, loc, nullptr);
+ if(node == nullptr)
+ {
+ unaryOpError(loc, getOperatorString(op), child->getCompleteString());
+ recover();
+ return child;
+ }
+ return node;
+}
+
+TIntermTyped *TParseContext::addUnaryMathLValue(TOperator op, TIntermTyped *child, const TSourceLoc &loc)
+{
+ if(lValueErrorCheck(loc, getOperatorString(op), child))
+ recover();
+ return addUnaryMath(op, child, loc);
+}
+
+bool TParseContext::binaryOpCommonCheck(TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc)
+{
+ if(left->isArray() || right->isArray())
+ {
+ if(shaderVersion < 300)
+ {
+ error(loc, "Invalid operation for arrays", getOperatorString(op));
+ return false;
+ }
+
+ if(left->isArray() != right->isArray())
+ {
+ error(loc, "array / non-array mismatch", getOperatorString(op));
+ return false;
+ }
+
+ switch(op)
+ {
+ case EOpEqual:
+ case EOpNotEqual:
+ case EOpAssign:
+ case EOpInitialize:
+ break;
+ default:
+ error(loc, "Invalid operation for arrays", getOperatorString(op));
+ return false;
+ }
+ // At this point, size of implicitly sized arrays should be resolved.
+ if(left->getArraySize() != right->getArraySize())
+ {
+ error(loc, "array size mismatch", getOperatorString(op));
+ return false;
+ }
+ }
+
+ // Check ops which require integer / ivec parameters
+ bool isBitShift = false;
+ switch(op)
+ {
+ case EOpBitShiftLeft:
+ case EOpBitShiftRight:
+ case EOpBitShiftLeftAssign:
+ case EOpBitShiftRightAssign:
+ // Unsigned can be bit-shifted by signed and vice versa, but we need to
+ // check that the basic type is an integer type.
+ isBitShift = true;
+ if(!IsInteger(left->getBasicType()) || !IsInteger(right->getBasicType()))
+ {
+ return false;
+ }
+ break;
+ case EOpBitwiseAnd:
+ case EOpBitwiseXor:
+ case EOpBitwiseOr:
+ case EOpBitwiseAndAssign:
+ case EOpBitwiseXorAssign:
+ case EOpBitwiseOrAssign:
+ // It is enough to check the type of only one operand, since later it
+ // is checked that the operand types match.
+ if(!IsInteger(left->getBasicType()))
+ {
+ return false;
+ }
+ break;
+ default:
+ break;
+ }
+
+ // GLSL ES 1.00 and 3.00 do not support implicit type casting.
+ // So the basic type should usually match.
+ if(!isBitShift && left->getBasicType() != right->getBasicType())
+ {
+ return false;
+ }
+
+ // Check that type sizes match exactly on ops that require that.
+ // Also check restrictions for structs that contain arrays or samplers.
+ switch(op)
+ {
+ case EOpAssign:
+ case EOpInitialize:
+ case EOpEqual:
+ case EOpNotEqual:
+ // ESSL 1.00 sections 5.7, 5.8, 5.9
+ if(shaderVersion < 300 && left->getType().isStructureContainingArrays())
+ {
+ error(loc, "undefined operation for structs containing arrays", getOperatorString(op));
+ return false;
+ }
+ // Samplers as l-values are disallowed also in ESSL 3.00, see section 4.1.7,
+ // we interpret the spec so that this extends to structs containing samplers,
+ // similarly to ESSL 1.00 spec.
+ if((shaderVersion < 300 || op == EOpAssign || op == EOpInitialize) &&
+ left->getType().isStructureContainingSamplers())
+ {
+ error(loc, "undefined operation for structs containing samplers", getOperatorString(op));
+ return false;
+ }
+ case EOpLessThan:
+ case EOpGreaterThan:
+ case EOpLessThanEqual:
+ case EOpGreaterThanEqual:
+ if((left->getNominalSize() != right->getNominalSize()) ||
+ (left->getSecondarySize() != right->getSecondarySize()))
+ {
+ return false;
+ }
+ default:
+ break;
+ }
+
+ return true;
+}
+
//
// Parse an array of strings using yyparse.
//