Merge branch 'patch-1' of https://github.com/HaydnTrigg/glslang into HaydnTrigg-patch-1
diff --git a/SPIRV/CMakeLists.txt b/SPIRV/CMakeLists.txt
index 1e5513c..bf2be16 100755
--- a/SPIRV/CMakeLists.txt
+++ b/SPIRV/CMakeLists.txt
@@ -60,6 +60,7 @@
         PRIVATE ${spirv-tools_SOURCE_DIR}/source
     )
     target_link_libraries(SPIRV glslang SPIRV-Tools-opt)
+    target_include_directories(SPIRV PUBLIC ../External)
 else()
     target_link_libraries(SPIRV glslang)
 endif(ENABLE_OPT)
diff --git a/SPIRV/disassemble.cpp b/SPIRV/disassemble.cpp
index b432e65..16770c4 100755
--- a/SPIRV/disassemble.cpp
+++ b/SPIRV/disassemble.cpp
@@ -716,4 +716,25 @@
     SpirvStream.processInstructions();
 }
 
+#if ENABLE_OPT
+
+#include "spirv-tools/source/disassemble.h"
+
+// Use the SPIRV-Tools disassembler to print SPIR-V.
+void SpirvToolsDisassemble(std::ostream& out, const std::vector<unsigned int>& spirv)
+{
+    spv_context context = spvContextCreate(SPV_ENV_UNIVERSAL_1_3);
+    spv_text text;
+    spv_diagnostic diagnostic = nullptr;
+    spvBinaryToText(context, &spirv.front(), spirv.size(),
+        SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | SPV_BINARY_TO_TEXT_OPTION_INDENT,
+        &text, &diagnostic);
+    if (diagnostic == nullptr)
+        out << text->str;
+    else
+        spvDiagnosticPrint(diagnostic);
+}
+
+#endif
+
 }; // end namespace spv
diff --git a/SPIRV/disassemble.h b/SPIRV/disassemble.h
index 47cef65..bdde5cb 100755
--- a/SPIRV/disassemble.h
+++ b/SPIRV/disassemble.h
@@ -45,8 +45,12 @@
 
 namespace spv {
 
+    // disassemble with glslang custom disassembler
     void Disassemble(std::ostream& out, const std::vector<unsigned int>&);
 
+    // disassemble with SPIRV-Tools disassembler
+    void SpirvToolsDisassemble(std::ostream& out, const std::vector<unsigned int>& stream);
+
 };  // end namespace spv
 
 #endif // disassembler_H
diff --git a/StandAlone/CMakeLists.txt b/StandAlone/CMakeLists.txt
index d500121..5cea53d 100755
--- a/StandAlone/CMakeLists.txt
+++ b/StandAlone/CMakeLists.txt
@@ -33,6 +33,7 @@
 
 target_link_libraries(glslangValidator ${LIBRARIES})
 target_link_libraries(spirv-remap ${LIBRARIES})
+target_include_directories(glslangValidator PUBLIC ../External)
 
 if(WIN32)
     source_group("Source" FILES ${SOURCES})
diff --git a/StandAlone/StandAlone.cpp b/StandAlone/StandAlone.cpp
old mode 100644
new mode 100755
index 6736dbc..549cb0d
--- a/StandAlone/StandAlone.cpp
+++ b/StandAlone/StandAlone.cpp
@@ -102,6 +102,7 @@
     EOptionDumpBareVersion      = (1 << 31),
 };
 bool targetHlslFunctionality1 = false;
+bool SpvToolsDisassembler = false;
 
 //
 // Return codes from main/exit().
@@ -506,6 +507,8 @@
                         sourceEntryPointName = argv[1];
                         bumpArg();
                         break;
+                    } else if (lowerword == "spirv-dis") {
+                        SpvToolsDisassembler = true;
                     } else if (lowerword == "stdin") {
                         Options |= EOptionStdin;
                         shaderStageName = argv[1];
@@ -982,9 +985,15 @@
                         } else {
                             glslang::OutputSpvBin(spirv, GetBinaryName((EShLanguage)stage));
                         }
-                        if (Options & EOptionHumanReadableSpv) {
+#if ENABLE_OPT
+                        if (SpvToolsDisassembler)
+                            spv::SpirvToolsDisassemble(std::cout, spirv);
+#else
+                        if (SpvToolsDisassembler)
+                            printf("SPIRV-Tools is not enabled; use -H for human readable SPIR-V\n");
+#endif
+                        if (!SpvToolsDisassembler && (Options & EOptionHumanReadableSpv))
                             spv::Disassemble(std::cout, spirv);
-                        }
                     }
                 }
             }
