diff --git a/Test/baseResults/420.vert.out b/Test/baseResults/420.vert.out index 50c20939..af98606c 100644 --- a/Test/baseResults/420.vert.out +++ b/Test/baseResults/420.vert.out @@ -4,6 +4,46 @@ ERROR: 0:7: '' : vertex input cannot be further qualified ERROR: 0:11: '' : can only have one interpolation qualifier (flat, smooth, noperspective) ERROR: 0:12: '' : can only have one auxiliary qualifier (centroid, patch, and sample) ERROR: 0:13: 'uniform' : too many storage qualifiers -ERROR: 5 compilation errors. No code generated. +ERROR: 0:21: 'j' : undeclared identifier +ERROR: 0:21: '=' : cannot convert from 'float' to 'int' +ERROR: 0:22: 'k' : undeclared identifier +ERROR: 0:22: '=' : cannot convert from 'float' to 'int' +ERROR: 0:23: 'j' : undeclared identifier +ERROR: 0:23: '=' : cannot convert from 'float' to 'int' +ERROR: 0:27: 'jj' : undeclared identifier +ERROR: 0:27: '=' : cannot convert from 'float' to 'int' +ERROR: 13 compilation errors. No code generated. +ERROR: node is still EOpNull! +0:15 Function Definition: main( (void) +0:15 Function Parameters: +0:? Sequence +0:18 Test condition and select (void) +0:18 Condition +0:18 Compare Equal (bool) +0:18 'i' (int) +0:18 3 (const int) +0:18 true case +0:19 Sequence +0:19 move second child to first child (int) +0:19 'j' (int) +0:19 'i' (int) +0:25 Loop with condition tested first +0:25 Loop Condition +0:25 true (const bool) +0:25 No loop body +0:30 Function Definition: bar(vf4; (void) +0:30 Function Parameters: +0:30 'v' (in 4-component vector of float) +0:? Linker Objects +0:? 'v2' (smooth out 2-component vector of float) +0:? 'badorder' (in 4-component vector of float) +0:? 'badorder2' (invariant smooth out 4-component vector of float) +0:? 'badorder4' (centroid in 4-component vector of float) +0:? 'badorder3' (flat out 4-component vector of float) +0:? 'rep' (smooth flat out 4-component vector of float) +0:? 'rep2' (centroid smooth sample out 4-component vector of float) +0:? 'rep3' (in 4-component vector of float) +0:? 'gl_VertexID' (gl_VertexId int) +0:? 'gl_InstanceID' (gl_InstanceId int) diff --git a/Test/baseResults/matrixError.vert.out b/Test/baseResults/matrixError.vert.out index 94123931..574ede51 100644 --- a/Test/baseResults/matrixError.vert.out +++ b/Test/baseResults/matrixError.vert.out @@ -22,7 +22,11 @@ ERROR: node is still EOpNull! 0:21 'm23' (2X3 matrix of float) 0:21 'm32' (uniform 3X2 matrix of float) 0:21 'v3' (in 3-component vector of float) -0:21 'm24' (float) +0:21 direct index (float) +0:21 direct index (4-component vector of float) +0:21 'm24' (2X4 matrix of float) +0:21 2 (const int) +0:21 4 (const int) 0:? Linker Objects 0:? 'v3' (in 3-component vector of float) 0:? 'm32' (uniform 3X2 matrix of float) diff --git a/Test/matrixError.vert b/Test/matrixError.vert index eb4b439b..3c8cc11f 100644 --- a/Test/matrixError.vert +++ b/Test/matrixError.vert @@ -7,16 +7,16 @@ uniform mat3x2 m32; const mat2x4 m24 = mat2x4(1.0, 2.0, 3.0, 4.0, 3.0, 4.0, - 3.0, 4.0, 5.0); + 3.0, 4.0, 5.0); // ERROR, too many arguments void main() { mat2x3 m23; vec3 a, b; - a = v3 * m23; - b = m32 * v3; - m23.xy; + a = v3 * m23; // ERROR, type mismatch + b = m32 * v3; // ERROR, type mismatch + m23.xy; // ERROR, can't use . - gl_Position = vec4(m23 * m32 * v3, m24[2][4]); + gl_Position = vec4(m23 * m32 * v3, m24[2][4]); // ERROR, 2 and 4 are out of range } diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp index 228850bf..88eeaba9 100644 --- a/glslang/MachineIndependent/ParseHelper.cpp +++ b/glslang/MachineIndependent/ParseHelper.cpp @@ -446,6 +446,9 @@ void C_DECL TParseContext::warn(TSourceLoc loc, const char *szReason, const char va_end(marker); } +// +// Handle seeing a variable identifier in the grammar. +// TIntermTyped* TParseContext::handleVariable(TSourceLoc loc, TSymbol* symbol, TString* string) { TIntermTyped* node = 0; @@ -485,6 +488,495 @@ TIntermTyped* TParseContext::handleVariable(TSourceLoc loc, TSymbol* symbol, TSt return node; } +// +// Handle seeing a base[index] dereference in the grammar. +// +TIntermTyped* TParseContext::handleBracketDereference(TSourceLoc loc, TIntermTyped* base, TIntermTyped* index) +{ + TIntermTyped* result = 0; + + variableCheck(base); + if (! base->isArray() && ! base->isMatrix() && ! base->isVector()) { + if (base->getAsSymbolNode()) + error(loc, " left of '[' is not of type array, matrix, or vector ", base->getAsSymbolNode()->getName().c_str(), ""); + else + error(loc, " left of '[' is not of type array, matrix, or vector ", "expression", ""); + } else if (base->getType().getQualifier().storage == EvqConst && index->getQualifier().storage == EvqConst) { + if (base->isArray()) { + // constant folding for arrays + result = addConstArrayNode(index->getAsConstantUnion()->getUnionArrayPointer()->getIConst(), base, loc); + } else if (base->isVector()) { + // constant folding for vectors + TVectorFields fields; + fields.num = 1; + fields.offsets[0] = index->getAsConstantUnion()->getUnionArrayPointer()->getIConst(); // need to do it this way because v.xy sends fields integer array + result = addConstVectorNode(fields, base, loc); + } else if (base->isMatrix()) { + // constant folding for matrices + result = addConstMatrixNode(index->getAsConstantUnion()->getUnionArrayPointer()->getIConst(), base, loc); + } + } else { + if (index->getQualifier().storage == EvqConst) { + int indexValue = index->getAsConstantUnion()->getUnionArrayPointer()->getIConst(); + if (! base->isArray() && (base->isVector() && base->getType().getVectorSize() <= indexValue || + base->isMatrix() && base->getType().getMatrixCols() <= indexValue)) + error(loc, "", "[", "index out of range '%d'", index->getAsConstantUnion()->getUnionArrayPointer()->getIConst()); + if (base->isArray()) { + if (base->getType().getArraySize() == 0) { + if (base->getType().getMaxArraySize() <= index->getAsConstantUnion()->getUnionArrayPointer()->getIConst()) + arraySetMaxSize(base->getAsSymbolNode(), base->getTypePointer(), index->getAsConstantUnion()->getUnionArrayPointer()->getIConst(), true, loc); + else + arraySetMaxSize(base->getAsSymbolNode(), base->getTypePointer(), 0, false, loc); + } else if ( index->getAsConstantUnion()->getUnionArrayPointer()->getIConst() >= base->getType().getArraySize() || + index->getAsConstantUnion()->getUnionArrayPointer()->getIConst() < 0) + error(loc, "", "[", "array index out of range '%d'", index->getAsConstantUnion()->getUnionArrayPointer()->getIConst()); + } + result = intermediate.addIndex(EOpIndexDirect, base, index, loc); + } else { + if (base->isArray() && base->getType().getArraySize() == 0) + error(loc, "", "[", "array must be redeclared with a size before being indexed with a variable"); + if (base->getBasicType() == EbtBlock) + requireProfile(base->getLoc(), static_cast(~EEsProfileMask), "variable indexing block array"); + if (base->getBasicType() == EbtSampler) { + requireProfile(base->getLoc(), static_cast(ECoreProfileMask | ECompatibilityProfileMask), "variable indexing sampler array"); + profileRequires(base->getLoc(), ECoreProfile, 400, 0, "variable indexing sampler array"); + } + + result = intermediate.addIndex(EOpIndexIndirect, base, index, loc); + } + } + + if (result == 0) { + constUnion *unionArray = new constUnion[1]; + unionArray->setDConst(0.0); + result = intermediate.addConstantUnion(unionArray, TType(EbtFloat, EvqConst), loc); + } else { + TType newType(base->getType()); + if (base->getType().getQualifier().storage == EvqConst && index->getQualifier().storage == EvqConst) + newType.getQualifier().storage = EvqConst; + newType.dereference(); + result->setType(newType); + } + + return result; +} + +// +// Handle seeing a base.field dereference in the grammar. +// +TIntermTyped* TParseContext::handleDotDereference(TSourceLoc loc, TIntermTyped* base, TString& field) +{ + TIntermTyped* result = base; + + variableCheck(base); + if (base->isArray()) { + // + // It can only be a method (e.g., length), which can't be resolved until + // we later see the function calling syntax. Save away the name for now. + // + + if (field == "length") { + profileRequires(loc, ENoProfile, 120, "GL_3DL_array_objects", ".length"); + result = intermediate.addMethod(base, TType(EbtInt), &field, loc); + } else + error(loc, "only the length method is supported for array", field.c_str(), ""); + } else if (base->isVector()) { + TVectorFields fields; + if (! parseVectorFields(loc, field, base->getVectorSize(), fields)) { + fields.num = 1; + fields.offsets[0] = 0; + } + + if (base->getType().getQualifier().storage == EvqConst) { // constant folding for vector fields + result = addConstVectorNode(fields, base, loc); + if (result == 0) + result = base; + else + result->setType(TType(base->getBasicType(), EvqConst, (int) (field).size())); + } else { + if (fields.num == 1) { + constUnion *unionArray = new constUnion[1]; + unionArray->setIConst(fields.offsets[0]); + TIntermTyped* index = intermediate.addConstantUnion(unionArray, TType(EbtInt, EvqConst), loc); + result = intermediate.addIndex(EOpIndexDirect, base, index, loc); + result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision)); + } else { + TString vectorString = field; + TIntermTyped* index = intermediate.addSwizzle(fields, loc); + result = intermediate.addIndex(EOpVectorSwizzle, base, index, loc); + result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision, (int) vectorString.size())); + } + } + } else if (base->isMatrix()) + error(loc, "field selection not allowed on matrix", ".", ""); + else if (base->getBasicType() == EbtStruct || base->getBasicType() == EbtBlock) { + bool fieldFound = false; + TTypeList* fields = base->getType().getStruct(); + if (fields == 0) + error(loc, "structure has no fields", "Internal Error", ""); + else { + unsigned int i; + for (i = 0; i < fields->size(); ++i) { + if ((*fields)[i].type->getFieldName() == field) { + fieldFound = true; + break; + } + } + if (fieldFound) { + if (base->getType().getQualifier().storage == EvqConst) { + result = addConstStruct(field, base, loc); + if (result == 0) + result = base; + else { + result->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. + result->getTypePointer()->getQualifier().storage = EvqConst; + } + } else { + constUnion *unionArray = new constUnion[1]; + unionArray->setIConst(i); + TIntermTyped* index = intermediate.addConstantUnion(unionArray, TType(EbtInt, EvqConst), loc); + result = intermediate.addIndex(EOpIndexDirectStruct, base, index, loc); + result->setType(*(*fields)[i].type); + } + } else + error(loc, " no such field in structure", field.c_str(), ""); + } + } else + error(loc, " dot operator requires structure, array, vector, or matrix on left hand side", field.c_str(), ""); + + return result; +} + +// +// Handle seeing a function prototype in the grammar. +// +TIntermAggregate* TParseContext::handleFunctionPrototype(TSourceLoc loc, TFunction& function) +{ + TSymbol* symbol = symbolTable.find(function.getMangledName()); + TFunction* prevDec = symbol ? symbol->getAsFunction() : 0; + + if (! prevDec) + error(loc, "can't find function name", function.getName().c_str(), ""); + + // + // Note: 'prevDec' could be 'function' if this is the first time we've seen function + // as it would have just been put in the symbol table. Otherwise, we're looking up + // an earlier occurance. + // + if (prevDec && prevDec->isDefined()) { + // + // Then this function already has a body. + // + error(loc, "function already has a body", function.getName().c_str(), ""); + } + if (prevDec) { + prevDec->setDefined(); + // + // Remember the return type for later checking for RETURN statements. + // + currentFunctionType = &(prevDec->getReturnType()); + } else + currentFunctionType = new TType(EbtVoid); + functionReturnsValue = false; + + // + // Raise error message if main function takes any parameters or return anything other than void + // + if (function.getName() == "main") { + if (function.getParamCount() > 0) + error(loc, "function cannot take any parameter(s)", function.getName().c_str(), ""); + if (function.getReturnType().getBasicType() != EbtVoid) + error(loc, "", function.getReturnType().getCompleteTypeString().c_str(), "main function cannot return a value"); + } + + // + // New symbol table scope for body of function plus its arguments + // + symbolTable.push(); + + // + // Insert parameters into the symbol table. + // If the parameter has no name, it's not an error, just don't insert it + // (could be used for unused args). + // + // Also, accumulate the list of parameters into the HIL, so lower level code + // knows where to find parameters. + // + TIntermAggregate* paramNodes = new TIntermAggregate; + for (int i = 0; i < function.getParamCount(); i++) { + TParameter& param = function[i]; + if (param.name != 0) { + TVariable *variable = new TVariable(param.name, *param.type); + // + // Insert the parameters with name in the symbol table. + // + if (! symbolTable.insert(*variable)) { + error(loc, "redefinition", variable->getName().c_str(), ""); + delete variable; + } + // + // Transfer ownership of name pointer to symbol table. + // + param.name = 0; + + // + // Add the parameter to the HIL + // + paramNodes = intermediate.growAggregate(paramNodes, + intermediate.addSymbol(variable->getUniqueId(), + variable->getName(), + variable->getType(), loc), + loc); + } else + paramNodes = intermediate.growAggregate(paramNodes, intermediate.addSymbol(0, "", *param.type, loc), loc); + } + intermediate.setAggregateOperator(paramNodes, EOpParameters, TType(EbtVoid), loc); + loopNestingLevel = 0; + + return paramNodes; +} + +// +// Handle seeing a function call in the grammar. +// +TIntermTyped* TParseContext::handleFunctionCall(TSourceLoc loc, TFunction* fnCall, TIntermNode* intermNode, TIntermAggregate* intermAggregate) +{ + TIntermTyped* result = 0; + + TOperator op = fnCall->getBuiltInOp(); + if (op == EOpArrayLength) { + if (fnCall->getParamCount() > 0) + error(loc, "method does not accept any arguments", fnCall->getName().c_str(), ""); + int length; + if (intermNode->getAsTyped() == 0 || ! intermNode->getAsTyped()->getType().isArray() || intermNode->getAsTyped()->getType().getArraySize() == 0) { + error(loc, "", fnCall->getName().c_str(), "array must be declared with a size before using this method"); + length = 1; + } else + length = intermNode->getAsTyped()->getType().getArraySize(); + + constUnion *unionArray = new constUnion[1]; + unionArray->setIConst(length); + result = intermediate.addConstantUnion(unionArray, TType(EbtInt, EvqConst), loc); + } else if (op != EOpNull) { + // + // Then this should be a constructor. + // Don't go through the symbol table for constructors. + // Their parameters will be verified algorithmically. + // + TType type(EbtVoid); // use this to get the type back + if (constructorError(loc, intermNode, *fnCall, op, type)) { + result = 0; + } else { + // + // It's a constructor, of type 'type'. + // + result = addConstructor(intermNode, type, op, fnCall, loc); + if (result == 0) + error(loc, "cannot construct with these arguments", type.getCompleteString().c_str(), ""); + } + + if (result == 0) + result = intermediate.setAggregateOperator(0, op, type, loc); + } else { + // + // Not a constructor. Find it in the symbol table. + // + const TFunction* fnCandidate; + bool builtIn; + fnCandidate = findFunction(loc, fnCall, &builtIn); + if (fnCandidate) { + // + // A declared function. But, it might still map to a built-in + // operation. + // + op = fnCandidate->getBuiltInOp(); + if (builtIn && op != EOpNull) { + // A function call mapped to a built-in operation. + result = intermediate.addBuiltInFunctionCall(loc, op, fnCandidate->getParamCount() == 1, intermNode, fnCandidate->getReturnType()); + if (result == 0) { + error(intermNode->getLoc(), " wrong operand type", "Internal Error", + "built in unary operator function. Type: %s", + static_cast(intermNode)->getCompleteString().c_str()); + return 0; + } + } else { + // This is a real function call + result = intermediate.setAggregateOperator(intermAggregate, EOpFunctionCall, fnCandidate->getReturnType(), loc); + + // this is how we know whether the given function is a builtIn function or a user defined function + // if builtIn == false, it's a userDefined -> could be an overloaded builtIn function also + // if builtIn == true, it's definitely a builtIn function with EOpNull + if (!builtIn) + result->getAsAggregate()->setUserDefined(); + result->getAsAggregate()->setName(fnCandidate->getMangledName()); + + TStorageQualifier qual; + TQualifierList& qualifierList = result->getAsAggregate()->getQualifierList(); + for (int i = 0; i < fnCandidate->getParamCount(); ++i) { + qual = (*fnCandidate)[i].type->getQualifier().storage; + if (qual == EvqOut || qual == EvqInOut) { + if (lValueErrorCheck(result->getLoc(), "assign", result->getAsAggregate()->getSequence()[i]->getAsTyped())) + error(intermNode->getLoc(), "Constant value cannot be passed for 'out' or 'inout' parameters.", "Error", ""); + } + qualifierList.push_back(qual); + } + + // built-in texturing functions get their return value precision from the precision of the sampler + if (builtIn && fnCandidate->getReturnType().getQualifier().precision == EpqNone && + fnCandidate->getParamCount() > 0 && (*fnCandidate)[0].type->getBasicType() == EbtSampler) + result->getQualifier().precision = result->getAsAggregate()->getSequence()[0]->getAsTyped()->getQualifier().precision; + } + } else { + // error message was put out by PaFindFunction() + // Put on a dummy node for error recovery + constUnion *unionArray = new constUnion[1]; + unionArray->setDConst(0.0); + result = intermediate.addConstantUnion(unionArray, TType(EbtFloat, EvqConst), loc); + } + } + + return result; +} + +// +// Handle seeing a built-in-type constructor call in the grammar. +// +TFunction* TParseContext::handleConstructorCall(TSourceLoc loc, TPublicType& publicType) +{ + if (publicType.arraySizes) { + profileRequires(loc, ENoProfile, 120, "GL_3DL_array_objects", "arrayed constructor"); + profileRequires(loc, EEsProfile, 300, "GL_3DL_array_objects", "arrayed constructor"); + } + + publicType.qualifier.precision = EpqNone; + if (publicType.userDef) { + TString tempString = ""; + TType type(publicType); + + return new TFunction(&tempString, type, EOpConstructStruct); + } + + TOperator op = EOpNull; + switch (publicType.basicType) { + case EbtFloat: + if (publicType.matrixCols) { + switch (publicType.matrixCols) { + case 2: + switch (publicType.matrixRows) { + case 2: op = EOpConstructMat2x2; break; + case 3: op = EOpConstructMat2x3; break; + case 4: op = EOpConstructMat2x4; break; + default: break; // some compilers want this + } + break; + case 3: + switch (publicType.matrixRows) { + case 2: op = EOpConstructMat3x2; break; + case 3: op = EOpConstructMat3x3; break; + case 4: op = EOpConstructMat3x4; break; + default: break; // some compilers want this + } + break; + case 4: + switch (publicType.matrixRows) { + case 2: op = EOpConstructMat4x2; break; + case 3: op = EOpConstructMat4x3; break; + case 4: op = EOpConstructMat4x4; break; + default: break; // some compilers want this + } + break; + default: break; // some compilers want this + } + } else { + switch(publicType.vectorSize) { + case 1: op = EOpConstructFloat; break; + case 2: op = EOpConstructVec2; break; + case 3: op = EOpConstructVec3; break; + case 4: op = EOpConstructVec4; break; + default: break; // some compilers want this + } + } + break; + case EbtDouble: + if (publicType.matrixCols) { + switch (publicType.matrixCols) { + case 2: + switch (publicType.matrixRows) { + case 2: op = EOpConstructDMat2x2; break; + case 3: op = EOpConstructDMat2x3; break; + case 4: op = EOpConstructDMat2x4; break; + default: break; // some compilers want this + } + break; + case 3: + switch (publicType.matrixRows) { + case 2: op = EOpConstructDMat3x2; break; + case 3: op = EOpConstructDMat3x3; break; + case 4: op = EOpConstructDMat3x4; break; + default: break; // some compilers want this + } + break; + case 4: + switch (publicType.matrixRows) { + case 2: op = EOpConstructDMat4x2; break; + case 3: op = EOpConstructDMat4x3; break; + case 4: op = EOpConstructDMat4x4; break; + default: break; // some compilers want this + } + break; + } + } else { + switch(publicType.vectorSize) { + case 1: op = EOpConstructDouble; break; + case 2: op = EOpConstructDVec2; break; + case 3: op = EOpConstructDVec3; break; + case 4: op = EOpConstructDVec4; break; + default: break; // some compilers want this + } + } + break; + case EbtInt: + switch(publicType.vectorSize) { + case 1: op = EOpConstructInt; break; + case 2: op = EOpConstructIVec2; break; + case 3: op = EOpConstructIVec3; break; + case 4: op = EOpConstructIVec4; break; + default: break; // some compilers want this + } + break; + case EbtUint: + switch(publicType.vectorSize) { + case 1: op = EOpConstructUint; break; + case 2: op = EOpConstructUVec2; break; + case 3: op = EOpConstructUVec3; break; + case 4: op = EOpConstructUVec4; break; + default: break; // some compilers want this + } + break; + case EbtBool: + switch(publicType.vectorSize) { + case 1: op = EOpConstructBool; break; + case 2: op = EOpConstructBVec2; break; + case 3: op = EOpConstructBVec3; break; + case 4: op = EOpConstructBVec4; break; + default: break; // some compilers want this + } + break; + default: break; // some compilers want this + } + if (op == EOpNull) { + error(loc, "cannot construct this type", TType::getBasicString(publicType.basicType), ""); + publicType.basicType = EbtFloat; + op = EOpConstructFloat; + } + TString tempString = ""; + TType type(publicType); + + return new TFunction(&tempString, type, op); +} + // // Same error message for all places assignments don't work. // diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h index 102810ed..68b8b015 100644 --- a/glslang/MachineIndependent/ParseHelper.h +++ b/glslang/MachineIndependent/ParseHelper.h @@ -122,6 +122,12 @@ public: bool reservedErrorCheck(TSourceLoc, const TString& identifier); TIntermTyped* handleVariable(TSourceLoc, TSymbol* symbol, TString* string); + TIntermTyped* handleBracketDereference(TSourceLoc, TIntermTyped* base, TIntermTyped* index); + TIntermTyped* handleDotDereference(TSourceLoc, TIntermTyped* base, TString& field); + TIntermAggregate* handleFunctionPrototype(TSourceLoc, TFunction&); + TIntermTyped* handleFunctionCall(TSourceLoc, TFunction*, TIntermNode*, TIntermAggregate*); + TFunction* handleConstructorCall(TSourceLoc, TPublicType&); + bool parseVectorFields(TSourceLoc, const TString&, int vecSize, TVectorFields&); void assignError(TSourceLoc, const char* op, TString left, TString right); void unaryOpError(TSourceLoc, const char* op, TString operand); diff --git a/glslang/MachineIndependent/glslang.y b/glslang/MachineIndependent/glslang.y index 16b62e59..763f9f41 100644 --- a/glslang/MachineIndependent/glslang.y +++ b/glslang/MachineIndependent/glslang.y @@ -260,157 +260,13 @@ postfix_expression $$ = $1; } | postfix_expression LEFT_BRACKET integer_expression RIGHT_BRACKET { - parseContext.variableCheck($1); - if (!$1->isArray() && !$1->isMatrix() && !$1->isVector()) { - if ($1->getAsSymbolNode()) - parseContext.error($2.loc, " left of '[' is not of type array, matrix, or vector ", $1->getAsSymbolNode()->getName().c_str(), ""); - else - parseContext.error($2.loc, " left of '[' is not of type array, matrix, or vector ", "expression", ""); - } - if ($1->getType().getQualifier().storage == EvqConst && $3->getQualifier().storage == EvqConst) { - if ($1->isArray()) { // constant folding for arrays - $$ = parseContext.addConstArrayNode($3->getAsConstantUnion()->getUnionArrayPointer()->getIConst(), $1, $2.loc); - } else if ($1->isVector()) { // constant folding for vectors - TVectorFields fields; - fields.num = 1; - fields.offsets[0] = $3->getAsConstantUnion()->getUnionArrayPointer()->getIConst(); // need to do it this way because v.xy sends fields integer array - $$ = parseContext.addConstVectorNode(fields, $1, $2.loc); - } else if ($1->isMatrix()) { // constant folding for matrices - $$ = parseContext.addConstMatrixNode($3->getAsConstantUnion()->getUnionArrayPointer()->getIConst(), $1, $2.loc); - } - } else { - if ($3->getQualifier().storage == EvqConst) { - int index = $3->getAsConstantUnion()->getUnionArrayPointer()->getIConst(); - if (! $1->isArray() && ($1->isVector() && $1->getType().getVectorSize() <= index || - $1->isMatrix() && $1->getType().getMatrixCols() <= index)) - parseContext.error($2.loc, "", "[", "index out of range '%d'", $3->getAsConstantUnion()->getUnionArrayPointer()->getIConst()); - else { - if ($1->isArray()) { - if ($1->getType().getArraySize() == 0) { - if ($1->getType().getMaxArraySize() <= $3->getAsConstantUnion()->getUnionArrayPointer()->getIConst()) - parseContext.arraySetMaxSize($1->getAsSymbolNode(), $1->getTypePointer(), $3->getAsConstantUnion()->getUnionArrayPointer()->getIConst(), true, $2.loc); - else - parseContext.arraySetMaxSize($1->getAsSymbolNode(), $1->getTypePointer(), 0, false, $2.loc); - } else if ( $3->getAsConstantUnion()->getUnionArrayPointer()->getIConst() >= $1->getType().getArraySize() || - $3->getAsConstantUnion()->getUnionArrayPointer()->getIConst() < 0) - parseContext.error($2.loc, "", "[", "array index out of range '%d'", $3->getAsConstantUnion()->getUnionArrayPointer()->getIConst()); - } - $$ = parseContext.intermediate.addIndex(EOpIndexDirect, $1, $3, $2.loc); - } - } else { - if ($1->isArray() && $1->getType().getArraySize() == 0) - parseContext.error($2.loc, "", "[", "array must be redeclared with a size before being indexed with a variable"); - if ($1->getBasicType() == EbtBlock) - parseContext.requireProfile($1->getLoc(), static_cast(~EEsProfileMask), "variable indexing block array"); - if ($1->getBasicType() == EbtSampler) { - parseContext.requireProfile($1->getLoc(), static_cast(ECoreProfileMask | ECompatibilityProfileMask), "variable indexing sampler array"); - parseContext.profileRequires($1->getLoc(), ECoreProfile, 400, 0, "variable indexing sampler array"); - } - - $$ = parseContext.intermediate.addIndex(EOpIndexIndirect, $1, $3, $2.loc); - } - } - - if ($$ == 0) { - constUnion *unionArray = new constUnion[1]; - unionArray->setDConst(0.0); - $$ = parseContext.intermediate.addConstantUnion(unionArray, TType(EbtFloat, EvqConst), $2.loc); - } else { - TType newType($1->getType()); - newType.dereference(); - $$->setType(newType); - // TODO: functionality: does this drop const qualification for const[const] ? - } + $$ = parseContext.handleBracketDereference($2.loc, $1, $3); } | function_call { $$ = $1; } | postfix_expression DOT FIELD_SELECTION { - parseContext.variableCheck($1); - if ($1->isArray()) { - // - // It can only be a method (e.g., length), which can't be resolved until - // we later see the function calling syntax. Save away the name for now. - // - - if (*$3.string == "length") { - parseContext.profileRequires($3.loc, ENoProfile, 120, "GL_3DL_array_objects", ".length"); - $$ = parseContext.intermediate.addMethod($1, TType(EbtInt), $3.string, $2.loc); - } else { - parseContext.error($3.loc, "only the length method is supported for array", $3.string->c_str(), ""); - $$ = $1; - } - } else if ($1->isVector()) { - TVectorFields fields; - if (! parseContext.parseVectorFields($3.loc, *$3.string, $1->getVectorSize(), fields)) { - fields.num = 1; - fields.offsets[0] = 0; - } - - if ($1->getType().getQualifier().storage == EvqConst) { // constant folding for vector fields - $$ = parseContext.addConstVectorNode(fields, $1, $3.loc); - if ($$ == 0) - $$ = $1; - else - $$->setType(TType($1->getBasicType(), EvqConst, (int) (*$3.string).size())); - } else { - if (fields.num == 1) { - constUnion *unionArray = new constUnion[1]; - unionArray->setIConst(fields.offsets[0]); - TIntermTyped* index = parseContext.intermediate.addConstantUnion(unionArray, TType(EbtInt, EvqConst), $3.loc); - $$ = parseContext.intermediate.addIndex(EOpIndexDirect, $1, index, $2.loc); - $$->setType(TType($1->getBasicType(), EvqTemporary, $1->getType().getQualifier().precision)); - } else { - TString vectorString = *$3.string; - TIntermTyped* index = parseContext.intermediate.addSwizzle(fields, $3.loc); - $$ = parseContext.intermediate.addIndex(EOpVectorSwizzle, $1, index, $2.loc); - $$->setType(TType($1->getBasicType(), EvqTemporary, $1->getType().getQualifier().precision, (int) vectorString.size())); - } - } - } else if ($1->isMatrix()) - parseContext.error($2.loc, "field selection not allowed on matrix", ".", ""); - else if ($1->getBasicType() == EbtStruct || $1->getBasicType() == EbtBlock) { - bool fieldFound = false; - TTypeList* fields = $1->getType().getStruct(); - if (fields == 0) { - parseContext.error($2.loc, "structure has no fields", "Internal Error", ""); - $$ = $1; - } else { - unsigned int i; - for (i = 0; i < fields->size(); ++i) { - if ((*fields)[i].type->getFieldName() == *$3.string) { - fieldFound = true; - break; - } - } - if (fieldFound) { - if ($1->getType().getQualifier().storage == EvqConst) { - $$ = parseContext.addConstStruct(*$3.string, $1, $2.loc); - if ($$ == 0) - $$ = $1; - else { - $$->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. - $$->getTypePointer()->getQualifier().storage = EvqConst; - } - } else { - constUnion *unionArray = new constUnion[1]; - unionArray->setIConst(i); - TIntermTyped* index = parseContext.intermediate.addConstantUnion(unionArray, TType(EbtInt, EvqConst), $3.loc); - $$ = parseContext.intermediate.addIndex(EOpIndexDirectStruct, $1, index, $2.loc); - $$->setType(*(*fields)[i].type); - } - } else { - parseContext.error($2.loc, " no such field in structure", $3.string->c_str(), ""); - $$ = $1; - } - } - } else { - parseContext.error($2.loc, " dot operator requires structure, array, vector, or matrix on left hand side", $3.string->c_str(), ""); - $$ = $1; - } - // don't delete $3.string, it's from the pool + $$ = parseContext.handleDotDereference($3.loc, $1, *$3.string); } | postfix_expression INC_OP { parseContext.variableCheck($1); @@ -441,99 +297,10 @@ integer_expression function_call : function_call_or_method { - TFunction* fnCall = $1.function; - TOperator op = fnCall->getBuiltInOp(); - if (op == EOpArrayLength) { - if (fnCall->getParamCount() > 0) - parseContext.error($1.loc, "method does not accept any arguments", fnCall->getName().c_str(), ""); - int length; - if ($1.intermNode->getAsTyped() == 0 || ! $1.intermNode->getAsTyped()->getType().isArray() || $1.intermNode->getAsTyped()->getType().getArraySize() == 0) { - parseContext.error($1.loc, "", fnCall->getName().c_str(), "array must be declared with a size before using this method"); - length = 1; - } else - length = $1.intermNode->getAsTyped()->getType().getArraySize(); - - constUnion *unionArray = new constUnion[1]; - unionArray->setIConst(length); - $$ = parseContext.intermediate.addConstantUnion(unionArray, TType(EbtInt, EvqConst), $1.loc); - } else if (op != EOpNull) { - // - // Then this should be a constructor. - // Don't go through the symbol table for constructors. - // Their parameters will be verified algorithmically. - // - TType type(EbtVoid); // use this to get the type back - if (parseContext.constructorError($1.loc, $1.intermNode, *fnCall, op, type)) { - $$ = 0; - } else { - // - // It's a constructor, of type 'type'. - // - $$ = parseContext.addConstructor($1.intermNode, type, op, fnCall, $1.loc); - if ($$ == 0) - parseContext.error($1.loc, "cannot construct with these arguments", type.getCompleteString().c_str(), ""); - } - - if ($$ == 0) - $$ = parseContext.intermediate.setAggregateOperator(0, op, type, $1.loc); - } else { - // - // Not a constructor. Find it in the symbol table. - // - const TFunction* fnCandidate; - bool builtIn; - fnCandidate = parseContext.findFunction($1.loc, fnCall, &builtIn); - if (fnCandidate) { - // - // A declared function. But, it might still map to a built-in - // operation. - // - op = fnCandidate->getBuiltInOp(); - if (builtIn && op != EOpNull) { - // A function call mapped to a built-in operation. - $$ = parseContext.intermediate.addBuiltInFunctionCall($1.loc, op, fnCandidate->getParamCount() == 1, $1.intermNode, fnCandidate->getReturnType()); - if ($$ == 0) { - parseContext.error($1.intermNode->getLoc(), " wrong operand type", "Internal Error", - "built in unary operator function. Type: %s", - static_cast($1.intermNode)->getCompleteString().c_str()); - YYERROR; - } - } else { - // This is a real function call - $$ = parseContext.intermediate.setAggregateOperator($1.intermAggregate, EOpFunctionCall, fnCandidate->getReturnType(), $1.loc); - - // this is how we know whether the given function is a builtIn function or a user defined function - // if builtIn == false, it's a userDefined -> could be an overloaded builtIn function also - // if builtIn == true, it's definitely a builtIn function with EOpNull - if (!builtIn) - $$->getAsAggregate()->setUserDefined(); - $$->getAsAggregate()->setName(fnCandidate->getMangledName()); - - TStorageQualifier qual; - TQualifierList& qualifierList = $$->getAsAggregate()->getQualifierList(); - for (int i = 0; i < fnCandidate->getParamCount(); ++i) { - qual = (*fnCandidate)[i].type->getQualifier().storage; - if (qual == EvqOut || qual == EvqInOut) { - if (parseContext.lValueErrorCheck($$->getLoc(), "assign", $$->getAsAggregate()->getSequence()[i]->getAsTyped())) - parseContext.error($1.intermNode->getLoc(), "Constant value cannot be passed for 'out' or 'inout' parameters.", "Error", ""); - } - qualifierList.push_back(qual); - } - - // built-in texturing functions get their return value precision from the precision of the sampler - if (builtIn && fnCandidate->getReturnType().getQualifier().precision == EpqNone && - fnCandidate->getParamCount() > 0 && (*fnCandidate)[0].type->getBasicType() == EbtSampler) - $$->getQualifier().precision = $$->getAsAggregate()->getSequence()[0]->getAsTyped()->getQualifier().precision; - } - } else { - // error message was put out by PaFindFunction() - // Put on a dummy node for error recovery - constUnion *unionArray = new constUnion[1]; - unionArray->setDConst(0.0); - $$ = parseContext.intermediate.addConstantUnion(unionArray, TType(EbtFloat, EvqConst), $1.loc); - } - } - delete fnCall; + $$ = parseContext.handleFunctionCall($1.loc, $1.function, $1.intermNode, $1.intermAggregate); + if ($$ == 0) + YYERROR; + delete $1.function; } ; @@ -588,142 +355,9 @@ function_call_header function_identifier : type_specifier { - // // Constructor - // - $$.function = 0; $$.intermNode = 0; - - if ($1.arraySizes) { - parseContext.profileRequires($1.loc, ENoProfile, 120, "GL_3DL_array_objects", "arrayed constructor"); - parseContext.profileRequires($1.loc, EEsProfile, 300, "GL_3DL_array_objects", "arrayed constructor"); - } - - $1.qualifier.precision = EpqNone; - if ($1.userDef) { - TString tempString = ""; - TType type($1); - TFunction *function = new TFunction(&tempString, type, EOpConstructStruct); - $$.function = function; - } else { - TOperator op = EOpNull; - switch ($1.basicType) { - case EbtFloat: - if ($1.matrixCols) { - switch ($1.matrixCols) { - case 2: - switch ($1.matrixRows) { - case 2: op = EOpConstructMat2x2; break; - case 3: op = EOpConstructMat2x3; break; - case 4: op = EOpConstructMat2x4; break; - default: break; // some compilers want this - } - break; - case 3: - switch ($1.matrixRows) { - case 2: op = EOpConstructMat3x2; break; - case 3: op = EOpConstructMat3x3; break; - case 4: op = EOpConstructMat3x4; break; - default: break; // some compilers want this - } - break; - case 4: - switch ($1.matrixRows) { - case 2: op = EOpConstructMat4x2; break; - case 3: op = EOpConstructMat4x3; break; - case 4: op = EOpConstructMat4x4; break; - default: break; // some compilers want this - } - break; - default: break; // some compilers want this - } - } else { - switch($1.vectorSize) { - case 1: op = EOpConstructFloat; break; - case 2: op = EOpConstructVec2; break; - case 3: op = EOpConstructVec3; break; - case 4: op = EOpConstructVec4; break; - default: break; // some compilers want this - } - } - break; - case EbtDouble: - if ($1.matrixCols) { - switch ($1.matrixCols) { - case 2: - switch ($1.matrixRows) { - case 2: op = EOpConstructDMat2x2; break; - case 3: op = EOpConstructDMat2x3; break; - case 4: op = EOpConstructDMat2x4; break; - default: break; // some compilers want this - } - break; - case 3: - switch ($1.matrixRows) { - case 2: op = EOpConstructDMat3x2; break; - case 3: op = EOpConstructDMat3x3; break; - case 4: op = EOpConstructDMat3x4; break; - default: break; // some compilers want this - } - break; - case 4: - switch ($1.matrixRows) { - case 2: op = EOpConstructDMat4x2; break; - case 3: op = EOpConstructDMat4x3; break; - case 4: op = EOpConstructDMat4x4; break; - default: break; // some compilers want this - } - break; - } - } else { - switch($1.vectorSize) { - case 1: op = EOpConstructDouble; break; - case 2: op = EOpConstructDVec2; break; - case 3: op = EOpConstructDVec3; break; - case 4: op = EOpConstructDVec4; break; - default: break; // some compilers want this - } - } - break; - case EbtInt: - switch($1.vectorSize) { - case 1: op = EOpConstructInt; break; - case 2: op = EOpConstructIVec2; break; - case 3: op = EOpConstructIVec3; break; - case 4: op = EOpConstructIVec4; break; - default: break; // some compilers want this - } - break; - case EbtUint: - switch($1.vectorSize) { - case 1: op = EOpConstructUint; break; - case 2: op = EOpConstructUVec2; break; - case 3: op = EOpConstructUVec3; break; - case 4: op = EOpConstructUVec4; break; - default: break; // some compilers want this - } - break; - case EbtBool: - switch($1.vectorSize) { - case 1: op = EOpConstructBool; break; - case 2: op = EOpConstructBVec2; break; - case 3: op = EOpConstructBVec3; break; - case 4: op = EOpConstructBVec4; break; - default: break; // some compilers want this - } - break; - default: break; // some compilers want this - } - if (op == EOpNull) { - parseContext.error($1.loc, "cannot construct this type", TType::getBasicString($1.basicType), ""); - $1.basicType = EbtFloat; - op = EOpConstructFloat; - } - TString tempString = ""; - TType type($1); - TFunction *function = new TFunction(&tempString, type, op); - $$.function = function; - } + $$.function = parseContext.handleConstructorCall($1.loc, $1); } | postfix_expression { // @@ -2821,90 +2455,7 @@ external_declaration function_definition : function_prototype { - TFunction& function = *($1.function); - TSymbol* symbol = parseContext.symbolTable.find(function.getMangledName()); - TFunction* prevDec = symbol ? symbol->getAsFunction() : 0; - - if (! prevDec) - parseContext.error($1.loc, "can't find function name", function.getName().c_str(), ""); - - // - // Note: 'prevDec' could be 'function' if this is the first time we've seen function - // as it would have just been put in the symbol table. Otherwise, we're looking up - // an earlier occurance. - // - if (prevDec && prevDec->isDefined()) { - // - // Then this function already has a body. - // - parseContext.error($1.loc, "function already has a body", function.getName().c_str(), ""); - } - if (prevDec) { - prevDec->setDefined(); - // - // Remember the return type for later checking for RETURN statements. - // - parseContext.currentFunctionType = &(prevDec->getReturnType()); - } else - parseContext.currentFunctionType = new TType(EbtVoid); - parseContext.functionReturnsValue = false; - - // - // Raise error message if main function takes any parameters or return anything other than void - // - if (function.getName() == "main") { - if (function.getParamCount() > 0) - parseContext.error($1.loc, "function cannot take any parameter(s)", function.getName().c_str(), ""); - if (function.getReturnType().getBasicType() != EbtVoid) - parseContext.error($1.loc, "", function.getReturnType().getCompleteTypeString().c_str(), "main function cannot return a value"); - } - - // - // New symbol table scope for body of function plus its arguments - // - parseContext.symbolTable.push(); - - // - // Insert parameters into the symbol table. - // If the parameter has no name, it's not an error, just don't insert it - // (could be used for unused args). - // - // Also, accumulate the list of parameters into the HIL, so lower level code - // knows where to find parameters. - // - TIntermAggregate* paramNodes = new TIntermAggregate; - for (int i = 0; i < function.getParamCount(); i++) { - TParameter& param = function[i]; - if (param.name != 0) { - TVariable *variable = new TVariable(param.name, *param.type); - // - // Insert the parameters with name in the symbol table. - // - if (! parseContext.symbolTable.insert(*variable)) { - parseContext.error($1.loc, "redefinition", variable->getName().c_str(), ""); - delete variable; - } - // - // Transfer ownership of name pointer to symbol table. - // - param.name = 0; - - // - // Add the parameter to the HIL - // - paramNodes = parseContext.intermediate.growAggregate( - paramNodes, - parseContext.intermediate.addSymbol(variable->getUniqueId(), - variable->getName(), - variable->getType(), $1.loc), - $1.loc); - } else { - paramNodes = parseContext.intermediate.growAggregate(paramNodes, parseContext.intermediate.addSymbol(0, "", *param.type, $1.loc), $1.loc); - } - } - parseContext.intermediate.setAggregateOperator(paramNodes, EOpParameters, TType(EbtVoid), $1.loc); - $1.intermAggregate = paramNodes; - parseContext.loopNestingLevel = 0; + $1.intermAggregate = parseContext.handleFunctionPrototype($1.loc, *$1.function); } compound_statement_no_new_scope { // May be best done as post process phase on intermediate code