Infrastructure: Non-functional: Move to rich description of environment.

This is for input languages, client APIs, code to generate, etc.
diff --git a/StandAlone/StandAlone.cpp b/StandAlone/StandAlone.cpp
index 6303735..2f5b953 100644
--- a/StandAlone/StandAlone.cpp
+++ b/StandAlone/StandAlone.cpp
@@ -715,9 +715,27 @@
         if (Options & EOptionAutoMapLocations)
             shader->setAutoMapLocations(true);
 
+        // Set up the environment, some subsettings take precedence over earlier
+        // ways of setting things.
+        if (Options & EOptionSpv) {
+            if (Options & EOptionVulkanRules) {
+                shader->setEnvInput((Options & EOptionReadHlsl) ? glslang::EShSourceHlsl
+                                                                : glslang::EShSourceGlsl,
+                                        compUnit.stage, glslang::EShClientVulkan, 100);
+                shader->setEnvClient(glslang::EShClientVulkan, 100);
+                shader->setEnvTarget(glslang::EshTargetSpv, 0x00001000);
+            } else {
+                shader->setEnvInput((Options & EOptionReadHlsl) ? glslang::EShSourceHlsl
+                                                                : glslang::EShSourceGlsl,
+                                        compUnit.stage, glslang::EShClientOpenGL, 100);
+                shader->setEnvClient(glslang::EShClientOpenGL, 450);
+                shader->setEnvTarget(glslang::EshTargetSpv, 0x00001000);
+            }
+        }
+
         shaders.push_back(shader);
 
-        const int defaultVersion = Options & EOptionDefaultDesktop? 110: 100;
+        const int defaultVersion = Options & EOptionDefaultDesktop ? 110 : 100;
 
         DirStackFileIncluder includer;
         std::for_each(IncludeDirectoryList.rbegin(), IncludeDirectoryList.rend(), [&includer](const std::string& dir) {
diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp
index f200234..517ac1d 100644
--- a/glslang/MachineIndependent/ParseHelper.cpp
+++ b/glslang/MachineIndependent/ParseHelper.cpp
@@ -50,7 +50,8 @@
 TParseContext::TParseContext(TSymbolTable& symbolTable, TIntermediate& interm, bool parsingBuiltins,
                              int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language,
                              TInfoSink& infoSink, bool forwardCompatible, EShMessages messages) :
-            TParseContextBase(symbolTable, interm, parsingBuiltins, version, profile, spvVersion, language, infoSink, forwardCompatible, messages),
+            TParseContextBase(symbolTable, interm, parsingBuiltins, version, profile, spvVersion, language,
+                              infoSink, forwardCompatible, messages),
             inMain(false),
             blockName(nullptr),
             limits(resources.limits),
diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp
index d75119b..26f81bd 100644
--- a/glslang/MachineIndependent/ShaderLang.cpp
+++ b/glslang/MachineIndependent/ShaderLang.cpp
@@ -569,6 +569,73 @@
     return correct;
 }
 
+// There are multiple paths in for setting environment stuff.
+// TEnvironment takes precedence, for what it sets, so sort all this out.
+// Ideally, the internal code could be made to use TEnvironment, but for
+// now, translate it to the historically used parameters.
+void TranslateEnvironment(const TEnvironment* environment, EShMessages& messages, EShSource& source,
+                          EShLanguage& stage, SpvVersion& spvVersion)
+{
+    // Set up environmental defaults, first ignoring 'environment'.
+    if (messages & EShMsgSpvRules)
+        spvVersion.spv = 0x00010000;
+    if (messages & EShMsgVulkanRules) {
+        spvVersion.vulkan = 100;
+        spvVersion.vulkanGlsl = 100;
+    } else if (spvVersion.spv != 0)
+        spvVersion.openGl = 100;
+
+    // Now, override, based on any content set in 'environment'.
+    // 'environment' must be cleared to ESh*None settings when items
+    // are not being set.
+    if (environment != nullptr) {
+        // input language
+        if (environment->input.languageFamily != EShSourceNone) {
+            stage = environment->input.stage;
+            switch (environment->input.dialect) {
+            case EShClientNone:
+                break;
+            case EShClientVulkan:
+                spvVersion.vulkanGlsl = environment->input.dialectVersion;
+                break;
+            case EShClientOpenGL:
+                spvVersion.openGl = environment->input.dialectVersion;
+                break;
+            }
+            switch (environment->input.languageFamily) {
+            case EShSourceNone:
+                break;
+            case EShSourceGlsl:
+                source = EShSourceGlsl;
+                messages = static_cast<EShMessages>(messages & ~EShMsgReadHlsl);
+                break;
+            case EShSourceHlsl:
+                source = EShSourceHlsl;
+                messages = static_cast<EShMessages>(messages | EShMsgReadHlsl);
+                break;
+            }
+        }
+
+        // client
+        switch (environment->client.client) {
+        case EShClientVulkan:
+            spvVersion.vulkan = environment->client.version;
+            break;
+        default:
+            break;
+        }
+
+        // generated code
+        switch (environment->target.language) {
+        case EshTargetSpv:
+            spvVersion.spv = environment->target.version;
+            break;
+        default:
+            break;
+        }
+    }
+}
+
 // This is the common setup and cleanup code for PreprocessDeferred and
 // CompileDeferred.
 // It takes any callable with a signature of
@@ -599,8 +666,8 @@
     ProcessingContext& processingContext,
     bool requireNonempty,
     TShader::Includer& includer,
-    const std::string sourceEntryPointName = ""
-    )
+    const std::string sourceEntryPointName = "",
+    const TEnvironment* environment = nullptr)  // optional way of fully setting all versions, overriding the above
 {
     if (! InitThread())
         return false;
@@ -641,6 +708,13 @@
             names[s + numPre] = nullptr;
     }
 
