WIP: HLSL: support global const initializers from non-constant rvalues
Semantic test left over from other source languages is removed, since this is permitted by HLSL.
Also, to support the functionality, a targeted test is performed for this case and it is
turned into a EvqGlobal qualifier to create an AST initialization segment when needed.
Constness is now propagated up aggregate chains during initializer construction. This
handles hierarchical cases such as the distinction between:
static const float2 a[2] = { { 1, 2 }, { 3, 4} };
vs
static const float2 a[2] = { { 1, 2 }, { cbuffer_member, 4} };
The first of which can use a first class constant initalization, and the second cannot.
This commit is contained in:
@@ -2553,8 +2553,19 @@ bool HlslGrammar::acceptInitializer(TIntermTyped*& node)
|
||||
expected("assignment expression in initializer list");
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool firstNode = (node == nullptr);
|
||||
|
||||
node = intermediate.growAggregate(node, expr, loc);
|
||||
|
||||
// If every sub-node in the list has qualifier EvqConst, the returned node becomes
|
||||
// EvqConst. Otherwise, it becomes EvqTemporary. That doesn't happen with e.g.
|
||||
// EvqIn or EvqPosition, since the collection isn't EvqPosition if all the members are.
|
||||
if (firstNode && expr->getQualifier().storage == EvqConst)
|
||||
node->getQualifier().storage = EvqConst;
|
||||
else if (expr->getQualifier().storage != EvqConst)
|
||||
node->getQualifier().storage = EvqTemporary;
|
||||
|
||||
// COMMA
|
||||
if (acceptTokenClass(EHTokComma)) {
|
||||
if (acceptTokenClass(EHTokRightBrace)) // allow trailing comma
|
||||
|
||||
@@ -7165,6 +7165,21 @@ TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, const TStr
|
||||
if (voidErrorCheck(loc, identifier, type.getBasicType()))
|
||||
return nullptr;
|
||||
|
||||
// Global consts with initializers that are non-const act like EvqGlobal in HLSL.
|
||||
// This test is implicitly recursive, because initializers propagate constness
|
||||
// up the aggregate node tree during creation. E.g, for:
|
||||
// { { 1, 2 }, { 3, 4 } }
|
||||
// the initializer list is marked EvqConst at the top node, and remains so here. However:
|
||||
// { 1, { myvar, 2 }, 3 }
|
||||
// is not a const intializer, and still becomes EvqGlobal here.
|
||||
|
||||
const bool nonConstInitializer = (initializer != nullptr && initializer->getQualifier().storage != EvqConst);
|
||||
|
||||
if (type.getQualifier().storage == EvqConst && symbolTable.atGlobalLevel() && nonConstInitializer) {
|
||||
// Force to global
|
||||
type.getQualifier().storage = EvqGlobal;
|
||||
}
|
||||
|
||||
// make const and initialization consistent
|
||||
fixConstInit(loc, identifier, type, initializer);
|
||||
|
||||
@@ -7345,11 +7360,6 @@ TIntermNode* HlslParseContext::executeInitializer(const TSourceLoc& loc, TInterm
|
||||
variable->getWritableType().getQualifier().storage = EvqTemporary;
|
||||
return nullptr;
|
||||
}
|
||||
if (qualifier == EvqConst && symbolTable.atGlobalLevel() && initializer->getType().getQualifier().storage != EvqConst) {
|
||||
error(loc, "global const initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str());
|
||||
variable->getWritableType().getQualifier().storage = EvqTemporary;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Const variables require a constant initializer
|
||||
if (qualifier == EvqConst) {
|
||||
|
||||
Reference in New Issue
Block a user