@@ -1405,6 +1414,8 @@
            "  --shift-UBO-binding [stage] [num set]... per-descriptor-set shift values\n"
            "  --shift-cbuffer-binding [stage] num  synonym for --shift-UBO-binding\n"
            "  --shift-cbuffer-binding [stage] [num set]... per-descriptor-set shift values\n"
+           "  --spirv-dis                          output standard form disassembly; works only\n"
+           "                                       when a SPIR-V generation option is also used\n"
            "  --sub [stage] num                    synonym for --shift-UBO-binding\n"
            "  --source-entrypoint <name>           the given shader source function is\n"
            "                                       renamed to be the <name> given in -e\n"
diff --git a/Test/baseResults/cppBad2.vert.out b/Test/baseResults/cppBad2.vert.out
index 0398e5e..af9ff38 100755
--- a/Test/baseResults/cppBad2.vert.out
+++ b/Test/baseResults/cppBad2.vert.out
@@ -1,7 +1,6 @@
 cppBad2.vert
 ERROR: 0:3: 'macro expansion' : End of input in macro b
-ERROR: 0:3: '' : compilation terminated 
-ERROR: 2 compilation errors.  No code generated.
+ERROR: 1 compilation errors.  No code generated.
 
 
 Shader version: 100
diff --git a/Test/baseResults/hlsl.pp.expand.frag.err b/Test/baseResults/hlsl.pp.expand.frag.err
new file mode 100644
index 0000000..1b5681f
--- /dev/null
+++ b/Test/baseResults/hlsl.pp.expand.frag.err
@@ -0,0 +1,3 @@
+ERROR: HLSL currently only supported when requesting SPIR-V for Vulkan.
+ERROR: HLSL currently only supported when requesting SPIR-V for Vulkan.
+
diff --git a/Test/baseResults/hlsl.pp.expand.frag.out b/Test/baseResults/hlsl.pp.expand.frag.out
new file mode 100644
index 0000000..7197891
--- /dev/null
+++ b/Test/baseResults/hlsl.pp.expand.frag.out
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+struct A
+{
+    float4 a;
+    float4 b;
+    float4 c = { 1, 2, 3, 4 };
+    float4 d = {({ {(({ 1, 2, 3, 4 }))} })}, { { 1, 2, 3, 4 } };
+};
+
+void main()
+{
+}
+
diff --git a/Test/baseResults/preprocessor.bad_arg.vert.err b/Test/baseResults/preprocessor.bad_arg.vert.err
new file mode 100644
index 0000000..ae970a0
--- /dev/null
+++ b/Test/baseResults/preprocessor.bad_arg.vert.err
@@ -0,0 +1,4 @@
+ERROR: 0:8: 'macro expansion' : End of input in macro EXP2
+ERROR: 1 compilation errors.  No code generated.
+
+
diff --git a/Test/baseResults/preprocessor.bad_arg.vert.out b/Test/baseResults/preprocessor.bad_arg.vert.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Test/baseResults/preprocessor.bad_arg.vert.out
diff --git a/Test/hlsl.pp.expand.frag b/Test/hlsl.pp.expand.frag
new file mode 100755
index 0000000..765d17e
--- /dev/null
+++ b/Test/hlsl.pp.expand.frag
@@ -0,0 +1,17 @@
+#define EMP1(a)

+#define EMP2(a, b)

+

+#define EXP1(a) = a

+#define EXP2(a, b) = a, b

+

+struct A

+{

+    float4 a EMP1({1,2,3,4});                           // No PP arg errors

+    float4 b EMP2({({{(({1,2,3,4}))}})}, {{1,2,3,4}});  // No PP arg errors

+    float4 c EXP1({1,2,3,4});                           // ERROR: No PP arg errors, but init error

+    float4 d EXP2({({{(({1,2,3,4}))}})}, {{1,2,3,4}});  // ERROR: No PP arg errors, but init error

+};

+

+void main()

+{

+}

diff --git a/Test/preprocessor.bad_arg.vert b/Test/preprocessor.bad_arg.vert
new file mode 100755
index 0000000..344fc4b
--- /dev/null
+++ b/Test/preprocessor.bad_arg.vert
@@ -0,0 +1,8 @@
+#define M(a) a

+int M(aou

+    = 2)  // Okay, one argument, split across newline

+    ;

+

+// end of file during an argument

+#define EXP2(a, b)

