Commit 6a4a427e by John Kessenich

GLSL: Implement correct semantic checking for run-time sized arrays.

parent 5a867aca
#version 450 core
buffer bn {
int a[];
float b[];
} buf;
uniform un {
int a[];
float b[];
} ubuf;
buffer bna {
int a[];
float b[];
} bufa[4];
uniform una {
int a[];
float b[];
} ubufa[4];
buffer abn {
int aba[];
float abb[];
};
uniform aun {
int aua[];
float aub[];
};
int i;
void main()
{
ubuf.a[3];
ubuf.b[3];
buf.a[3];
buf.b[3];
ubufa[3].a[3];
ubufa[3].b[3];
bufa[3].a[3];
bufa[3].b[3];
aua[3];
aub[3];
aba[3];
abb[3];
ubuf.a[i]; // ERROR
ubuf.b[i]; // ERROR
buf.a[i]; // ERROR
buf.b[i];
ubuf.a.length(); // ERROR
ubuf.b.length(); // ERROR
buf.a.length(); // ERROR
buf.b.length();
ubufa[1].a[i]; // ERROR
ubufa[1].b[i]; // ERROR
bufa[1].a[i]; // ERROR
bufa[1].b[i];
ubufa[1].a.length(); // ERROR
ubufa[1].b.length(); // ERROR
bufa[1].a.length(); // ERROR
bufa[1].b.length();
aua[i]; // ERROR
aub[i]; // ERROR
aba[i]; // ERROR
abb[i];
aua.length(); // ERROR
aub.length(); // ERROR
aba.length(); // ERROR
abb.length();
}
...@@ -390,7 +390,7 @@ TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIn ...@@ -390,7 +390,7 @@ TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIn
error(loc, "", "[", "array must be sized by a redeclaration or layout qualifier before being indexed with a variable"); error(loc, "", "[", "array must be sized by a redeclaration or layout qualifier before being indexed with a variable");
else { else {
// it is okay for a run-time sized array // it is okay for a run-time sized array
if (base->getType().getQualifier().storage != EvqBuffer) if (!isRuntimeSizable(*base))
error(loc, "", "[", "array must be redeclared with a size before being indexed with a variable"); error(loc, "", "[", "array must be redeclared with a size before being indexed with a variable");
} }
base->getWritableType().setArrayVariablyIndexed(); base->getWritableType().setArrayVariablyIndexed();
...@@ -1235,7 +1235,7 @@ TIntermTyped* TParseContext::handleLengthMethod(const TSourceLoc& loc, TFunction ...@@ -1235,7 +1235,7 @@ TIntermTyped* TParseContext::handleLengthMethod(const TSourceLoc& loc, TFunction
if (length == 0) { if (length == 0) {
if (intermNode->getAsSymbolNode() && isIoResizeArray(type)) if (intermNode->getAsSymbolNode() && isIoResizeArray(type))
error(loc, "", function->getName().c_str(), "array must first be sized by a redeclaration or layout qualifier"); error(loc, "", function->getName().c_str(), "array must first be sized by a redeclaration or layout qualifier");
else if (type.getQualifier().isUniformOrBuffer()) { else if (isRuntimeLength(*intermNode->getAsTyped())) {
// Create a unary op and let the back end handle it // Create a unary op and let the back end handle it
return intermediate.addBuiltInFunctionCall(loc, EOpArrayLength, true, intermNode, TType(EbtInt)); return intermediate.addBuiltInFunctionCall(loc, EOpArrayLength, true, intermNode, TType(EbtInt));
} else } else
...@@ -3268,6 +3268,31 @@ void TParseContext::declareArray(const TSourceLoc& loc, const TString& identifie ...@@ -3268,6 +3268,31 @@ void TParseContext::declareArray(const TSourceLoc& loc, const TString& identifie
checkIoArraysConsistency(loc); checkIoArraysConsistency(loc);
} }
// Policy decision for whether a node could potentially be sized at runtime.
bool TParseContext::isRuntimeSizable(const TIntermTyped& base) const
{
const TType& type = base.getType();
if (type.getQualifier().storage == EvqBuffer) {
// in a buffer block
const TIntermBinary* binary = base.getAsBinaryNode();
if (binary != nullptr && binary->getOp() == EOpIndexDirectStruct) {
// is it the last member?
const int index = binary->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst();
const int memberCount = (int)binary->getLeft()->getType().getStruct()->size();
if (index == memberCount - 1)
return true;
}
}
return false;
}
// Policy decision for whether a run-time .length() is allowed.
bool TParseContext::isRuntimeLength(const TIntermTyped& base) const
{
return isRuntimeSizable(base);
}
// Returns true if the first argument to the #line directive is the line number for the next line. // Returns true if the first argument to the #line directive is the line number for the next line.
// //
// Desktop, pre-version 3.30: "After processing this directive // Desktop, pre-version 3.30: "After processing this directive
......
...@@ -427,6 +427,8 @@ protected: ...@@ -427,6 +427,8 @@ protected:
TVariable* makeInternalVariable(const char* name, const TType&) const; TVariable* makeInternalVariable(const char* name, const TType&) const;
TVariable* declareNonArray(const TSourceLoc&, const TString& identifier, const TType&); TVariable* declareNonArray(const TSourceLoc&, const TString& identifier, const TType&);
void declareArray(const TSourceLoc&, const TString& identifier, const TType&, TSymbol*&); void declareArray(const TSourceLoc&, const TString& identifier, const TType&, TSymbol*&);
bool isRuntimeSizable(const TIntermTyped&) const;
bool isRuntimeLength(const TIntermTyped&) const;
TIntermNode* executeInitializer(const TSourceLoc&, TIntermTyped* initializer, TVariable* variable); TIntermNode* executeInitializer(const TSourceLoc&, TIntermTyped* initializer, TVariable* variable);
TIntermTyped* convertInitializerList(const TSourceLoc&, const TType&, TIntermTyped* initializer); TIntermTyped* convertInitializerList(const TSourceLoc&, const TType&, TIntermTyped* initializer);
void finish() override; void finish() override;
......
...@@ -204,6 +204,7 @@ INSTANTIATE_TEST_CASE_P( ...@@ -204,6 +204,7 @@ INSTANTIATE_TEST_CASE_P(
"Operations.frag", "Operations.frag",
"overlongLiteral.frag", "overlongLiteral.frag",
"prepost.frag", "prepost.frag",
"runtimeArray.vert",
"simpleFunctionCall.frag", "simpleFunctionCall.frag",
"structAssignment.frag", "structAssignment.frag",
"structDeref.frag", "structDeref.frag",
......
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