Commit 55bde916 by Olli Etuaho Committed by Jamie Madill

Parse array specifier with a separate grammar rule

This brings the grammar closer to the GLSL ES 3.10 spec. Some corner cases related to handling unsized arrays are fixed. BUG=angleproject:2125 TEST=angle_unittests, angle_end2end_tests Change-Id: I9bcf87b17b97da0e2ec2954d32037c272fde3080 Reviewed-on: https://chromium-review.googlesource.com/738233Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent f7480ad2
...@@ -1030,13 +1030,13 @@ bool TParseContext::checkIsValidTypeAndQualifierForArray(const TSourceLoc &index ...@@ -1030,13 +1030,13 @@ bool TParseContext::checkIsValidTypeAndQualifierForArray(const TSourceLoc &index
// Enforce non-initializer type/qualifier rules. // Enforce non-initializer type/qualifier rules.
void TParseContext::checkCanBeDeclaredWithoutInitializer(const TSourceLoc &line, void TParseContext::checkCanBeDeclaredWithoutInitializer(const TSourceLoc &line,
const TString &identifier, const TString &identifier,
TPublicType *type) TType *type)
{ {
ASSERT(type != nullptr); ASSERT(type != nullptr);
if (type->qualifier == EvqConst) if (type->getQualifier() == EvqConst)
{ {
// Make the qualifier make sense. // Make the qualifier make sense.
type->qualifier = EvqTemporary; type->setQualifier(EvqTemporary);
// Generate informative error messages for ESSL1. // Generate informative error messages for ESSL1.
// In ESSL3 arrays and structures containing arrays can be constant. // In ESSL3 arrays and structures containing arrays can be constant.
...@@ -1053,10 +1053,9 @@ void TParseContext::checkCanBeDeclaredWithoutInitializer(const TSourceLoc &line, ...@@ -1053,10 +1053,9 @@ void TParseContext::checkCanBeDeclaredWithoutInitializer(const TSourceLoc &line,
} }
return; return;
} }
if (type->isUnsizedArray()) // This will make the type sized if it isn't sized yet.
{ checkIsNotUnsizedArray(line, "implicitly sized arrays need to be initialized",
error(line, "implicitly sized arrays need to be initialized", identifier.c_str()); identifier.c_str(), type);
}
} }
// Do some simple checks that are shared between all variable declarations, // Do some simple checks that are shared between all variable declarations,
...@@ -1253,10 +1252,9 @@ void TParseContext::atomicCounterQualifierErrorCheck(const TPublicType &publicTy ...@@ -1253,10 +1252,9 @@ void TParseContext::atomicCounterQualifierErrorCheck(const TPublicType &publicTy
} }
} }
void TParseContext::emptyDeclarationErrorCheck(const TPublicType &publicType, void TParseContext::emptyDeclarationErrorCheck(const TType &type, const TSourceLoc &location)
const TSourceLoc &location)
{ {
if (publicType.isUnsizedArray()) if (type.isUnsizedArray())
{ {
// ESSL3 spec section 4.1.9: Array declaration which leaves the size unspecified is an // ESSL3 spec section 4.1.9: Array declaration which leaves the size unspecified is an
// error. It is assumed that this applies to empty declarations as well. // error. It is assumed that this applies to empty declarations as well.
...@@ -1317,9 +1315,9 @@ void TParseContext::nonEmptyDeclarationErrorCheck(const TPublicType &publicType, ...@@ -1317,9 +1315,9 @@ void TParseContext::nonEmptyDeclarationErrorCheck(const TPublicType &publicType,
{ {
// Valid uniform declarations can't be unsized arrays since uniforms can't be initialized. // Valid uniform declarations can't be unsized arrays since uniforms can't be initialized.
// But invalid shaders may still reach here with an unsized array declaration. // But invalid shaders may still reach here with an unsized array declaration.
if (!publicType.isUnsizedArray()) TType type(publicType);
if (!type.isUnsizedArray())
{ {
TType type(publicType);
checkUniformLocationInRange(identifierLocation, type.getLocationCount(), checkUniformLocationInRange(identifierLocation, type.getLocationCount(),
publicType.layoutQualifier); publicType.layoutQualifier);
} }
...@@ -1805,7 +1803,7 @@ TIntermTyped *TParseContext::parseVariableIdentifier(const TSourceLoc &location, ...@@ -1805,7 +1803,7 @@ TIntermTyped *TParseContext::parseVariableIdentifier(const TSourceLoc &location,
ASSERT(mGeometryShaderInputArraySize > 0u); ASSERT(mGeometryShaderInputArraySize > 0u);
node = new TIntermSymbol(variable->getUniqueId(), variable->getName(), variableType); node = new TIntermSymbol(variable->getUniqueId(), variable->getName(), variableType);
node->getTypePointer()->setArraySize(0, mGeometryShaderInputArraySize); node->getTypePointer()->sizeOutermostUnsizedArray(mGeometryShaderInputArraySize);
} }
else else
{ {
...@@ -1822,13 +1820,12 @@ TIntermTyped *TParseContext::parseVariableIdentifier(const TSourceLoc &location, ...@@ -1822,13 +1820,12 @@ TIntermTyped *TParseContext::parseVariableIdentifier(const TSourceLoc &location,
// Returns true on success. // Returns true on success.
bool TParseContext::executeInitializer(const TSourceLoc &line, bool TParseContext::executeInitializer(const TSourceLoc &line,
const TString &identifier, const TString &identifier,
const TPublicType &pType, TType type,
TIntermTyped *initializer, TIntermTyped *initializer,
TIntermBinary **initNode) TIntermBinary **initNode)
{ {
ASSERT(initNode != nullptr); ASSERT(initNode != nullptr);
ASSERT(*initNode == nullptr); ASSERT(*initNode == nullptr);
TType type = TType(pType);
TVariable *variable = nullptr; TVariable *variable = nullptr;
if (type.isUnsizedArray()) if (type.isUnsizedArray())
...@@ -1943,7 +1940,8 @@ TIntermNode *TParseContext::addConditionInitializer(const TPublicType &pType, ...@@ -1943,7 +1940,8 @@ TIntermNode *TParseContext::addConditionInitializer(const TPublicType &pType,
{ {
checkIsScalarBool(loc, pType); checkIsScalarBool(loc, pType);
TIntermBinary *initNode = nullptr; TIntermBinary *initNode = nullptr;
if (executeInitializer(loc, identifier, pType, initializer, &initNode)) TType type(pType);
if (executeInitializer(loc, identifier, type, initializer, &initNode))
{ {
// The initializer is valid. The init condition needs to have a node - either the // The initializer is valid. The init condition needs to have a node - either the
// initializer node, or a constant node in case the initialized variable is const and won't // initializer node, or a constant node in case the initialized variable is const and won't
...@@ -2315,7 +2313,7 @@ TIntermDeclaration *TParseContext::parseSingleDeclaration( ...@@ -2315,7 +2313,7 @@ TIntermDeclaration *TParseContext::parseSingleDeclaration(
TIntermSymbol *symbol = nullptr; TIntermSymbol *symbol = nullptr;
if (emptyDeclaration) if (emptyDeclaration)
{ {
emptyDeclarationErrorCheck(publicType, identifierOrTypeLocation); emptyDeclarationErrorCheck(type, identifierOrTypeLocation);
// In most cases we don't need to create a symbol node for an empty declaration. // In most cases we don't need to create a symbol node for an empty declaration.
// But if the empty declaration is declaring a struct type, the symbol node will store that. // But if the empty declaration is declaring a struct type, the symbol node will store that.
if (type.getBasicType() == EbtStruct) if (type.getBasicType() == EbtStruct)
...@@ -2331,7 +2329,7 @@ TIntermDeclaration *TParseContext::parseSingleDeclaration( ...@@ -2331,7 +2329,7 @@ TIntermDeclaration *TParseContext::parseSingleDeclaration(
{ {
nonEmptyDeclarationErrorCheck(publicType, identifierOrTypeLocation); nonEmptyDeclarationErrorCheck(publicType, identifierOrTypeLocation);
checkCanBeDeclaredWithoutInitializer(identifierOrTypeLocation, identifier, &publicType); checkCanBeDeclaredWithoutInitializer(identifierOrTypeLocation, identifier, &type);
if (IsAtomicCounter(publicType.getBasicType())) if (IsAtomicCounter(publicType.getBasicType()))
{ {
...@@ -2359,35 +2357,34 @@ TIntermDeclaration *TParseContext::parseSingleDeclaration( ...@@ -2359,35 +2357,34 @@ TIntermDeclaration *TParseContext::parseSingleDeclaration(
return declaration; return declaration;
} }
TIntermDeclaration *TParseContext::parseSingleArrayDeclaration(TPublicType &publicType, TIntermDeclaration *TParseContext::parseSingleArrayDeclaration(TPublicType &elementType,
const TSourceLoc &identifierLocation, const TSourceLoc &identifierLocation,
const TString &identifier, const TString &identifier,
const TSourceLoc &indexLocation, const TSourceLoc &indexLocation,
TIntermTyped *indexExpression) unsigned int arraySize)
{ {
mDeferredNonEmptyDeclarationErrorCheck = false; mDeferredNonEmptyDeclarationErrorCheck = false;
declarationQualifierErrorCheck(publicType.qualifier, publicType.layoutQualifier, declarationQualifierErrorCheck(elementType.qualifier, elementType.layoutQualifier,
identifierLocation); identifierLocation);
nonEmptyDeclarationErrorCheck(publicType, identifierLocation); nonEmptyDeclarationErrorCheck(elementType, identifierLocation);
checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, &publicType);
checkIsValidTypeAndQualifierForArray(indexLocation, publicType); checkIsValidTypeAndQualifierForArray(indexLocation, elementType);
TType arrayType(publicType); TType arrayType(elementType);
arrayType.makeArray(arraySize);
if (indexExpression == nullptr) if (IsGeometryShaderInput(mShaderType, elementType.qualifier))
{ {
if (IsGeometryShaderInput(mShaderType, publicType.qualifier)) if (arrayType.isUnsizedArray())
{ {
// Set size for the unsized geometry shader inputs if they are declared after a valid // Set size for the unsized geometry shader inputs if they are declared after a valid
// input primitive declaration. // input primitive declaration.
if (mGeometryShaderInputPrimitiveType != EptUndefined) if (mGeometryShaderInputPrimitiveType != EptUndefined)
{ {
ASSERT(mGeometryShaderInputArraySize > 0u); ASSERT(mGeometryShaderInputArraySize > 0u);
arrayType.makeArray(mGeometryShaderInputArraySize); arrayType.sizeOutermostUnsizedArray(mGeometryShaderInputArraySize);
} }
else else
{ {
...@@ -2402,27 +2399,17 @@ TIntermDeclaration *TParseContext::parseSingleArrayDeclaration(TPublicType &publ ...@@ -2402,27 +2399,17 @@ TIntermDeclaration *TParseContext::parseSingleArrayDeclaration(TPublicType &publ
} }
else else
{ {
// Unsized array declarations are only allowed in declaring geometry shader inputs. setGeometryShaderInputArraySize(arrayType.getOutermostArraySize(), indexLocation);
error(indexLocation, "Invalid unsized array declaration", "");
} }
} }
else
{
unsigned int size = checkIsValidArraySize(identifierLocation, indexExpression);
if (IsGeometryShaderInput(mShaderType, publicType.qualifier))
{
setGeometryShaderInputArraySize(size, indexLocation);
}
// Make the type an array even if size check failed. checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, &arrayType);
// This ensures useless error messages regarding the variable's non-arrayness won't follow.
arrayType.makeArray(size);
if (IsAtomicCounter(publicType.getBasicType())) if (IsAtomicCounter(elementType.getBasicType()))
{ {
checkAtomicCounterOffsetIsNotOverlapped(publicType, kAtomicCounterArrayStride * size, checkAtomicCounterOffsetIsNotOverlapped(
false, identifierLocation, arrayType); elementType, kAtomicCounterArrayStride * arrayType.getArraySizeProduct(), false,
} identifierLocation, arrayType);
} }
TVariable *variable = nullptr; TVariable *variable = nullptr;
...@@ -2458,7 +2445,8 @@ TIntermDeclaration *TParseContext::parseSingleInitDeclaration(const TPublicType ...@@ -2458,7 +2445,8 @@ TIntermDeclaration *TParseContext::parseSingleInitDeclaration(const TPublicType
declaration->setLine(identifierLocation); declaration->setLine(identifierLocation);
TIntermBinary *initNode = nullptr; TIntermBinary *initNode = nullptr;
if (executeInitializer(identifierLocation, identifier, publicType, initializer, &initNode)) TType type(publicType);
if (executeInitializer(identifierLocation, identifier, type, initializer, &initNode))
{ {
if (initNode) if (initNode)
{ {
...@@ -2469,35 +2457,25 @@ TIntermDeclaration *TParseContext::parseSingleInitDeclaration(const TPublicType ...@@ -2469,35 +2457,25 @@ TIntermDeclaration *TParseContext::parseSingleInitDeclaration(const TPublicType
} }
TIntermDeclaration *TParseContext::parseSingleArrayInitDeclaration( TIntermDeclaration *TParseContext::parseSingleArrayInitDeclaration(
TPublicType &publicType, TPublicType &elementType,
const TSourceLoc &identifierLocation, const TSourceLoc &identifierLocation,
const TString &identifier, const TString &identifier,
const TSourceLoc &indexLocation, const TSourceLoc &indexLocation,
TIntermTyped *indexExpression, unsigned int arraySize,
const TSourceLoc &initLocation, const TSourceLoc &initLocation,
TIntermTyped *initializer) TIntermTyped *initializer)
{ {
mDeferredNonEmptyDeclarationErrorCheck = false; mDeferredNonEmptyDeclarationErrorCheck = false;
declarationQualifierErrorCheck(publicType.qualifier, publicType.layoutQualifier, declarationQualifierErrorCheck(elementType.qualifier, elementType.layoutQualifier,
identifierLocation); identifierLocation);
nonEmptyDeclarationErrorCheck(publicType, identifierLocation); nonEmptyDeclarationErrorCheck(elementType, identifierLocation);
checkIsValidTypeAndQualifierForArray(indexLocation, publicType); checkIsValidTypeAndQualifierForArray(indexLocation, elementType);
TPublicType arrayType(publicType); TType arrayType(elementType);
arrayType.makeArray(arraySize);
unsigned int size = 0u;
// If indexExpression is nullptr, then the array will eventually get its size implicitly from
// the initializer.
if (indexExpression != nullptr)
{
size = checkIsValidArraySize(identifierLocation, indexExpression);
}
// Make the type an array even if size check failed.
// This ensures useless error messages regarding the variable's non-arrayness won't follow.
arrayType.setArraySize(size);
TIntermDeclaration *declaration = new TIntermDeclaration(); TIntermDeclaration *declaration = new TIntermDeclaration();
declaration->setLine(identifierLocation); declaration->setLine(identifierLocation);
...@@ -2586,10 +2564,10 @@ void TParseContext::parseDeclarator(TPublicType &publicType, ...@@ -2586,10 +2564,10 @@ void TParseContext::parseDeclarator(TPublicType &publicType,
checkDeclaratorLocationIsNotSpecified(identifierLocation, publicType); checkDeclaratorLocationIsNotSpecified(identifierLocation, publicType);
checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, &publicType);
TVariable *variable = nullptr; TVariable *variable = nullptr;
TType type(publicType); TType type(publicType);
checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, &type);
if (IsAtomicCounter(publicType.getBasicType())) if (IsAtomicCounter(publicType.getBasicType()))
{ {
checkAtomicCounterOffsetIsNotOverlapped(publicType, kAtomicCounterSize, true, checkAtomicCounterOffsetIsNotOverlapped(publicType, kAtomicCounterSize, true,
...@@ -2605,35 +2583,35 @@ void TParseContext::parseDeclarator(TPublicType &publicType, ...@@ -2605,35 +2583,35 @@ void TParseContext::parseDeclarator(TPublicType &publicType,
} }
} }
void TParseContext::parseArrayDeclarator(TPublicType &publicType, void TParseContext::parseArrayDeclarator(TPublicType &elementType,
const TSourceLoc &identifierLocation, const TSourceLoc &identifierLocation,
const TString &identifier, const TString &identifier,
const TSourceLoc &arrayLocation, const TSourceLoc &arrayLocation,
TIntermTyped *indexExpression, unsigned int arraySize,
TIntermDeclaration *declarationOut) TIntermDeclaration *declarationOut)
{ {
// If the declaration starting this declarator list was empty (example: int,), some checks were // If the declaration starting this declarator list was empty (example: int,), some checks were
// not performed. // not performed.
if (mDeferredNonEmptyDeclarationErrorCheck) if (mDeferredNonEmptyDeclarationErrorCheck)
{ {
nonEmptyDeclarationErrorCheck(publicType, identifierLocation); nonEmptyDeclarationErrorCheck(elementType, identifierLocation);
mDeferredNonEmptyDeclarationErrorCheck = false; mDeferredNonEmptyDeclarationErrorCheck = false;
} }
checkDeclaratorLocationIsNotSpecified(identifierLocation, publicType); checkDeclaratorLocationIsNotSpecified(identifierLocation, elementType);
checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, &publicType);
if (checkIsValidTypeAndQualifierForArray(arrayLocation, publicType)) if (checkIsValidTypeAndQualifierForArray(arrayLocation, elementType))
{ {
TType arrayType = TType(publicType); TType arrayType(elementType);
unsigned int size = checkIsValidArraySize(arrayLocation, indexExpression); arrayType.makeArray(arraySize);
arrayType.makeArray(size);
if (IsAtomicCounter(publicType.getBasicType())) checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, &arrayType);
if (IsAtomicCounter(elementType.getBasicType()))
{ {
checkAtomicCounterOffsetIsNotOverlapped(publicType, kAtomicCounterArrayStride * size, checkAtomicCounterOffsetIsNotOverlapped(
true, identifierLocation, arrayType); elementType, kAtomicCounterArrayStride * arrayType.getArraySizeProduct(), true,
identifierLocation, arrayType);
} }
TVariable *variable = nullptr; TVariable *variable = nullptr;
...@@ -2667,7 +2645,8 @@ void TParseContext::parseInitDeclarator(const TPublicType &publicType, ...@@ -2667,7 +2645,8 @@ void TParseContext::parseInitDeclarator(const TPublicType &publicType,
checkDeclaratorLocationIsNotSpecified(identifierLocation, publicType); checkDeclaratorLocationIsNotSpecified(identifierLocation, publicType);
TIntermBinary *initNode = nullptr; TIntermBinary *initNode = nullptr;
if (executeInitializer(identifierLocation, identifier, publicType, initializer, &initNode)) TType type(publicType);
if (executeInitializer(identifierLocation, identifier, type, initializer, &initNode))
{ {
// //
// build the intermediate representation // build the intermediate representation
...@@ -2679,11 +2658,11 @@ void TParseContext::parseInitDeclarator(const TPublicType &publicType, ...@@ -2679,11 +2658,11 @@ void TParseContext::parseInitDeclarator(const TPublicType &publicType,
} }
} }
void TParseContext::parseArrayInitDeclarator(const TPublicType &publicType, void TParseContext::parseArrayInitDeclarator(const TPublicType &elementType,
const TSourceLoc &identifierLocation, const TSourceLoc &identifierLocation,
const TString &identifier, const TString &identifier,
const TSourceLoc &indexLocation, const TSourceLoc &indexLocation,
TIntermTyped *indexExpression, unsigned int arraySize,
const TSourceLoc &initLocation, const TSourceLoc &initLocation,
TIntermTyped *initializer, TIntermTyped *initializer,
TIntermDeclaration *declarationOut) TIntermDeclaration *declarationOut)
...@@ -2692,26 +2671,16 @@ void TParseContext::parseArrayInitDeclarator(const TPublicType &publicType, ...@@ -2692,26 +2671,16 @@ void TParseContext::parseArrayInitDeclarator(const TPublicType &publicType,
// not performed. // not performed.
if (mDeferredNonEmptyDeclarationErrorCheck) if (mDeferredNonEmptyDeclarationErrorCheck)
{ {
nonEmptyDeclarationErrorCheck(publicType, identifierLocation); nonEmptyDeclarationErrorCheck(elementType, identifierLocation);
mDeferredNonEmptyDeclarationErrorCheck = false; mDeferredNonEmptyDeclarationErrorCheck = false;
} }
checkDeclaratorLocationIsNotSpecified(identifierLocation, publicType); checkDeclaratorLocationIsNotSpecified(identifierLocation, elementType);
checkIsValidTypeAndQualifierForArray(indexLocation, publicType);
TPublicType arrayType(publicType); checkIsValidTypeAndQualifierForArray(indexLocation, elementType);
unsigned int size = 0u; TType arrayType(elementType);
// If indexExpression is nullptr, then the array will eventually get its size implicitly from arrayType.makeArray(arraySize);
// the initializer.
if (indexExpression != nullptr)
{
size = checkIsValidArraySize(identifierLocation, indexExpression);
}
// Make the type an array even if size check failed.
// This ensures useless error messages regarding the variable's non-arrayness won't follow.
arrayType.setArraySize(size);
// initNode will correspond to the whole of "b[n] = initializer". // initNode will correspond to the whole of "b[n] = initializer".
TIntermBinary *initNode = nullptr; TIntermBinary *initNode = nullptr;
...@@ -3124,6 +3093,17 @@ TIntermFunctionPrototype *TParseContext::createPrototypeNodeFromFunction( ...@@ -3124,6 +3093,17 @@ TIntermFunctionPrototype *TParseContext::createPrototypeNodeFromFunction(
error(location, "redefinition", param.name->c_str()); error(location, "redefinition", param.name->c_str());
} }
} }
// Unsized type of a named parameter should have already been checked and sanitized.
ASSERT(!param.type->isUnsizedArray());
}
else
{
if (param.type->isUnsizedArray())
{
error(location, "function parameter array must be sized at compile time", "[]");
// We don't need to size the arrays since the parameter is unnamed and hence
// inaccessible.
}
} }
if (!symbol) if (!symbol)
{ {
...@@ -3389,30 +3369,52 @@ TFunction *TParseContext::addConstructorFunc(const TPublicType &publicType) ...@@ -3389,30 +3369,52 @@ TFunction *TParseContext::addConstructorFunc(const TPublicType &publicType)
return new TFunction(&symbolTable, nullptr, type, EOpConstruct); return new TFunction(&symbolTable, nullptr, type, EOpConstruct);
} }
TParameter TParseContext::parseParameterDeclarator(const TPublicType &publicType, void TParseContext::checkIsNotUnsizedArray(const TSourceLoc &line,
const char *errorMessage,
const char *token,
TType *arrayType)
{
if (arrayType->isUnsizedArray())
{
error(line, errorMessage, token);
arrayType->sizeUnsizedArrays(TVector<unsigned int>());
}
}
TParameter TParseContext::parseParameterDeclarator(TType *type,
const TString *name, const TString *name,
const TSourceLoc &nameLoc) const TSourceLoc &nameLoc)
{ {
if (publicType.getBasicType() == EbtVoid) ASSERT(type);
checkIsNotUnsizedArray(nameLoc, "function parameter array must specify a size", name->c_str(),
type);
if (type->getBasicType() == EbtVoid)
{ {
error(nameLoc, "illegal use of type 'void'", name->c_str()); error(nameLoc, "illegal use of type 'void'", name->c_str());
} }
checkIsNotReserved(nameLoc, *name); checkIsNotReserved(nameLoc, *name);
TType *type = new TType(publicType);
TParameter param = {name, type}; TParameter param = {name, type};
return param; return param;
} }
TParameter TParseContext::parseParameterArrayDeclarator(const TString *identifier, TParameter TParseContext::parseParameterDeclarator(const TPublicType &publicType,
const TSourceLoc &identifierLoc, const TString *name,
TIntermTyped *arraySize, const TSourceLoc &nameLoc)
{
TType *type = new TType(publicType);
return parseParameterDeclarator(type, name, nameLoc);
}
TParameter TParseContext::parseParameterArrayDeclarator(const TString *name,
const TSourceLoc &nameLoc,
unsigned int arraySize,
const TSourceLoc &arrayLoc, const TSourceLoc &arrayLoc,
TPublicType *type) TPublicType *elementType)
{ {
checkArrayElementIsNotArray(arrayLoc, *type); checkArrayElementIsNotArray(arrayLoc, *elementType);
unsigned int size = checkIsValidArraySize(arrayLoc, arraySize); TType *arrayType = new TType(*elementType);
type->setArraySize(size); arrayType->makeArray(arraySize);
return parseParameterDeclarator(*type, identifier, identifierLoc); return parseParameterDeclarator(arrayType, name, nameLoc);
} }
bool TParseContext::checkUnsizedArrayConstructorArgumentDimensionality(TIntermSequence *arguments, bool TParseContext::checkUnsizedArrayConstructorArgumentDimensionality(TIntermSequence *arguments,
...@@ -4599,23 +4601,26 @@ TFieldList *TParseContext::addStructDeclaratorList(const TPublicType &typeSpecif ...@@ -4599,23 +4601,26 @@ TFieldList *TParseContext::addStructDeclaratorList(const TPublicType &typeSpecif
checkWorkGroupSizeIsNotSpecified(typeSpecifier.getLine(), typeSpecifier.layoutQualifier); checkWorkGroupSizeIsNotSpecified(typeSpecifier.getLine(), typeSpecifier.layoutQualifier);
for (unsigned int i = 0; i < declaratorList->size(); ++i) for (TField *declarator : *declaratorList)
{ {
auto declaratorArraySizes = (*declaratorList)[i]->type()->getArraySizes(); auto declaratorArraySizes = declarator->type()->getArraySizes();
// don't allow arrays of arrays // don't allow arrays of arrays
if (!declaratorArraySizes.empty()) if (!declaratorArraySizes.empty())
{ {
checkArrayElementIsNotArray(typeSpecifier.getLine(), typeSpecifier); checkArrayElementIsNotArray(typeSpecifier.getLine(), typeSpecifier);
} }
TType *type = (*declaratorList)[i]->type(); TType *type = declarator->type();
*type = TType(typeSpecifier); *type = TType(typeSpecifier);
for (unsigned int arraySize : declaratorArraySizes) for (unsigned int arraySize : declaratorArraySizes)
{ {
type->makeArray(arraySize); type->makeArray(arraySize);
} }
checkIsNotUnsizedArray(typeSpecifier.getLine(),
"array members of structs must specify a size",
declarator->name().c_str(), type);
checkIsBelowStructNestingLimit(typeSpecifier.getLine(), *(*declaratorList)[i]); checkIsBelowStructNestingLimit(typeSpecifier.getLine(), *declarator);
} }
return declaratorList; return declaratorList;
......
...@@ -148,7 +148,7 @@ class TParseContext : angle::NonCopyable ...@@ -148,7 +148,7 @@ class TParseContext : angle::NonCopyable
void nonEmptyDeclarationErrorCheck(const TPublicType &publicType, void nonEmptyDeclarationErrorCheck(const TPublicType &publicType,
const TSourceLoc &identifierLocation); const TSourceLoc &identifierLocation);
// Done only for empty declarations. // Done only for empty declarations.
void emptyDeclarationErrorCheck(const TPublicType &publicType, const TSourceLoc &location); void emptyDeclarationErrorCheck(const TType &type, const TSourceLoc &location);
void checkLayoutQualifierSupported(const TSourceLoc &location, void checkLayoutQualifierSupported(const TSourceLoc &location,
const TString &layoutQualifierName, const TString &layoutQualifierName,
...@@ -180,7 +180,7 @@ class TParseContext : angle::NonCopyable ...@@ -180,7 +180,7 @@ class TParseContext : angle::NonCopyable
// is not needed in the AST. // is not needed in the AST.
bool executeInitializer(const TSourceLoc &line, bool executeInitializer(const TSourceLoc &line,
const TString &identifier, const TString &identifier,
const TPublicType &pType, TType type,
TIntermTyped *initializer, TIntermTyped *initializer,
TIntermBinary **initNode); TIntermBinary **initNode);
TIntermNode *addConditionInitializer(const TPublicType &pType, TIntermNode *addConditionInitializer(const TPublicType &pType,
...@@ -205,11 +205,11 @@ class TParseContext : angle::NonCopyable ...@@ -205,11 +205,11 @@ class TParseContext : angle::NonCopyable
TIntermDeclaration *parseSingleDeclaration(TPublicType &publicType, TIntermDeclaration *parseSingleDeclaration(TPublicType &publicType,
const TSourceLoc &identifierOrTypeLocation, const TSourceLoc &identifierOrTypeLocation,
const TString &identifier); const TString &identifier);
TIntermDeclaration *parseSingleArrayDeclaration(TPublicType &publicType, TIntermDeclaration *parseSingleArrayDeclaration(TPublicType &elementType,
const TSourceLoc &identifierLocation, const TSourceLoc &identifierLocation,
const TString &identifier, const TString &identifier,
const TSourceLoc &indexLocation, const TSourceLoc &indexLocation,
TIntermTyped *indexExpression); unsigned int arraySize);
TIntermDeclaration *parseSingleInitDeclaration(const TPublicType &publicType, TIntermDeclaration *parseSingleInitDeclaration(const TPublicType &publicType,
const TSourceLoc &identifierLocation, const TSourceLoc &identifierLocation,
const TString &identifier, const TString &identifier,
...@@ -218,11 +218,11 @@ class TParseContext : angle::NonCopyable ...@@ -218,11 +218,11 @@ class TParseContext : angle::NonCopyable
// Parse a declaration like "type a[n] = initializer" // Parse a declaration like "type a[n] = initializer"
// Note that this does not apply to declarations like "type[n] a = initializer" // Note that this does not apply to declarations like "type[n] a = initializer"
TIntermDeclaration *parseSingleArrayInitDeclaration(TPublicType &publicType, TIntermDeclaration *parseSingleArrayInitDeclaration(TPublicType &elementType,
const TSourceLoc &identifierLocation, const TSourceLoc &identifierLocation,
const TString &identifier, const TString &identifier,
const TSourceLoc &indexLocation, const TSourceLoc &indexLocation,
TIntermTyped *indexExpression, unsigned int arraySize,
const TSourceLoc &initLocation, const TSourceLoc &initLocation,
TIntermTyped *initializer); TIntermTyped *initializer);
...@@ -236,11 +236,11 @@ class TParseContext : angle::NonCopyable ...@@ -236,11 +236,11 @@ class TParseContext : angle::NonCopyable
const TSourceLoc &identifierLocation, const TSourceLoc &identifierLocation,
const TString &identifier, const TString &identifier,
TIntermDeclaration *declarationOut); TIntermDeclaration *declarationOut);
void parseArrayDeclarator(TPublicType &publicType, void parseArrayDeclarator(TPublicType &elementType,
const TSourceLoc &identifierLocation, const TSourceLoc &identifierLocation,
const TString &identifier, const TString &identifier,
const TSourceLoc &arrayLocation, const TSourceLoc &arrayLocation,
TIntermTyped *indexExpression, unsigned int arraySize,
TIntermDeclaration *declarationOut); TIntermDeclaration *declarationOut);
void parseInitDeclarator(const TPublicType &publicType, void parseInitDeclarator(const TPublicType &publicType,
const TSourceLoc &identifierLocation, const TSourceLoc &identifierLocation,
...@@ -250,11 +250,11 @@ class TParseContext : angle::NonCopyable ...@@ -250,11 +250,11 @@ class TParseContext : angle::NonCopyable
TIntermDeclaration *declarationOut); TIntermDeclaration *declarationOut);
// Parse a declarator like "a[n] = initializer" // Parse a declarator like "a[n] = initializer"
void parseArrayInitDeclarator(const TPublicType &publicType, void parseArrayInitDeclarator(const TPublicType &elementType,
const TSourceLoc &identifierLocation, const TSourceLoc &identifierLocation,
const TString &identifier, const TString &identifier,
const TSourceLoc &indexLocation, const TSourceLoc &indexLocation,
TIntermTyped *indexExpression, unsigned int arraySize,
const TSourceLoc &initLocation, const TSourceLoc &initLocation,
TIntermTyped *initializer, TIntermTyped *initializer,
TIntermDeclaration *declarationOut); TIntermDeclaration *declarationOut);
...@@ -281,11 +281,12 @@ class TParseContext : angle::NonCopyable ...@@ -281,11 +281,12 @@ class TParseContext : angle::NonCopyable
TParameter parseParameterDeclarator(const TPublicType &publicType, TParameter parseParameterDeclarator(const TPublicType &publicType,
const TString *name, const TString *name,
const TSourceLoc &nameLoc); const TSourceLoc &nameLoc);
TParameter parseParameterArrayDeclarator(const TString *identifier,
const TSourceLoc &identifierLoc, TParameter parseParameterArrayDeclarator(const TString *name,
TIntermTyped *arraySize, const TSourceLoc &nameLoc,
unsigned int arraySize,
const TSourceLoc &arrayLoc, const TSourceLoc &arrayLoc,
TPublicType *type); TPublicType *elementType);
TIntermTyped *addIndexExpression(TIntermTyped *baseExpression, TIntermTyped *addIndexExpression(TIntermTyped *baseExpression,
const TSourceLoc &location, const TSourceLoc &location,
...@@ -453,7 +454,11 @@ class TParseContext : angle::NonCopyable ...@@ -453,7 +454,11 @@ class TParseContext : angle::NonCopyable
void checkCanBeDeclaredWithoutInitializer(const TSourceLoc &line, void checkCanBeDeclaredWithoutInitializer(const TSourceLoc &line,
const TString &identifier, const TString &identifier,
TPublicType *type); TType *type);
TParameter parseParameterDeclarator(TType *type,
const TString *name,
const TSourceLoc &nameLoc);
bool checkIsValidTypeAndQualifierForArray(const TSourceLoc &indexLocation, bool checkIsValidTypeAndQualifierForArray(const TSourceLoc &indexLocation,
const TPublicType &elementType); const TPublicType &elementType);
...@@ -502,6 +507,13 @@ class TParseContext : angle::NonCopyable ...@@ -502,6 +507,13 @@ class TParseContext : angle::NonCopyable
TType type, TType type,
const TSourceLoc &line); const TSourceLoc &line);
// Will size any unsized array type so unsized arrays won't need to be taken into account
// further along the line in parsing.
void checkIsNotUnsizedArray(const TSourceLoc &line,
const char *errorMessage,
const char *token,
TType *arrayType);
TIntermTyped *addBinaryMathInternal(TOperator op, TIntermTyped *addBinaryMathInternal(TOperator op,
TIntermTyped *left, TIntermTyped *left,
TIntermTyped *right, TIntermTyped *right,
......
...@@ -577,6 +577,12 @@ void TType::sizeUnsizedArrays(const TVector<unsigned int> &arraySizes) ...@@ -577,6 +577,12 @@ void TType::sizeUnsizedArrays(const TVector<unsigned int> &arraySizes)
invalidateMangledName(); invalidateMangledName();
} }
void TType::sizeOutermostUnsizedArray(unsigned int arraySize)
{
ASSERT(isArray());
mArraySizes.back() = arraySize;
}
TStructure::TStructure(TSymbolTable *symbolTable, const TString *name, TFieldList *fields) TStructure::TStructure(TSymbolTable *symbolTable, const TString *name, TFieldList *fields)
: TFieldListCollection(name, fields), : TFieldListCollection(name, fields),
mDeepestNesting(0), mDeepestNesting(0),
......
...@@ -349,6 +349,9 @@ class TType ...@@ -349,6 +349,9 @@ class TType
// than there are sizes in arraySizes, defaults to setting array sizes to 1. // than there are sizes in arraySizes, defaults to setting array sizes to 1.
void sizeUnsizedArrays(const TVector<unsigned int> &arraySizes); void sizeUnsizedArrays(const TVector<unsigned int> &arraySizes);
// Will size the outermost array according to arraySize.
void sizeOutermostUnsizedArray(unsigned int arraySize);
// Note that the array element type might still be an array type in GLSL ES version >= 3.10. // Note that the array element type might still be an array type in GLSL ES version >= 3.10.
void toArrayElementType() void toArrayElementType()
{ {
...@@ -636,7 +639,6 @@ struct TPublicType ...@@ -636,7 +639,6 @@ struct TPublicType
return typeSpecifierNonArray.userDef->containsType(t); return typeSpecifierNonArray.userDef->containsType(t);
} }
bool isUnsizedArray() const { return array && arraySize == 0; }
void setArraySize(int s) void setArraySize(int s)
{ {
array = true; array = true;
......
...@@ -86,6 +86,7 @@ using namespace sh; ...@@ -86,6 +86,7 @@ using namespace sh;
TIntermCase *intermCase; TIntermCase *intermCase;
}; };
union { union {
unsigned int arraySize;
TTypeSpecifierNonArray typeSpecifierNonArray; TTypeSpecifierNonArray typeSpecifierNonArray;
TPublicType type; TPublicType type;
TPrecision precision; TPrecision precision;
...@@ -218,6 +219,8 @@ extern void yyerror(YYLTYPE* yylloc, TParseContext* context, void *scanner, cons ...@@ -218,6 +219,8 @@ extern void yyerror(YYLTYPE* yylloc, TParseContext* context, void *scanner, cons
%type <interm.param> parameter_declaration parameter_declarator parameter_type_specifier %type <interm.param> parameter_declaration parameter_declarator parameter_type_specifier
%type <interm.layoutQualifier> layout_qualifier_id_list layout_qualifier_id %type <interm.layoutQualifier> layout_qualifier_id_list layout_qualifier_id
%type <interm.arraySize> array_specifier
%type <interm.type> fully_specified_type type_specifier %type <interm.type> fully_specified_type type_specifier
%type <interm.precision> precision_qualifier %type <interm.precision> precision_qualifier
...@@ -681,8 +684,8 @@ parameter_declarator ...@@ -681,8 +684,8 @@ parameter_declarator
: type_specifier identifier { : type_specifier identifier {
$$ = context->parseParameterDeclarator($1, $2.string, @2); $$ = context->parseParameterDeclarator($1, $2.string, @2);
} }
| type_specifier identifier LEFT_BRACKET constant_expression RIGHT_BRACKET { | type_specifier identifier array_specifier {
$$ = context->parseParameterArrayDeclarator($2.string, @2, $4, @3, &$1); $$ = context->parseParameterArrayDeclarator($2.string, @2, $3, @3, &$1);
} }
; ;
...@@ -720,19 +723,14 @@ init_declarator_list ...@@ -720,19 +723,14 @@ init_declarator_list
$$ = $1; $$ = $1;
context->parseDeclarator($$.type, @3, *$3.string, $$.intermDeclaration); context->parseDeclarator($$.type, @3, *$3.string, $$.intermDeclaration);
} }
| init_declarator_list COMMA identifier LEFT_BRACKET constant_expression RIGHT_BRACKET { | init_declarator_list COMMA identifier array_specifier {
$$ = $1;
context->parseArrayDeclarator($$.type, @3, *$3.string, @4, $5, $$.intermDeclaration);
}
| init_declarator_list COMMA identifier LEFT_BRACKET RIGHT_BRACKET EQUAL initializer {
ES3_OR_NEWER("[]", @3, "implicitly sized array");
$$ = $1; $$ = $1;
context->parseArrayInitDeclarator($$.type, @3, *$3.string, @4, nullptr, @6, $7, $$.intermDeclaration); context->parseArrayDeclarator($$.type, @3, *$3.string, @4, $4, $$.intermDeclaration);
} }
| init_declarator_list COMMA identifier LEFT_BRACKET constant_expression RIGHT_BRACKET EQUAL initializer { | init_declarator_list COMMA identifier array_specifier EQUAL initializer {
ES3_OR_NEWER("=", @7, "first-class arrays (array initializer)"); ES3_OR_NEWER("=", @5, "first-class arrays (array initializer)");
$$ = $1; $$ = $1;
context->parseArrayInitDeclarator($$.type, @3, *$3.string, @4, $5, @7, $8, $$.intermDeclaration); context->parseArrayInitDeclarator($$.type, @3, *$3.string, @4, $4, @5, $6, $$.intermDeclaration);
} }
| init_declarator_list COMMA identifier EQUAL initializer { | init_declarator_list COMMA identifier EQUAL initializer {
$$ = $1; $$ = $1;
...@@ -749,24 +747,14 @@ single_declaration ...@@ -749,24 +747,14 @@ single_declaration
$$.type = $1; $$.type = $1;
$$.intermDeclaration = context->parseSingleDeclaration($$.type, @2, *$2.string); $$.intermDeclaration = context->parseSingleDeclaration($$.type, @2, *$2.string);
} }
| fully_specified_type identifier LEFT_BRACKET RIGHT_BRACKET { | fully_specified_type identifier array_specifier {
ES3_1_ONLY("[]", @3, "implicitly sized array declaration");
$$.type = $1; $$.type = $1;
$$.intermDeclaration = context->parseSingleArrayDeclaration($$.type, @2, *$2.string, @3, nullptr); $$.intermDeclaration = context->parseSingleArrayDeclaration($$.type, @2, *$2.string, @3, $3);
} }
| fully_specified_type identifier LEFT_BRACKET constant_expression RIGHT_BRACKET { | fully_specified_type identifier array_specifier EQUAL initializer {
ES3_OR_NEWER("[]", @3, "first-class arrays (array initializer)");
$$.type = $1; $$.type = $1;
$$.intermDeclaration = context->parseSingleArrayDeclaration($$.type, @2, *$2.string, @3, $4); $$.intermDeclaration = context->parseSingleArrayInitDeclaration($$.type, @2, *$2.string, @3, $3, @4, $5);
}
| fully_specified_type identifier LEFT_BRACKET RIGHT_BRACKET EQUAL initializer {
ES3_OR_NEWER("[]", @3, "implicitly sized array");
$$.type = $1;
$$.intermDeclaration = context->parseSingleArrayInitDeclaration($$.type, @2, *$2.string, @3, nullptr, @5, $6);
}
| fully_specified_type identifier LEFT_BRACKET constant_expression RIGHT_BRACKET EQUAL initializer {
ES3_OR_NEWER("=", @6, "first-class arrays (array initializer)");
$$.type = $1;
$$.intermDeclaration = context->parseSingleArrayInitDeclaration($$.type, @2, *$2.string, @3, $4, @6, $7);
} }
| fully_specified_type identifier EQUAL initializer { | fully_specified_type identifier EQUAL initializer {
$$.type = $1; $$.type = $1;
...@@ -941,15 +929,22 @@ type_specifier_no_prec ...@@ -941,15 +929,22 @@ type_specifier_no_prec
: type_specifier_nonarray { : type_specifier_nonarray {
$$.initialize($1, (context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary)); $$.initialize($1, (context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary));
} }
| type_specifier_nonarray LEFT_BRACKET RIGHT_BRACKET { | type_specifier_nonarray array_specifier {
ES3_OR_NEWER("[]", @2, "implicitly sized array");
$$.initialize($1, (context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary)); $$.initialize($1, (context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary));
$$.setArraySize(0); $$.setArraySize($2);
} }
| type_specifier_nonarray LEFT_BRACKET constant_expression RIGHT_BRACKET { ;
$$.initialize($1, (context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary));
unsigned int size = context->checkIsValidArraySize(@2, $3); array_specifier
$$.setArraySize(size); : LEFT_BRACKET RIGHT_BRACKET {
ES3_OR_NEWER("[]", @1, "implicitly sized array");
$$ = 0u;
}
| LEFT_BRACKET constant_expression RIGHT_BRACKET {
unsigned int size = context->checkIsValidArraySize(@1, $2);
// Make the type an array even if size check failed.
// This ensures useless error messages regarding a variable's non-arrayness won't follow.
$$ = size;
} }
; ;
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -238,6 +238,7 @@ union YYSTYPE ...@@ -238,6 +238,7 @@ union YYSTYPE
TIntermCase *intermCase; TIntermCase *intermCase;
}; };
union { union {
unsigned int arraySize;
TTypeSpecifierNonArray typeSpecifierNonArray; TTypeSpecifierNonArray typeSpecifierNonArray;
TPublicType type; TPublicType type;
TPrecision precision; TPrecision precision;
......
...@@ -5104,3 +5104,51 @@ TEST_F(FragmentShaderValidationTest, UnsizedInputs) ...@@ -5104,3 +5104,51 @@ TEST_F(FragmentShaderValidationTest, UnsizedInputs)
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog; FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
} }
} }
// Test that unsized struct members are not allowed.
TEST_F(FragmentShaderValidationTest, UnsizedStructMember)
{
const std::string &shaderString =
R"(#version 300 es
precision highp float;
out vec4 color;
struct S
{
int[] foo;
};
void main()
{
color = vec4(1.0);
})";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Test that unsized parameters without a name are not allowed.
// GLSL ES 3.10 section 6.1 Function Definitions.
TEST_F(FragmentShaderValidationTest, UnsizedNamelessParameter)
{
const std::string &shaderString =
R"(#version 300 es
precision highp float;
out vec4 color;
void foo(int[]);
void main()
{
color = vec4(1.0);
})";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
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