+    // Get all the stages, languages, clients, and other environment
+    // stuff sorted out.
+    EShSource source = (messages & EShMsgReadHlsl) != 0 ? EShSourceHlsl : EShSourceGlsl;
+    SpvVersion spvVersion;
+    EShLanguage stage = compiler->getLanguage();
+    TranslateEnvironment(environment, messages, source, stage, spvVersion);
+
     // First, without using the preprocessor or parser, find the #version, so we know what
     // symbol tables, processing rules, etc. to set up.  This does not need the extra strings
     // outlined above, just the user shader, after the system and user preambles.
@@ -648,11 +722,11 @@
     int version = 0;
     EProfile profile = ENoProfile;
     bool versionNotFirstToken = false;
-    bool versionNotFirst = (messages & EShMsgReadHlsl) ?
-                                true :
-                                userInput.scanVersion(version, profile, versionNotFirstToken);
+    bool versionNotFirst = (source == EShSourceHlsl)
+                                ? true
+                                : userInput.scanVersion(version, profile, versionNotFirstToken);
     bool versionNotFound = version == 0;
-    if (forceDefaultVersionAndProfile && (messages & EShMsgReadHlsl) == 0) {
+    if (forceDefaultVersionAndProfile && source == EShSourceGlsl) {
         if (! (messages & EShMsgSuppressWarnings) && ! versionNotFound &&
             (version != defaultVersion || profile != defaultProfile)) {
             compiler->infoSink.info << "Warning, (version, profile) forced to be ("
@@ -669,15 +743,8 @@
         version = defaultVersion;
         profile = defaultProfile;
     }
-    SpvVersion spvVersion;
-    if (messages & EShMsgSpvRules)
-        spvVersion.spv = 0x00010000;    // TODO: eventually have this come from the outside
-    EShSource source = (messages & EShMsgReadHlsl) ? EShSourceHlsl : EShSourceGlsl;
-    if (messages & EShMsgVulkanRules)
-        spvVersion.vulkan = 100;     // TODO: eventually have this come from the outside
-    else if (spvVersion.spv != 0)
-        spvVersion.openGl = 100;     // TODO: eventually have this come from the outside
-    bool goodVersion = DeduceVersionProfile(compiler->infoSink, compiler->getLanguage(),
+
+    bool goodVersion = DeduceVersionProfile(compiler->infoSink, stage,
                                             versionNotFirst, defaultVersion, source, version, profile, spvVersion);
     bool versionWillBeError = (versionNotFound || (profile == EEsProfile && version >= 300 && versionNotFirst));
     bool warnVersionNotFirst = false;
@@ -694,7 +761,7 @@
     intermediate.setSpv(spvVersion);
     if (spvVersion.vulkan >= 100)
         intermediate.setOriginUpperLeft();
-    if ((messages & EShMsgHlslOffsets) || (messages & EShMsgReadHlsl))
+    if ((messages & EShMsgHlslOffsets) || source == EShSourceHlsl)
         intermediate.setHlslOffsets();
     if (messages & EShMsgDebugInfo) {
         intermediate.setSourceFile(names[numPre]);
@@ -707,7 +774,7 @@
                                                   [MapSpvVersionToIndex(spvVersion)]
                                                   [MapProfileToIndex(profile)]
                                                   [MapSourceToIndex(source)]
-                                                  [compiler->getLanguage()];
+                                                  [stage];
 
     // Dynamically allocate the symbol table so we can control when it is deallocated WRT the pool.
     TSymbolTable* symbolTableMemory = new TSymbolTable;
@@ -718,7 +785,7 @@
     // Add built-in symbols that are potentially context dependent;
     // they get popped again further down.
     if (! AddContextSpecificSymbols(resources, compiler->infoSink, symbolTable, version, profile, spvVersion,
-                                    compiler->getLanguage(), source))
+                                    stage, source))
         return false;
 
     //
@@ -726,14 +793,14 @@
     //
 
     TParseContextBase* parseContext = CreateParseContext(symbolTable, intermediate, version, profile, source,
-                                                         compiler->getLanguage(), compiler->infoSink,
+                                                         stage, compiler->infoSink,
                                                          spvVersion, forwardCompatible, messages, false, sourceEntryPointName);
 
     TPpContext ppContext(*parseContext, names[numPre] ? names[numPre] : "", includer);
 
     // only GLSL (bison triggered, really) needs an externally set scan context
     glslang::TScanContext scanContext(*parseContext);
-    if ((messages & EShMsgReadHlsl) == 0)
+    if (source == EShSourceGlsl)
         parseContext->setScanContext(&scanContext);
 
     parseContext->setPpContext(&ppContext);
@@ -1052,14 +1119,15 @@
     EShMessages messages,       // warnings/errors/AST; things to print out
     TIntermediate& intermediate,// returned tree, etc.
     TShader::Includer& includer,
-    const std::string sourceEntryPointName = "")
+    const std::string sourceEntryPointName = "",
+    TEnvironment* environment = nullptr)
 {
     DoFullParse parser;
     return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames,
                            preamble, optLevel, resources, defaultVersion,
                            defaultProfile, forceDefaultVersionAndProfile,
                            forwardCompatible, messages, intermediate, parser,
-                           true, includer, sourceEntryPointName);
+                           true, includer, sourceEntryPointName, environment);
 }
 
 } // end anonymous namespace for local functions