+EXP2(((((1,2,3,4))), );

diff --git a/Test/runtests b/Test/runtests
index a084cb5..390acfc 100755
--- a/Test/runtests
+++ b/Test/runtests
@@ -216,6 +216,13 @@
 diff -b $BASEDIR/hlsl.noSemantic.functionality1.comp.out $TARGETDIR/hlsl.noSemantic.functionality1.comp.out || HASERROR=1
 
 #
+# Testing HLSL-specific PP feature expansion
+#
+$EXE -D -E hlsl.pp.expand.frag > $TARGETDIR/hlsl.pp.expand.frag.out 2> $TARGETDIR/hlsl.pp.expand.frag.err
+diff -b $BASEDIR/hlsl.pp.expand.frag.out $TARGETDIR/hlsl.pp.expand.frag.out || HASERROR=1
+diff -b $BASEDIR/hlsl.pp.expand.frag.err $TARGETDIR/hlsl.pp.expand.frag.err || HASERROR=1
+
+#
 # Final checking
 #
 if [ $HASERROR -eq 0 ]
diff --git a/glslang/Include/revision.h b/glslang/Include/revision.h
index f163142..91ea3e6 100644
--- a/glslang/Include/revision.h
+++ b/glslang/Include/revision.h
@@ -1,3 +1,3 @@
 // This header is generated by the make-revision script.
 
-#define GLSLANG_PATCH_LEVEL 2776
+#define GLSLANG_PATCH_LEVEL 2787
diff --git a/glslang/MachineIndependent/preprocessor/Pp.cpp b/glslang/MachineIndependent/preprocessor/Pp.cpp
old mode 100644
new mode 100755
index 8048fa5..1235355
--- a/glslang/MachineIndependent/preprocessor/Pp.cpp
+++ b/glslang/MachineIndependent/preprocessor/Pp.cpp
@@ -515,15 +515,16 @@
 int TPpContext::evalToToken(int token, bool shortCircuit, int& res, bool& err, TPpToken* ppToken)
 {
     while (token == PpAtomIdentifier && strcmp("defined", ppToken->name) != 0) {
-        int macroReturn = MacroExpand(ppToken, true, false);
-        if (macroReturn == 0) {
+        switch (MacroExpand(ppToken, true, false)) {
+        case MacroExpandNotStarted:
+        case MacroExpandError:
             parseContext.ppError(ppToken->loc, "can't evaluate expression", "preprocessor evaluation", "");
             err = true;
             res = 0;
-            token = scanToken(ppToken);
             break;
-        }
-        if (macroReturn == -1) {
+        case MacroExpandStarted:
+            break;
+        case MacroExpandUndef:
             if (! shortCircuit && parseContext.profile == EEsProfile) {
                 const char* message = "undefined macro in expression not allowed in es profile";
                 if (parseContext.relaxedErrors())
@@ -531,8 +532,11 @@
                 else
                     parseContext.ppError(ppToken->loc, message, "preprocessor evaluation", ppToken->name);
             }
+            break;
         }
         token = scanToken(ppToken);
+        if (err)
+            break;
     }
 
     return token;
@@ -1011,15 +1015,25 @@
     int token;
     while ((token = scanToken(ppToken)) != tMarkerInput::marker && token != EndOfInput) {
         token = tokenPaste(token, *ppToken);
+        if (token == PpAtomIdentifier) {
+            switch (MacroExpand(ppToken, false, newLineOkay)) {
+            case MacroExpandNotStarted:
+                break;
+            case MacroExpandError:
+                token = EndOfInput;
+                break;
+            case MacroExpandStarted:
+            case MacroExpandUndef:
+                continue;
+            }
+        }
         if (token == tMarkerInput::marker || token == EndOfInput)
             break;
-        if (token == PpAtomIdentifier && MacroExpand(ppToken, false, newLineOkay) != 0)
-            continue;
         expandedArg->putToken(token, ppToken);
     }
 
     if (token == EndOfInput) {
-        // MacroExpand ate the marker, so had bad input, recover
+        // Error, or MacroExpand ate the marker, so had bad input, recover
         delete expandedArg;
         expandedArg = nullptr;
     } else {
@@ -1115,14 +1129,18 @@
 }
 
 //
-// Check a token to see if it is a macro that should be expanded.
-// If it is, and defined, push a tInput that will produce the appropriate expansion
-// and return 1.
-// If it is, but undefined, and expandUndef is requested, push a tInput that will
-// expand to 0 and return -1.
-// Otherwise, return 0 to indicate no expansion, which is not necessarily an error.
+// Check a token to see if it is a macro that should be expanded:
+// - If it is, and defined, push a tInput that will produce the appropriate
+//   expansion and return MacroExpandStarted.
+// - If it is, but undefined, and expandUndef is requested, push a tInput
+//   that will expand to 0 and return MacroExpandUndef.
+// - Otherwise, there is no expansion, and there are two cases:
+//   * It might be okay there is no expansion, and no specific error was
+//     detected. Returns MacroExpandNotStarted.
+//   * The expansion was started, but could not be completed, due to an error
+//     that cannot be recovered from. Returns MacroExpandError.
 //
-int TPpContext::MacroExpand(TPpToken* ppToken, bool expandUndef, bool newLineOkay)
+MacroExpandResult TPpContext::MacroExpand(TPpToken* ppToken, bool expandUndef, bool newLineOkay)
 {
     ppToken->space = false;
     int macroAtom = atomStrings.getAtom(ppToken->name);
@@ -1131,7 +1149,7 @@
         ppToken->ival = parseContext.getCurrentLoc().line;
         snprintf(ppToken->name, sizeof(ppToken->name), "%d", ppToken->ival);
         UngetToken(PpAtomConstInt, ppToken);
-        return 1;
+        return MacroExpandStarted;
 
     case PpAtomFileMacro: {
         if (parseContext.getCurrentLoc().name)
@@ -1139,34 +1157,33 @@
         ppToken->ival = parseContext.getCurrentLoc().string;
         snprintf(ppToken->name, sizeof(ppToken->name), "%s", ppToken->loc.getStringNameOrNum().c_str());
         UngetToken(PpAtomConstInt, ppToken);
-        return 1;
+        return MacroExpandStarted;
     }
 
     case PpAtomVersionMacro:
         ppToken->ival = parseContext.version;
         snprintf(ppToken->name, sizeof(ppToken->name), "%d", ppToken->ival);
         UngetToken(PpAtomConstInt, ppToken);
-        return 1;
+        return MacroExpandStarted;
 
     default:
         break;
     }
 
     MacroSymbol* macro = macroAtom == 0 ? nullptr : lookupMacroDef(macroAtom);
-    int depth = 0;
 
     // no recursive expansions
     if (macro != nullptr && macro->busy)
-        return 0;
+        return MacroExpandNotStarted;
 
     // not expanding undefined macros
     if ((macro == nullptr || macro->undef) && ! expandUndef)
-        return 0;
+        return MacroExpandNotStarted;
 
     // 0 is the value of an undefined macro
     if ((macro == nullptr || macro->undef) && expandUndef) {
         pushInput(new tZeroInput(this));
-        return -1;
+        return MacroExpandUndef;
     }
 
     tMacroInput *in = new tMacroInput(this);
@@ -1182,7 +1199,7 @@
         if (token != '(') {
             UngetToken(token, ppToken);
             delete in;
-            return 0;
+            return MacroExpandNotStarted;
         }
         in->args.resize(in->mac->args.size());
         for (size_t i = 0; i < in->mac->args.size(); i++)
@@ -1193,39 +1210,44 @@
         size_t arg = 0;
         bool tokenRecorded = false;
         do {
-            depth = 0;
-            while (1) {
+            TVector<char> nestStack;
+            while (true) {
                 token = scanToken(ppToken);
                 if (token == EndOfInput || token == tMarkerInput::marker) {
                     parseContext.ppError(loc, "End of input in macro", "macro expansion", atomStrings.getString(macroAtom));
                     delete in;
-                    return 0;
+                    return MacroExpandError;
                 }
                 if (token == '\n') {
                     if (! newLineOkay) {
                         parseContext.ppError(loc, "End of line in macro substitution:", "macro expansion", atomStrings.getString(macroAtom));
                         delete in;
-                        return 0;
+                        return MacroExpandError;
                     }
                     continue;
                 }
                 if (token == '#') {
                     parseContext.ppError(ppToken->loc, "unexpected '#'", "macro expansion", atomStrings.getString(macroAtom));
                     delete in;
-                    return 0;
+                    return MacroExpandError;
                 }
                 if (in->mac->args.size() == 0 && token != ')')
                     break;
-                if (depth == 0 && (token == ',' || token == ')'))
+                if (nestStack.size() == 0 && (token == ',' || token == ')'))
                     break;
                 if (token == '(')
-                    depth++;
-                if (token == ')')
-                    depth--;
+                    nestStack.push_back(')');
+                else if (token == '{' && parseContext.isReadingHLSL())
+                    nestStack.push_back('}');
+                else if (nestStack.size() > 0 && token == nestStack.back())
+                    nestStack.pop_back();
                 in->args[arg]->putToken(token, ppToken);
                 tokenRecorded = true;
             }
+            // end of single argument scan
+
             if (token == ')') {
+                // closing paren of call
                 if (in->mac->args.size() == 1 && tokenRecorded == 0)
                     break;
                 arg++;
@@ -1233,23 +1255,25 @@
             }
             arg++;
         } while (arg < in->mac->args.size());
+        // end of all arguments scan
 
         if (arg < in->mac->args.size())
             parseContext.ppError(loc, "Too few args in Macro", "macro expansion", atomStrings.getString(macroAtom));
         else if (token != ')') {
-            depth=0;
+            // Error recover code; find end of call, if possible
+            int depth = 0;
             while (token != EndOfInput && (depth > 0 || token != ')')) {
-                if (token == ')')
+                if (token == ')' || token == '}')
                     depth--;
                 token = scanToken(ppToken);
-                if (token == '(')
+                if (token == '(' || token == '{')
                     depth++;
             }
 
             if (token == EndOfInput) {
                 parseContext.ppError(loc, "End of input in macro", "macro expansion", atomStrings.getString(macroAtom));
                 delete in;
-                return 0;
+                return MacroExpandError;
             }
             parseContext.ppError(loc, "Too many args in macro", "macro expansion", atomStrings.getString(macroAtom));
         }
@@ -1264,7 +1288,7 @@
     macro->busy = 1;
     macro->body.reset();
 
-    return 1;
+    return MacroExpandStarted;
 }
 
 } // end namespace glslang
diff --git a/glslang/MachineIndependent/preprocessor/PpContext.h b/glslang/MachineIndependent/preprocessor/PpContext.h
index b3a39c5..5c26081 100755
--- a/glslang/MachineIndependent/preprocessor/PpContext.h
+++ b/glslang/MachineIndependent/preprocessor/PpContext.h
@@ -183,6 +183,13 @@
 
 class TInputScanner;
 
+enum MacroExpandResult {
+    MacroExpandNotStarted, // macro not expanded, which might not be an error
+    MacroExpandError,      // a clear error occurred while expanding, no expansion
+    MacroExpandStarted,    // macro expansion process has started
+    MacroExpandUndef       // macro is undefined and will be expanded
+};
+
 // This class is the result of turning a huge pile of C code communicating through globals
 // into a class.  This was done to allowing instancing to attain thread safety.
 // Don't expect too much in terms of OO design.
@@ -400,7 +407,7 @@
     int readCPPline(TPpToken * ppToken);
     int scanHeaderName(TPpToken* ppToken, char delimit);
     TokenStream* PrescanMacroArg(TokenStream&, TPpToken*, bool newLineOkay);
-    int MacroExpand(TPpToken* ppToken, bool expandUndef, bool newLineOkay);
+    MacroExpandResult MacroExpand(TPpToken* ppToken, bool expandUndef, bool newLineOkay);
 
     //
     // From PpTokens.cpp
diff --git a/glslang/MachineIndependent/preprocessor/PpScanner.cpp b/glslang/MachineIndependent/preprocessor/PpScanner.cpp
index 0c620a5..02b93f9 100755
--- a/glslang/MachineIndependent/preprocessor/PpScanner.cpp
+++ b/glslang/MachineIndependent/preprocessor/PpScanner.cpp
@@ -1061,8 +1061,17 @@
             continue;
 
         // expand macros
-        if (token == PpAtomIdentifier && MacroExpand(&ppToken, false, true) != 0)
-            continue;
+        if (token == PpAtomIdentifier) {
+            switch (MacroExpand(&ppToken, false, true)) {
+            case MacroExpandNotStarted:
+                break;
+            case MacroExpandError:
+                return EndOfInput;
+            case MacroExpandStarted:
+            case MacroExpandUndef:
+                continue;
+            }
+        }
 
         switch (token) {
         case PpAtomIdentifier:
diff --git a/gtests/Pp.FromFile.cpp b/gtests/Pp.FromFile.cpp
old mode 100644
new mode 100755
index 13daac0..1bea877
--- a/gtests/Pp.FromFile.cpp
+++ b/gtests/Pp.FromFile.cpp
@@ -50,6 +50,7 @@
 INSTANTIATE_TEST_CASE_P(
     Glsl, PreprocessingTest,
     ::testing::ValuesIn(std::vector<std::string>({
+        "preprocessor.bad_arg.vert",
         "preprocessor.cpp_style_line_directive.vert",
         "preprocessor.cpp_style___FILE__.vert",
         "preprocessor.edge_cases.vert",