Merge pull request #978 from LoopDawg/global-const-init-fix

HLSL: support global const initializers from non-constant rvalues
diff --git a/Test/baseResults/hlsl.global-const-init.frag.out b/Test/baseResults/hlsl.global-const-init.frag.out
new file mode 100644
index 0000000..659ad53
--- /dev/null
+++ b/Test/baseResults/hlsl.global-const-init.frag.out
@@ -0,0 +1,178 @@
+hlsl.global-const-init.frag
+Shader version: 500
+gl_FragCoord origin is upper left
+0:? Sequence
+0:6  Sequence
+0:6    move second child to first child ( temp 4-component vector of float)
+0:6      'bar' ( global 4-component vector of float)
+0:6      foo: direct index for structure (layout( row_major std140) uniform 4-component vector of float)
+0:6        'anon@0' (layout( row_major std140) uniform block{layout( row_major std140) uniform 4-component vector of float foo})
+0:6        Constant:
+0:6          0 (const uint)
+0:8  Sequence
+0:8    move second child to first child ( temp 2-element array of 2-component vector of float)
+0:8      'a1' ( global 2-element array of 2-component vector of float)
+0:8      Construct vec2 ( temp 2-element array of 2-component vector of float)
+0:8        Constant:
+0:8          1.000000
+0:8          2.000000
+0:8        Construct vec2 ( temp 2-component vector of float)
+0:8          direct index ( temp float)
+0:8            foo: direct index for structure (layout( row_major std140) uniform 4-component vector of float)
+0:8              'anon@0' (layout( row_major std140) uniform block{layout( row_major std140) uniform 4-component vector of float foo})
+0:8              Constant:
+0:8                0 (const uint)
+0:8            Constant:
+0:8              0 (const int)
+0:8          Constant:
+0:8            4.000000
+0:12  Function Definition: @main( ( temp 4-component vector of float)
+0:12    Function Parameters: 
+0:?     Sequence
+0:13      Branch: Return with expression
+0:13        'bar' ( global 4-component vector of float)
+0:12  Function Definition: main( ( temp void)
+0:12    Function Parameters: 
+0:?     Sequence
+0:12      move second child to first child ( temp 4-component vector of float)
+0:?         '@entryPointOutput' (layout( location=0) out 4-component vector of float)
+0:12        Function Call: @main( ( temp 4-component vector of float)
+0:?   Linker Objects
+0:?     'anon@0' (layout( row_major std140) uniform block{layout( row_major std140) uniform 4-component vector of float foo})
+0:?     'bar' ( global 4-component vector of float)
+0:?     'a1' ( global 2-element array of 2-component vector of float)
+0:?     'a2' ( const 2-element array of 2-component vector of float)
+0:?       5.000000
+0:?       6.000000
+0:?       7.000000
+0:?       8.000000
+0:?     '@entryPointOutput' (layout( location=0) out 4-component vector of float)
+
+
+Linked fragment stage:
+
+
+Shader version: 500
+gl_FragCoord origin is upper left
+0:? Sequence
+0:6  Sequence
+0:6    move second child to first child ( temp 4-component vector of float)
+0:6      'bar' ( global 4-component vector of float)
+0:6      foo: direct index for structure (layout( row_major std140) uniform 4-component vector of float)
+0:6        'anon@0' (layout( row_major std140) uniform block{layout( row_major std140) uniform 4-component vector of float foo})
+0:6        Constant:
+0:6          0 (const uint)
+0:8  Sequence
+0:8    move second child to first child ( temp 2-element array of 2-component vector of float)
+0:8      'a1' ( global 2-element array of 2-component vector of float)
+0:8      Construct vec2 ( temp 2-element array of 2-component vector of float)
+0:8        Constant:
+0:8          1.000000
+0:8          2.000000
+0:8        Construct vec2 ( temp 2-component vector of float)
+0:8          direct index ( temp float)
+0:8            foo: direct index for structure (layout( row_major std140) uniform 4-component vector of float)
+0:8              'anon@0' (layout( row_major std140) uniform block{layout( row_major std140) uniform 4-component vector of float foo})
+0:8              Constant:
+0:8                0 (const uint)
+0:8            Constant:
+0:8              0 (const int)
+0:8          Constant:
+0:8            4.000000
+0:12  Function Definition: @main( ( temp 4-component vector of float)
+0:12    Function Parameters: 
+0:?     Sequence
+0:13      Branch: Return with expression
+0:13        'bar' ( global 4-component vector of float)
+0:12  Function Definition: main( ( temp void)
+0:12    Function Parameters: 
+0:?     Sequence
+0:12      move second child to first child ( temp 4-component vector of float)
+0:?         '@entryPointOutput' (layout( location=0) out 4-component vector of float)
+0:12        Function Call: @main( ( temp 4-component vector of float)
+0:?   Linker Objects
+0:?     'anon@0' (layout( row_major std140) uniform block{layout( row_major std140) uniform 4-component vector of float foo})
+0:?     'bar' ( global 4-component vector of float)
+0:?     'a1' ( global 2-element array of 2-component vector of float)
+0:?     'a2' ( const 2-element array of 2-component vector of float)
+0:?       5.000000
+0:?       6.000000
+0:?       7.000000
+0:?       8.000000
+0:?     '@entryPointOutput' (layout( location=0) out 4-component vector of float)
+
+// Module Version 10000
+// Generated by (magic number): 80001
+// Id's are bound by 50
+
+                              Capability Shader
+               1:             ExtInstImport  "GLSL.std.450"
+                              MemoryModel Logical GLSL450
+                              EntryPoint Fragment 4  "main" 41
+                              ExecutionMode 4 OriginUpperLeft
+                              Source HLSL 500
+                              Name 4  "main"
+                              Name 9  "@main("
+                              Name 12  "bar"
+                              Name 13  "CB"
+                              MemberName 13(CB) 0  "foo"
+                              Name 15  ""
+                              Name 26  "a1"
+                              Name 41  "@entryPointOutput"
+                              MemberDecorate 13(CB) 0 Offset 0
+                              Decorate 13(CB) Block
+                              Decorate 15 DescriptorSet 0
+                              Decorate 41(@entryPointOutput) Location 0
+               2:             TypeVoid
+               3:             TypeFunction 2
+               6:             TypeFloat 32
+               7:             TypeVector 6(float) 4
+               8:             TypeFunction 7(fvec4)
+              11:             TypePointer Private 7(fvec4)
+         12(bar):     11(ptr) Variable Private
+          13(CB):             TypeStruct 7(fvec4)
+              14:             TypePointer Uniform 13(CB)
+              15:     14(ptr) Variable Uniform
+              16:             TypeInt 32 1
+              17:     16(int) Constant 0
+              18:             TypePointer Uniform 7(fvec4)
+              21:             TypeVector 6(float) 2
+              22:             TypeInt 32 0
+              23:     22(int) Constant 2
+              24:             TypeArray 21(fvec2) 23
+              25:             TypePointer Private 24
+          26(a1):     25(ptr) Variable Private
+              27:    6(float) Constant 1065353216
+              28:    6(float) Constant 1073741824
+              29:   21(fvec2) ConstantComposite 27 28
+              30:     22(int) Constant 0
+              31:             TypePointer Uniform 6(float)
+              34:    6(float) Constant 1082130432
+              40:             TypePointer Output 7(fvec4)
+41(@entryPointOutput):     40(ptr) Variable Output
+              43:    6(float) Constant 1084227584
+              44:    6(float) Constant 1086324736
+              45:   21(fvec2) ConstantComposite 43 44
+              46:    6(float) Constant 1088421888
+              47:    6(float) Constant 1090519040
+              48:   21(fvec2) ConstantComposite 46 47
+              49:          24 ConstantComposite 45 48
+         4(main):           2 Function None 3
+               5:             Label
+              19:     18(ptr) AccessChain 15 17
+              20:    7(fvec4) Load 19
+                              Store 12(bar) 20
+              32:     31(ptr) AccessChain 15 17 30
+              33:    6(float) Load 32
+              35:   21(fvec2) CompositeConstruct 33 34
+              36:          24 CompositeConstruct 29 35
+                              Store 26(a1) 36
+              42:    7(fvec4) FunctionCall 9(@main()
+                              Store 41(@entryPointOutput) 42
+                              Return
+                              FunctionEnd
+       9(@main():    7(fvec4) Function None 8
+              10:             Label
+              37:    7(fvec4) Load 12(bar)
+                              ReturnValue 37
+                              FunctionEnd
diff --git a/Test/hlsl.global-const-init.frag b/Test/hlsl.global-const-init.frag
new file mode 100644
index 0000000..d8f36c9
--- /dev/null
+++ b/Test/hlsl.global-const-init.frag
@@ -0,0 +1,14 @@
+
+cbuffer CB {
+    float4 foo;
+};
+
+static const float4 bar = foo; // test const (in the immutable sense) initializer from non-const.
+
+static const float2 a1[2] = { { 1, 2 }, { foo.x, 4 } }; // not entirely constant
+static const float2 a2[2] = { { 5, 6 }, { 7, 8 } };     // entirely constant
+
+float4 main() : SV_Target0
+{
+    return bar;
+}
diff --git a/gtests/Hlsl.FromFile.cpp b/gtests/Hlsl.FromFile.cpp
index d6fe367..ea90b62 100644
--- a/gtests/Hlsl.FromFile.cpp
+++ b/gtests/Hlsl.FromFile.cpp
@@ -137,6 +137,7 @@
         {"hlsl.getdimensions.rw.dx10.frag", "main"},
         {"hlsl.getdimensions.dx10.vert", "main"},
         {"hlsl.getsampleposition.dx10.frag", "main"},
+        {"hlsl.global-const-init.frag", "main"},
         {"hlsl.domain.1.tese", "main"},
         {"hlsl.domain.2.tese", "main"},
         {"hlsl.domain.3.tese", "main"},
diff --git a/hlsl/hlslGrammar.cpp b/hlsl/hlslGrammar.cpp
index 95869e8..05c95d3 100755
--- a/hlsl/hlslGrammar.cpp
+++ b/hlsl/hlslGrammar.cpp
@@ -2553,8 +2553,19 @@
             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
diff --git a/hlsl/hlslParseHelper.cpp b/hlsl/hlslParseHelper.cpp
index 27dac88..50d88d2 100755
--- a/hlsl/hlslParseHelper.cpp
+++ b/hlsl/hlslParseHelper.cpp
@@ -7165,6 +7165,21 @@
     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 @@
         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) {