Commit 82ae8c31 by John Kessenich

HLSL: Fix #924: Convert between two different arrays with cast.

parent 0320d090
float4 main() : SV_POSITION
{
int4 int4_array[3];
float4 float4_array_times[2] = (float4[2])int4_array;
float2 float2_array_times2[4] = (float2[4])int4_array;
int4 int4_array2[2] = (int4[2])int4_array;
int int1_array[2] = (int[2])int4_array;
return (float4)0.0;
}
...@@ -5425,6 +5425,9 @@ TIntermTyped* TParseContext::convertInitializerList(const TSourceLoc& loc, const ...@@ -5425,6 +5425,9 @@ TIntermTyped* TParseContext::convertInitializerList(const TSourceLoc& loc, const
// Test for the correctness of the parameters passed to various constructor functions // Test for the correctness of the parameters passed to various constructor functions
// and also convert them to the right data type, if allowed and required. // and also convert them to the right data type, if allowed and required.
// //
// 'node' is what to construct from.
// 'type' is what type to construct.
//
// Returns nullptr for an error or the constructed node (aggregate or typed) for no error. // Returns nullptr for an error or the constructed node (aggregate or typed) for no error.
// //
TIntermTyped* TParseContext::addConstructor(const TSourceLoc& loc, TIntermNode* node, const TType& type) TIntermTyped* TParseContext::addConstructor(const TSourceLoc& loc, TIntermNode* node, const TType& type)
......
...@@ -100,6 +100,7 @@ INSTANTIATE_TEST_CASE_P( ...@@ -100,6 +100,7 @@ INSTANTIATE_TEST_CASE_P(
{"hlsl.comparison.vec.frag", "main"}, {"hlsl.comparison.vec.frag", "main"},
{"hlsl.conditional.frag", "PixelShaderFunction"}, {"hlsl.conditional.frag", "PixelShaderFunction"},
{"hlsl.constantbuffer.frag", "main"}, {"hlsl.constantbuffer.frag", "main"},
{"hlsl.constructArray.vert", "main"},
{"hlsl.constructexpr.frag", "main"}, {"hlsl.constructexpr.frag", "main"},
{"hlsl.constructimat.frag", "main"}, {"hlsl.constructimat.frag", "main"},
{"hlsl.depthGreater.frag", "PixelShaderFunction"}, {"hlsl.depthGreater.frag", "PixelShaderFunction"},
......
...@@ -2732,9 +2732,14 @@ bool HlslGrammar::acceptUnaryExpression(TIntermTyped*& node) ...@@ -2732,9 +2732,14 @@ bool HlslGrammar::acceptUnaryExpression(TIntermTyped*& node)
if (acceptTokenClass(EHTokLeftParen)) { if (acceptTokenClass(EHTokLeftParen)) {
TType castType; TType castType;
if (acceptType(castType)) { if (acceptType(castType)) {
// recognize any array_specifier as part of the type
TArraySizes* arraySizes = nullptr;
acceptArraySpecifier(arraySizes);
if (arraySizes != nullptr)
castType.newArraySizes(*arraySizes);
TSourceLoc loc = token.loc;
if (acceptTokenClass(EHTokRightParen)) { if (acceptTokenClass(EHTokRightParen)) {
// We've matched "(type)" now, get the expression to cast // We've matched "(type)" now, get the expression to cast
TSourceLoc loc = token.loc;
if (! acceptUnaryExpression(node)) if (! acceptUnaryExpression(node))
return false; return false;
...@@ -2754,6 +2759,11 @@ bool HlslGrammar::acceptUnaryExpression(TIntermTyped*& node) ...@@ -2754,6 +2759,11 @@ bool HlslGrammar::acceptUnaryExpression(TIntermTyped*& node)
// the '(int' part. We must back up twice. // the '(int' part. We must back up twice.
recedeToken(); recedeToken();
recedeToken(); recedeToken();
// Note, there are no array constructors like
// (float[2](...))
if (arraySizes != nullptr)
parseContext.error(loc, "parenthesized array constructor not allowed", "([]())", "", "");
} }
} else { } else {
// This isn't a type cast, but it still started "(", so if it is a // This isn't a type cast, but it still started "(", so if it is a
......
...@@ -5401,7 +5401,8 @@ bool HlslParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node ...@@ -5401,7 +5401,8 @@ bool HlslParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node
if (type.isImplicitlySizedArray()) { if (type.isImplicitlySizedArray()) {
// auto adapt the constructor type to the number of arguments // auto adapt the constructor type to the number of arguments
type.changeOuterArraySize(function.getParamCount()); type.changeOuterArraySize(function.getParamCount());
} else if (type.getOuterArraySize() != function.getParamCount()) { } else if (type.getOuterArraySize() != function.getParamCount() &&
type.computeNumComponents() > size) {
error(loc, "array constructor needs one argument per array element", "constructor", ""); error(loc, "array constructor needs one argument per array element", "constructor", "");
return true; return true;
} }
...@@ -5430,6 +5431,12 @@ bool HlslParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node ...@@ -5430,6 +5431,12 @@ bool HlslParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node
} }
} }
// Some array -> array type casts are okay
if (arrayArg && function.getParamCount() == 1 && op != EOpConstructStruct && type.isArray() &&
!type.isArrayOfArrays() && !function[0].type->isArrayOfArrays() &&
type.getVectorSize() >= 1 && function[0].type->getVectorSize() >= 1)
return false;
if (arrayArg && op != EOpConstructStruct && ! type.isArrayOfArrays()) { if (arrayArg && op != EOpConstructStruct && ! type.isArrayOfArrays()) {
error(loc, "constructing non-array constituent from array argument", "constructor", ""); error(loc, "constructing non-array constituent from array argument", "constructor", "");
return true; return true;
...@@ -7336,17 +7343,20 @@ TIntermTyped* HlslParseContext::handleConstructor(const TSourceLoc& loc, TInterm ...@@ -7336,17 +7343,20 @@ TIntermTyped* HlslParseContext::handleConstructor(const TSourceLoc& loc, TInterm
// Add a constructor, either from the grammar, or other programmatic reasons. // Add a constructor, either from the grammar, or other programmatic reasons.
// //
// 'node' is what to construct from.
// 'type' is what type to construct.
//
// Returns the constructed object.
// Return nullptr if it can't be done. // Return nullptr if it can't be done.
// //
TIntermTyped* HlslParseContext::addConstructor(const TSourceLoc& loc, TIntermTyped* node, const TType& type) TIntermTyped* HlslParseContext::addConstructor(const TSourceLoc& loc, TIntermTyped* node, const TType& type)
{ {
TIntermAggregate* aggrNode = node->getAsAggregate();
TOperator op = intermediate.mapTypeToConstructorOp(type); TOperator op = intermediate.mapTypeToConstructorOp(type);
// Combined texture-sampler constructors are completely semantic checked // Combined texture-sampler constructors are completely semantic checked
// in constructorTextureSamplerError() // in constructorTextureSamplerError()
if (op == EOpConstructTextureSampler) if (op == EOpConstructTextureSampler)
return intermediate.setAggregateOperator(aggrNode, op, type, loc); return intermediate.setAggregateOperator(node->getAsAggregate(), op, type, loc);
TTypeList::const_iterator memberTypes; TTypeList::const_iterator memberTypes;
if (op == EOpConstructStruct) if (op == EOpConstructStruct)
...@@ -7360,7 +7370,8 @@ TIntermTyped* HlslParseContext::addConstructor(const TSourceLoc& loc, TIntermTyp ...@@ -7360,7 +7370,8 @@ TIntermTyped* HlslParseContext::addConstructor(const TSourceLoc& loc, TIntermTyp
elementType.shallowCopy(type); elementType.shallowCopy(type);
bool singleArg; bool singleArg;
if (aggrNode) { TIntermAggregate* aggrNode = node->getAsAggregate();
if (aggrNode != nullptr) {
if (aggrNode->getOp() != EOpNull || aggrNode->getSequence().size() == 1) if (aggrNode->getOp() != EOpNull || aggrNode->getSequence().size() == 1)
singleArg = true; singleArg = true;
else else
...@@ -7370,9 +7381,15 @@ TIntermTyped* HlslParseContext::addConstructor(const TSourceLoc& loc, TIntermTyp ...@@ -7370,9 +7381,15 @@ TIntermTyped* HlslParseContext::addConstructor(const TSourceLoc& loc, TIntermTyp
TIntermTyped *newNode; TIntermTyped *newNode;
if (singleArg) { if (singleArg) {
// Handle array -> array conversion
// Constructing an array of one type from an array of another type is allowed,
// assuming there are enough components available (semantic-checked earlier).
if (type.isArray() && node->isArray())
newNode = convertArray(node, type);
// If structure constructor or array constructor is being called // If structure constructor or array constructor is being called
// for only one parameter inside the structure, we need to call constructAggregate function once. // for only one parameter inside the structure, we need to call constructAggregate function once.
if (type.isArray()) else if (type.isArray())
newNode = constructAggregate(node, elementType, 1, node->getLoc()); newNode = constructAggregate(node, elementType, 1, node->getLoc());
else if (op == EOpConstructStruct) else if (op == EOpConstructStruct)
newNode = constructAggregate(node, *(*memberTypes).type, 1, node->getLoc()); newNode = constructAggregate(node, *(*memberTypes).type, 1, node->getLoc());
...@@ -7537,13 +7554,86 @@ TIntermTyped* HlslParseContext::constructBuiltIn(const TType& type, TOperator op ...@@ -7537,13 +7554,86 @@ TIntermTyped* HlslParseContext::constructBuiltIn(const TType& type, TOperator op
return intermediate.setAggregateOperator(newNode, op, type, loc); return intermediate.setAggregateOperator(newNode, op, type, loc);
} }
// Convert the array in node to the requested type, which is also an array.
// Returns nullptr on failure, otherwise returns aggregate holding the list of
// elements needed to construct the array.
TIntermTyped* HlslParseContext::convertArray(TIntermTyped* node, const TType& type)
{
assert(node->isArray() && type.isArray());
if (node->getType().computeNumComponents() < type.computeNumComponents())
return nullptr;
// TODO: write an argument replicator, for the case the argument should not be
// executed multiple times, yet multiple copies are needed.
TIntermTyped* constructee = node->getAsTyped();
// track where we are in consuming the argument
int constructeeElement = 0;
int constructeeComponent = 0;
// bump up to the next component to consume
const auto getNextComponent = [&]() {
TIntermTyped* component;
component = handleBracketDereference(node->getLoc(), constructee,
intermediate.addConstantUnion(constructeeElement, node->getLoc()));
if (component->isVector())
component = handleBracketDereference(node->getLoc(), component,
intermediate.addConstantUnion(constructeeComponent, node->getLoc()));
// bump component pointer up
++constructeeComponent;
if (constructeeComponent == constructee->getVectorSize()) {
constructeeComponent = 0;
++constructeeElement;
}
return component;
};
// make one subnode per constructed array element
TIntermAggregate* constructor = nullptr;
TType derefType(type, 0);
TType speculativeComponentType(derefType, 0);
TType* componentType = derefType.isVector() ? &speculativeComponentType : &derefType;
TOperator componentOp = intermediate.mapTypeToConstructorOp(*componentType);
TType crossType(node->getBasicType(), EvqTemporary, type.getVectorSize());
for (int e = 0; e < type.getOuterArraySize(); ++e) {
// construct an element
TIntermTyped* elementArg;
if (type.getVectorSize() == constructee->getVectorSize()) {
// same element shape
elementArg = handleBracketDereference(node->getLoc(), constructee,
intermediate.addConstantUnion(e, node->getLoc()));
} else {
// mismatched element shapes
if (type.getVectorSize() == 1)
elementArg = getNextComponent();
else {
// make a vector
TIntermAggregate* elementConstructee = nullptr;
for (int c = 0; c < type.getVectorSize(); ++c)
elementConstructee = intermediate.growAggregate(elementConstructee, getNextComponent());
elementArg = addConstructor(node->getLoc(), elementConstructee, crossType);
}
}
// convert basic types
elementArg = intermediate.addConversion(componentOp, derefType, elementArg);
if (elementArg == nullptr)
return nullptr;
// combine with top-level constructor
constructor = intermediate.growAggregate(constructor, elementArg);
}
return constructor;
}
// This function tests for the type of the parameters to the structure or array constructor. Raises // This function tests for the type of the parameters to the structure or array constructor. Raises
// an error message if the expected type does not match the parameter passed to the constructor. // an error message if the expected type does not match the parameter passed to the constructor.
// //
// Returns nullptr for an error or the input node itself if the expected and the given parameter types match. // Returns nullptr for an error or the input node itself if the expected and the given parameter types match.
// //
TIntermTyped* HlslParseContext::constructAggregate(TIntermNode* node, const TType& type, int paramCount, const TSourceLoc& loc) TIntermTyped* HlslParseContext::constructAggregate(TIntermNode* node, const TType& type, int paramCount,
const TSourceLoc& loc)
{ {
// Handle cases that map more 1:1 between constructor arguments and constructed.
TIntermTyped* converted = intermediate.addConversion(EOpConstructStruct, type, node->getAsTyped()); TIntermTyped* converted = intermediate.addConversion(EOpConstructStruct, type, node->getAsTyped());
if (! converted || converted->getType() != type) { if (! converted || converted->getType() != type) {
error(loc, "", "constructor", "cannot convert parameter %d from '%s' to '%s'", paramCount, error(loc, "", "constructor", "cannot convert parameter %d from '%s' to '%s'", paramCount,
......
...@@ -146,6 +146,7 @@ public: ...@@ -146,6 +146,7 @@ public:
void lengthenList(const TSourceLoc&, TIntermSequence& list, int size, TIntermTyped* scalarInit); void lengthenList(const TSourceLoc&, TIntermSequence& list, int size, TIntermTyped* scalarInit);
TIntermTyped* handleConstructor(const TSourceLoc&, TIntermTyped*, const TType&); TIntermTyped* handleConstructor(const TSourceLoc&, TIntermTyped*, const TType&);
TIntermTyped* addConstructor(const TSourceLoc&, TIntermTyped*, const TType&); TIntermTyped* addConstructor(const TSourceLoc&, TIntermTyped*, const TType&);
TIntermTyped* convertArray(TIntermTyped*, const TType&);
TIntermTyped* constructAggregate(TIntermNode*, const TType&, int, const TSourceLoc&); TIntermTyped* constructAggregate(TIntermNode*, const TType&, int, const TSourceLoc&);
TIntermTyped* constructBuiltIn(const TType&, TOperator, TIntermTyped*, const TSourceLoc&, bool subset); TIntermTyped* constructBuiltIn(const TType&, TOperator, TIntermTyped*, const TSourceLoc&, bool subset);
void declareBlock(const TSourceLoc&, TType&, const TString* instanceName = 0, TArraySizes* arraySizes = 0); void declareBlock(const TSourceLoc&, TType&, const TString* instanceName = 0, TArraySizes* arraySizes = 0);
......
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