@@ -1485,6 +1553,12 @@
     infoSink = new TInfoSink;
     compiler = new TDeferredCompiler(stage, *infoSink);
     intermediate = new TIntermediate(s);
+
+    // clear environment (avoid constructors in them for use in a C interface)
+    environment.input.languageFamily = EShSourceNone;
+    environment.input.dialect = EShClientNone;
+    environment.client.client = EShClientNone;
+    environment.target.language = EShTargetNone;
 }
 
 TShader::~TShader()
@@ -1572,7 +1646,8 @@
     return CompileDeferred(compiler, strings, numStrings, lengths, stringNames,
                            preamble, EShOptNone, builtInResources, defaultVersion,
                            defaultProfile, forceDefaultVersionAndProfile,
-                           forwardCompatible, messages, *intermediate, includer, sourceEntryPointName);
+                           forwardCompatible, messages, *intermediate, includer, sourceEntryPointName,
+                           &environment);
 }
 
 // Fill in a string with the result of preprocessing ShaderStrings
diff --git a/glslang/MachineIndependent/Versions.cpp b/glslang/MachineIndependent/Versions.cpp
index 800956c..a800ea6 100644
--- a/glslang/MachineIndependent/Versions.cpp
+++ b/glslang/MachineIndependent/Versions.cpp
@@ -355,9 +355,9 @@
     // #define VULKAN XXXX
     const int numberBufSize = 12;
     char numberBuf[numberBufSize];
-    if (spvVersion.vulkan > 0) {
+    if (spvVersion.vulkanGlsl > 0) {
         preamble += "#define VULKAN ";
-        snprintf(numberBuf, numberBufSize, "%d", spvVersion.vulkan);
+        snprintf(numberBuf, numberBufSize, "%d", spvVersion.vulkanGlsl);
         preamble += numberBuf;
         preamble += "\n";
     }
diff --git a/glslang/MachineIndependent/Versions.h b/glslang/MachineIndependent/Versions.h
index 702a5ae..63bb517 100644
--- a/glslang/MachineIndependent/Versions.h
+++ b/glslang/MachineIndependent/Versions.h
@@ -72,14 +72,19 @@
 }
 
 //
