Commit 70866b89 by Martin Radev Committed by Commit Bot

Change grammar to support features from es31

The grammar has been changed so that ES31 grammar is followed more closely. The ES31 grammar is not fully supported, only functionality related to qualifier enumeration is added. The ParseContext is changed so that type qualifiers can be now joined together (i.e. like layout qualifiers). This will allow enumeration of multiple storage qualifiers (i.e. uniform readonly coherent) which is essential for support of ES31 features. Some of the error checks had to be moved closer to the root of the parse tree since some of the information about the expression might be missing. Unfortunately, as there is no explicit ordering imposed by the grammar, additional checks for proper order of qualifiers had to be added. I also included unit tests which test against malformed shaders. BUG=angleproject:1442 TEST=angle_end2end_tests TEST=angle_unittests TEST=dEQP-GLES3.functional.shaders.*precision* TEST=dEQP-GLES3.functional.shaders.*function* TEST=dEQP-GLES2.functional.shaders.* Change-Id: Ib3653a1ed1bfced099a6b2cbf35a7cd480c9100d Reviewed-on: https://chromium-review.googlesource.com/362940 Commit-Queue: Corentin Wallez <cwallez@chromium.org> Reviewed-by: 's avatarOlli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org>
parent b962aab6
...@@ -81,6 +81,8 @@ ...@@ -81,6 +81,8 @@
'compiler/translator/Pragma.h', 'compiler/translator/Pragma.h',
'compiler/translator/PruneEmptyDeclarations.cpp', 'compiler/translator/PruneEmptyDeclarations.cpp',
'compiler/translator/PruneEmptyDeclarations.h', 'compiler/translator/PruneEmptyDeclarations.h',
'compiler/translator/QualifierTypes.h',
'compiler/translator/QualifierTypes.cpp',
'compiler/translator/RecordConstantPrecision.cpp', 'compiler/translator/RecordConstantPrecision.cpp',
'compiler/translator/RecordConstantPrecision.h', 'compiler/translator/RecordConstantPrecision.h',
'compiler/translator/RegenerateStructNames.cpp', 'compiler/translator/RegenerateStructNames.cpp',
......
...@@ -341,10 +341,11 @@ enum TQualifier ...@@ -341,10 +341,11 @@ enum TQualifier
EvqLastFragData, EvqLastFragData,
// GLSL ES 3.0 vertex output and fragment input // GLSL ES 3.0 vertex output and fragment input
EvqSmooth, // Incomplete qualifier, smooth is the default EvqSmooth, // Incomplete qualifier, smooth is the default
EvqFlat, // Incomplete qualifier EvqFlat, // Incomplete qualifier
EvqSmoothOut = EvqSmooth, EvqCentroid, // Incomplete qualifier
EvqFlatOut = EvqFlat, EvqSmoothOut,
EvqFlatOut,
EvqCentroidOut, // Implies smooth EvqCentroidOut, // Implies smooth
EvqSmoothIn, EvqSmoothIn,
EvqFlatIn, EvqFlatIn,
...@@ -363,6 +364,11 @@ enum TQualifier ...@@ -363,6 +364,11 @@ enum TQualifier
EvqLast EvqLast
}; };
inline bool IsQualifierUnspecified(TQualifier qualifier)
{
return (qualifier == EvqTemporary || qualifier == EvqGlobal);
}
enum TLayoutMatrixPacking enum TLayoutMatrixPacking
{ {
EmpUnspecified, EmpUnspecified,
...@@ -482,6 +488,9 @@ inline const char* getQualifierString(TQualifier q) ...@@ -482,6 +488,9 @@ inline const char* getQualifierString(TQualifier q)
case EvqSmoothIn: return "smooth in"; case EvqSmoothIn: return "smooth in";
case EvqFlatIn: return "flat in"; case EvqFlatIn: return "flat in";
case EvqCentroidIn: return "smooth centroid in"; case EvqCentroidIn: return "smooth centroid in";
case EvqCentroid: return "centroid";
case EvqFlat: return "flat";
case EvqSmooth: return "smooth";
case EvqComputeIn: return "in"; case EvqComputeIn: return "in";
case EvqNumWorkGroups: return "NumWorkGroups"; case EvqNumWorkGroups: return "NumWorkGroups";
case EvqWorkGroupSize: return "WorkGroupSize"; case EvqWorkGroupSize: return "WorkGroupSize";
......
...@@ -532,6 +532,7 @@ class TIntermAggregate : public TIntermOperator ...@@ -532,6 +532,7 @@ class TIntermAggregate : public TIntermOperator
TIntermTyped *fold(TInfoSink &infoSink); TIntermTyped *fold(TInfoSink &infoSink);
TIntermSequence *getSequence() { return &mSequence; } TIntermSequence *getSequence() { return &mSequence; }
const TIntermSequence *getSequence() const { return &mSequence; }
void setNameObj(const TName &name) { mName = name; } void setNameObj(const TName &name) { mName = name; }
const TName &getNameObj() const { return mName; } const TName &getNameObj() const { return mName; }
......
...@@ -213,6 +213,12 @@ void TParseContext::checkPrecisionSpecified(const TSourceLoc &line, ...@@ -213,6 +213,12 @@ void TParseContext::checkPrecisionSpecified(const TSourceLoc &line,
{ {
if (!mChecksPrecisionErrors) if (!mChecksPrecisionErrors)
return; return;
if (precision != EbpUndefined && !SupportsPrecision(type))
{
error(line, "illegal type for precision qualifier", getBasicString(type));
}
if (precision == EbpUndefined) if (precision == EbpUndefined)
{ {
switch (type) switch (type)
...@@ -923,27 +929,24 @@ bool TParseContext::declareVariable(const TSourceLoc &line, ...@@ -923,27 +929,24 @@ bool TParseContext::declareVariable(const TSourceLoc &line,
return true; return true;
} }
void TParseContext::checkIsParameterQualifierValid(const TSourceLoc &line, void TParseContext::checkIsParameterQualifierValid(
TQualifier qualifier, const TSourceLoc &line,
TQualifier paramQualifier, const TTypeQualifierBuilder &typeQualifierBuilder,
TType *type) TType *type)
{ {
if (qualifier != EvqConst && qualifier != EvqTemporary) TTypeQualifier typeQualifier = typeQualifierBuilder.getParameterTypeQualifier(&mDiagnostics);
if (typeQualifier.qualifier == EvqOut || typeQualifier.qualifier == EvqInOut)
{ {
error(line, "qualifier not allowed on function parameter", getQualifierString(qualifier)); checkOutParameterIsNotSampler(line, typeQualifier.qualifier, *type);
return;
} }
if (qualifier == EvqConst && paramQualifier != EvqIn)
type->setQualifier(typeQualifier.qualifier);
if (typeQualifier.precision != EbpUndefined)
{ {
error(line, "qualifier not allowed with ", getQualifierString(qualifier), type->setPrecision(typeQualifier.precision);
getQualifierString(paramQualifier));
return;
} }
if (qualifier == EvqConst)
type->setQualifier(EvqConstReadOnly);
else
type->setQualifier(paramQualifier);
} }
bool TParseContext::checkCanUseExtension(const TSourceLoc &line, const TString &extension) bool TParseContext::checkCanUseExtension(const TSourceLoc &line, const TString &extension)
...@@ -1072,12 +1075,27 @@ void TParseContext::functionCallLValueErrorCheck(const TFunction *fnCandidate, ...@@ -1072,12 +1075,27 @@ void TParseContext::functionCallLValueErrorCheck(const TFunction *fnCandidate,
} }
} }
void TParseContext::checkInvariantIsOutVariableES3(const TQualifier qualifier, void TParseContext::checkInvariantVariableQualifier(bool invariant,
const TSourceLoc &invariantLocation) const TQualifier qualifier,
const TSourceLoc &invariantLocation)
{ {
if (!sh::IsVaryingOut(qualifier) && qualifier != EvqFragmentOut) if (!invariant)
return;
if (mShaderVersion < 300)
{ {
error(invariantLocation, "Only out variables can be invariant.", "invariant"); // input variables in the fragment shader can be also qualified as invariant
if (!sh::CanBeInvariantESSL1(qualifier))
{
error(invariantLocation, "Cannot be qualified as invariant.", "invariant");
}
}
else
{
if (!sh::CanBeInvariantESSL3OrGreater(qualifier))
{
error(invariantLocation, "Cannot be qualified as invariant.", "invariant");
}
} }
} }
...@@ -1380,17 +1398,27 @@ bool TParseContext::executeInitializer(const TSourceLoc &line, ...@@ -1380,17 +1398,27 @@ bool TParseContext::executeInitializer(const TSourceLoc &line,
return false; return false;
} }
TPublicType TParseContext::addFullySpecifiedType(TQualifier qualifier, TPublicType TParseContext::addFullySpecifiedType(const TTypeQualifierBuilder &typeQualifierBuilder,
bool invariant,
TLayoutQualifier layoutQualifier,
const TPublicType &typeSpecifier) const TPublicType &typeSpecifier)
{ {
TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics);
TPublicType returnType = typeSpecifier; TPublicType returnType = typeSpecifier;
returnType.qualifier = qualifier; returnType.qualifier = typeQualifier.qualifier;
returnType.invariant = invariant; returnType.invariant = typeQualifier.invariant;
returnType.layoutQualifier = layoutQualifier; returnType.layoutQualifier = typeQualifier.layoutQualifier;
returnType.precision = typeSpecifier.precision;
if (typeQualifier.precision != EbpUndefined)
{
returnType.precision = typeQualifier.precision;
}
checkWorkGroupSizeIsNotSpecified(typeSpecifier.line, layoutQualifier); checkPrecisionSpecified(typeSpecifier.line, returnType.precision, typeSpecifier.type);
checkInvariantVariableQualifier(returnType.invariant, returnType.qualifier, typeSpecifier.line);
checkWorkGroupSizeIsNotSpecified(typeSpecifier.line, returnType.layoutQualifier);
if (mShaderVersion < 300) if (mShaderVersion < 300)
{ {
...@@ -1400,29 +1428,32 @@ TPublicType TParseContext::addFullySpecifiedType(TQualifier qualifier, ...@@ -1400,29 +1428,32 @@ TPublicType TParseContext::addFullySpecifiedType(TQualifier qualifier,
returnType.clearArrayness(); returnType.clearArrayness();
} }
if (qualifier == EvqAttribute && if (returnType.qualifier == EvqAttribute &&
(typeSpecifier.type == EbtBool || typeSpecifier.type == EbtInt)) (typeSpecifier.type == EbtBool || typeSpecifier.type == EbtInt))
{ {
error(typeSpecifier.line, "cannot be bool or int", getQualifierString(qualifier)); error(typeSpecifier.line, "cannot be bool or int",
getQualifierString(returnType.qualifier));
} }
if ((qualifier == EvqVaryingIn || qualifier == EvqVaryingOut) && if ((returnType.qualifier == EvqVaryingIn || returnType.qualifier == EvqVaryingOut) &&
(typeSpecifier.type == EbtBool || typeSpecifier.type == EbtInt)) (typeSpecifier.type == EbtBool || typeSpecifier.type == EbtInt))
{ {
error(typeSpecifier.line, "cannot be bool or int", getQualifierString(qualifier)); error(typeSpecifier.line, "cannot be bool or int",
getQualifierString(returnType.qualifier));
} }
} }
else else
{ {
if (!layoutQualifier.isEmpty()) if (!returnType.layoutQualifier.isEmpty())
{ {
checkIsAtGlobalLevel(typeSpecifier.line, "layout"); checkIsAtGlobalLevel(typeSpecifier.line, "layout");
} }
if (sh::IsVarying(qualifier) || qualifier == EvqVertexIn || qualifier == EvqFragmentOut) if (sh::IsVarying(returnType.qualifier) || returnType.qualifier == EvqVertexIn ||
returnType.qualifier == EvqFragmentOut)
{ {
checkInputOutputTypeIsValidES3(qualifier, typeSpecifier, typeSpecifier.line); checkInputOutputTypeIsValidES3(returnType.qualifier, typeSpecifier, typeSpecifier.line);
} }
if (qualifier == EvqComputeIn) if (returnType.qualifier == EvqComputeIn)
{ {
error(typeSpecifier.line, "'in' can be only used to specify the local group size", error(typeSpecifier.line, "'in' can be only used to specify the local group size",
"in"); "in");
...@@ -1663,40 +1694,58 @@ TIntermAggregate *TParseContext::parseSingleArrayInitDeclaration( ...@@ -1663,40 +1694,58 @@ TIntermAggregate *TParseContext::parseSingleArrayInitDeclaration(
} }
} }
TIntermAggregate *TParseContext::parseInvariantDeclaration(const TSourceLoc &invariantLoc, TIntermAggregate *TParseContext::parseInvariantDeclaration(
const TSourceLoc &identifierLoc, const TTypeQualifierBuilder &typeQualifierBuilder,
const TString *identifier, const TSourceLoc &identifierLoc,
const TSymbol *symbol) const TString *identifier,
const TSymbol *symbol)
{ {
// invariant declaration TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics);
if (!checkIsAtGlobalLevel(invariantLoc, "invariant varying"))
return nullptr;
if (!typeQualifier.invariant)
{
error(identifierLoc, "Expected invariant", identifier->c_str());
return nullptr;
}
if (!checkIsAtGlobalLevel(identifierLoc, "invariant varying"))
{
return nullptr;
}
if (!symbol) if (!symbol)
{ {
error(identifierLoc, "undeclared identifier declared as invariant", identifier->c_str()); error(identifierLoc, "undeclared identifier declared as invariant", identifier->c_str());
return nullptr; return nullptr;
} }
else if (!IsQualifierUnspecified(typeQualifier.qualifier))
{ {
const TString kGlFrontFacing("gl_FrontFacing"); error(identifierLoc, "invariant declaration specifies qualifier",
if (*identifier == kGlFrontFacing) getQualifierString(typeQualifier.qualifier));
{
error(identifierLoc, "identifier should not be declared as invariant",
identifier->c_str());
return nullptr;
}
symbolTable.addInvariantVarying(std::string(identifier->c_str()));
const TVariable *variable = getNamedVariable(identifierLoc, identifier, symbol);
ASSERT(variable);
const TType &type = variable->getType();
TIntermSymbol *intermSymbol =
intermediate.addSymbol(variable->getUniqueId(), *identifier, type, identifierLoc);
TIntermAggregate *aggregate = intermediate.makeAggregate(intermSymbol, identifierLoc);
aggregate->setOp(EOpInvariantDeclaration);
return aggregate;
} }
if (typeQualifier.precision != EbpUndefined)
{
error(identifierLoc, "invariant declaration specifies precision",
getPrecisionString(typeQualifier.precision));
}
if (!typeQualifier.layoutQualifier.isEmpty())
{
error(identifierLoc, "invariant declaration specifies layout", "'layout'");
}
const TVariable *variable = getNamedVariable(identifierLoc, identifier, symbol);
ASSERT(variable);
const TType &type = variable->getType();
checkInvariantVariableQualifier(typeQualifier.invariant, type.getQualifier(),
typeQualifier.line);
symbolTable.addInvariantVarying(std::string(identifier->c_str()));
TIntermSymbol *intermSymbol =
intermediate.addSymbol(variable->getUniqueId(), *identifier, type, identifierLoc);
TIntermAggregate *aggregate = intermediate.makeAggregate(intermSymbol, identifierLoc);
aggregate->setOp(EOpInvariantDeclaration);
return aggregate;
} }
TIntermAggregate *TParseContext::parseDeclarator(TPublicType &publicType, TIntermAggregate *TParseContext::parseDeclarator(TPublicType &publicType,
...@@ -1857,10 +1906,14 @@ TIntermAggregate *TParseContext::parseArrayInitDeclarator(const TPublicType &pub ...@@ -1857,10 +1906,14 @@ TIntermAggregate *TParseContext::parseArrayInitDeclarator(const TPublicType &pub
} }
} }
void TParseContext::parseGlobalLayoutQualifier(const TPublicType &typeQualifier) void TParseContext::parseGlobalLayoutQualifier(const TTypeQualifierBuilder &typeQualifierBuilder)
{ {
TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics);
const TLayoutQualifier layoutQualifier = typeQualifier.layoutQualifier; const TLayoutQualifier layoutQualifier = typeQualifier.layoutQualifier;
checkInvariantVariableQualifier(typeQualifier.invariant, typeQualifier.qualifier,
typeQualifier.line);
// It should never be the case, but some strange parser errors can send us here. // It should never be the case, but some strange parser errors can send us here.
if (layoutQualifier.isEmpty()) if (layoutQualifier.isEmpty())
{ {
...@@ -2421,23 +2474,31 @@ TIntermTyped *TParseContext::addConstStruct(const TString &identifier, ...@@ -2421,23 +2474,31 @@ TIntermTyped *TParseContext::addConstStruct(const TString &identifier,
// //
// Interface/uniform blocks // Interface/uniform blocks
// //
TIntermAggregate *TParseContext::addInterfaceBlock(const TPublicType &typeQualifier, TIntermAggregate *TParseContext::addInterfaceBlock(
const TSourceLoc &nameLine, const TTypeQualifierBuilder &typeQualifierBuilder,
const TString &blockName, const TSourceLoc &nameLine,
TFieldList *fieldList, const TString &blockName,
const TString *instanceName, TFieldList *fieldList,
const TSourceLoc &instanceLine, const TString *instanceName,
TIntermTyped *arrayIndex, const TSourceLoc &instanceLine,
const TSourceLoc &arrayIndexLine) TIntermTyped *arrayIndex,
const TSourceLoc &arrayIndexLine)
{ {
checkIsNotReserved(nameLine, blockName); checkIsNotReserved(nameLine, blockName);
TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics);
if (typeQualifier.qualifier != EvqUniform) if (typeQualifier.qualifier != EvqUniform)
{ {
error(typeQualifier.line, "invalid qualifier:", getQualifierString(typeQualifier.qualifier), error(typeQualifier.line, "invalid qualifier:", getQualifierString(typeQualifier.qualifier),
"interface blocks must be uniform"); "interface blocks must be uniform");
} }
if (typeQualifier.invariant)
{
error(typeQualifier.line, "invalid qualifier on interface block member", "invariant");
}
TLayoutQualifier blockLayoutQualifier = typeQualifier.layoutQualifier; TLayoutQualifier blockLayoutQualifier = typeQualifier.layoutQualifier;
checkLocationIsNotSpecified(typeQualifier.line, blockLayoutQualifier); checkLocationIsNotSpecified(typeQualifier.line, blockLayoutQualifier);
...@@ -2482,6 +2543,11 @@ TIntermAggregate *TParseContext::addInterfaceBlock(const TPublicType &typeQualif ...@@ -2482,6 +2543,11 @@ TIntermAggregate *TParseContext::addInterfaceBlock(const TPublicType &typeQualif
break; break;
} }
if (fieldType->isInvariant())
{
error(field->line(), "invalid qualifier on interface block member", "invariant");
}
// check layout qualifiers // check layout qualifiers
TLayoutQualifier fieldLayoutQualifier = fieldType->getLayoutQualifier(); TLayoutQualifier fieldLayoutQualifier = fieldType->getLayoutQualifier();
checkLocationIsNotSpecified(field->line(), fieldLayoutQualifier); checkLocationIsNotSpecified(field->line(), fieldLayoutQualifier);
...@@ -3140,66 +3206,28 @@ TLayoutQualifier TParseContext::joinLayoutQualifiers(TLayoutQualifier leftQualif ...@@ -3140,66 +3206,28 @@ TLayoutQualifier TParseContext::joinLayoutQualifiers(TLayoutQualifier leftQualif
return joinedQualifier; return joinedQualifier;
} }
TPublicType TParseContext::joinInterpolationQualifiers(const TSourceLoc &interpolationLoc, TFieldList *TParseContext::addStructDeclaratorListWithQualifiers(
TQualifier interpolationQualifier, const TTypeQualifierBuilder &typeQualifierBuilder,
const TSourceLoc &storageLoc, TPublicType *typeSpecifier,
TQualifier storageQualifier) TFieldList *fieldList)
{ {
TQualifier mergedQualifier = EvqSmoothIn; TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics);
if (storageQualifier == EvqFragmentIn) typeSpecifier->qualifier = typeQualifier.qualifier;
{ typeSpecifier->layoutQualifier = typeQualifier.layoutQualifier;
if (interpolationQualifier == EvqSmooth) typeSpecifier->invariant = typeQualifier.invariant;
mergedQualifier = EvqSmoothIn; if (typeQualifier.precision != EbpUndefined)
else if (interpolationQualifier == EvqFlat)
mergedQualifier = EvqFlatIn;
else
UNREACHABLE();
}
else if (storageQualifier == EvqCentroidIn)
{
if (interpolationQualifier == EvqSmooth)
mergedQualifier = EvqCentroidIn;
else if (interpolationQualifier == EvqFlat)
mergedQualifier = EvqFlatIn;
else
UNREACHABLE();
}
else if (storageQualifier == EvqVertexOut)
{
if (interpolationQualifier == EvqSmooth)
mergedQualifier = EvqSmoothOut;
else if (interpolationQualifier == EvqFlat)
mergedQualifier = EvqFlatOut;
else
UNREACHABLE();
}
else if (storageQualifier == EvqCentroidOut)
{
if (interpolationQualifier == EvqSmooth)
mergedQualifier = EvqCentroidOut;
else if (interpolationQualifier == EvqFlat)
mergedQualifier = EvqFlatOut;
else
UNREACHABLE();
}
else
{ {
error(interpolationLoc, typeSpecifier->precision = typeQualifier.precision;
"interpolation qualifier requires a fragment 'in' or vertex 'out' storage qualifier",
getInterpolationString(interpolationQualifier));
mergedQualifier = storageQualifier;
} }
return addStructDeclaratorList(*typeSpecifier, fieldList);
TPublicType type;
type.setBasic(EbtVoid, mergedQualifier, storageLoc);
return type;
} }
TFieldList *TParseContext::addStructDeclaratorList(const TPublicType &typeSpecifier, TFieldList *TParseContext::addStructDeclaratorList(const TPublicType &typeSpecifier,
TFieldList *fieldList) TFieldList *fieldList)
{ {
checkPrecisionSpecified(typeSpecifier.line, typeSpecifier.precision, typeSpecifier.type);
checkIsNonVoid(typeSpecifier.line, (*fieldList)[0]->name(), typeSpecifier.type); checkIsNonVoid(typeSpecifier.line, (*fieldList)[0]->name(), typeSpecifier.type);
checkWorkGroupSizeIsNotSpecified(typeSpecifier.line, typeSpecifier.layoutQualifier); checkWorkGroupSizeIsNotSpecified(typeSpecifier.line, typeSpecifier.layoutQualifier);
...@@ -3216,6 +3244,7 @@ TFieldList *TParseContext::addStructDeclaratorList(const TPublicType &typeSpecif ...@@ -3216,6 +3244,7 @@ TFieldList *TParseContext::addStructDeclaratorList(const TPublicType &typeSpecif
type->setPrecision(typeSpecifier.precision); type->setPrecision(typeSpecifier.precision);
type->setQualifier(typeSpecifier.qualifier); type->setQualifier(typeSpecifier.qualifier);
type->setLayoutQualifier(typeSpecifier.layoutQualifier); type->setLayoutQualifier(typeSpecifier.layoutQualifier);
type->setInvariant(typeSpecifier.invariant);
// don't allow arrays of arrays // don't allow arrays of arrays
if (type->isArray()) if (type->isArray())
...@@ -3273,6 +3302,12 @@ TPublicType TParseContext::addStructure(const TSourceLoc &structLine, ...@@ -3273,6 +3302,12 @@ TPublicType TParseContext::addStructure(const TSourceLoc &structLine,
getQualifierString(qualifier)); getQualifierString(qualifier));
break; break;
} }
if (field.type()->isInvariant())
{
error(field.line(), "invalid qualifier on struct member", "invariant");
}
checkLocationIsNotSpecified(field.line(), field.type()->getLayoutQualifier());
} }
TPublicType publicType; TPublicType publicType;
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "compiler/translator/DirectiveHandler.h" #include "compiler/translator/DirectiveHandler.h"
#include "compiler/translator/Intermediate.h" #include "compiler/translator/Intermediate.h"
#include "compiler/translator/SymbolTable.h" #include "compiler/translator/SymbolTable.h"
#include "compiler/translator/QualifierTypes.h"
#include "compiler/preprocessor/Preprocessor.h" #include "compiler/preprocessor/Preprocessor.h"
struct TMatrixFields struct TMatrixFields
...@@ -67,7 +68,8 @@ class TParseContext : angle::NonCopyable ...@@ -67,7 +68,8 @@ class TParseContext : angle::NonCopyable
mUsesSecondaryOutputs(false), mUsesSecondaryOutputs(false),
mMinProgramTexelOffset(resources.MinProgramTexelOffset), mMinProgramTexelOffset(resources.MinProgramTexelOffset),
mMaxProgramTexelOffset(resources.MaxProgramTexelOffset), mMaxProgramTexelOffset(resources.MaxProgramTexelOffset),
mComputeShaderLocalSizeDeclared(false) mComputeShaderLocalSizeDeclared(false),
mDeclaringFunction(false)
{ {
mComputeShaderLocalSize.fill(-1); mComputeShaderLocalSize.fill(-1);
} }
...@@ -119,6 +121,12 @@ class TParseContext : angle::NonCopyable ...@@ -119,6 +121,12 @@ class TParseContext : angle::NonCopyable
bool isComputeShaderLocalSizeDeclared() const { return mComputeShaderLocalSizeDeclared; } bool isComputeShaderLocalSizeDeclared() const { return mComputeShaderLocalSizeDeclared; }
sh::WorkGroupSize getComputeShaderLocalSize() const; sh::WorkGroupSize getComputeShaderLocalSize() const;
void enterFunctionDeclaration() { mDeclaringFunction = true; }
void exitFunctionDeclaration() { mDeclaringFunction = false; }
bool declaringFunction() const { return mDeclaringFunction; }
// This method is guaranteed to succeed, even if no variable with 'name' exists. // This method is guaranteed to succeed, even if no variable with 'name' exists.
const TVariable *getNamedVariable(const TSourceLoc &location, const TString *name, const TSymbol *symbol); const TVariable *getNamedVariable(const TSourceLoc &location, const TString *name, const TSymbol *symbol);
TIntermTyped *parseVariableIdentifier(const TSourceLoc &location, TIntermTyped *parseVariableIdentifier(const TSourceLoc &location,
...@@ -160,8 +168,7 @@ class TParseContext : angle::NonCopyable ...@@ -160,8 +168,7 @@ class TParseContext : angle::NonCopyable
TQualifier qualifier, TQualifier qualifier,
const TType &type); const TType &type);
void checkIsParameterQualifierValid(const TSourceLoc &line, void checkIsParameterQualifierValid(const TSourceLoc &line,
TQualifier qualifier, const TTypeQualifierBuilder &typeQualifierBuilder,
TQualifier paramQualifier,
TType *type); TType *type);
bool checkCanUseExtension(const TSourceLoc &line, const TString &extension); bool checkCanUseExtension(const TSourceLoc &line, const TString &extension);
void singleDeclarationErrorCheck(const TPublicType &publicType, void singleDeclarationErrorCheck(const TPublicType &publicType,
...@@ -173,8 +180,9 @@ class TParseContext : angle::NonCopyable ...@@ -173,8 +180,9 @@ class TParseContext : angle::NonCopyable
const TLayoutQualifier &layoutQualifier); const TLayoutQualifier &layoutQualifier);
void functionCallLValueErrorCheck(const TFunction *fnCandidate, TIntermAggregate *fnCall); void functionCallLValueErrorCheck(const TFunction *fnCandidate, TIntermAggregate *fnCall);
void checkInvariantIsOutVariableES3(const TQualifier qualifier, void checkInvariantVariableQualifier(bool invariant,
const TSourceLoc &invariantLocation); const TQualifier qualifier,
const TSourceLoc &invariantLocation);
void checkInputOutputTypeIsValidES3(const TQualifier qualifier, void checkInputOutputTypeIsValidES3(const TQualifier qualifier,
const TPublicType &type, const TPublicType &type,
const TSourceLoc &qualifierLocation); const TSourceLoc &qualifierLocation);
...@@ -195,9 +203,7 @@ class TParseContext : angle::NonCopyable ...@@ -195,9 +203,7 @@ class TParseContext : angle::NonCopyable
TIntermTyped *initializer, TIntermTyped *initializer,
TIntermNode **intermNode); TIntermNode **intermNode);
TPublicType addFullySpecifiedType(TQualifier qualifier, TPublicType addFullySpecifiedType(const TTypeQualifierBuilder &typeQualifierBuilder,
bool invariant,
TLayoutQualifier layoutQualifier,
const TPublicType &typeSpecifier); const TPublicType &typeSpecifier);
TIntermAggregate *parseSingleDeclaration(TPublicType &publicType, TIntermAggregate *parseSingleDeclaration(TPublicType &publicType,
...@@ -224,7 +230,7 @@ class TParseContext : angle::NonCopyable ...@@ -224,7 +230,7 @@ class TParseContext : angle::NonCopyable
const TSourceLoc &initLocation, const TSourceLoc &initLocation,
TIntermTyped *initializer); TIntermTyped *initializer);
TIntermAggregate *parseInvariantDeclaration(const TSourceLoc &invariantLoc, TIntermAggregate *parseInvariantDeclaration(const TTypeQualifierBuilder &typeQualifierBuilder,
const TSourceLoc &identifierLoc, const TSourceLoc &identifierLoc,
const TString *identifier, const TString *identifier,
const TSymbol *symbol); const TSymbol *symbol);
...@@ -256,7 +262,7 @@ class TParseContext : angle::NonCopyable ...@@ -256,7 +262,7 @@ class TParseContext : angle::NonCopyable
const TSourceLoc &initLocation, const TSourceLoc &initLocation,
TIntermTyped *initializer); TIntermTyped *initializer);
void parseGlobalLayoutQualifier(const TPublicType &typeQualifier); void parseGlobalLayoutQualifier(const TTypeQualifierBuilder &typeQualifierBuilder);
TIntermAggregate *addFunctionPrototypeDeclaration(const TFunction &function, TIntermAggregate *addFunctionPrototypeDeclaration(const TFunction &function,
const TSourceLoc &location); const TSourceLoc &location);
TIntermAggregate *addFunctionDefinition(const TFunction &function, TIntermAggregate *addFunctionDefinition(const TFunction &function,
...@@ -287,20 +293,24 @@ class TParseContext : angle::NonCopyable ...@@ -287,20 +293,24 @@ class TParseContext : angle::NonCopyable
const TString &fieldString, const TString &fieldString,
const TSourceLoc &fieldLocation); const TSourceLoc &fieldLocation);
TFieldList *addStructDeclaratorListWithQualifiers(
const TTypeQualifierBuilder &typeQualifierBuilder,
TPublicType *typeSpecifier,
TFieldList *fieldList);
TFieldList *addStructDeclaratorList(const TPublicType &typeSpecifier, TFieldList *fieldList); TFieldList *addStructDeclaratorList(const TPublicType &typeSpecifier, TFieldList *fieldList);
TPublicType addStructure(const TSourceLoc &structLine, TPublicType addStructure(const TSourceLoc &structLine,
const TSourceLoc &nameLine, const TSourceLoc &nameLine,
const TString *structName, const TString *structName,
TFieldList *fieldList); TFieldList *fieldList);
TIntermAggregate* addInterfaceBlock(const TPublicType &typeQualifier, TIntermAggregate *addInterfaceBlock(const TTypeQualifierBuilder &typeQualifierBuilder,
const TSourceLoc &nameLine, const TSourceLoc &nameLine,
const TString &blockName, const TString &blockName,
TFieldList *fieldList, TFieldList *fieldList,
const TString *instanceName, const TString *instanceName,
const TSourceLoc &instanceLine, const TSourceLoc &instanceLine,
TIntermTyped *arrayIndex, TIntermTyped *arrayIndex,
const TSourceLoc& arrayIndexLine); const TSourceLoc &arrayIndexLine);
void parseLocalSize(const TString &qualifierType, void parseLocalSize(const TString &qualifierType,
const TSourceLoc &qualifierTypeLine, const TSourceLoc &qualifierTypeLine,
...@@ -318,8 +328,6 @@ class TParseContext : angle::NonCopyable ...@@ -318,8 +328,6 @@ class TParseContext : angle::NonCopyable
TLayoutQualifier joinLayoutQualifiers(TLayoutQualifier leftQualifier, TLayoutQualifier joinLayoutQualifiers(TLayoutQualifier leftQualifier,
TLayoutQualifier rightQualifier, TLayoutQualifier rightQualifier,
const TSourceLoc &rightQualifierLocation); const TSourceLoc &rightQualifierLocation);
TPublicType joinInterpolationQualifiers(const TSourceLoc &interpolationLoc, TQualifier interpolationQualifier,
const TSourceLoc &storageLoc, TQualifier storageQualifier);
// Performs an error check for embedded struct declarations. // Performs an error check for embedded struct declarations.
void enterStructDeclaration(const TSourceLoc &line, const TString &identifier); void enterStructDeclaration(const TSourceLoc &line, const TString &identifier);
...@@ -438,6 +446,8 @@ class TParseContext : angle::NonCopyable ...@@ -438,6 +446,8 @@ class TParseContext : angle::NonCopyable
// keep track of local group size declared in layout. It should be declared only once. // keep track of local group size declared in layout. It should be declared only once.
bool mComputeShaderLocalSizeDeclared; bool mComputeShaderLocalSizeDeclared;
sh::WorkGroupSize mComputeShaderLocalSize; sh::WorkGroupSize mComputeShaderLocalSize;
// keeps track whether we are declaring / defining a function
bool mDeclaringFunction;
}; };
int PaParseStrings( int PaParseStrings(
......
//
// Copyright (c) 2002-2016 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#include "QualifierTypes.h"
#include "Diagnostics.h"
namespace
{
bool IsScopeQualifier(TQualifier qualifier)
{
return qualifier == EvqGlobal || qualifier == EvqTemporary;
}
bool IsScopeQualifierWrapper(const TQualifierWrapperBase *qualifier)
{
if (qualifier->getType() != QtStorage)
return false;
const TStorageQualifierWrapper *storageQualifier =
static_cast<const TStorageQualifierWrapper *>(qualifier);
TQualifier q = storageQualifier->getQualifier();
return IsScopeQualifier(q);
}
// Returns true if the invariant for the qualifier sequence holds
bool IsInvariantCorrect(const std::vector<const TQualifierWrapperBase *> &qualifiers)
{
// We should have at least one qualifier.
// The first qualifier always tells the scope.
return qualifiers.size() >= 1 && IsScopeQualifierWrapper(qualifiers[0]);
}
// Returns true if there are qualifiers which have been specified multiple times
bool HasRepeatingQualifiers(const std::vector<const TQualifierWrapperBase *> &qualifiers,
std::string *errorMessage)
{
bool invariantFound = false;
bool precisionFound = false;
bool layoutFound = false;
bool interpolationFound = false;
// The iteration starts from one since the first qualifier only reveals the scope of the
// expression. It is inserted first whenever the sequence gets created.
for (size_t i = 1; i < qualifiers.size(); ++i)
{
switch (qualifiers[i]->getType())
{
case QtInvariant:
{
if (invariantFound)
{
*errorMessage = "The invariant qualifier specified multiple times.";
return true;
}
invariantFound = true;
break;
}
case QtPrecision:
{
if (precisionFound)
{
*errorMessage = "The precision qualifier specified multiple times.";
return true;
}
precisionFound = true;
break;
}
case QtLayout:
{
if (layoutFound)
{
*errorMessage = "The layout qualifier specified multiple times.";
return true;
}
layoutFound = true;
break;
}
case QtInterpolation:
{
// 'centroid' is treated as a storage qualifier
// 'flat centroid' will be squashed to 'flat'
// 'smooth centroid' will be squashed to 'centroid'
if (interpolationFound)
{
*errorMessage = "The interpolation qualifier specified multiple times.";
return true;
}
interpolationFound = true;
break;
}
case QtStorage:
{
// Go over all of the storage qualifiers up until the current one and check for
// repetitions.
TQualifier currentQualifier =
static_cast<const TStorageQualifierWrapper *>(qualifiers[i])->getQualifier();
for (size_t j = 1; j < i; ++j)
{
if (qualifiers[j]->getType() == QtStorage)
{
const TStorageQualifierWrapper *previousQualifierWrapper =
static_cast<const TStorageQualifierWrapper *>(qualifiers[j]);
TQualifier previousQualifier = previousQualifierWrapper->getQualifier();
if (currentQualifier == previousQualifier)
{
*errorMessage = previousQualifierWrapper->getQualifierString().c_str();
*errorMessage += " specified multiple times";
return true;
}
}
}
break;
}
default:
UNREACHABLE();
}
}
return false;
}
// GLSL ES 3.00_6, 4.7 Order of Qualification
// The correct order of qualifiers is:
// invariant-qualifier interpolation-qualifier storage-qualifier precision-qualifier
// layout-qualifier has to be before storage-qualifier.
bool AreQualifiersInOrder(const std::vector<const TQualifierWrapperBase *> &qualifiers,
std::string *errorMessage)
{
bool foundInterpolation = false;
bool foundStorage = false;
bool foundPrecision = false;
for (size_t i = 1; i < qualifiers.size(); ++i)
{
switch (qualifiers[i]->getType())
{
case QtInvariant:
if (foundInterpolation || foundStorage || foundPrecision)
{
*errorMessage = "The invariant qualifier has to be first in the expression.";
return false;
}
break;
case QtInterpolation:
if (foundStorage)
{
*errorMessage = "Storage qualifiers have to be after interpolation qualifiers.";
return false;
}
else if (foundPrecision)
{
*errorMessage =
"Precision qualifiers have to be after interpolation qualifiers.";
return false;
}
foundInterpolation = true;
break;
case QtLayout:
if (foundStorage)
{
*errorMessage = "Storage qualifiers have to be after layout qualifiers.";
return false;
}
else if (foundPrecision)
{
*errorMessage = "Precision qualifiers have to be after layout qualifiers.";
return false;
}
break;
case QtStorage:
if (foundPrecision)
{
*errorMessage = "Precision qualifiers have to be after storage qualifiers.";
return false;
}
foundStorage = true;
break;
case QtPrecision:
foundPrecision = true;
break;
default:
UNREACHABLE();
}
}
return true;
}
} // namespace
TTypeQualifier::TTypeQualifier(TQualifier scope, const TSourceLoc &loc)
: layoutQualifier(TLayoutQualifier::create()),
precision(EbpUndefined),
qualifier(scope),
invariant(false),
line(loc)
{
ASSERT(IsScopeQualifier(qualifier));
}
TTypeQualifierBuilder::TTypeQualifierBuilder(const TStorageQualifierWrapper *scope)
{
ASSERT(IsScopeQualifier(scope->getQualifier()));
mQualifiers.push_back(scope);
}
void TTypeQualifierBuilder::appendQualifier(const TQualifierWrapperBase *qualifier)
{
mQualifiers.push_back(qualifier);
}
bool TTypeQualifierBuilder::checkOrderIsValid(TDiagnostics *diagnostics) const
{
std::string errorMessage;
if (HasRepeatingQualifiers(mQualifiers, &errorMessage))
{
diagnostics->error(mQualifiers[0]->getLine(), "qualifier sequence", errorMessage.c_str(),
"");
return false;
}
if (!AreQualifiersInOrder(mQualifiers, &errorMessage))
{
diagnostics->error(mQualifiers[0]->getLine(), "qualifier sequence", errorMessage.c_str(),
"");
return false;
}
return true;
}
TTypeQualifier TTypeQualifierBuilder::getParameterTypeQualifier(TDiagnostics *diagnostics) const
{
ASSERT(IsInvariantCorrect(mQualifiers));
ASSERT(static_cast<const TStorageQualifierWrapper *>(mQualifiers[0])->getQualifier() ==
EvqTemporary);
TTypeQualifier typeQualifier(EvqTemporary, mQualifiers[0]->getLine());
if (!checkOrderIsValid(diagnostics))
{
return typeQualifier;
}
for (size_t i = 1; i < mQualifiers.size(); ++i)
{
const TQualifierWrapperBase *qualifier = mQualifiers[i];
bool isQualifierValid = false;
switch (qualifier->getType())
{
case QtInvariant:
case QtInterpolation:
case QtLayout:
break;
case QtStorage:
isQualifierValid = joinParameterStorageQualifier(
&typeQualifier.qualifier,
static_cast<const TStorageQualifierWrapper *>(qualifier)->getQualifier());
break;
case QtPrecision:
isQualifierValid = true;
typeQualifier.precision =
static_cast<const TPrecisionQualifierWrapper *>(qualifier)->getQualifier();
ASSERT(typeQualifier.precision != EbpUndefined);
break;
default:
UNREACHABLE();
}
if (!isQualifierValid)
{
const TString &qualifierString = qualifier->getQualifierString();
diagnostics->error(qualifier->getLine(), "invalid parameter qualifier",
qualifierString.c_str(), "");
break;
}
}
switch (typeQualifier.qualifier)
{
case EvqIn:
case EvqConstReadOnly: // const in
case EvqOut:
case EvqInOut:
break;
case EvqConst:
typeQualifier.qualifier = EvqConstReadOnly;
break;
case EvqTemporary:
// no qualifier has been specified, set it to EvqIn which is the default
typeQualifier.qualifier = EvqIn;
break;
default:
diagnostics->error(mQualifiers[0]->getLine(), "Invalid parameter qualifier ",
getQualifierString(typeQualifier.qualifier), "");
}
return typeQualifier;
}
TTypeQualifier TTypeQualifierBuilder::getVariableTypeQualifier(TDiagnostics *diagnostics) const
{
ASSERT(IsInvariantCorrect(mQualifiers));
TQualifier scope =
static_cast<const TStorageQualifierWrapper *>(mQualifiers[0])->getQualifier();
TTypeQualifier typeQualifier = TTypeQualifier(scope, mQualifiers[0]->getLine());
if (!checkOrderIsValid(diagnostics))
{
return typeQualifier;
}
for (size_t i = 1; i < mQualifiers.size(); ++i)
{
const TQualifierWrapperBase *qualifier = mQualifiers[i];
bool isQualifierValid = false;
switch (qualifier->getType())
{
case QtInvariant:
isQualifierValid = true;
typeQualifier.invariant = true;
break;
case QtInterpolation:
{
switch (typeQualifier.qualifier)
{
case EvqGlobal:
isQualifierValid = true;
typeQualifier.qualifier =
static_cast<const TInterpolationQualifierWrapper *>(qualifier)
->getQualifier();
break;
default:
isQualifierValid = false;
}
break;
}
case QtLayout:
isQualifierValid = true;
typeQualifier.layoutQualifier =
static_cast<const TLayoutQualifierWrapper *>(qualifier)->getQualifier();
break;
case QtStorage:
isQualifierValid = joinVariableStorageQualifier(
&typeQualifier.qualifier,
static_cast<const TStorageQualifierWrapper *>(qualifier)->getQualifier());
break;
case QtPrecision:
isQualifierValid = true;
typeQualifier.precision =
static_cast<const TPrecisionQualifierWrapper *>(qualifier)->getQualifier();
ASSERT(typeQualifier.precision != EbpUndefined);
break;
default:
UNREACHABLE();
}
if (!isQualifierValid)
{
const TString &qualifierString = qualifier->getQualifierString();
diagnostics->error(qualifier->getLine(), "invalid qualifier combination",
qualifierString.c_str(), "");
break;
}
}
return typeQualifier;
}
bool TTypeQualifierBuilder::joinVariableStorageQualifier(TQualifier *joinedQualifier,
TQualifier storageQualifier) const
{
switch (*joinedQualifier)
{
case EvqGlobal:
*joinedQualifier = storageQualifier;
break;
case EvqTemporary:
{
switch (storageQualifier)
{
case EvqConst:
*joinedQualifier = storageQualifier;
break;
default:
return false;
}
break;
}
case EvqSmooth:
{
switch (storageQualifier)
{
case EvqCentroid:
*joinedQualifier = EvqCentroid;
break;
case EvqVertexOut:
*joinedQualifier = EvqSmoothOut;
break;
case EvqFragmentIn:
*joinedQualifier = EvqSmoothIn;
break;
default:
return false;
}
break;
}
case EvqFlat:
{
switch (storageQualifier)
{
case EvqCentroid:
*joinedQualifier = EvqFlat;
break;
case EvqVertexOut:
*joinedQualifier = EvqFlatOut;
break;
case EvqFragmentIn:
*joinedQualifier = EvqFlatIn;
break;
default:
return false;
}
break;
}
case EvqCentroid:
{
switch (storageQualifier)
{
case EvqVertexOut:
*joinedQualifier = EvqCentroidOut;
break;
case EvqFragmentIn:
*joinedQualifier = EvqCentroidIn;
break;
default:
return false;
}
break;
}
default:
return false;
}
return true;
}
bool TTypeQualifierBuilder::joinParameterStorageQualifier(TQualifier *joinedQualifier,
TQualifier storageQualifier) const
{
switch (*joinedQualifier)
{
case EvqTemporary:
*joinedQualifier = storageQualifier;
break;
case EvqConst:
{
switch (storageQualifier)
{
case EvqIn:
*joinedQualifier = EvqConstReadOnly;
break;
default:
return false;
}
break;
}
default:
return false;
}
return true;
}
//
// Copyright (c) 2002-2016 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#ifndef COMPILER_TRANSLATOR_QUALIFIER_TYPES_H_
#define COMPILER_TRANSLATOR_QUALIFIER_TYPES_H_
#include "BaseTypes.h"
#include "common/angleutils.h"
#include "Types.h"
#include <vector>
class TDiagnostics;
enum TQualifierType
{
QtInvariant,
QtInterpolation,
QtLayout,
QtStorage,
QtPrecision
};
class TQualifierWrapperBase : angle::NonCopyable
{
public:
TQualifierWrapperBase(const TSourceLoc &line) : mLine(line) {}
virtual ~TQualifierWrapperBase(){};
virtual TQualifierType getType() const = 0;
virtual TString getQualifierString() const = 0;
const TSourceLoc &getLine() const { return mLine; }
private:
TSourceLoc mLine;
};
class TInvariantQualifierWrapper final : public TQualifierWrapperBase
{
public:
TInvariantQualifierWrapper(const TSourceLoc &line) : TQualifierWrapperBase(line) {}
~TInvariantQualifierWrapper() {}
TQualifierType getType() const { return QtInvariant; }
TString getQualifierString() const { return "invariant"; }
};
class TInterpolationQualifierWrapper final : public TQualifierWrapperBase
{
public:
TInterpolationQualifierWrapper(TQualifier interpolationQualifier, const TSourceLoc &line)
: TQualifierWrapperBase(line), mInterpolationQualifier(interpolationQualifier)
{
}
~TInterpolationQualifierWrapper() {}
TQualifierType getType() const { return QtInterpolation; }
TString getQualifierString() const { return ::getQualifierString(mInterpolationQualifier); }
TQualifier getQualifier() const { return mInterpolationQualifier; }
private:
TQualifier mInterpolationQualifier;
};
class TLayoutQualifierWrapper final : public TQualifierWrapperBase
{
public:
TLayoutQualifierWrapper(TLayoutQualifier layoutQualifier, const TSourceLoc &line)
: TQualifierWrapperBase(line), mLayoutQualifier(layoutQualifier)
{
}
~TLayoutQualifierWrapper() {}
TQualifierType getType() const { return QtLayout; }
TString getQualifierString() const { return "layout"; }
const TLayoutQualifier &getQualifier() const { return mLayoutQualifier; }
private:
TLayoutQualifier mLayoutQualifier;
};
class TStorageQualifierWrapper final : public TQualifierWrapperBase
{
public:
TStorageQualifierWrapper(TQualifier storageQualifier, const TSourceLoc &line)
: TQualifierWrapperBase(line), mStorageQualifier(storageQualifier)
{
}
~TStorageQualifierWrapper() {}
TQualifierType getType() const { return QtStorage; }
TString getQualifierString() const { return ::getQualifierString(mStorageQualifier); }
TQualifier getQualifier() const { return mStorageQualifier; }
private:
TQualifier mStorageQualifier;
};
class TPrecisionQualifierWrapper final : public TQualifierWrapperBase
{
public:
TPrecisionQualifierWrapper(TPrecision precisionQualifier, const TSourceLoc &line)
: TQualifierWrapperBase(line), mPrecisionQualifier(precisionQualifier)
{
}
~TPrecisionQualifierWrapper() {}
TQualifierType getType() const { return QtPrecision; }
TString getQualifierString() const { return ::getPrecisionString(mPrecisionQualifier); }
TPrecision getQualifier() const { return mPrecisionQualifier; }
private:
TPrecision mPrecisionQualifier;
};
// TTypeQualifier tightly covers type_qualifier from the grammar
struct TTypeQualifier
{
// initializes all of the qualifiers and sets the scope
TTypeQualifier(TQualifier scope, const TSourceLoc &loc);
TLayoutQualifier layoutQualifier;
TPrecision precision;
TQualifier qualifier;
bool invariant;
TSourceLoc line;
};
// TTypeQualifierBuilder contains all of the qualifiers when type_qualifier gets parsed.
// It is to be used to validate the qualifier sequence and build a TTypeQualifier from it.
class TTypeQualifierBuilder : angle::NonCopyable
{
public:
TTypeQualifierBuilder(const TStorageQualifierWrapper *scope);
// Adds the passed qualifier to the end of the sequence.
void appendQualifier(const TQualifierWrapperBase *qualifier);
// Checks for the order of qualification and repeating qualifiers.
bool checkOrderIsValid(TDiagnostics *diagnostics) const;
// Goes over the qualifier sequence and parses it to form a type qualifier for a function
// parameter.
// The returned object is initialized even if the parsing fails.
TTypeQualifier getParameterTypeQualifier(TDiagnostics *diagnostics) const;
// Goes over the qualifier sequence and parses it to form a type qualifier for a variable.
// The returned object is initialized even if the parsing fails.
TTypeQualifier getVariableTypeQualifier(TDiagnostics *diagnostics) const;
private:
// Handles the joining of storage qualifiers for a parameter in a function.
bool joinParameterStorageQualifier(TQualifier *joinedQualifier,
TQualifier storageQualifier) const;
// Handles the joining of storage qualifiers for variables.
bool joinVariableStorageQualifier(TQualifier *joinedQualifier,
TQualifier storageQualifier) const;
std::vector<const TQualifierWrapperBase *> mQualifiers;
};
#endif // COMPILER_TRANSLATOR_QUALIFIER_TYPES_H_
...@@ -88,6 +88,8 @@ WHICH GENERATES THE GLSL ES PARSER (glslang_tab.cpp AND glslang_tab.h). ...@@ -88,6 +88,8 @@ WHICH GENERATES THE GLSL ES PARSER (glslang_tab.cpp AND glslang_tab.h).
TParameter param; TParameter param;
TField* field; TField* field;
TFieldList* fieldList; TFieldList* fieldList;
TQualifierWrapperBase* qualifierWrapper;
TTypeQualifierBuilder* typeQualifierBuilder;
}; };
} interm; } interm;
} }
...@@ -205,11 +207,16 @@ extern void yyerror(YYLTYPE* yylloc, TParseContext* context, void *scanner, cons ...@@ -205,11 +207,16 @@ extern void yyerror(YYLTYPE* yylloc, TParseContext* context, void *scanner, cons
%type <interm> single_declaration init_declarator_list %type <interm> single_declaration init_declarator_list
%type <interm> parameter_declaration parameter_declarator parameter_type_specifier %type <interm> parameter_declaration parameter_declarator parameter_type_specifier
%type <interm.qualifier> parameter_qualifier parameter_type_qualifier %type <interm.layoutQualifier> layout_qualifier_id_list layout_qualifier_id
%type <interm.layoutQualifier> layout_qualifier layout_qualifier_id_list layout_qualifier_id
%type <interm.type> fully_specified_type type_specifier
%type <interm.precision> precision_qualifier %type <interm.precision> precision_qualifier
%type <interm.type> type_qualifier fully_specified_type type_specifier storage_qualifier interpolation_qualifier %type <interm.layoutQualifier> layout_qualifier
%type <interm.qualifier> storage_qualifier interpolation_qualifier
%type <interm.qualifierWrapper> single_type_qualifier invariant_qualifier
%type <interm.typeQualifierBuilder> type_qualifier
%type <interm.type> type_specifier_no_prec type_specifier_nonarray %type <interm.type> type_specifier_no_prec type_specifier_nonarray
%type <interm.type> struct_specifier %type <interm.type> struct_specifier
%type <interm.field> struct_declarator %type <interm.field> struct_declarator
...@@ -609,26 +616,31 @@ declaration ...@@ -609,26 +616,31 @@ declaration
$$ = 0; $$ = 0;
} }
| type_qualifier enter_struct struct_declaration_list RIGHT_BRACE SEMICOLON { | type_qualifier enter_struct struct_declaration_list RIGHT_BRACE SEMICOLON {
ES3_OR_NEWER(getQualifierString($1.qualifier), @1, "interface blocks"); ES3_OR_NEWER($2.string->c_str(), @1, "interface blocks");
$$ = context->addInterfaceBlock($1, @2, *$2.string, $3, NULL, @$, NULL, @$); $$ = context->addInterfaceBlock(*$1, @2, *$2.string, $3, NULL, @$, NULL, @$);
} }
| type_qualifier enter_struct struct_declaration_list RIGHT_BRACE IDENTIFIER SEMICOLON { | type_qualifier enter_struct struct_declaration_list RIGHT_BRACE IDENTIFIER SEMICOLON {
ES3_OR_NEWER(getQualifierString($1.qualifier), @1, "interface blocks"); ES3_OR_NEWER($2.string->c_str(), @1, "interface blocks");
$$ = context->addInterfaceBlock($1, @2, *$2.string, $3, $5.string, @5, NULL, @$); $$ = context->addInterfaceBlock(*$1, @2, *$2.string, $3, $5.string, @5, NULL, @$);
} }
| type_qualifier enter_struct struct_declaration_list RIGHT_BRACE IDENTIFIER LEFT_BRACKET constant_expression RIGHT_BRACKET SEMICOLON { | type_qualifier enter_struct struct_declaration_list RIGHT_BRACE IDENTIFIER LEFT_BRACKET constant_expression RIGHT_BRACKET SEMICOLON {
ES3_OR_NEWER(getQualifierString($1.qualifier), @1, "interface blocks"); ES3_OR_NEWER($2.string->c_str(), @1, "interface blocks");
$$ = context->addInterfaceBlock($1, @2, *$2.string, $3, $5.string, @5, $7, @6); $$ = context->addInterfaceBlock(*$1, @2, *$2.string, $3, $5.string, @5, $7, @6);
} }
| type_qualifier SEMICOLON { | type_qualifier SEMICOLON {
context->parseGlobalLayoutQualifier($1); context->parseGlobalLayoutQualifier(*$1);
$$ = 0; $$ = 0;
} }
| type_qualifier IDENTIFIER SEMICOLON // e.g. to qualify an existing variable as invariant
{
$$ = context->parseInvariantDeclaration(*$1, @2, $2.string, $2.symbol);
}
; ;
function_prototype function_prototype
: function_declarator RIGHT_PAREN { : function_declarator RIGHT_PAREN {
$$.function = context->parseFunctionDeclarator(@2, $1); $$.function = context->parseFunctionDeclarator(@2, $1);
context->exitFunctionDeclaration();
} }
; ;
...@@ -675,6 +687,7 @@ function_header ...@@ -675,6 +687,7 @@ function_header
$$ = context->parseFunctionHeader($1, $2.string, @2); $$ = context->parseFunctionHeader($1, $2.string, @2);
context->symbolTable.push(); context->symbolTable.push();
context->enterFunctionDeclaration();
} }
; ;
...@@ -713,41 +726,21 @@ parameter_declaration ...@@ -713,41 +726,21 @@ parameter_declaration
// //
// Type + name // Type + name
// //
: parameter_type_qualifier parameter_qualifier parameter_declarator { : type_qualifier parameter_declarator {
$$ = $3;
context->checkIsParameterQualifierValid(@3, $1, $2, $$.param.type);
}
| parameter_qualifier parameter_declarator {
$$ = $2; $$ = $2;
context->checkOutParameterIsNotSampler(@2, $1, *$2.param.type); context->checkIsParameterQualifierValid(@2, *$1, $2.param.type);
context->checkIsParameterQualifierValid(@2, EvqTemporary, $1, $$.param.type);
} }
// | parameter_declarator {
// Only type $$ = $1;
// $$.param.type->setQualifier(EvqIn);
| parameter_type_qualifier parameter_qualifier parameter_type_specifier {
$$ = $3;
context->checkIsParameterQualifierValid(@3, $1, $2, $$.param.type);
} }
| parameter_qualifier parameter_type_specifier { | type_qualifier parameter_type_specifier {
$$ = $2; $$ = $2;
context->checkOutParameterIsNotSampler(@2, $1, *$2.param.type); context->checkIsParameterQualifierValid(@2, *$1, $2.param.type);
context->checkIsParameterQualifierValid(@2, EvqTemporary, $1, $$.param.type);
}
;
parameter_qualifier
: /* empty */ {
$$ = EvqIn;
}
| IN_QUAL {
$$ = EvqIn;
}
| OUT_QUAL {
$$ = EvqOut;
} }
| INOUT_QUAL { | parameter_type_specifier {
$$ = EvqInOut; $$ = $1;
$$.param.type->setQualifier(EvqIn);
} }
; ;
...@@ -813,10 +806,6 @@ single_declaration ...@@ -813,10 +806,6 @@ single_declaration
$$.type = $1; $$.type = $1;
$$.intermAggregate = context->parseSingleInitDeclaration($$.type, @2, *$2.string, @3, $4); $$.intermAggregate = context->parseSingleInitDeclaration($$.type, @2, *$2.string, @3, $4);
} }
| INVARIANT IDENTIFIER {
// $$.type is not used in invariant declarations.
$$.intermAggregate = context->parseInvariantDeclaration(@1, @2, $2.string, $2.symbol);
}
; ;
fully_specified_type fully_specified_type
...@@ -830,124 +819,134 @@ fully_specified_type ...@@ -830,124 +819,134 @@ fully_specified_type
} }
} }
} }
| type_qualifier type_specifier { | type_qualifier type_specifier {
$$ = context->addFullySpecifiedType($1.qualifier, $1.invariant, $1.layoutQualifier, $2); $$ = context->addFullySpecifiedType(*$1, $2);
} }
; ;
interpolation_qualifier interpolation_qualifier
: SMOOTH { : SMOOTH {
$$.qualifier = EvqSmooth; $$ = EvqSmooth;
} }
| FLAT { | FLAT {
$$.qualifier = EvqFlat; $$ = EvqFlat;
}
;
parameter_type_qualifier
: CONST_QUAL {
$$ = EvqConst;
} }
; ;
type_qualifier type_qualifier
: ATTRIBUTE { : single_type_qualifier {
VERTEX_ONLY("attribute", @1); $$ = new TTypeQualifierBuilder(new TStorageQualifierWrapper(context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary, @1));
ES2_ONLY("attribute", @1); $$->appendQualifier($1);
context->checkIsAtGlobalLevel(@1, "attribute");
$$.setBasic(EbtVoid, EvqAttribute, @1);
} }
| VARYING { | type_qualifier single_type_qualifier {
ES2_ONLY("varying", @1); $$ = $1;
context->checkIsAtGlobalLevel(@1, "varying"); $$->appendQualifier($2);
if (context->getShaderType() == GL_VERTEX_SHADER)
$$.setBasic(EbtVoid, EvqVaryingOut, @1);
else
$$.setBasic(EbtVoid, EvqVaryingIn, @1);
} }
| INVARIANT VARYING { ;
ES2_ONLY("varying", @1);
context->checkIsAtGlobalLevel(@1, "invariant varying"); invariant_qualifier
if (context->getShaderType() == GL_VERTEX_SHADER) : INVARIANT {
$$.setBasic(EbtVoid, EvqVaryingOut, @1); // empty
else
$$.setBasic(EbtVoid, EvqVaryingIn, @1);
$$.invariant = true;
} }
| storage_qualifier { ;
if ($1.qualifier != EvqConst && !context->symbolTable.atGlobalLevel())
single_type_qualifier
: storage_qualifier {
if (!context->declaringFunction() && $1 != EvqConst && !context->symbolTable.atGlobalLevel())
{ {
context->error(@1, "Local variables can only use the const storage qualifier.", getQualifierString($1.qualifier)); context->error(@1, "Local variables can only use the const storage qualifier.", getQualifierString($1));
} }
$$.setBasic(EbtVoid, $1.qualifier, @1); $$ = new TStorageQualifierWrapper($1, @1);
}
| interpolation_qualifier storage_qualifier {
$$ = context->joinInterpolationQualifiers(@1, $1.qualifier, @2, $2.qualifier);
}
| interpolation_qualifier {
context->error(@1, "interpolation qualifier requires a fragment 'in' or vertex 'out' storage qualifier", getInterpolationString($1.qualifier));
TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
$$.setBasic(EbtVoid, qual, @1);
} }
| layout_qualifier { | layout_qualifier {
$$.qualifier = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; context->checkIsAtGlobalLevel(@1, "layout");
$$.layoutQualifier = $1; $$ = new TLayoutQualifierWrapper($1, @1);
} }
| layout_qualifier storage_qualifier { | precision_qualifier {
$$.setBasic(EbtVoid, $2.qualifier, @2); $$ = new TPrecisionQualifierWrapper($1, @1);
$$.layoutQualifier = $1;
} }
| INVARIANT storage_qualifier { | interpolation_qualifier {
context->checkInvariantIsOutVariableES3($2.qualifier, @1); $$ = new TInterpolationQualifierWrapper($1, @1);
$$.setBasic(EbtVoid, $2.qualifier, @2);
$$.invariant = true;
} }
| INVARIANT interpolation_qualifier storage_qualifier { | invariant_qualifier {
context->checkInvariantIsOutVariableES3($3.qualifier, @1); context->checkIsAtGlobalLevel(@1, "invariant");
$$ = context->joinInterpolationQualifiers(@2, $2.qualifier, @3, $3.qualifier); $$ = new TInvariantQualifierWrapper(@1);
$$.invariant = true;
} }
; ;
storage_qualifier storage_qualifier
: CONST_QUAL { :
$$.qualifier = EvqConst; ATTRIBUTE {
VERTEX_ONLY("attribute", @1);
ES2_ONLY("attribute", @1);
context->checkIsAtGlobalLevel(@1, "attribute");
$$ = EvqAttribute;
}
| VARYING {
ES2_ONLY("varying", @1);
context->checkIsAtGlobalLevel(@1, "varying");
if (context->getShaderType() == GL_VERTEX_SHADER)
$$ = EvqVaryingOut;
else
$$ = EvqVaryingIn;
}
| CONST_QUAL {
$$ = EvqConst;
} }
| IN_QUAL { | IN_QUAL {
if (context->getShaderType() == GL_FRAGMENT_SHADER) if (context->declaringFunction())
{
$$ = EvqIn;
}
else if (context->getShaderType() == GL_FRAGMENT_SHADER)
{ {
ES3_OR_NEWER("in", @1, "storage qualifier"); ES3_OR_NEWER("in", @1, "storage qualifier");
$$.qualifier = EvqFragmentIn; $$ = EvqFragmentIn;
} }
else if (context->getShaderType() == GL_VERTEX_SHADER) else if (context->getShaderType() == GL_VERTEX_SHADER)
{ {
ES3_OR_NEWER("in", @1, "storage qualifier"); ES3_OR_NEWER("in", @1, "storage qualifier");
$$.qualifier = EvqVertexIn; $$ = EvqVertexIn;
} }
else else
{ {
$$.qualifier = EvqComputeIn; $$ = EvqComputeIn;
} }
} }
| OUT_QUAL { | OUT_QUAL {
ES3_OR_NEWER("out", @1, "storage qualifier"); if (context->declaringFunction())
NON_COMPUTE_ONLY("out", @1); {
$$.qualifier = (context->getShaderType() == GL_FRAGMENT_SHADER) ? EvqFragmentOut : EvqVertexOut; $$ = EvqOut;
}
else
{
ES3_OR_NEWER("out", @1, "storage qualifier");
NON_COMPUTE_ONLY("out", @1);
if (context->getShaderType() == GL_FRAGMENT_SHADER)
{
$$ = EvqFragmentOut;
}
else
{
$$ = EvqVertexOut;
}
}
} }
| CENTROID IN_QUAL { | INOUT_QUAL {
ES3_OR_NEWER("centroid in", @1, "storage qualifier"); if (!context->declaringFunction())
FRAG_ONLY("centroid in", @1); {
$$.qualifier = EvqCentroidIn; context->error(@1, "invalid inout qualifier", "'inout' can be only used with function parameters");
}
$$ = EvqInOut;
} }
| CENTROID OUT_QUAL { | CENTROID {
ES3_OR_NEWER("centroid out", @1, "storage qualifier"); ES3_OR_NEWER("centroid", @1, "storage qualifier");
VERTEX_ONLY("centroid out", @1); $$ = EvqCentroid;
$$.qualifier = EvqCentroidOut;
} }
| UNIFORM { | UNIFORM {
context->checkIsAtGlobalLevel(@1, "uniform"); context->checkIsAtGlobalLevel(@1, "uniform");
$$.qualifier = EvqUniform; $$ = EvqUniform;
} }
; ;
...@@ -957,15 +956,6 @@ type_specifier ...@@ -957,15 +956,6 @@ type_specifier
if ($$.precision == EbpUndefined) { if ($$.precision == EbpUndefined) {
$$.precision = context->symbolTable.getDefaultPrecision($1.type); $$.precision = context->symbolTable.getDefaultPrecision($1.type);
context->checkPrecisionSpecified(@1, $$.precision, $1.type);
}
}
| precision_qualifier type_specifier_no_prec {
$$ = $2;
$$.precision = $1;
if (!SupportsPrecision($2.type)) {
context->error(@1, "illegal type for precision qualifier", getBasicString($2.type));
} }
} }
; ;
...@@ -1280,9 +1270,7 @@ struct_declaration ...@@ -1280,9 +1270,7 @@ struct_declaration
} }
| type_qualifier type_specifier struct_declarator_list SEMICOLON { | type_qualifier type_specifier struct_declarator_list SEMICOLON {
// ES3 Only, but errors should be handled elsewhere // ES3 Only, but errors should be handled elsewhere
$2.qualifier = $1.qualifier; $$ = context->addStructDeclaratorListWithQualifiers(*$1, &$2, $3);
$2.layoutQualifier = $1.layoutQualifier;
$$ = context->addStructDeclaratorList($2, $3);
} }
; ;
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -216,6 +216,8 @@ union YYSTYPE ...@@ -216,6 +216,8 @@ union YYSTYPE
TParameter param; TParameter param;
TField* field; TField* field;
TFieldList* fieldList; TFieldList* fieldList;
TQualifierWrapperBase *qualifierWrapper;
TTypeQualifierBuilder *typeQualifierBuilder;
}; };
} interm; } interm;
......
...@@ -537,4 +537,52 @@ template void GetVariableTraverser::traverse(const TType &, const TString &, std ...@@ -537,4 +537,52 @@ template void GetVariableTraverser::traverse(const TType &, const TString &, std
template void GetVariableTraverser::traverse(const TType &, const TString &, std::vector<Uniform> *); template void GetVariableTraverser::traverse(const TType &, const TString &, std::vector<Uniform> *);
template void GetVariableTraverser::traverse(const TType &, const TString &, std::vector<Varying> *); template void GetVariableTraverser::traverse(const TType &, const TString &, std::vector<Varying> *);
// GLSL ES 1.0.17 4.6.1 The Invariant Qualifier
bool CanBeInvariantESSL1(TQualifier qualifier)
{
return IsVaryingIn(qualifier) || IsVaryingOut(qualifier) ||
IsBuiltinOutputVariable(qualifier) ||
(IsBuiltinFragmentInputVariable(qualifier) && qualifier != EvqFrontFacing);
}
// GLSL ES 3.00 Revision 6, 4.6.1 The Invariant Qualifier
// GLSL ES 3.10 Revision 4, 4.8.1 The Invariant Qualifier
bool CanBeInvariantESSL3OrGreater(TQualifier qualifier)
{
return IsVaryingOut(qualifier) || qualifier == EvqFragmentOut ||
IsBuiltinOutputVariable(qualifier);
}
bool IsBuiltinOutputVariable(TQualifier qualifier)
{
switch (qualifier)
{
case EvqPosition:
case EvqPointSize:
case EvqFragDepth:
case EvqFragDepthEXT:
case EvqFragColor:
case EvqSecondaryFragColorEXT:
case EvqFragData:
case EvqSecondaryFragDataEXT:
return true;
default:
break;
}
return false;
}
bool IsBuiltinFragmentInputVariable(TQualifier qualifier)
{
switch (qualifier)
{
case EvqFragCoord:
case EvqPointCoord:
case EvqFrontFacing:
return true;
default:
break;
}
return false;
} }
} // namespace sh
...@@ -65,6 +65,10 @@ class GetVariableTraverser : angle::NonCopyable ...@@ -65,6 +65,10 @@ class GetVariableTraverser : angle::NonCopyable
const TSymbolTable &mSymbolTable; const TSymbolTable &mSymbolTable;
}; };
bool IsBuiltinOutputVariable(TQualifier qualifier);
bool IsBuiltinFragmentInputVariable(TQualifier qualifier);
bool CanBeInvariantESSL1(TQualifier qualifier);
bool CanBeInvariantESSL3OrGreater(TQualifier qualifier);
} }
#endif // COMPILER_TRANSLATOR_UTIL_H_ #endif // COMPILER_TRANSLATOR_UTIL_H_
...@@ -56,6 +56,7 @@ ...@@ -56,6 +56,7 @@
'<(angle_path)/src/tests/compiler_tests/Pack_Unpack_test.cpp', '<(angle_path)/src/tests/compiler_tests/Pack_Unpack_test.cpp',
'<(angle_path)/src/tests/compiler_tests/PruneEmptyDeclarations_test.cpp', '<(angle_path)/src/tests/compiler_tests/PruneEmptyDeclarations_test.cpp',
'<(angle_path)/src/tests/compiler_tests/PruneUnusedFunctions_test.cpp', '<(angle_path)/src/tests/compiler_tests/PruneUnusedFunctions_test.cpp',
'<(angle_path)/src/tests/compiler_tests/QualificationOrder_test.cpp',
'<(angle_path)/src/tests/compiler_tests/RecordConstantPrecision_test.cpp', '<(angle_path)/src/tests/compiler_tests/RecordConstantPrecision_test.cpp',
'<(angle_path)/src/tests/compiler_tests/RemovePow_test.cpp', '<(angle_path)/src/tests/compiler_tests/RemovePow_test.cpp',
'<(angle_path)/src/tests/compiler_tests/ShaderExtension_test.cpp', '<(angle_path)/src/tests/compiler_tests/ShaderExtension_test.cpp',
......
...@@ -2323,7 +2323,7 @@ TEST_F(MalformedComputeShaderTest, CorrectUsageOfComputeBuiltins) ...@@ -2323,7 +2323,7 @@ TEST_F(MalformedComputeShaderTest, CorrectUsageOfComputeBuiltins)
} }
} }
// It is illegal to write to a special variable // It is illegal to write to a special variable.
TEST_F(MalformedComputeShaderTest, SpecialVariableNumWorkGroups) TEST_F(MalformedComputeShaderTest, SpecialVariableNumWorkGroups)
{ {
const std::string &shaderString = const std::string &shaderString =
...@@ -2339,7 +2339,7 @@ TEST_F(MalformedComputeShaderTest, SpecialVariableNumWorkGroups) ...@@ -2339,7 +2339,7 @@ TEST_F(MalformedComputeShaderTest, SpecialVariableNumWorkGroups)
} }
} }
// It is illegal to write to a special variable // It is illegal to write to a special variable.
TEST_F(MalformedComputeShaderTest, SpecialVariableWorkGroupID) TEST_F(MalformedComputeShaderTest, SpecialVariableWorkGroupID)
{ {
const std::string &shaderString = const std::string &shaderString =
...@@ -2355,7 +2355,7 @@ TEST_F(MalformedComputeShaderTest, SpecialVariableWorkGroupID) ...@@ -2355,7 +2355,7 @@ TEST_F(MalformedComputeShaderTest, SpecialVariableWorkGroupID)
} }
} }
// It is illegal to write to a special variable // It is illegal to write to a special variable.
TEST_F(MalformedComputeShaderTest, SpecialVariableLocalInvocationID) TEST_F(MalformedComputeShaderTest, SpecialVariableLocalInvocationID)
{ {
const std::string &shaderString = const std::string &shaderString =
...@@ -2371,7 +2371,7 @@ TEST_F(MalformedComputeShaderTest, SpecialVariableLocalInvocationID) ...@@ -2371,7 +2371,7 @@ TEST_F(MalformedComputeShaderTest, SpecialVariableLocalInvocationID)
} }
} }
// It is illegal to write to a special variable // It is illegal to write to a special variable.
TEST_F(MalformedComputeShaderTest, SpecialVariableGlobalInvocationID) TEST_F(MalformedComputeShaderTest, SpecialVariableGlobalInvocationID)
{ {
const std::string &shaderString = const std::string &shaderString =
...@@ -2387,7 +2387,7 @@ TEST_F(MalformedComputeShaderTest, SpecialVariableGlobalInvocationID) ...@@ -2387,7 +2387,7 @@ TEST_F(MalformedComputeShaderTest, SpecialVariableGlobalInvocationID)
} }
} }
// It is illegal to write to a special variable // It is illegal to write to a special variable.
TEST_F(MalformedComputeShaderTest, SpecialVariableLocalInvocationIndex) TEST_F(MalformedComputeShaderTest, SpecialVariableLocalInvocationIndex)
{ {
const std::string &shaderString = const std::string &shaderString =
...@@ -2403,7 +2403,7 @@ TEST_F(MalformedComputeShaderTest, SpecialVariableLocalInvocationIndex) ...@@ -2403,7 +2403,7 @@ TEST_F(MalformedComputeShaderTest, SpecialVariableLocalInvocationIndex)
} }
} }
// It is illegal to write to a special variable // It is illegal to write to a special variable.
TEST_F(MalformedComputeShaderTest, SpecialVariableWorkGroupSize) TEST_F(MalformedComputeShaderTest, SpecialVariableWorkGroupSize)
{ {
const std::string &shaderString = const std::string &shaderString =
...@@ -2435,3 +2435,84 @@ TEST_F(MalformedShaderTest, SamplerUnaryOperator) ...@@ -2435,3 +2435,84 @@ TEST_F(MalformedShaderTest, SamplerUnaryOperator)
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
} }
} }
// Invariant cannot be used with a work group size declaration.
TEST_F(MalformedComputeShaderTest, InvariantBlockSize)
{
const std::string &shaderString =
"#version 310 es\n"
"invariant layout(local_size_x = 15) in;\n"
"void main() {\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// Invariant cannot be used with a non-output variable in ESSL3.
TEST_F(MalformedShaderTest, InvariantNonOuput)
{
const std::string &shaderString =
"#version 300 es\n"
"invariant int value;\n"
"void main() {\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// Invariant declaration should follow the following format "invariant <out variable name>".
// Test having an incorrect qualifier in the invariant declaration.
TEST_F(MalformedShaderTest, InvariantDeclarationWithStorageQualifier)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"out vec4 foo;\n"
"invariant centroid foo;\n"
"void main() {\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// Invariant declaration should follow the following format "invariant <out variable name>".
// Test having an incorrect precision qualifier in the invariant declaration.
TEST_F(MalformedShaderTest, InvariantDeclarationWithPrecisionQualifier)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"out vec4 foo;\n"
"invariant highp foo;\n"
"void main() {\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// Invariant declaration should follow the following format "invariant <out variable name>".
// Test having an incorrect layout qualifier in the invariant declaration.
TEST_F(MalformedShaderTest, InvariantDeclarationWithLayoutQualifier)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"out vec4 foo;\n"
"invariant layout(location=0) foo;\n"
"void main() {\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
//
// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// QualificationOrder_test.cpp:
// OpenGL ES 3.1 removes the strict order of qualifiers imposed by the grammar.
// This file contains tests for invalid order and usage of qualifiers.
#include "angle_gl.h"
#include "gtest/gtest.h"
#include "GLSLANG/ShaderLang.h"
#include "compiler/translator/TranslatorESSL.h"
class QualificationOrderShaderTest : public testing::Test
{
public:
QualificationOrderShaderTest() {}
protected:
virtual void SetUp() {}
virtual void TearDown() {}
// Return true when compilation succeeds
bool compile(const std::string &shaderString, GLenum shaderType, ShShaderSpec spec)
{
ShBuiltInResources resources;
ShInitBuiltInResources(&resources);
TranslatorESSL *translator = new TranslatorESSL(shaderType, spec);
EXPECT_TRUE(translator->Init(resources));
const char *shaderStrings[] = {shaderString.c_str()};
bool compilationSuccess = translator->compile(shaderStrings, 1, SH_INTERMEDIATE_TREE);
TInfoSink &infoSink = translator->getInfoSink();
mInfoLog = infoSink.info.c_str();
delete translator;
return compilationSuccess;
}
protected:
std::string mInfoLog;
};
// Repeating centroid qualifier is invalid.
TEST_F(QualificationOrderShaderTest, RepeatingCentroid)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"flat centroid centroid in float myValue;\n"
"void main() {\n"
"}\n";
if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_1_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// Repeating uniform storage qualifiers is invalid.
TEST_F(QualificationOrderShaderTest, RepeatingUniforms)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"uniform uniform float myValue;\n"
"void main() {\n"
"}\n";
if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_1_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// Repeating varying storage qualifiers is invalid.
TEST_F(QualificationOrderShaderTest, RepeatingVaryings)
{
const std::string &shaderString =
"precision mediump float;\n"
"varying varying vec4 myColor;\n"
"void main() {\n"
"}\n";
if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_1_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// Layout qualifier should be before the storage qualifiers.
TEST_F(QualificationOrderShaderTest, WrongOrderQualifiers)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"out layout(location=1) vec4 myColor;\n"
"void main() {\n"
"}\n";
if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_1_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// Centroid out is the correct order. Out centroid is incorrect.
TEST_F(QualificationOrderShaderTest, WrongOrderCentroidOut)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"in vec4 uv;\n"
"out centroid vec4 position;\n"
"void main() {\n"
"position = uv;\n"
"gl_Position = uv;\n"
"}\n";
if (compile(shaderString, GL_VERTEX_SHADER, SH_GLES3_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// Centroid in is the correct order. In centroid is incorrect.
TEST_F(QualificationOrderShaderTest, WrongOrderCentroidIn)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"in centroid vec4 colorIN;\n"
"out vec4 colorOUT;\n"
"void main() {\n"
"colorOUT = colorIN;\n"
"}\n";
if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// Type cannot be before the storage qualifier.
TEST_F(QualificationOrderShaderTest, WrongOrderTypeStorage)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"centroid in vec4 colorIN;\n"
"vec4 out colorOUT;\n"
"void main() {\n"
"colorOUT = colorIN;\n"
"}\n";
if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// A variable cannot have two conflicting storage qualifiers.
TEST_F(QualificationOrderShaderTest, RepeatingDifferentStorageQualifiers)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"centroid in vec4 colorIN;\n"
"uniform out vec4 colorOUT;\n"
"void main() {\n"
"colorOUT = colorIN;\n"
"}\n";
if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// A variable cannot have two different layout qualifiers.
TEST_F(QualificationOrderShaderTest, RepeatingLayoutQualifiers)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"in vec4 colorIN;\n"
"layout(location=0) layout(location=0) out vec4 colorOUT;\n"
"void main() {\n"
"colorOUT = colorIN;\n"
"}\n";
if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// A variable cannot have repeating invariant qualifiers.
TEST_F(QualificationOrderShaderTest, RepeatingInvariantQualifiers)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"in vec4 colorIN;\n"
"invariant invariant out vec4 colorOUT;\n"
"void main() {\n"
"colorOUT = colorIN;\n"
"}\n";
if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// A variable cannot have repeating storage qualifiers.
TEST_F(QualificationOrderShaderTest, RepeatingAttributes)
{
const std::string &shaderString =
"precision mediump float;\n"
"attribute attribute vec4 positionIN;\n"
"void main() {\n"
"gl_Position = positionIN;\n"
"}\n";
if (compile(shaderString, GL_VERTEX_SHADER, SH_GLES3_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// Wrong order for invariant varying. It should be 'invariant varying', not 'varying invariant'.
TEST_F(QualificationOrderShaderTest, VaryingInvariantWrongOrder)
{
const std::string &shaderString =
"precision mediump float;\n"
"attribute vec4 positionIN;\n"
"varying invariant vec4 dataOUT;\n"
"void main() {\n"
"gl_Position = positionIN;\n"
"dataOUT = 0.5 * dataOUT + vec4(0.5);\n"
"}\n";
if (compile(shaderString, GL_VERTEX_SHADER, SH_GLES3_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// A variable cannot have repeating storage qualifiers.
TEST_F(QualificationOrderShaderTest, AttributeVaryingMix)
{
const std::string &shaderString1 =
"precision mediump float;\n"
"attribute varying vec4 positionIN;\n"
"void main() {\n"
"gl_Position = positionIN;\n"
"}\n";
const std::string &shaderString2 =
"precision mediump float;\n"
"varying attribute vec4 positionIN;\n"
"void main() {\n"
"gl_Position = positionIN;\n"
"}\n";
if (compile(shaderString1, GL_VERTEX_SHADER, SH_GLES3_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
if (compile(shaderString2, GL_VERTEX_SHADER, SH_GLES3_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// A variable cannot have repeating interpolation qualifiers.
TEST_F(QualificationOrderShaderTest, RepeatingInterpolationQualifiers)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"in vec4 positionIN;\n"
"flat flat out vec4 dataOUT;\n"
"void main() {\n"
"gl_Position = positionIN;\n"
"dataOUT = 0.5 * dataOUT + vec4(0.5);\n"
"}\n";
if (compile(shaderString, GL_VERTEX_SHADER, SH_GLES3_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// Wrong order for the interpolation and storage qualifier. The correct order is interpolation
// qualifier and then storage qualifier.
TEST_F(QualificationOrderShaderTest, WrongOrderInterpolationStorageQualifiers)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"in vec4 positionIN;\n"
"out flat vec4 dataOUT;\n"
"void main() {\n"
"gl_Position = positionIN;\n"
"dataOUT = 0.5 * dataOUT + vec4(0.5);\n"
"}\n";
if (compile(shaderString, GL_VERTEX_SHADER, SH_GLES3_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// The correct order is invariant, interpolation, storage.
TEST_F(QualificationOrderShaderTest, WrongOrderInvariantInterpolationStorageQualifiers)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"in vec4 positionIN;\n"
"flat invariant out vec4 dataOUT;\n"
"void main() {\n"
"gl_Position = positionIN;\n"
"dataOUT = 0.5 * dataOUT + vec4(0.5);\n"
"}\n";
if (compile(shaderString, GL_VERTEX_SHADER, SH_GLES3_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// The invariant qualifer has to be before the storage qualifiers.
TEST_F(QualificationOrderShaderTest, WrongOrderInvariantNotFirst)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"in vec4 positionIN;\n"
"centroid out invariant vec4 dataOUT;\n"
"void main() {\n"
"gl_Position = positionIN;\n"
"dataOUT = 0.5 * dataOUT + vec4(0.5);\n"
"}\n";
if (compile(shaderString, GL_VERTEX_SHADER, SH_GLES3_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// The precision qualifier is after the storage qualifiers.
TEST_F(QualificationOrderShaderTest, WrongOrderPrecision)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"in vec4 positionIN;\n"
"highp centroid out vec4 dataOUT;\n"
"void main() {\n"
"gl_Position = positionIN;\n"
"dataOUT = 0.5 * dataOUT + vec4(0.5);\n"
"}\n";
if (compile(shaderString, GL_VERTEX_SHADER, SH_GLES3_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// A variable cannot have multiple declarations of the 'in' storage qualifier.
TEST_F(QualificationOrderShaderTest, RepeatingInQualifier)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"in in vec4 positionIN;\n"
"void main() {\n"
"gl_Position = positionIN;\n"
"}\n";
if (compile(shaderString, GL_VERTEX_SHADER, SH_GLES3_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// A variable cannot have multiple declarations of the 'attribute' storage qualifier.
TEST_F(QualificationOrderShaderTest, RepeatingAttributeQualifier)
{
const std::string &shaderString =
"precision mediump float;\n"
"attribute attribute vec4 positionIN;\n"
"void main() {\n"
"gl_Position = positionIN;\n"
"}\n";
if (compile(shaderString, GL_VERTEX_SHADER, SH_GLES3_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// Vertex input cannot be qualified with invariant.
TEST_F(QualificationOrderShaderTest, InvariantVertexInput)
{
const std::string &shaderString =
"precision mediump float;\n"
"invariant attribute vec4 positionIN;\n"
"void main() {\n"
"gl_Position = positionIN;\n"
"}\n";
if (compile(shaderString, GL_VERTEX_SHADER, SH_GLES3_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// Cannot have a function parameter with the invariant qualifier.
TEST_F(QualificationOrderShaderTest, InvalidFunctionParametersInvariant)
{
const std::string &shaderString =
"precision lowp float;\n"
"varying float value;\n"
"float foo0 (invariant in float x) {\n"
" return 2.0*x;\n"
"}\n"
"void main()\n"
"{\n"
" gl_FragColor = vec4(foo0(value));\n"
"}\n";
if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure" << mInfoLog;
}
}
// Cannot have a function parameter with the attribute qualifier.
TEST_F(QualificationOrderShaderTest, InvalidFunctionParametersAttribute)
{
const std::string &shaderString =
"precision lowp float;\n"
"varying float value;\n"
"float foo0 (attribute float x) {\n"
" return 2.0*x;\n"
"}\n"
"void main()\n"
"{\n"
" gl_FragColor = vec4(foo0(value));\n"
"}\n";
if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure" << mInfoLog;
}
}
// Cannot have a function parameter with the varying qualifier.
TEST_F(QualificationOrderShaderTest, InvalidFunctionParametersVarying)
{
const std::string &shaderString =
"precision lowp float;\n"
"varying float value;\n"
"float foo0 (varying float x) {\n"
" return 2.0*x;\n"
"}\n"
"void main()\n"
"{\n"
" gl_FragColor = vec4(foo0(value));\n"
"}\n";
if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure" << mInfoLog;
}
}
// Cannot have a function parameter with the layout qualifier
TEST_F(QualificationOrderShaderTest, InvalidFunctionParametersLayout)
{
const std::string &shaderString =
"#version 300 es\n"
"precision lowp float;\n"
"in float value;\n"
"float foo0 (layout(location = 3) in float x) {\n"
" return 2.0*x;\n"
"}\n"
"out vec4 colorOUT;\n"
"void main()\n"
"{\n"
" colorOUT = vec4(foo0(value));\n"
"}\n";
if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure" << mInfoLog;
}
}
// Cannot have a function parameter with the centroid qualifier
TEST_F(QualificationOrderShaderTest, InvalidFunctionParametersCentroidIn)
{
const std::string &shaderString =
"#version 300 es\n"
"precision lowp float;\n"
"in float value;\n"
"float foo0 (centroid in float x) {\n"
" return 2.0*x;\n"
"}\n"
"out vec4 colorOUT;\n"
"void main()\n"
"{\n"
" colorOUT = vec4(foo0(value));\n"
"}\n";
if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure" << mInfoLog;
}
}
// Cannot have a function parameter with the flat qualifier
TEST_F(QualificationOrderShaderTest, InvalidFunctionParametersFlatIn)
{
const std::string &shaderString =
"#version 300 es\n"
"precision lowp float;\n"
"in float value;\n"
"float foo0 (flat in float x) {\n"
" return 2.0*x;\n"
"}\n"
"out vec4 colorOUT;\n"
"void main()\n"
"{\n"
" colorOUT = vec4(foo0(value));\n"
"}\n";
if (compile(shaderString, GL_FRAGMENT_SHADER, SH_GLES3_SPEC))
{
FAIL() << "Shader compilation succeeded, expecting failure" << 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