-// SPIR-V has versions for multiple things; tie them together.
-// 0 means a target or rule set is not enabled.
+// What source rules, validation rules, target language, etc. are needed
+// desired for SPIR-V?
+//
+// 0 means a target or rule set is not enabled (ignore rules from that entity).
+// Non-0 means to apply semantic rules arising from that version of its rule set.
+// The union of all requested rule sets will be applied.
 //
 struct SpvVersion {
-    SpvVersion() : spv(0), vulkan(0), openGl(0) {}
-    unsigned int spv; // the version of the targeted SPIR-V, as defined by SPIR-V in word 1 of the SPIR-V binary header
-    int vulkan;       // the version of semantics for Vulkan; e.g., for GLSL from KHR_vulkan_glsl "#define VULKAN"
-    int openGl;       // the version of semantics for OpenGL; e.g., for GLSL from KHR_vulkan_glsl "#define GL_SPIRV"
+    SpvVersion() : spv(0), vulkanGlsl(0), vulkan(0), openGl(0) {}
+    unsigned int spv; // the version of SPIR-V to target, as defined by "word 1" of the SPIR-V binary header
+    int vulkanGlsl;   // the version of GLSL semantics for Vulkan, from GL_KHR_vulkan_glsl, for "#define VULKAN XXX"
+    int vulkan;       // the version of Vulkan, for which SPIR-V execution environment rules to use (100 means 1.0)
+    int openGl;       // the version of GLSL semantics for OpenGL, from GL_ARB_gl_spirv, for "#define GL_SPIRV XXX"
 };
 
 //
diff --git a/glslang/Public/ShaderLang.h b/glslang/Public/ShaderLang.h
index b2a4dea..10c1b1c 100644
--- a/glslang/Public/ShaderLang.h
+++ b/glslang/Public/ShaderLang.h
@@ -110,7 +110,44 @@
     EShSourceNone,
     EShSourceGlsl,
     EShSourceHlsl,
-} EShSource;          // if EShLanguage were EShStage, this could be EShLanguage instead
+} EShSource;                  // if EShLanguage were EShStage, this could be EShLanguage instead
+
+typedef enum {
+    EShClientNone,
+    EShClientVulkan,
+    EShClientOpenGL,
+} EShClient;
+
+typedef enum {
+    EShTargetNone,
+    EshTargetSpv,
+} EShTargetLanguage;
+
+struct TInputLanguage {
+    EShSource languageFamily; // redundant information with other input, this one overrides when not EShSourceNone
+    EShLanguage stage;        // redundant information with other input, this one overrides when not EShSourceNone
+    EShClient dialect;
+    int dialectVersion;       // version of client's language definition, not the client (when not EShClientNone)
+};
+
+struct TClient {
+    EShClient client;
+    int version;              // version of client itself (not the client's input dialect)
+};
+
+struct TTarget {
+    EShTargetLanguage language;
+    unsigned int version;     // the version to target, if SPIR-V, defined by "word 1" of the SPIR-V binary header
+};
+
+// All source/client/target versions and settings.
+// Can override previous methods of setting, when items are set here.
+// Expected to grow, as more are added, rather than growing parameter lists.
+struct TEnvironment {
+    TInputLanguage input;     // definition of the input language
+    TClient client;           // what client is the overall compilation being done for?
+    TTarget target;           // what to generate
+};
 
 const char* StageName(EShLanguage);
 
@@ -324,6 +361,25 @@
     void setNoStorageFormat(bool useUnknownFormat);
     void setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode);
 
+    // For setting up the environment (initialized in the constructor):
+    void setEnvInput(EShSource lang, EShLanguage stage, EShClient client, int version)
+    {
+        environment.input.languageFamily = lang;
+        environment.input.stage = stage;
+        environment.input.dialect = client;
+        environment.input.dialectVersion = version;
+    }
+    void setEnvClient(EShClient client, int version)
+    {
+        environment.client.client = client;
+        environment.client.version = version;
+    }
+    void setEnvTarget(EShTargetLanguage lang, unsigned int version)
+    {
+        environment.target.language = lang;
+        environment.target.version = version;
+    }
+
     // Interface to #include handlers.
     //
     // To support #include, a client of Glslang does the following:
@@ -463,6 +519,8 @@
     // a function in the source string can be renamed FROM this TO the name given in setEntryPoint.
     std::string sourceEntryPointName;
 
+    TEnvironment environment;
+
     friend class TProgram;
 
 private: