Kaynağa Gözat

Initial commit and copy of 064c645

Signed-off-by: AMZN-alexpete <[email protected]>
AMZN-alexpete 4 yıl önce
işleme
87b56737a6
100 değiştirilmiş dosya ile 50861 ekleme ve 0 silme
  1. 50 0
      .github/workflows/configaction_azslc.yml
  2. 19 0
      .gitignore
  3. 32 0
      EditorsLint/AZSL_notepad++_bright.xml
  4. 32 0
      EditorsLint/AZSL_notepad++_dark.xml
  5. 109 0
      GRAMMAR.md
  6. 342 0
      HOWTO.md
  7. 27 0
      LICENSE.txt
  8. 201 0
      LICENSE_APACHE2.TXT
  9. 7 0
      LICENSE_MIT.TXT
  10. 3 0
      Platform/AppleTV/README.md
  11. 3 0
      Platform/Linux/README.md
  12. 3 0
      Platform/Mac/README.md
  13. 4 0
      Platform/Windows/README.md
  14. 129 0
      Platform/Windows/src/DirectX12PlatformEmitter.cpp
  15. 28 0
      Platform/Windows/src/DirectX12PlatformEmitter.h
  16. 82 0
      Platform/Windows/src/VulkanPlatformEmitter.cpp
  17. 31 0
      Platform/Windows/src/VulkanPlatformEmitter.h
  18. 3 0
      Platform/iOS/README.md
  19. 50 0
      Platform/iOS/src/MetalPlatformEmitter.cpp
  20. 30 0
      Platform/iOS/src/MetalPlatformEmitter.h
  21. 110 0
      README.md
  22. 58 0
      build_win.bat
  23. 18 0
      clean_build.bat
  24. 70 0
      convert-hlsl_lines-to-pattern_lines.py
  25. 23 0
      launch_grun.bat
  26. 65 0
      prepare_solution_darwin.sh
  27. 59 0
      prepare_solution_linux.sh
  28. 66 0
      prepare_solution_win.bat
  29. 175 0
      pull_from_git.py
  30. 765 0
      src/AzslcBackend.cpp
  31. 204 0
      src/AzslcBackend.h
  32. 43 0
      src/AzslcCodeEmissionMutator.h
  33. 87 0
      src/AzslcCommon.h
  34. 1293 0
      src/AzslcEmitter.cpp
  35. 203 0
      src/AzslcEmitter.h
  36. 357 0
      src/AzslcException.h
  37. 164 0
      src/AzslcHomonymVisitor.h
  38. 638 0
      src/AzslcIntermediateRepresentation.cpp
  39. 253 0
      src/AzslcIntermediateRepresentation.h
  40. 1109 0
      src/AzslcKindInfo.h
  41. 461 0
      src/AzslcListener.cpp
  42. 74 0
      src/AzslcListener.h
  43. 729 0
      src/AzslcMain.cpp
  44. 627 0
      src/AzslcMangling.h
  45. 100 0
      src/AzslcPlatformEmitter.cpp
  46. 78 0
      src/AzslcPlatformEmitter.h
  47. 269 0
      src/AzslcPredefinedTypes.h
  48. 1010 0
      src/AzslcReflection.cpp
  49. 75 0
      src/AzslcReflection.h
  50. 80 0
      src/AzslcScopeTracker.cpp
  51. 46 0
      src/AzslcScopeTracker.h
  52. 1969 0
      src/AzslcSemanticOrchestrator.cpp
  53. 434 0
      src/AzslcSemanticOrchestrator.h
  54. 337 0
      src/AzslcSymbolAggregator.cpp
  55. 163 0
      src/AzslcSymbolAggregator.h
  56. 79 0
      src/AzslcSymbolTable.cpp
  57. 39 0
      src/AzslcSymbolTable.h
  58. 220 0
      src/AzslcSymbolTranslation.cpp
  59. 163 0
      src/AzslcSymbolTranslation.h
  60. 38 0
      src/AzslcTokenToAst.h
  61. 456 0
      src/AzslcTypes.h
  62. 223 0
      src/AzslcUnboundedArraysValidator.cpp
  63. 98 0
      src/AzslcUnboundedArraysValidator.h
  64. 1322 0
      src/AzslcUtils.h
  65. 156 0
      src/CMakeLists.txt
  66. 99 0
      src/DependencySolver.h
  67. 253 0
      src/DependencySolver.tpl
  68. 167 0
      src/DiagnosticStream.h
  69. 612 0
      src/GenericUtils.h
  70. 426 0
      src/MetaUtils.h
  71. 44 0
      src/PreprocessorLineDirectiveFinder.h
  72. 194 0
      src/ReflectableEnums.h
  73. 42 0
      src/ReflectableEnumsUtils.h
  74. 162 0
      src/StdUtils.h
  75. 447 0
      src/Texture2DMSto2DCodeMutator.cpp
  76. 99 0
      src/Texture2DMSto2DCodeMutator.h
  77. 558 0
      src/azslLexer.g4
  78. 945 0
      src/azslParser.g4
  79. 78 0
      src/exportKeywords.py
  80. BIN
      src/external/antlr-4.7.1-complete.jar
  81. 23 0
      src/external/docopt/LICENSE-MIT
  82. 689 0
      src/external/docopt/docopt.cpp
  83. 94 0
      src/external/docopt/docopt.h
  84. 676 0
      src/external/docopt/docopt_private.h
  85. 122 0
      src/external/docopt/docopt_util.h
  86. 341 0
      src/external/docopt/docopt_value.h
  87. 255 0
      src/external/jsoncpp/dist/json/json-forwards.h
  88. 2014 0
      src/external/jsoncpp/dist/json/json.h
  89. 5124 0
      src/external/jsoncpp/dist/jsoncpp.cpp
  90. 23 0
      src/external/mpark-variant/LICENSE.md
  91. 37 0
      src/external/mpark-variant/README.md
  92. 2784 0
      src/external/mpark-variant/master/variant.hpp
  93. 1729 0
      src/external/mpark-variant/v1.0.0/variant.hpp
  94. 1798 0
      src/external/mpark-variant/v1.0.1/variant.hpp
  95. 2360 0
      src/external/mpark-variant/v1.1.0/variant.hpp
  96. 2365 0
      src/external/mpark-variant/v1.2.0/variant.hpp
  97. 2424 0
      src/external/mpark-variant/v1.2.1/variant.hpp
  98. 2416 0
      src/external/mpark-variant/v1.2.2/variant.hpp
  99. 2457 0
      src/external/mpark-variant/v1.3.0/variant.hpp
  100. 2813 0
      src/external/mpark-variant/v1.4.0/variant.hpp

+ 50 - 0
.github/workflows/configaction_azslc.yml

@@ -0,0 +1,50 @@
+name: AZSL Compiler
+
+on: pull_request
+
+jobs:
+  build:
+
+    runs-on: ubuntu-latest
+    strategy:
+      max-parallel: 1
+      matrix:
+        python-version: [3.8]
+
+    steps:
+    - uses: actions/checkout@v1
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v1
+      with:
+        python-version: ${{ matrix.python-version }}
+    - name: actions-setup-cmake
+      uses: jwlawson/[email protected]
+      env:
+        ACTIONS_ALLOW_UNSECURE_COMMANDS: true
+      with:
+        # The version of cmake to setup.
+        cmake-version: 3.15.0
+    
+    # Dependencies for linux environment
+    - name: Install dependencies (Linux) 
+      env:
+        ACTIONS_ALLOW_UNSECURE_COMMANDS: true
+      run: |
+        sudo gem install apt-spy2
+        sudo apt-spy2 check
+        sudo apt-spy2 fix --commit
+        sudo apt-get update
+        sudo apt-get install cmake
+        sudo apt-get install gcc-8
+        sudo apt-get install g++-8
+        echo "::set-env name=CC::gcc-8"
+        echo "::set-env name=CXX::g++-8"
+        sudo apt-get install uuid-dev
+        python -m pip install --upgrade pip
+        pip install pyyaml
+    # Build and execute all the tests for Debug & Release
+    - name: Build and Execute AZSLc tests
+      run: |
+        cd Overhaul
+        export PYTHONPATH=${PYTHONPATH}:`pwd`
+        python test.and.py

+ 19 - 0
.gitignore

@@ -0,0 +1,19 @@
+*.lib
+*.pyc
+*.tmp
+dev/*
+bin/*
+src/generated/*.interp
+src/generated/*.tokens
+src/generated/java/
+.vs
+build
+src/external/antlr4*
+src/external/boost*
+/src/.antlr/azslLexer.interp
+/src/.antlr/azslLexer.java
+/src/.antlr/azslLexer.tokens
+/src/.antlr/azslParser.interp
+/src/.antlr/azslParser.java
+/src/.antlr/azslParser.tokens
+/tests/testfuncs.pyc

Dosya farkı çok büyük olduğundan ihmal edildi
+ 32 - 0
EditorsLint/AZSL_notepad++_bright.xml


Dosya farkı çok büyük olduğundan ihmal edildi
+ 32 - 0
EditorsLint/AZSL_notepad++_dark.xml


+ 109 - 0
GRAMMAR.md

@@ -0,0 +1,109 @@
+# Amazon Shading Language
+
+AZSL is a thin extension of HLSL.
+
+For the most part everything that works in HLSL SM 6.0 and above should work in AZSL too, with a few additions and exceptions.
+
+## Samples
+
+The easiest way to get started is to check the test suite, which is a collection of 100+ tests that are also meant to be samples of what is allowed and what is disallowed in the grammar.
+```
+tests\Samples             // More advanced samples
+tests\Semantic            // Gramatically and semantically correct tests - you can write such code
+tests\Semantic\AsError    // Gramatically correct, but semantically incorrect - these samples don't work because of higher level language rules
+tests\Syntax              // Gramatically correct language
+tests\Syntax\AsError      // Gibberish - you shouldn't even write this, AZSLc can't make any sense of such files 
+```
+
+
+## Exceptions to HLSL
+
+### Shader Resource Group
+
+### Classes vs Structs
+
+Classes can hold methods, while Structs cannot.
+Only Structs may be defined within ShaderResourceGroups.
+Both of them have public visibility members, since there is no notion of visibility at all.
+
+## Additions to HLSL
+
+HLSL6 supports them, but HLSL5 did not. AZSL does support:
+enum
+typedef
+
+### Inheritance
+
+Class may inherit from Interfaces, to be forced to respect a contract (the implementation of functions).
+
+### Generics
+
+Not generally supported in AZSL 1.6 nor under. At the exception of intrinsic HLSL types like vector, matrix or StructuredBuffer...
+
+#### Associated Types
+
+Not supported in AZSL 1.6 nor under.
+
+### Attribute Sequences
+
+In addition to HLSL attributes, we also use [attribute specifier sequences](https://en.cppreference.com/w/cpp/language/attributes) as a non-intrusive way of providing API specific information in AZSL.
+Although not strictly related to C++ standards, currently we don't support `using` (C++17 standard) or contract-attributes (C++20 standard) specified in the linked document.
+
+Example of usage are: 
+```
+[[namespace::attribute]]
+[[namespace::attribute(arg1, arg2), attribute]]
+[[namespace::attribute(arg1, arg2), namespace::attribute(arg), namespace::attribute, attribute]]
+```
+
+Namespace is used to filter out attributes and has no correlation to the namespaces used in the shader code.
+Attribute is the name of the attribute specified. It can have no arguments or one or more arguments, which are literals - boolean, integer, float or string.
+You can have more than one attribute in a sequence, separated by a comma.
+
+
+The attributes function the way they are described in the C++ standard and affect the next declaration, expression or scope.
+Attributes with no namespace are always enabled.
+Attributes with a namespace are only enabled if the namespace is enabled when compiling the shader (`--namespace=<nspc>`).
+The `void` namespace is always disabled. (This feature is still WIP, the grammar doesn't accept keywords like `void` as namespace identifiers yet.)
+
+#### Filter namespaces
+
+Attribute sequences without a namespace are always enabled, for example `[[location(1)]]`.
+
+Attribute sequences with a namespace (for example `[[vk::location(1)]]`) have to be enabled when compiling by passing the `--namespace=vk` attribute. 
+Multiple namespaces can be specified, for example `--namespace=vk,mobile,debug`, comma separated with no whitespaces. Note that only one such namespace can be a graphics API. For example `--namespace=vk,dx` is not allowed - you compile the file either for DirectX or Vulkan, but not both at the same time!
+
+Throughout this document we use some naming conventions for namespaces - for example `[[dx::]]` for DirectX12, `[[vk::]]` for Vulkan, etc.
+However, AZSLc doesn't restrict the choice of namespace. It is left to the shader authors and the consumer application. 
+
+
+#### Global vs Attached attributes
+
+To differentiate between attributes which belong to the global scope and attributes attached to the next declaration, we use the root namespace `global::`.
+This is required since both cases appear in the same space! Think about attribute sequence before a function declaration in the global scope.
+
+Thus `[[global::attribute]]` is a global attribute with no namespace (`global` is ignored as a namespace in this case) and `[[global::dx::atttribute]]` is a global attribute in the `dx` namespace.
+
+#### Special Attributes
+
+AZSLc uses some attributes and passes others. The special attributes are listed below.
+
+- `[[global::verbatim("// Text will be re-emitted as its!")]]`
+
+Verbatim attributes will emit all their arguments as-is, on a single line with a single whitespace between them. AZSLc makes no sense of what's in the verbatim block. You can use macros and includes here, but those macros will have to target the next compiler (dxc, spriv-cross, etc.), AZSLc will make no sense of what's being included.
+
+It is possible to include files with the `#include` directive this way, refer to `tests/Advanced/simple-surface.azsl` for an example.
+
+- `[[global::output_format("R16G16B16A16_FLOAT")]]`
+
+The `output_format` specifies pixel shader entry output format hint. If no index is provided, it affects all render targets, otherwise you can specify a render target (0 to 7) before the format.
+
+Refer to `tests/Samples/PixelShaderOutputAttributes.azsl` for details.
+
+### Enumerations
+
+AZSLc supports enumerations, both unscoped (`enum`) and scoped (`enum class` and `enum struct`).
+
+The usage is the same as in C++ for the most part with the exception that explicit declaration of underlying type is not supported. Also, unlike in Microsoft C++ Compiler (cl), non-class enumerators cannot be refered to using explicit qualification.
+
+Refer to `tests/Samples/Enumeration.azsl` for details.

+ 342 - 0
HOWTO.md

@@ -0,0 +1,342 @@
+# AZSLc HowTo guide
+
+`TL;DR` Use azslc from command line (or process invoke) to translate .azsl files to .hlsl, or extract data such as symbols reference table, shader resource table layout, input assembly layout and output merger layout.
+
+Version `AZSL Compiler 1.6.5` will display the following with the `azslc --help` command:
+
+```
+Amazon Shader Language Compiler
+
+         Usage:
+          azslc (- | FILE) [--use-spaces] [--unique-idx] [--cb-body] [--root-sig] [--inline-const=<count>] [--Zpc] [--Zpr] [--namespace=<nspc>] [-o OUTFILE]
+                           [--W0|--W1|--W2|--W3] [--Wx|--Wx1|--Wx2|--Wx3]
+          azslc (- | FILE) --full [--use-spaces] [--unique-idx] [--cb-body] [--root-sig] [--inline-const=<count>] [--Zpc] [--Zpr] [--pack-dx12] [--pack-vulkan] [--pack-opengl] [--namespace=<nspc>] [-o OUTFILE]
+                           [--W0|--W1|--W2|--W3] [--Wx|--Wx1|--Wx2|--Wx3]
+          azslc (- | FILE) --ia [--use-spaces] [--unique-idx] [--cb-body] [--root-sig] [--Zpc] [--Zpr] [--pack-dx12] [--pack-vulkan] [--pack-opengl] [--namespace=<nspc>] [-o OUTFILE]
+          azslc (- | FILE) --om [--use-spaces] [--unique-idx] [--cb-body] [--root-sig] [--Zpc] [--Zpr] [--pack-dx12] [--pack-vulkan] [--pack-opengl] [--namespace=<nspc>] [-o OUTFILE]
+          azslc (- | FILE) --srg [--use-spaces] [--unique-idx] [--cb-body] [--root-sig] [--inline-const=<count>] [--Zpc] [--Zpr] [--pack-dx12] [--pack-vulkan] [--pack-opengl] [--namespace=<nspc>] [-o OUTFILE]
+          azslc (- | FILE) --options [--use-spaces] [--unique-idx] [--cb-body] [--root-sig] [--Zpc] [--Zpr] [--pack-dx12] [--pack-vulkan] [--pack-opengl] [--namespace=<nspc>] [-o OUTFILE]
+          azslc (- | FILE) --semantic [--verbose] [--W0|--W1|--W2|--W3] [--Wx|--Wx1|--Wx2|--Wx3] [--inline-const=<count>]
+          azslc (- | FILE) --syntax
+          azslc (- | FILE) --dumpsym
+          azslc (- | FILE) --ast
+          azslc (- | FILE) --bindingdep [--use-spaces] [--unique-idx] [--cb-body] [--Zpc] [--Zpr] [--pack-dx12] [--pack-vulkan] [--pack-opengl] [--namespace=<nspc>] [-o OUTFILE]
+          azslc (- | FILE) --visitsym MQNAME [-d] [-v] [-f] [-r]
+          azslc --listpredefined
+          azslc -h | --help | --version
+
+        Arguments:
+          FILE              Input file (optional if use of stdin)
+
+        Options:
+          -o OUTFILE                Output file (optional if use of stdout)
+          --use-spaces              Use logical space index per SRG.
+          --unique-idx              Use unique indices for all registries.
+          --cb-body                 Emit ConstantBuffer body rather than using <T>
+          --root-sig                Emit RootSignature for parameter binding in the shader
+          --inline-const=<count>    Number of inline constants allowed
+          --Zpc                     Pack matrices in column-major order (default). Cannot be specified together with -Zpr
+          --Zpr                     Pack matrices in row-major order. Cannot be specified together with -Zpc
+          --pack-dx12               Pack buffers using strict DX12 packing rules. If not specified it will use relaxed packing rules.
+          --pack-vulkan             Pack buffers using strict Vulkan packing rules. That's vector-relaxed std140 for uniform and std430 for storage buffers.
+          --pack-opengl             Pack buffers using strict OpenGL packing rules. That's vector-strict std140 for uniform and std430 for storage buffers.
+          --namespace=<nspc>        The list of namespaces (comma separated, no white spaces) indicates which attribute namespaces are active.
+          --ia                      Output a list of vs entries with their Input Assembler layout *and* a list of cs entries with their numthreads
+          --om                      Output the Output Merger layout, not the shader code
+          --srg                     Output the Shader Resource Group layout, not the shader code
+          --options                 Output the list of available shader options for this shader
+          --dumpsym                 Dump symbols
+          --syntax                  Check syntax    (no output means no complaints)
+          --semantic                Check semantics (no output means no complaints)
+          --ast                     Output the syntax tree
+          --bindingdep              Output binding dependencies (what entry points access what external resource)
+          --visitsym MQNAME         Output the locations of all relationships of the symbol MQNAME
+          --full                    Output the shader code, Input Assembler layout, Output Merger layout, Shader Resource Group layout,
+                                    the list of available shader options, and the binding dependencies
+          -d                        (Option of --visitsym) Visit direct references
+          -v                        (Option of --visitsym) Visit overload-set
+          -f                        (Option of --visitsym) Visit family
+          -r                        (Option of --visitsym) Visit recursively
+          --listpredefined          Output a list of all predefined types in AZSLang
+
+        Diagnostic Options:
+          --W0                      Suppresses all warnings.
+          --W1                      Severe warnings activated. (default)
+          --W2                      Maybe significant warnings activated.
+          --W3                      Low-confidence-diagnostic warnings activated.
+          --Wx                      Treat any currently activated warning as error.
+          --Wx1                     Treat level-1 warning as error.
+          --Wx2                     Treat up to level-2 warning as error.
+          --Wx3                     Treat up to level-3 warning as error.
+```
+
+# Commands breakdown
+
+## General usage
+
+Invoking `acslc` from command line requires either a file input (`.azsl` file written in the AZSL grammar) or the `stdin` (use the `-` option). It will transpile the AZSL shader code to HLSL code and it will emit it to the standard output.
+
+Everything else is optional.
+
+For most outputs, specifying `-o OUTFILE` redirects the code emission to a file. Warnings and errors still use the standard error output and verbose messages use the standard output.
+
+## Generate shader source
+
+### `--use-spaces`
+
+When specified, all shader resources (data views, constant buffers and sampler states) will be grouped using logical space indices. The logical space index for each group is based on the resource's Shader Resource Group.
+(All resources from the same SRG share the same logical space index.)
+
+This option must be specified for DirectX 12 and Vulkan.
+
+### `--cb-body`
+
+This feature exists for legacy shaders support only (Shader Model 5). When compiling with dxc it shouldn't be used.
+
+All constant buffers will use
+```
+cbuffer MyBuffer
+{
+  // Body
+};
+```
+rather than
+```
+struct MyStruct
+{
+  // Body
+};
+
+ConstantBuffer<MyStruct> MyBuffer;
+```
+
+### `--root-sig`
+
+Emits a RootSignature with the fixed name `sig` in the shader code for all resources, or the equivalent binding root for different graphic API.
+This argument also requires the `--namespace=<nspc>` argument. For example passing `--namespace=dx` together will emit a DirectX 12 style RootSignature.
+
+This argument only emits the struct, but it will not modify any other code. The signature must be specified when compiling with `dxc.exe` by using `rootsig-define sig -extractrootsignature` (if compiling for DirectX12) otherwise it will have no effect on the shader code. Note that the name has to match.
+
+### Examples per platform
+
+#### DirectX 12
+
+Use `azslc Shader.azsl --namespace=dx --use-spaces -o Shader.hlsl`
+
+#### Vulkan
+
+Use `azslc Shader.azsl --namespace=vk --use-spaces --unique-idx -o Shader.hlsl`
+
+#### Metal
+
+Use `azslc Shader.azsl --namespace=mt --use-spaces --unique-idx -o Shader.hlsl`
+
+## `--semantic`
+
+Only performs semantic check of the code, no shader generation. No output if correct, use `--verbose` if you want to printout compiler internals trace data.
+
+## `--syntax`
+
+Only performs syntax check of the code, no shader generation. Ends with a 0 process code if the program is valid.
+
+## `--dumpsym`
+
+Dumps the symbol table to the standard output in yaml format.
+
+It prints the symbol name, **all** lines which reference it, where is it declared and members (functions, variables, attributes, etc.).
+```
+Symbol /ExampleSRG:
+  kind: ShaderResourceGroup
+  references:
+    - {line: 71, col: 29}
+    - {line: 86, col: 29}
+    - {line: 92, col: 24}
+    - {line: 92, col: 55}
+    - {line: 94, col: 24}
+    - {line: 94, col: 67}
+  line: 7
+  structs: JustForPacking, ModelStruct, UserStruct,
+  srViews: m_diffuseMap, m_bufferView1, m_bufferView2, m_bufferView4, m_bufferView5, m_bufferView6, m_bufferView7, m_bufferView8a,
+  samplers: m_sampler,
+  CBs: m_modelConstants, m_arrayOfFour,
+Symbol /ExampleSRG/JustForPacking:
+  kind: Struct
+  references:
+    - {line: 26, col: 20}
+  line: 9
+  members:
+    - {kind: Variable, name: /ExampleSRG/JustForPacking/m_somePair}
+    - {kind: Variable, name: /ExampleSRG/JustForPacking/m_columnMatrix}
+    - {kind: Variable, name: /ExampleSRG/JustForPacking/m_someVector}
+    - {kind: Variable, name: /ExampleSRG/JustForPacking/m_someScalar}
+    - {kind: Variable, name: /ExampleSRG/JustForPacking/m_rowMatrix}
+```
+
+## `--ia` InputAssembly layout
+
+Emits the InputAssembler layout instead of the shader source. The data is a Json array which lists all functions in the shader eligible for vertex shader entry point.
+
+```
+{
+  "entry" : "MainVS",                # Name of the candidate entry point
+  "streams" :                        # List of vertex streams it requires
+  [
+    {
+      "baseType" : "float",          # Base type of the arithmetic type
+      "cols" : 3,                    # 0 if scalar (assume it's 1 element)
+                                     # number of elements if vector
+                                     # number of columns if matrix
+      "dimensions" : [],             # list of dimensions if array or multiarray
+      "fullType" : "?float3",        # The arithmetic type as declared
+      "name" : "m_position",         # Name of the attribute
+      "rows" : 0,                    # 0 if scalar or vector (assume it's 1 row)
+                                     # number of rows if matrix
+      "semanticIndex" : 0,           # Semantic index
+      "semanticName" : "POSITION",   # Semantic name without the index
+      "systemValue" : false          # Is the semantic a system value
+    }
+  ]
+},
+```
+
+## `--om` OutputMerger layout
+
+Emits the OutputMerger layout instead of the shader source. The data is a Json array which lists all functions in the shader eligible for pixel shader entry point.
+
+```
+{
+  "entry" : "MainPS",                # Name of the candidate entry point
+  "renderTargets" :                  # List of render targets
+  [
+    {
+      "baseType" : "float",          # Base type of the arithmetic type
+      "cols" : 4,                    # 0 if scalar
+                                     # number of elements if vector
+                                     # cannot be a matrix
+      "format" : "R8G8B8A8_UNORM",   # Format of the render target (hint)
+      "semanticIndex" : 0,           # Semantic index
+      "semanticName" : "SV_Target"   # Semantic name without the index
+    }
+  ]
+}
+```
+
+## `--srg` ShaderResourceGroup layout
+
+Emits the ShaderResourceGroup layout instead of the shader source. The data is a Json array which lists all resource data (data views, samplers and constant buffer packing data) in each SRG.
+
+```
+{
+			"bindingSlot" : 0,                       # Binding slot for the SRG
+			"id" : "ExampleSRG",                     # Name of the SRG
+
+			"inputsForBufferViews" :                 # List of buffer views
+			[
+				{
+					"count" : 4,                         # Number of views (1 if not an array)
+					"id" : "m_myBuffer",                 # Name of the buffer view
+					"type" : "ConstantBuffer<MyStruct>", # Type of the buffer
+					"usage" : "Read"                     # Usage (Read or ReadWrite)
+				}
+			],
+
+			"inputsForImageViews" :                  # List of image views
+			[
+				{
+					"count" : 1,                         # Number of views (1 if not an array)
+					"id" : "m_diffuseMap",               # Name of the image view
+					"type" : "Texture2D",                # Type of the image
+					"usage" : "Read"                     # Usage (Read or ReadWrite)
+				}
+			],
+
+			"inputsForSRGConstants" :                # List of all SRG constants (implicit ConstantBuffer)
+			[
+				{
+					"constantByteOffset" : 0,                     # Offset of the element
+					"constantByteSize" : 64,                      # Size of the element
+					"constantId" : "m_myModel.m_modelToWorld",    # Id of the element
+					"qualifiedName" : "/ExampleSRG/ModelStruct/m_modelToWorld",
+					"typeDimensions" : [],                        # List of dimensions of array or multiarray
+					"typeKind" : "Predefined",                    # Predefined or Struct
+					"typeName" : "?float4x4"                      # Type of the element
+				},
+				{
+					"constantByteOffset" : 0,                     # Structs have same offset as their first element
+					"constantByteSize" : 64,                      # Structs have the same size as the combined size of their elements
+					"constantId" : "m_myModel",                   # Id of the struct. Must respect packing rules if used directly
+					"qualifiedName" : "/ExampleSRG/m_myModel",
+					"typeDimensions" : [],
+					"typeKind" : "Struct",                        # Predefined or Struct
+					"typeName" : "/ExampleSRG/ModelStruct"        # Type of the struct
+				},
+				{
+					"constantByteOffset" : 64,
+					"constantByteSize" : 32,                      # float2x3 row-major takes 2 (num of rows) registries (16 bytes each) = 32 bytes
+					"constantId" : "m_rowMajorConst",
+					"qualifiedName" : "/ExampleSRG/m_rowMajorConst",
+					"typeDimensions" : [],
+					"typeKind" : "Predefined",
+					"typeName" : "?float2x3"
+				},
+				{
+					"constantByteOffset" : 96,
+					"constantByteSize" : 48,                     # float2x3 column-major takes 3 (num of cols) registries (16 bytes each) = 48 bytes
+					"constantId" : "m_colMajorConst",
+					"qualifiedName" : "/ExampleSRG/m_colMajorConst",
+					"typeDimensions" : [],
+					"typeKind" : "Predefined",
+					"typeName" : "?float2x3"
+				},
+			],
+			"inputsForSamplers" :                            # List of samplers. Attribute names are self-descriptive
+			[
+				{
+					"addressU" : "TEXTURE_ADDRESS_WRAP",
+					"addressV" : "TEXTURE_ADDRESS_WRAP",
+					"addressW" : "TEXTURE_ADDRESS_WRAP",
+					"anisotropyEnable" : true,
+					"anisotropyMax" : 16,
+					"borderColor" : "STATIC_BORDER_COLOR_TRANSPARENT_BLACK",
+					"comparisonFunc" : "COMPARISON_ALWAYS",
+					"filterMag" : "Point",
+					"filterMin" : "Point",
+					"filterMip" : "Point",
+					"isComparison" : false,
+					"mipLodBias" : 0,
+					"mipLodMax" : 15,
+					"mipLodMin" : 0,
+					"reductionType" : "Filter"
+				}
+			]
+		}
+```
+
+
+## `--ast` Abstract Syntax Tree
+
+This option will print out on stdout a lisp-like tree of the syntax of an input program as parsed by AntlR.
+Example:
+`AZSLc\dev\bin\win_x64\Release>azslc.exe --ast ..\..\..\tests\Syntax\comma-separated-declarators.azsl`
+
+```
+  (compilationUnit
+    (topLevelDeclaration
+      (variableDeclarationStatement
+        (variableDeclaration storageFlags
+          (type
+            (predefinedType
+              (scalarType int)
+            )
+          )
+
+          (variableDeclarators
+            (variableDeclarator a)
+           ,
+            (variableDeclarator b)
+          )
+        )
+       ;)
+    )
+   <EOF>)
+```

+ 27 - 0
LICENSE.txt

@@ -0,0 +1,27 @@
+OPEN 3D ENGINE LICENSING
+The default license for Open 3D Engine is the Apache License, Version 2.0 
+(see LICENSE_APACHE2.TXT); you may elect at your option to use the Open 3D 
+Engine under the MIT License (see LICENSE_MIT.TXT).  Contributions must be 
+made under both licenses.
+ 
+THIRD PARTY COMPONENTS
+Open 3D Engine requires the use of (and in some cases makes available to you) 
+software and assets that have been developed by third parties and are subject 
+to separate license terms (such as code licensed under other open source 
+licenses).  It is your responsibility to comply with the applicable licenses. 
+Information on third party materials, and the applicable license terms, are 
+referenced in or included with the materials, such as in separate LICENSE.txt 
+files accompanying the materials.
+ 
+Please note that certain materials are subject to "copyleft" licenses, which 
+require distribution of source code, including:
+ 
+- Qt Toolkit https://github.com/qtproject/, which is subject to the GNU 
+Lesser General Public License version 3 (with certain exceptions). A copy of 
+the source code for Qt Toolkit may be found at 
+https://s3-us-west-2.amazonaws.com/ly-legal/LicenseConformance/Qt/Src.zip
+ 
+- The AWS Python SDK uses Chardet https://chardet.github.io/, which is 
+subject to the GNU Lesser General Public License version 2.1. A copy of the 
+source code may be found at https://github.com/chardet/chardet.
+ 

+ 201 - 0
LICENSE_APACHE2.TXT

@@ -0,0 +1,201 @@
+                                Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 7 - 0
LICENSE_MIT.TXT

@@ -0,0 +1,7 @@
+Copyright Contributors to the Open 3D Engine
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 3 - 0
Platform/AppleTV/README.md

@@ -0,0 +1,3 @@
+# AppleTV platform restricted code
+
+Everything which should be restricted to AppleTV developers only.

+ 3 - 0
Platform/Linux/README.md

@@ -0,0 +1,3 @@
+# Linux platform restricted code
+
+Everything which should be restricted to Linux developers only.

+ 3 - 0
Platform/Mac/README.md

@@ -0,0 +1,3 @@
+# Mac platform restricted code
+
+Everything which should be restricted to Mac developers only.

+ 4 - 0
Platform/Windows/README.md

@@ -0,0 +1,4 @@
+# Windows platform restricted code
+
+Everything which should be restricted to Windows developers only.
+

+ 129 - 0
Platform/Windows/src/DirectX12PlatformEmitter.cpp

@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AzslcEmitter.h>
+#include "DirectX12PlatformEmitter.h"
+
+namespace AZ::ShaderCompiler
+{
+    static constexpr char DirectX12PlatformEmitterName[] = "dx";
+    static const PlatformEmitter* s_platformEmitter = DirectX12PlatformEmitter::RegisterPlatformEmitter();
+
+    const PlatformEmitter* DirectX12PlatformEmitter::RegisterPlatformEmitter() noexcept(false)
+    {
+        static DirectX12PlatformEmitter platformEmitter; // Static linkage, will be destroyed
+
+        static bool alreadyRegistered = false;
+        if (!alreadyRegistered)
+        {
+            PlatformEmitter::SetEmitter(DirectX12PlatformEmitterName, &platformEmitter);
+            alreadyRegistered = true;
+        }
+
+        return &platformEmitter;
+    }
+    
+    string DirectX12PlatformEmitter::GetRootSig(const CodeEmitter& codeEmitter, const RootSigDesc& rootSig, const Options&, BindingPair::Set querySet) const
+    {
+        vector<string> rootAttrList;
+        // SRG Constants are emitted as one CB per SRG, bound to the RootSignature as RootDescriptor
+        for (auto& srg : rootSig.m_srGroups)
+        {
+            for (auto& param : srg.m_parameters)
+            {
+                std::stringstream rootParam;
+                const Binding& location = param.m_registerBinding.m_pair[querySet];
+                switch (param.m_type)
+                {
+                case RootParamType::SrgConstantCB:
+                {
+                    assert(param.m_registerRange == 1);
+                    rootParam << "            \"CBV(b" << std::to_string(location.m_registerIndex)
+                              << ", space = " << std::to_string(location.m_logicalSpace)
+                              << ", visibility=SHADER_VISIBILITY_ALL)";
+                    rootAttrList.push_back(rootParam.str());
+                    break;
+                }
+                case RootParamType::RootConstantCB:
+                    rootParam << "            \"RootConstants(num32BitConstants=" << std::to_string(param.m_num32BitConstants)
+                              << ", b" << std::to_string(location.m_registerIndex)
+                              << ", space = " << std::to_string(location.m_logicalSpace)
+                              << ", visibility=SHADER_VISIBILITY_ALL)";
+                    rootAttrList.push_back(rootParam.str());
+                    break;
+                default:
+                    break;
+                }
+            }
+        }
+
+        // Next, process the remaining descriptor tables by frequency.
+        for (auto& srg : rootSig.m_srGroups)
+        {
+            vector<string> descriptorTable, samplerTable;
+
+            for (auto& param : srg.m_parameters)
+            {
+                if (param.m_type != RootParamType::SrgConstantCB && param.m_type != RootParamType::RootConstantCB) // already treated in the previous loop
+                {
+                    std::stringstream rootParam;
+                    rootParam << RootParamType::ToStr(param.m_type)
+                        << "(" << ToLower(BindingType::ToStr(RootParamTypeToBindingType(param.m_type)));  // eg "CBV(b" or "SRV(t" or "Sampler(s"
+                    rootParam << std::to_string(param.m_registerBinding.m_pair[querySet].m_registerIndex)
+                        << ", space = " << std::to_string(param.m_registerBinding.m_pair[querySet].m_logicalSpace)
+                        << ", numDescriptors = " << std::to_string(param.m_registerRange) << ")";
+
+                    const auto* memberInfo = codeEmitter.GetIR()->GetSymbolSubAs<VarInfo>(param.m_uid.m_name);
+                    bool isSampler = param.m_type == RootParamType::Sampler;
+                    auto& destinationVector = isSampler ? samplerTable : descriptorTable;
+                    if (!isSampler || memberInfo->m_samplerState->m_isDynamic)
+                    {
+                        destinationVector.push_back(rootParam.str());
+                    }
+                }
+            }
+
+            if (!descriptorTable.empty())
+            {
+                rootAttrList.push_back(Decorate("            \"DescriptorTable(", Join(descriptorTable.begin(), descriptorTable.end()       , ", \" \\\n                            \""), ", visibility=SHADER_VISIBILITY_ALL)"));
+            }
+            if (!samplerTable.empty())
+            {
+                rootAttrList.push_back(Decorate("            \"DescriptorTable(", Join(samplerTable.begin(), samplerTable.end(), ", \" \\\n                            \""), ", visibility=SHADER_VISIBILITY_ALL)"));
+            }
+        }
+
+        // Static samplers
+        for (auto& srg : rootSig.m_srGroups)
+        {
+            for (auto& param : srg.m_parameters)
+            {
+                std::stringstream rootParam;
+
+                if (param.m_type == RootParamType::Sampler)
+                {
+                    const auto* memberInfo = codeEmitter.GetIR()->GetSymbolSubAs<VarInfo>(param.m_uid.m_name);
+                    const auto& samplerInfo = *memberInfo->m_samplerState;
+                    if (!samplerInfo.m_isDynamic)
+                    {
+                        rootParam << "            \"StaticSampler(s" << std::to_string(param.m_registerBinding.m_pair[querySet].m_registerIndex)
+                                  << ", space = " << std::to_string(param.m_registerBinding.m_pair[querySet].m_logicalSpace)
+                                  << ", visibility=SHADER_VISIBILITY_ALL"
+                                  << samplerInfo << ")";
+                        rootAttrList.push_back(rootParam.str());
+                    }
+                }
+            }
+        }
+
+        rootAttrList.insert(rootAttrList.begin(), "\"RootFlags(0)");
+
+        return Decorate("#define sig ", Join(rootAttrList.begin(), rootAttrList.end(), ", \" \\\n"), "\"\n\n");
+    }
+    
+}

+ 28 - 0
Platform/Windows/src/DirectX12PlatformEmitter.h

@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <AzslcPlatformEmitter.h>
+
+namespace AZ::ShaderCompiler
+{
+    // PlatformEmitter is not a Backend by design. It's a supplement to CodeEmitter, not a replacement
+    struct DirectX12PlatformEmitter : PlatformEmitter 
+    {
+    public:
+        //! This method will be called once and only once when the platform emitter registers itself to the system.
+        //! Returns a singleton object of this class.
+        static const PlatformEmitter* RegisterPlatformEmitter() noexcept(false);
+
+        [[nodiscard]]
+        string GetRootSig(const CodeEmitter& codeEmitter, const RootSigDesc& rootSig, const Options& options, BindingPair::Set signatureQuery) const override final;
+
+    private:
+        DirectX12PlatformEmitter() : PlatformEmitter {} {};
+    };
+}

+ 82 - 0
Platform/Windows/src/VulkanPlatformEmitter.cpp

@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AzslcEmitter.h>
+#include "VulkanPlatformEmitter.h"
+
+namespace AZ::ShaderCompiler
+{
+    static constexpr char VulkanPlatformEmitterName[] = "vk";
+    static const PlatformEmitter* s_platformEmitter = VulkanPlatformEmitter::RegisterPlatformEmitter();
+
+    const PlatformEmitter* VulkanPlatformEmitter::RegisterPlatformEmitter() noexcept(false)
+    {
+        static VulkanPlatformEmitter platformEmitter; // Static linkage, will be destroyed
+
+        static bool alreadyRegistered = false;
+        if (!alreadyRegistered)
+        {
+            PlatformEmitter::SetEmitter(VulkanPlatformEmitterName, &platformEmitter);
+            alreadyRegistered = true;
+        }
+
+        return &platformEmitter;
+    }
+
+    string VulkanPlatformEmitter::GetRootConstantsView(const CodeEmitter& codeEmitter, const RootSigDesc&, const Options&, BindingPair::Set) const
+    {
+        std::stringstream strOut;
+
+        const auto& structUid = codeEmitter.GetIR()->m_rootConstantStructUID;
+        const auto& rootCBForEmission = codeEmitter.GetTranslatedName(RootConstantsViewName, UsageContext::DeclarationSite);
+        strOut << "[[vk::push_constant]]\n";
+        strOut << UnmangleTrimedName(structUid.GetName()) << " " << rootCBForEmission << ";\n\n";
+
+        return strOut.str();        
+    }
+
+    std::pair<string, string> VulkanPlatformEmitter::GetDataViewHeaderFooter(const CodeEmitter& codeEmitter, const IdentifierUID& symbol, uint32_t bindInfoRegisterIndex, string_view registerTypeLetter, optional<string> stringifiedLogicalSpace) const
+    {
+        std::stringstream stream;
+        optional<AttributeInfo> inputAttachmentIndexAttribute = codeEmitter.GetIR()->m_symbols.GetAttribute(symbol, "input_attachment_index");
+        if (inputAttachmentIndexAttribute)
+        {
+            // example result in HLSL:
+            /*
+                   #ifdef AZ_USE_SUBPASSINPUT
+                   [[vk::binding(0,0)]]
+                   [[vk::input_attachment_index(0)]]
+                   #endif
+                   AzSubpassInput srg_myData;
+            */
+            stream << "#ifdef AZ_USE_SUBPASSINPUT\n";
+            inputAttachmentIndexAttribute->m_namespace = "vk";
+            inputAttachmentIndexAttribute->m_category = AttributeCategory::Sequence;
+            CodeEmitter::EmitAttribute(*inputAttachmentIndexAttribute, stream);
+            stream << "[[vk::binding(" << bindInfoRegisterIndex;
+            if (stringifiedLogicalSpace)
+            {
+                stream << ", " << *stringifiedLogicalSpace;
+            }
+            stream << ")]]\n";
+            stream << "#else\n static\n#endif\n";  // for the stub mode, we don't export the phony variable
+        }
+
+        string registerString;
+        if (!inputAttachmentIndexAttribute)
+        {   // fallback to the base behavior in non-input-attachment cases for the `.. : register();` syntax.
+            registerString = PlatformEmitter::GetDataViewHeaderFooter(codeEmitter,
+                                                                      symbol,
+                                                                      bindInfoRegisterIndex,
+                                                                      registerTypeLetter,
+                                                                      stringifiedLogicalSpace).second;
+        }
+        return { stream.str(), registerString };
+    }
+    
+}

+ 31 - 0
Platform/Windows/src/VulkanPlatformEmitter.h

@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <AzslcPlatformEmitter.h>
+
+namespace AZ::ShaderCompiler
+{
+    // PlatformEmitter is not a Backend by design. It's a supplement to CodeEmitter, not a replacement
+    struct VulkanPlatformEmitter : PlatformEmitter 
+    {
+    public:
+        //! This method will be called once and only once when the platform emitter registers itself to the system.
+        //! Returns a singleton object of this class.
+        static const PlatformEmitter* RegisterPlatformEmitter() noexcept(false);
+
+        [[nodiscard]]
+        string GetRootConstantsView(const CodeEmitter& codeEmitter, const RootSigDesc& rootSig, const Options& options, BindingPair::Set signatureQuery) const override final;
+
+        [[nodiscard]]
+        std::pair<string, string> GetDataViewHeaderFooter(const CodeEmitter& codeEmitter, const IdentifierUID& symbol, uint32_t bindInfoRegisterIndex, string_view registerTypeLetter, optional<string> stringifiedLogicalSpace) const override final;
+
+    private:
+        VulkanPlatformEmitter() : PlatformEmitter {} {};
+    };
+}

+ 3 - 0
Platform/iOS/README.md

@@ -0,0 +1,3 @@
+# iOS platform restricted code
+
+Everything which should be restricted to iOS developers only.

+ 50 - 0
Platform/iOS/src/MetalPlatformEmitter.cpp

@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AzslcEmitter.h>
+#include "MetalPlatformEmitter.h"
+
+namespace AZ::ShaderCompiler
+{
+    static constexpr char MetalPlatformEmitterName[] = "mt";
+    static const PlatformEmitter* s_platformEmitter = MetalPlatformEmitter::RegisterPlatformEmitter();
+
+    const PlatformEmitter* MetalPlatformEmitter::RegisterPlatformEmitter() noexcept(false)
+    {
+        static MetalPlatformEmitter platformEmitter; // Static linkage, will be destroyed
+
+        static bool alreadyRegistered = false;
+        if (!alreadyRegistered)
+        {
+            PlatformEmitter::SetEmitter(MetalPlatformEmitterName, &platformEmitter);
+            alreadyRegistered = true;
+        }
+
+        return &platformEmitter;
+    }
+
+    std::string MetalPlatformEmitter::GetRootConstantsView(const CodeEmitter& codeEmitter, const RootSigDesc& rootSig, const Options& options, BindingPair::Set signatureQuery) const
+    {
+        std::stringstream strOut;
+
+        strOut << "[[vk::push_constant]]\n";
+        const auto& structUid = codeEmitter.GetIR()->m_rootConstantStructUID;
+        const auto& bindInfo = rootSig.Get(structUid);
+        assert(structUid == bindInfo.m_uid);
+        const auto& rootCBForEmission = codeEmitter.GetTranslatedName(RootConstantsViewName, UsageContext::DeclarationSite);
+        const auto& rootConstClassForEmission = codeEmitter.GetTranslatedName(structUid.GetName(), UsageContext::ReferenceSite);
+        const auto& spaceX = (options.m_useLogicalSpaces) ? ", space" + std::to_string(bindInfo.m_registerBinding.m_pair[signatureQuery].m_logicalSpace) : "";
+        strOut << "ConstantBuffer<" << rootConstClassForEmission << "> " << rootCBForEmission << " : register(b" << bindInfo.m_registerBinding.m_pair[signatureQuery].m_registerIndex << spaceX << ");\n\n";
+        return strOut.str();
+    }
+	
+    uint32_t MetalPlatformEmitter::AlignRootConstants(uint32_t size) const
+    {
+        return Packing::AlignUp(size, Packing::s_bytesPerRegister);
+	}
+}

+ 30 - 0
Platform/iOS/src/MetalPlatformEmitter.h

@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <AzslcPlatformEmitter.h>
+
+namespace AZ::ShaderCompiler
+{
+    // PlatformEmitter is not a Backend by design. It's a supplement to CodeEmitter, not a replacement
+    struct MetalPlatformEmitter : PlatformEmitter 
+    {
+    public:
+        //! This method will be called once and only once when the platform emitter registers itself to the system.
+        //! Returns a singleton object of this class.
+        static const PlatformEmitter* RegisterPlatformEmitter() noexcept(false);
+
+        [[nodiscard]]
+        std::string GetRootConstantsView(const CodeEmitter& codeEmitter, const RootSigDesc& rootSig, const Options& options, BindingPair::Set signatureQuery) const override final;
+
+        uint32_t AlignRootConstants(uint32_t size) const override final;
+
+    private:
+        MetalPlatformEmitter() : PlatformEmitter {} {};
+    };
+}

+ 110 - 0
README.md

@@ -0,0 +1,110 @@
+# Amazon Shading Language Compiler
+AZSLc is a stand-alone command line compiler for the Amazon Shading Language.
+It converts Amazon Shading Language (AZSL) shaders to High Level Shading Language Shader Model 6+ (HLSL) shaders.
+
+For more information, see (https://o3de.org/docs/atom-guide/dev-guide/shaders/)
+
+# Features and goals
+
+AZSLc is a compiler for the Amazon Shading Language, which is a thin extension of HLSL. It unifies resource bindings across all supported graphics hardware using binding strategy which models modern API such as DirectX 12 and Vulkan. Future plans include support for interfaces, generics and associated types and aims to bring the shader languages closer to modern programming languages while still allowing users to write shaders in plain HLSL code.
+
+Currently supported graphics API are:
+ - DirectX 12 (PC)
+ - Vulkan (PC, Android)
+ - Metal 2 (Mac OSX, iOS)
+
+# Build instructions
+
+## Supported build architectures:
+ - Windows 10 (win_x64)
+ - MacOSX 10.14.0 or newer
+ - linux (tested with debian)
+
+## Prerequisites
+
+### All platforms
+Make sure Python 3.7+ is in your `$PATH` (as 'python'), and run:
+```
+python  .\test.and.py
+or
+python  .\test.and.py --dev D:\o3de
+```
+(`D:\o3de` should be replaced with your local path for the O3DE root directory)
+
+The script will tell you the prerequisites per platform, which are also detailed below.
+If all prerequisites are installed, the script will make, build and test the shader compiler.
+
+
+### Windows
+ - Python 3.7
+ - MSBuild 15.9 or higher (from https://github.com/Microsoft/msbuild, MS Build Tools 2019 or VS2019)
+ - CMake 3.15.0 (tested with 3.15.0, some higher versions might work, 3.14 doesn't)
+ - [PyYAML](https://pyyaml.org/) (`pip install pyyaml`) - only required to run the tests
+ - (optional) Visual Studio 2019 for IDE
+ - (optional) Java JDK 1.6 or higher (only required to regenerate the Antlr grammar)
+
+### MacOSX
+ - Python 3.7
+ - CMake (tested with 3.14.0, but 3.7 or higher should also work)
+ - Apple LLVM 9.0.0 (clang-900.0.39.2) or newer (tested on 9.0.0 and 10.0.0)
+   - comes with Xcode 9.2 or Command Line Tools for Xcode 9.2 or newer
+ - [PyYAML](https://pyyaml.org/) (`pip install pyyaml`) - only required to run the tests
+ - (optional) Java JDK 1.6 or higher (only required to regenerate the Antlr grammar)
+
+### Linux
+ - Python 3.7
+ - gcc 8
+ - CMake
+ - sudo apt-get install uuid-dev (or equivalent in your distribution)
+ - [PyYAML](https://pyyaml.org/) (`pip install pyyaml`) - only required to run the tests
+ - (optional) Java JDK 1.6 or higher (only required to regenerate the Antlr grammar)
+
+## (optional) Regenerate the AZSL ANTLR grammar
+1. Install Java JDK (version 1.6 or higher)
+2. (Windows) The generated source files can be recreated by running `regenerate_azsl_antlr.bat` script under `src` folder, it will regenerate the files under `src/generated`.
+ - Should work on MacOSX as well, but hasn't been tested. Follow the steps in the batch file to invoke Java and rebuild the generated sources
+
+## (optional) azslLexer.g4 keyword changes
+You'll probably need to regenerate the `AzslcPredefinedTypes.h` header file if you made a change to the lexer keywords. For that purpose, execute regenerate-predefined-types.bat (or simply directly `python exportKeywords.py` if not on windows)
+
+
+## Build on Windows
+
+After installing the prerequisites, run either command from a console:
+```
+build_win.bat Debug
+build_win.bat Release
+```
+
+You can find the binary `azslc.exe` in the `bin` directory.
+
+## Build on MacOSX
+
+After installing the prerequisites, run from the terminal:
+```
+./prepare_solution_darwin.sh
+```
+
+You can find the binaries `azslc` in the `bin/darwin/release` and the `bin/darwin/debug` directories.
+
+## Build on Linux
+
+After installing the prerequisites, run from the terminal:
+```
+./prepare_solution_linux.sh
+```
+
+You can find the binaries `azslc` in the `bin/linux/release` and the `bin/linux/debug` directories.
+
+# Running the tests
+The `tests` folder includes all the unit and integration tests for AZSLc.
+
+Tests require Python 3.7 to run. And pyyaml for a complete run. (do pip install pyyaml)
+
+For Windows, run either `launch_tests.bat` or `launch_tests_debug.bat` to launch the test suite.
+
+For MacOSX, run either `launch_tests.sh` or `launch_tests_debug.sh` to launch the test suite.
+
+## License
+
+For terms please see the LICENSE*.TXT file at the root of this distribution.

+ 58 - 0
build_win.bat

@@ -0,0 +1,58 @@
+@ECHO OFF
+REM 
+REM Copyright (c) Contributors to the Open 3D Engine Project.
+REM For complete copyright and license terms please see the LICENSE at the root of this distribution.
+REM 
+REM SPDX-License-Identifier: Apache-2.0 OR MIT
+REM
+
+if [%1] == [] goto ShowHelp
+goto SetParams
+
+:ShowHelp
+echo Builds AZSLc for Windows.
+echo.
+echo build_win.bat configuration
+echo.
+echo   configuration     Release or Debug
+exit /b 1
+
+:SetParams
+set configuration=%1
+set platform=win
+set architecture=x64
+
+set curr_dir=%~dp0
+set build_path=%curr_dir%\build\%platform%_%architecture%
+set build_output_path=%build_path%\%configuration%
+set output_path=%curr_dir%\bin\%platform%_%architecture%\%configuration%
+set cmake_exe=%curr_dir%\..\lib\CMake\bin\cmake.exe
+
+REM Configuration has to be 'Release' or 'Debug'
+if ["%configuration%"] == ["Release"] goto Build
+if ["%configuration%"] == ["Debug"] goto Build
+
+echo Error: incorrect configuration.
+goto ShowHelp
+
+:Build
+REM ----------------------------------
+REM Prepare Solution in build folder
+call prepare_solution_win.bat nopause %2
+if %errorlevel% neq 0 goto :error
+
+REM ----------------------------------
+REM Build AZSLc project
+if not exist "%build_path%" goto :error
+cd "%build_path%"
+
+%cmake_exe% --build . --config %configuration% --target azslc || goto :error
+
+if %errorlevel% equ 0 @echo build_win.bat: build ok
+cd "%curr_dir%"
+exit /b 0
+
+:error
+@echo Build failed :(
+cd "%curr_dir%"
+exit /b 1

+ 18 - 0
clean_build.bat

@@ -0,0 +1,18 @@
+@ECHO OFF
+REM 
+REM Copyright (c) Contributors to the Open 3D Engine Project.
+REM For complete copyright and license terms please see the LICENSE at the root of this distribution.
+REM 
+REM SPDX-License-Identifier: Apache-2.0 OR MIT
+REM
+
+del /q open_azslc*
+cd  bin || goto :NEXT
+del /q *
+for /d %%x in (*) do @rd /s /q "%%x"
+cd ..
+:NEXT
+cd  build || goto :EOF
+del /q *
+for /d %%x in (*) do @rd /s /q "%%x"
+cd ..

+ 70 - 0
convert-hlsl_lines-to-pattern_lines.py

@@ -0,0 +1,70 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+"""
+Copyright (c) Contributors to the Open 3D Engine Project.
+For complete copyright and license terms please see the LICENSE at the root of this distribution.
+
+SPDX-License-Identifier: Apache-2.0 OR MIT
+"""
+
+import io
+import re
+
+def GetPatternLineFromHlslLine(line):
+    """
+    Returns a string, with the pattern version of the input string.
+    Example:
+    The following HLSL line produced by AZSLc:
+        float2 samplePos = ::PassSrg_GetSamplePosition ( IN . m_sampleIndex ) ;
+    Will become like this when converted to pattern line as required by testhelper.verifyEmissionPattern():
+        "float2 samplePos = :: PassSrg_GetSamplePosition ( IN . m_sampleIndex ) ;"
+    The '"' characters are also included in the returned string, which is required
+    by testhelper.verifyEmissionPattern().
+    """
+    line = line.rstrip("\r\n")
+    if line == "":
+        return None
+    allidents = re.split("([a-zA-Z_]+[a-zA-Z_0-9]*)|(;)|(\()|(\))|(<)|(>)|( )", line)
+    allidents = filter(lambda s: s is not None and s!="" and s!=" ", allidents)  # remove empties from ['', 'code', '', 'piece']...
+    line = " ".join(allidents)
+    return f"\"{line}\""
+    
+
+def GeneratePatternLinesFromHlslFile(filePath):
+    """
+    Returns all lines in the file named @filePath
+    as pattern lines.
+    In general, @filePath is output HLSL code produced by AZSLc. 
+    """
+    resultLines = []
+    with io.open(filePath, "r", encoding="latin-1") as f:
+        for line in f:
+            predicateLine = GetPatternLineFromHlslLine(line)
+            if not (predicateLine is None): resultLines.append(predicateLine)
+    return resultLines
+
+
+# The main purpose of this helper script is to take
+# an HLSL output produced by AZSLc and convert each line
+# into a pattern line. The idea is that the pattern
+# line version is what is used for emission validation.
+# The pattern line version is what is required when testhelper.verifyEmissionPattern() is called.
+#
+# Example:
+# The following HLSL line produced by AZSLc:
+#     float2 samplePos = ::PassSrg_GetSamplePosition ( IN . m_sampleIndex ) ;
+# Will become like this when converted to pattern line as required by testhelper.verifyEmissionPattern():
+#     "float2 samplePos = :: PassSrg_GetSamplePosition ( IN . m_sampleIndex ) ;"
+#
+# This script is NOT intended to be used during testing,
+# it is used to pre-generate/bake data files data that can be used during testing.
+if __name__ == "__main__":
+    import sys
+    HELP = f"{sys.argv[0]} <hlslFile>"
+    if len(sys.argv) != 2:
+        print(HELP)
+        sys.exit(-1)
+    filePath = sys.argv[1]
+    lines = GeneratePatternLinesFromHlslFile(filePath)
+    for line in lines:
+        print(line)

+ 23 - 0
launch_grun.bat

@@ -0,0 +1,23 @@
+REM this batch is a helper for developers who installed ANTLR4 in their system (and JDK),
+REM to launch the ANTLR SDK Tool: GRUN. Grun is a trivial client of the Antlr4 API,
+REM it just parses a source. It can output the AST in text mode, with -ast switch.
+REM or, like in this batch, a window with a graph visualizer with the -gui switch.
+REM This helps when working with the grammar file azslParser.g4 to see what happens
+REM to the parse tree.
+@echo off
+@echo launch_grun is a helper to visualize parse trees from .azsl files
+setlocal
+if "%~1" == "" (
+    set /p thepath="Enter path to file to compile: "
+) else (
+    set thepath=%~1
+)
+pushd
+
+cd "%~dp0\src\generated\java"
+set antlr4=java org.antlr.v4.Tool
+set grun=java org.antlr.v4.gui.TestRig
+%grun% azsl compilationUnit -gui "%thepath%"
+
+popd
+endlocal

+ 65 - 0
prepare_solution_darwin.sh

@@ -0,0 +1,65 @@
+#!/bin/bash
+
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+# 
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+
+# Build
+
+mkdir build
+mkdir build/release
+mkdir build/debug
+mkdir build/release/external
+mkdir build/release/external/antlr-runtime-cpp
+mkdir build/debug/external
+mkdir build/debug/external/antlr-runtime-cpp
+
+CMAKE='cmake'
+if ! command -v $CMAKE &> /dev/null
+then
+    OLDCMAKE=$CMAKE
+    CMAKE='-/Applications/CMake.app/Contents/bin/cmake'
+    echo "$OLDCMAKE not found in PATH. Defaulting to: $CMAKE"
+fi
+
+echo "Pulling ANTLR from git..."
+python pull_from_git.py --git-url https://github.com/galibzon/antlr4.git --destination-dir src/external --git-tag o3de-4.7.1
+
+$CMAKE -DMAKE_BUILD_TYPE=Release -S "src/external/antlr4/runtime/Cpp/" -B "build/release/external/antlr4/runtime/Cpp/"
+pushd build/release/external/antlr4/runtime/Cpp
+make -j16
+popd
+
+$CMAKE -DMAKE_BUILD_TYPE=Release -S "src/" -B "build/release"
+pushd build/release
+echo "Building release..."
+make -j16
+ls
+echo "Release version:"
+./azslc --version
+popd
+
+$CMAKE -DMAKE_BUILD_TYPE=Debug -S "src/external/antlr4/runtime/Cpp/" -B "build/debug/external/antlr4/runtime/Cpp/"
+pushd build/debug/external/antlr4/runtime/Cpp
+make -j16
+popd
+
+$CMAKE -DMAKE_BUILD_TYPE=Debug -S "src/" -B "build/debug"
+pushd build/debug
+echo "Building debug..."
+make -j16
+ls
+echo "Debug version:"
+./azslc --version
+popd
+
+# Deploy
+mkdir bin
+mkdir bin/darwin
+mkdir bin/darwin/release
+mkdir bin/darwin/debug
+cp build/release/azslc bin/darwin/release/azslc
+cp build/debug/azslc bin/darwin/debug/azslc

+ 59 - 0
prepare_solution_linux.sh

@@ -0,0 +1,59 @@
+#!/bin/bash
+
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+# 
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+
+# Build
+
+mkdir build
+mkdir build/release
+mkdir build/debug
+mkdir build/release/external
+mkdir build/release/external/antlr-runtime-cpp
+mkdir build/debug/external
+mkdir build/debug/external/antlr-runtime-cpp
+
+echo "Pulling ANTLR from git..."
+python pull_from_git.py --git-url https://github.com/galibzon/antlr4.git --destination-dir src/external --git-tag o3de-4.7.1
+
+CMAKE='cmake'
+
+$CMAKE -DMAKE_BUILD_TYPE=Release -S "src/external/antlr4/runtime/Cpp/" -B "build/release/external/antlr4/runtime/Cpp/"
+pushd build/release/external/antlr4/runtime/Cpp
+make -j16
+popd
+
+$CMAKE -DMAKE_BUILD_TYPE=Release -S "src/" -B "build/release"
+pushd build/release
+echo "Building release..."
+make -j16
+ls
+echo "Release version:"
+./azslc --version
+popd
+
+$CMAKE -DMAKE_BUILD_TYPE=Debug -S "src/external/antlr4/runtime/Cpp/" -B "build/debug/external/antlr4/runtime/Cpp/"
+pushd build/debug/external/antlr4/runtime/Cpp
+make -j16
+popd
+
+$CMAKE -DMAKE_BUILD_TYPE=Debug -S "src/" -B "build/debug"
+pushd build/debug
+echo "Building debug..."
+make -j16
+ls
+echo "Debug version:"
+./azslc --version
+popd
+
+# Deploy
+mkdir bin
+mkdir bin/linux
+mkdir bin/linux/release
+mkdir bin/linux/debug
+cp build/release/azslc bin/linux/release/azslc
+cp build/debug/azslc bin/linux/debug/azslc

+ 66 - 0
prepare_solution_win.bat

@@ -0,0 +1,66 @@
+@ECHO OFF
+REM 
+REM Copyright (c) Contributors to the Open 3D Engine Project.
+REM For complete copyright and license terms please see the LICENSE at the root of this distribution.
+REM 
+REM SPDX-License-Identifier: Apache-2.0 OR MIT
+REM
+
+set platform=win
+set architecture=x64
+set generator="Visual Studio 16 2019"
+
+set curr_dir=%~dp0
+set src_path=%curr_dir%\src
+set build_path=%curr_dir%\build\%platform%_%architecture%
+set cmake_exe=cmake
+
+REM boost-regex is required on windows only because docopt needs a better std::regex implementation
+REM than then one provided by visual studio.
+REM ----------------------------------
+REM Pulling boost from git
+python pull_from_git.py --git-url https://github.com/boostorg/boost.git --destination-dir src/external --git-tag boost-1.70.0 || goto :error
+
+REM Compiling boost-regex multithreaded dynamic libraries
+cd src\external\boost
+CALL .\bootstrap.bat
+.\b2 threading=multi link=static,shared runtime-link=shared address-model=64 -j64 --with-regex || goto :error
+cd ..\..\..\
+
+REM ----------------------------------
+REM Pulling ANTLR from git
+python pull_from_git.py --git-url https://github.com/galibzon/antlr4.git --destination-dir src/external --git-tag o3de-4.7.1 || goto :error
+
+if not exist "%build_path%" mkdir "%build_path%"
+cd "%build_path%" || goto :error
+
+REM ----------------------------------
+REM Generate ANTLR runtime project
+if not exist external mkdir external
+cd external
+if not exist antlr4 mkdir antlr4
+cd antlr4
+if not exist runtime mkdir runtime
+cd runtime
+if not exist Cpp mkdir Cpp
+cd Cpp
+
+%cmake_exe% -G %generator% "%src_path%\external\antlr4\runtime\Cpp\" || goto :error
+
+python -c "import sys; sys.stdout.write( open(sys.argv[1], 'r', encoding='utf-8_sig').read().replace('</RuntimeLibrary>', 'DLL</RuntimeLibrary>'))" runtime\antlr4_static.vcxproj > runtime\antlr4_static2.vcxproj || goto :error
+copy /Y "runtime\antlr4_static2.vcxproj" "runtime\antlr4_static.vcxproj"
+
+REM ----------------------------------
+REM Generate AZSLc project
+cd "%build_path%"
+
+%cmake_exe% -G %generator% "%src_path%" || goto :error
+
+if [%1] == [] pause
+goto :EOF
+
+:error
+@echo Failed to prepare solution :(
+cd "%curr_dir%"
+if [%1] == [] pause
+exit /b 1

+ 175 - 0
pull_from_git.py

@@ -0,0 +1,175 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+# 
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+import argparse
+import sys
+import os
+import subprocess
+import platform
+from urllib.parse import urlparse
+
+def SwitchRootDirectory(destinationDir):
+    """
+    @returns a tuple previousDir, currentDir if successful
+    """
+    currentDir = os.getcwd()
+    destDir = currentDir
+    print(f"Current dir is {currentDir}")
+    if destinationDir is not None:
+        if not os.path.isabs(destinationDir):
+            destDir = os.path.join(currentDir, destinationDir)
+        else:
+            destDir = destinationDir
+        if not os.path.isdir(destDir):
+            raise Exception(f"{destDir} is NOT a directory")
+    # Change working directory before cloning the project
+    if currentDir != destDir:
+        os.chdir(destDir)
+    return currentDir, destDir
+
+def SubpArgs(args):
+    """
+    According to subcommand, when using shell=True, its recommended not to pass in an argument list but the full command line as a single string.
+    That means in the argument list in the configuration make sure to provide the proper escapements or double-quotes for paths with spaces
+
+    :param args: The list of arguments to transform
+    """
+    argString = " ".join([arg for arg in args])
+    print(f"Command: {argString}")
+    return argString
+
+def GetFilenameFromUrl(gitUrl):
+    aPath = urlparse(gitUrl)
+    filenameWithExtension = os.path.basename(aPath.path)
+    return os.path.splitext(filenameWithExtension)[0]
+
+def DeleteFolder(folder):
+    """
+    Use the system's remove folder command instead of os.rmdir
+    """
+    if platform.system() == 'Windows':
+        callResult = subprocess.run(subp_args(['rmdir', '/Q', '/S', str(folder.name)]),
+                                     shell=True,
+                                     capture_output=True,
+                                     cwd=str(folder.parent.absolute()))
+    else:
+        callResult = subprocess.run(subp_args(['rm', '-rf', str(folder.name)]),
+                                     shell=True,
+                                     capture_output=True,
+                                     cwd=str(folder.parent.absolute()))
+    if callResult.returncode != 0:
+        raise Exception(f"Unable to delete folder {str(folder)}: {str(call_result.stderr)}")
+
+
+def IsGitProjectCheckedOut(gitUrl, projectName, currentParentDir):
+    """
+    @param gitUrl The github url of the project
+    @param projectName [optional] If defined, this will be the local name of the project root folder
+    @param currentParentDir The full path of the current working directory which is the parent folder of the git project.
+    """
+    if projectName is None:
+        projectName = GetFilenameFromUrl(gitUrl)
+    projectPath = os.path.join(currentParentDir, projectName)
+    if not os.path.exists(projectPath):
+        return False
+    # The directory exists. Need to validate it's a valid git project
+    gitStashCmd = ['git', 'stash']
+    callResult = subprocess.run(SubpArgs(gitStashCmd),
+                                 shell=True,
+                                 capture_output=True,
+                                 cwd=projectPath)
+    if callResult.returncode != 0:
+        # Not a valid git folder, okay to remove and re-clone
+        DeleteFolder(projectPath)
+        return False
+
+    # Do not re-pull for now.
+    ## Do a re-pull
+    #gitPullCmd = ['git', 'pull']
+    #callResult = subprocess.run(SubpArgs(gitPullCmd),
+    #                             shell=True,
+    #                             capture_output=True,
+    #                             cwd=projectPath)
+    #if callResult.returncode != 0:
+    #    raise Exception(f"Error pulling source from GitHub: {callResult.stderr.decode('UTF-8', 'ignore')}")
+    return True
+
+
+def FetchGitProject(gitUrl, projectName, destinationDir, gitTag, gitCommit):
+    """
+    @param gitUrl The github url of the project
+    @param projectName [optional] If defined, this will be the local name of the project root folder
+    @param destinationDir [optional] If defined, this is the directory where the project will be cloned into
+    """
+    currentDir, destinationDir = SwitchRootDirectory(destinationDir)
+    
+    # Before cloning see if the repo already exists.
+    if IsGitProjectCheckedOut(gitUrl, projectName, destinationDir):
+        print(f"Git project {gitUrl} already cloned under parent directory {destinationDir}")
+        return
+
+    cloneCmd = ['git',
+                'clone',
+                '--single-branch',
+                '--recursive']
+    if gitTag is not None:
+        cloneCmd.extend(['--branch', gitTag])
+    cloneCmd.append(gitUrl)
+    if projectName is not None:
+        cloneCmd.append(projectName)
+    else:
+        projectName = GetFilenameFromUrl(gitUrl)
+    cloneResult = subprocess.run(SubpArgs(cloneCmd),
+                                 shell=True,
+                                 capture_output=True,
+                                 cwd=destinationDir)
+    if cloneResult.returncode != 0:
+        raise Exception(f"Error cloning from GitHub: {cloneResult.stderr.decode('UTF-8', 'ignore')}")
+    if gitCommit is not None:
+        # Allow the package to specify a specific commit to check out. This is useful for upstream repos that do
+        # not tag their releases.
+        # Checking out 
+        checkoutResult = subprocess.run(
+            SubpArgs(['git', 'checkout', gitCommit]),
+            shell=True,
+            capture_output=True,
+            cwd=projectName)
+        if checkoutResult.returncode != 0:
+            raise Exception(f"Error checking out {gitCommit}: {checkoutResult.stderr.decode('UTF-8', 'ignore')}")
+    print(f"Successfully cloned Git project {gitUrl} under parent directory {destinationDir}")
+
+if __name__ == '__main__':
+    try:
+        argParser = argparse.ArgumentParser(
+            description="Tool to pull a github project if not already pulled",
+            formatter_class=argparse.RawDescriptionHelpFormatter)
+        argParser.add_argument('--git-url',
+                            help='The github url of the project',
+                            required=True)
+        argParser.add_argument('--name',
+                            help='If defined, this will be the local name of the project root folder',
+                            required=False)
+        argParser.add_argument('--destination-dir',
+                            help='If defined, this is the directory where the project will be cloned into',
+                            required=False)
+        argParser.add_argument('--git-tag',
+                            help='If defined, the project will be checked out at this tag.',
+                            required=False)
+        argParser.add_argument('--git-commit',
+                            help='If defined, the project will be checked out at this commit hash.',
+                            required=False)
+        parsedArgs = argParser.parse_args(sys.argv[1:])
+        FetchGitProject(
+            gitUrl = parsedArgs.git_url,
+            projectName = parsedArgs.name,
+            destinationDir = parsedArgs.destination_dir,
+            gitTag = parsedArgs.git_tag,
+            gitCommit = parsedArgs.git_commit)
+    except Exception as err:
+        print(err)
+        sys.exit(1)

+ 765 - 0
src/AzslcBackend.cpp

@@ -0,0 +1,765 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include "AzslcBackend.h"
+#include "AzslcPlatformEmitter.h"
+
+#include <tuple>
+#include <cmath>
+
+namespace AZ::ShaderCompiler
+{
+    bool IsReadWriteView(string_view viewName)
+    {
+        return (StartsWith(viewName, "RW") || StartsWith(viewName, "RasterizerOrdered") ||
+                StartsWith(viewName, "Append") || StartsWith(viewName, "Consume"));
+    }
+
+    QualifiedName MakeSrgConstantsStructName(IdentifierUID srg)
+    {
+        return QualifiedName{srg.m_name + "_SRGConstantsStruct"};
+    }
+
+    QualifiedName MakeSrgConstantsCBName(IdentifierUID srg)
+    {
+        return QualifiedName{srg.m_name + "_SRGConstantBuffer"};
+    }
+
+    string UnmangleTrimedName(const QualifiedNameView name)
+    {
+        return Replace(string{Trim(name.data(), "/")}, "/", "::");
+    }
+
+    string JoinAllNestedNamesWithUnderscore(const QualifiedNameView name)
+    {
+        return Replace(UnmangleTrimedName(name), "::", "_");
+    }
+
+    string GetGlobalRootConstantVarName(const QualifiedNameView name)
+    {
+        return "_g_" + Replace(UnmangleTrimedName(name), "::", Underscore);
+    }
+
+    string GetShaderKeyFunctionName(const IdentifierUID& uid)
+    {
+        return "GetShaderVariantKey_" + JoinAllNestedNamesWithUnderscore(uid.m_name) + "()";
+    }
+
+    string GetRootConstFunctionName(const IdentifierUID& uid)
+    {
+        return "GetShaderRootConst_" + JoinAllNestedNamesWithUnderscore(uid.m_name) + "()";
+    }
+
+    // this doesn't go through translation because now it's not as risky as before considering the name is clear
+    string UnmangleTrimedName(const ExtendedTypeInfo& extTypeInfo)
+    {
+        auto fullName = UnmangleTrimedName(extTypeInfo.m_coreType.m_typeId.m_name);
+        if (HasGenericParameter(extTypeInfo.m_coreType.m_typeClass))
+        {
+            fullName += "<" + UnmangleTrimedName(extTypeInfo.m_genericParameter.m_typeId.m_name) + ">";
+        }
+        return fullName;
+    }
+
+    // Define emission for sampler states
+    // Reference: https://github.com/Microsoft/DirectXShaderCompiler/blob/master/tools/clang/unittests/HLSL/FunctionTest.cpp
+
+    std::ostream &operator << (std::ostream &out, const SamplerStateDesc::AddressMode& addressMode)
+    {
+        return out << ((addressMode == SamplerStateDesc::AddressMode::Wrap)   ? "TEXTURE_ADDRESS_WRAP"
+                     : (addressMode == SamplerStateDesc::AddressMode::Clamp)  ? "TEXTURE_ADDRESS_CLAMP"
+                     : (addressMode == SamplerStateDesc::AddressMode::Border) ? "TEXTURE_ADDRESS_BORDER"
+                     : (addressMode == SamplerStateDesc::AddressMode::Mirror) ? "TEXTURE_ADDRESS_MIRROR"
+                     :                                                          "TEXTURE_ADDRESS_MIRROR_ONCE");
+    }
+
+    std::ostream &operator << (std::ostream &out, const SamplerStateDesc::ComparisonFunc& compFunc)
+    {
+        return out << ((compFunc == SamplerStateDesc::ComparisonFunc::Never)        ? "COMPARISON_NEVER"
+                     : (compFunc == SamplerStateDesc::ComparisonFunc::Less)         ? "COMPARISON_LESS"
+                     : (compFunc == SamplerStateDesc::ComparisonFunc::Equal)        ? "COMPARISON_EQUAL"
+                     : (compFunc == SamplerStateDesc::ComparisonFunc::LessEqual)    ? "COMPARISON_LESS_EQUAL"
+                     : (compFunc == SamplerStateDesc::ComparisonFunc::Greater)      ? "COMPARISON_GREATER"
+                     : (compFunc == SamplerStateDesc::ComparisonFunc::NotEqual)     ? "COMPARISON_NOT_EQUAL"
+                     : (compFunc == SamplerStateDesc::ComparisonFunc::GreaterEqual) ? "COMPARISON_GREATER_EQUAL"
+                     :                                                                "COMPARISON_ALWAYS");
+    }
+
+    std::ostream &operator << (std::ostream &out, const SamplerStateDesc::BorderColor& borderColor)
+    {
+        return out << ((borderColor == SamplerStateDesc::BorderColor::OpaqueBlack) ? "STATIC_BORDER_COLOR_OPAQUE_BLACK"
+                     : (borderColor == SamplerStateDesc::BorderColor::OpaqueWhite) ? "STATIC_BORDER_COLOR_OPAQUE_WHITE"
+                     :                                                               "STATIC_BORDER_COLOR_TRANSPARENT_BLACK");
+    }
+
+    std::ostream &operator << (std::ostream &out, const SamplerStateDesc& samplerDesc)
+    {
+        // Resolving the filter is the hardest part of the emission
+        out << ", filter = FILTER_";
+
+        out << ((samplerDesc.m_reductionType == SamplerStateDesc::ReductionType::Comparison) ? "COMPARISON_"
+              : (samplerDesc.m_reductionType == SamplerStateDesc::ReductionType::Maximum)    ? "MAXIMUM_"
+              : (samplerDesc.m_reductionType == SamplerStateDesc::ReductionType::Minimum)    ? "MINIMUM_"
+              :                                                                                "");
+
+        // The correct order is MIN -> MAG -> MIP
+        // Reference: https://github.com/Microsoft/DirectXShaderCompiler/blob/master/tools/clang/unittests/HLSL/FunctionTest.cpp
+        out << "MIN_"
+            << (samplerDesc.m_filterMin == samplerDesc.m_filterMag ? "MAG_" : "")
+            << (samplerDesc.m_filterMin == samplerDesc.m_filterMag && samplerDesc.m_filterMag == samplerDesc.m_filterMip ? "MIP_" : "")
+            << (samplerDesc.m_filterMin == SamplerStateDesc::FilterMode::Point ? "POINT" : "LINEAR");
+
+        if (samplerDesc.m_filterMin != samplerDesc.m_filterMag)
+        {
+            out << "_MAG_"
+                << (samplerDesc.m_filterMag == samplerDesc.m_filterMip ? "MIP_" : "")
+                << (samplerDesc.m_filterMag == SamplerStateDesc::FilterMode::Point ? "POINT" : "LINEAR");
+        }
+
+        if (samplerDesc.m_filterMag != samplerDesc.m_filterMip)
+        {
+            out << "_MIP_"
+                << (samplerDesc.m_filterMip == SamplerStateDesc::FilterMode::Point ? "POINT" : "LINEAR");
+        }
+        // ^ The filter should be resolved
+
+        return out << ", addressU = " << samplerDesc.m_addressU
+                   << ", addressV = " << samplerDesc.m_addressV
+                   << ", addressW = " << samplerDesc.m_addressW
+                   << ", minLOD = " << samplerDesc.m_mipLodMin
+                   << ", maxLOD = " << samplerDesc.m_mipLodMax
+                   << ", mipLODBias = " << samplerDesc.m_mipLodBias
+                   << ", BorderColor = " << samplerDesc.m_borderColor
+                   << ", maxAnisotropy = " << samplerDesc.m_anisotropyMax
+                   << ", ComparisonFunc  = " << samplerDesc.m_comparisonFunc;
+    }
+
+    // map root param type to register type
+    BindingType RootParamTypeToBindingType(RootParamType paramType)
+    {
+        // the 2 enum orders are arranged the same for this to work
+        return BindingType::EnumType( paramType < BindingType::EndEnumeratorSentinel_ ? int(paramType) : BindingType::B );
+    }
+
+    RootParamType FindParamType(const ExtendedTypeInfo& typeInfoExt)
+    {
+        TypeClass coreTypeClass = typeInfoExt.m_coreType.m_typeClass;
+        if (coreTypeClass == TypeClass::Sampler)
+        {
+            return RootParamType::Sampler;
+        }
+        if (coreTypeClass == TypeClass::ConstantBuffer)
+        {
+            return RootParamType::CBV;
+        }
+        if (IsViewType(coreTypeClass))
+        {
+            return IsReadWriteView(typeInfoExt.m_coreType.m_typeId.GetNameLeaf()) ? RootParamType::UAV : RootParamType::SRV;
+        }
+        return RootParamType::EndEnumeratorSentinel_;
+    }
+
+    BindingType GetBindingType(const ExtendedTypeInfo& extendedTypeInfo)
+    {
+        RootParamType paramType = FindParamType(extendedTypeInfo);
+        BindingType bindingType = RootParamTypeToBindingType(paramType);
+        return bindingType;
+    }
+
+    void SingleBindingLocationTracker::IncrementSpace()
+    {
+        for (auto bufferType : BindingType::Enumerate{})
+        {
+            m_accumulated[bufferType] += m_registerPos[bufferType];
+            m_registerPos[bufferType] = 0;
+        }
+        ++m_space;
+    }
+
+    // Equalizes all register indices (important for platforms with no B,T,S,U distinction)
+    void SingleBindingLocationTracker::UnifyIndices()
+    {
+        using Reg = BindingType;
+        auto maxValue = std::max(
+            std::max(m_registerPos[Reg::B], m_registerPos[Reg::T]),
+            std::max(m_registerPos[Reg::S], m_registerPos[Reg::U]));
+
+        for (auto bufferType : Reg::Enumerate{})
+        {
+            m_accumulatedUnused[bufferType] += maxValue - m_registerPos[bufferType];
+            m_registerPos[bufferType] = maxValue;
+        }
+    }
+
+    int SingleBindingLocationTracker::GetAccumulated(BindingType r) const
+    {
+        return (m_accumulated[r] - m_accumulatedUnused[r]) + m_registerPos[r];
+    }
+
+    void MultiBindingLocationMaker::SignalIncrementSpace(std::function<void(int, int)> warningMessageFunctionForMinDescOvershoot)
+    {
+        if (m_options.m_useLogicalSpaces)
+        {
+            if (m_options.m_minAvailableDescriptors.m_spaces >= 0
+                && m_untainted.m_space >= m_options.m_minAvailableDescriptors.m_spaces)
+            {
+                warningMessageFunctionForMinDescOvershoot(m_untainted.m_space, m_options.m_minAvailableDescriptors.m_spaces);
+            }
+
+            m_untainted.IncrementSpace();
+            if (m_options.m_maxSpaces > m_merged.m_space + 1) // example: (1 > 0 + 1)  <=>  (1 > 1)  <=>  (false)  --> never increment space. stay at 0. which respects max-spaces=1
+            {
+                m_merged.IncrementSpace();
+            }
+        }
+    }
+
+    void MultiBindingLocationMaker::SignalUnifyIndices()
+    {
+        if (m_options.m_useUniqueIndices)
+        {
+            m_untainted.UnifyIndices();
+            m_merged.UnifyIndices();
+        }
+    }
+
+    void MultiBindingLocationMaker::SignalRegisterIncrement(BindingType regType, int count /* = 1*/)
+    {
+        m_untainted.m_registerPos[regType] += count;
+        m_merged.m_registerPos[regType] += count;
+    }
+
+    BindingPair MultiBindingLocationMaker::GetCurrent(BindingType regType)
+    {
+        BindingPair pair;
+        pair.m_pair[BindingPair::Set::Merged   ].m_logicalSpace = m_merged.m_space;
+        pair.m_pair[BindingPair::Set::Untainted].m_logicalSpace = m_untainted.m_space;
+        pair.m_pair[BindingPair::Set::Merged   ].m_registerIndex = m_merged.m_registerPos[regType];
+        pair.m_pair[BindingPair::Set::Untainted].m_registerIndex = m_untainted.m_registerPos[regType];
+        return pair;
+    }
+
+    std::ostream &operator << (std::ostream &out, const SamplerStateDesc::ReductionType& redcType)
+    {
+        return out << ((redcType == SamplerStateDesc::ReductionType::Comparison)  ? "Comparison"
+                     : (redcType == SamplerStateDesc::ReductionType::Filter)      ? "Filter"
+                     : (redcType == SamplerStateDesc::ReductionType::Minimum)     ? "Minimum"
+                     :                                                              "Maximum");
+    }
+
+    const PlatformEmitter& Backend::GetPlatformEmitter() const
+    {
+        for (const auto& attr : m_ir->m_metaData.m_attributeNamespaceFilters)
+        {
+            // We can have multiple attribute scopes enabled
+            // By design only one can have associated platform emitter, so return the first match
+            const auto p = PlatformEmitter::GetEmitter(attr);
+            if (p)
+            {
+                return *p;
+            }
+        }
+
+        return *PlatformEmitter::GetDefaultEmitter();
+    }
+
+    //! Gets the next and increments tokenIndex. TokenIndex must be in the [misc::Interval.a, misc::Interval.b] range. Token cannot be nullptr.
+    antlr4::Token* Backend::GetNextToken(ssize_t& tokenIndex, size_t channel) const
+    {
+        auto* token = m_tokens->get(tokenIndex++);
+        assert(token); // Higher level logic must ensure tokens are in the [interval.a, interval.b] range. They cannot be null, this is a flaw in the code.
+
+        while (token->getChannel() != channel)
+        {
+            token = m_tokens->get(tokenIndex++);
+            assert(token); // same
+        }
+
+        return token;
+    }
+
+    void Backend::GetTextInStream(misc::Interval interval, std::ostream& output) const
+    {
+        ssize_t ii = interval.a;
+        while (ii <= interval.b)
+        {
+            auto* token = GetNextToken(ii /*inout*/);
+            auto str = token->getText();
+            output << str << ((str == ";" || str == "{") ? '\n' : ' ');
+        }
+    }
+
+    string Backend::GetTextAsString(misc::Interval interval) const
+    {
+        static std::stringstream ss;
+        ss.str({});
+        ss.clear();
+        GetTextInStream(interval, ss);
+        return ss.str();
+    }
+
+    string Backend::GetInitializerClause(const VarInfo* varInfo) const
+    {
+        if (!varInfo->m_declNode ||
+            !varInfo->m_declNode->variableInitializer() ||
+            !varInfo->m_declNode->variableInitializer()->standardVariableInitializer())
+        {
+            return "";
+        }
+
+        auto* initClause = varInfo->m_declNode->variableInitializer()->standardVariableInitializer();
+        return RemoveWhitespaces(GetTextAsString(initClause->getSourceInterval()));
+    }
+
+    void Backend::AppendOptionRange(Json::Value& varOption, const IdentifierUID& varUid, const VarInfo* varInfo, const Options& options) const
+    {
+        Json::Value optValues(Json::arrayValue);
+        bool isRange = false;
+        uint32_t numberOfOptions = 0;
+
+        auto& [uid, info] = *m_ir->GetIdAndKindInfo(varInfo->GetTypeId().GetName());
+
+        if (info.IsKindOneOf(Kind::Enum))
+        {
+            auto isScoped = get<EnumerationInfo>(info.GetSubRefAs<ClassInfo>().m_subInfo).m_isScoped;
+            auto prefix = isScoped ? UnmangleTrimedName(uid.m_name) + "::" : "";
+
+            auto& list = info.GetSubRefAs<ClassInfo>().GetMemberFields();
+            std::for_each(list.begin(), list.end(),
+                          [&optValues, &prefix](const IdentifierUID& member) { optValues.append(prefix + member.GetNameLeaf()); });
+
+            numberOfOptions = optValues.size();
+        }
+        else if (info.GetKind() == Kind::Type)
+        {
+            auto typeRef = info.GetSubRefAs<TypeRefInfo>();
+
+            if (typeRef.m_typeId.m_name.find("bool") != string::npos)
+            {
+                optValues.append("false");
+                optValues.append("true");
+                numberOfOptions = 2;
+            }
+            else if (typeRef.m_typeId.m_name.find("int") != string::npos)
+            {
+                // Not adding anything to optValues means we have no valid options
+                // This is intentional, but it means the variable won't be exported as an option
+                isRange = true;
+                numberOfOptions = 0;
+
+                // Integers should support a range of possible values, for example [0 .. 63] (or any other range)
+                // Range should be added as an attribute specifier. In the above example it will be [[range(0, 63]] 
+                auto rangeAttribute = m_ir->m_symbols.GetAttribute(varUid, "range");
+                if (!rangeAttribute)
+                {
+                    throw AzslcEmitterException(EMITTER_INTEGER_HAS_NO_RANGE,
+                                                none, none, ConcatString("Option (", varUid.m_name, ") must specify a range with a minimum and maximum values"));
+                }
+
+                if (rangeAttribute->m_argList.size() != 2)
+                {
+                    throw AzslcEmitterException(EMITTER_INTEGER_RANGE_NEEDS_ATTRIBUTE,
+                                                none, none, ConcatString("Option (", varUid.m_name, ") must specify a range with exactly 2 values, min & max - [range(min, max)]"));
+                }
+
+                const auto rangeMin = rangeAttribute->m_argList[0];
+                if (!holds_alternative<ConstNumericVal>(rangeMin))
+                {
+                    throw AzslcEmitterException(EMITTER_INTEGER_RANGE_MIN_IS_NOT_CONST,
+                                                none, none, ConcatString("Option (", varUid.m_name, ") must specify a numeric const for its range's minimum!"));
+                }
+
+                const auto rangeMax = rangeAttribute->m_argList[1];
+                if (!holds_alternative<ConstNumericVal>(rangeMax))
+                {
+                    throw AzslcEmitterException(EMITTER_INTEGER_RANGE_MAX_IS_NOT_CONST,
+                                                none, none, ConcatString("Option (", varUid.m_name, ") must specify a numeric const for its range's maximum!"));
+                }
+
+                const auto rangeMinValue = ExtractValueAsInt64(get<ConstNumericVal>(rangeMin));
+                const auto rangeMaxValue = ExtractValueAsInt64(get<ConstNumericVal>(rangeMax));
+                const auto rangeCount = (rangeMaxValue - rangeMinValue + 1);
+
+                if (rangeMinValue > rangeMaxValue)
+                {
+                    throw AzslcEmitterException(EMITTER_INTEGER_RANGE_MIN_IS_BIGGER_THAN_MAX,
+                                                none, none, ConcatString("Option (", varUid.m_name, ") cannot specify a minimum for its range that is greater than its maximum!"));
+                }
+
+                if (rangeCount > kIntegerMaxShaderVariantValues)
+                {
+                    PrintWarning(Warn::W1, none, "Option (", varUid.m_name, ") must specify a range of values smaller than ", kIntegerMaxShaderVariantValues);
+                }
+
+                optValues.append(std::to_string(rangeMinValue));
+                optValues.append(std::to_string(rangeMaxValue));
+                numberOfOptions = static_cast<uint32_t>(rangeMaxValue - rangeMinValue + 1);
+            }
+            else
+            {
+                // There is no immediate plan to support floats or more complex structures
+                throw AzslcEmitterException(EMITTER_OPTION_HAS_UNSUPPORTED_TYPE,
+                                            none, none, ConcatString("Option (", varUid.m_name, ") must be of type bool, int, or enum"));
+            }
+        }
+
+        if (numberOfOptions > 0)
+        {
+            uint32_t keySizeInBits = 1;
+            uint32_t maxNumberOfOptions = 2;
+            while (numberOfOptions > maxNumberOfOptions)
+            {
+                maxNumberOfOptions *= 2;
+                keySizeInBits++;
+            }
+            varOption["keySize"] = keySizeInBits;
+        }
+
+        varOption["values"]  = optValues;
+        varOption["range"]   = isRange;
+    }
+
+
+    Json::Value Backend::GetVariantList(const Options& options, bool includeEmpty) const
+    {
+        Json::Value varRoot(Json::objectValue);
+        varRoot["meta"] = "Variant options list exported by AZSLc";
+
+        Json::Value shaderOptions(Json::arrayValue);
+        uint32_t keyOffsetBits = 0;
+
+        uint32_t optionOrder = 0;
+        for (auto& [uid, varInfo] : m_ir->m_symbols.GetOrderedSymbolsOfSubType_2<VarInfo>())
+        {
+            // skip non-option variables
+            if (!varInfo->CheckHasStorageFlag(StorageFlag::Option))
+            {
+                continue;
+            }
+
+            Json::Value shaderOption(Json::objectValue);
+            shaderOption["name"] = UnmangleTrimedName(uid.m_name);
+            shaderOption["type"] = UnmangleTrimedName(varInfo->m_typeInfoExt);
+            shaderOption["defaultValue"] = GetInitializerClause(varInfo);
+
+            // The order (or rank) of the option matches the order of declaration
+            // We reserve the right to change it in the future so we make it explicit attribute here
+            shaderOption["order"] = optionOrder;
+            optionOrder++;
+
+            bool isUdt = IsUserDefined(varInfo->GetTypeClass());
+            assert(isUdt || IsPredefinedType(varInfo->GetTypeClass()));
+            shaderOption["kind"] = isUdt ? "user-defined" : "predefined";
+
+            AppendOptionRange(shaderOption, uid, varInfo, options);
+
+            if (includeEmpty || (shaderOption["values"].isArray() && shaderOption["values"].size() > 0))
+            {   // We only emit variant options which have positive number of valid values
+                // Because we use uint on the shader source side no shader option can cross the 32-bit boundary
+                uint32_t keySizeInBits = shaderOption["keySize"].asUInt();
+
+                if (keySizeInBits > kShaderVariantKeyElementSize)
+                {
+                    const string errorMessage = ConcatString("Shader option {", UnmangleTrimedName(uid.m_name), "} uses a bitmask which crosses the ",
+                                                             kShaderVariantKeyElementSize, "-bit boundary!");
+                    throw AzslcEmitterException(EMITTER_OVERFLOW_BIT_BOUNDARY, errorMessage);
+                }
+
+                uint32_t remainingBitsAfterOffset = kShaderVariantKeyElementSize - (keyOffsetBits % kShaderVariantKeyElementSize);
+                if (keySizeInBits > remainingBitsAfterOffset)
+                {
+                    keyOffsetBits += remainingBitsAfterOffset;
+                }
+
+                shaderOption["keyOffset"] = keyOffsetBits;
+                keyOffsetBits += keySizeInBits;
+
+                shaderOptions.append(shaderOption);
+            }
+        }
+
+        varRoot["ShaderOptions"] = shaderOptions;
+
+        return varRoot;
+    }
+
+    // little check utility
+    static void CheckHasOneFoldedDimensionOrThrow(const ArrayDimensions& dims, string_view callSite)
+    {
+        if (!dims.AreAllDimsFullyConstantFolded())
+        {
+            throw AzslcEmitterException(EMITTER_INVALID_ARRAY_DIMENSIONS,
+                                        ConcatString(callSite, " Invalid array dimensions (more than 1 or non constant size)"));
+        }
+    }
+
+    static void PrintWarningsRelatedToSpecOvershoots(MultiBindingLocationMaker& bindInfo, const Options& options, const SRGInfo* srgInfo, const IdentifierUID& srgUid)
+    {
+        int numSamplerUsed = bindInfo.m_untainted.GetAccumulated(BindingType::S);
+        if (options.m_minAvailableDescriptors.m_samplers >= 0
+            && numSamplerUsed > options.m_minAvailableDescriptors.m_samplers)
+        {
+            PrintWarning(Warn::W1, srgInfo->m_declNode->start,
+                         "SRG ", srgUid.m_name,
+                         " bumped samplers to ", numSamplerUsed, " which overshoots the minimum supported sampler count guaranteed by the specification (from --min-descriptors argument currently set to ",
+                         options.m_minAvailableDescriptors.m_samplers, ")");
+        }
+
+        int numTextureUsed = bindInfo.m_untainted.GetAccumulated(BindingType::T);
+        if (options.m_minAvailableDescriptors.m_textures >= 0
+            && numTextureUsed > options.m_minAvailableDescriptors.m_textures)
+        {
+            PrintWarning(Warn::W1, srgInfo->m_declNode->start,
+                         "SRG ", srgUid.m_name,
+                         " bumped textures to ", numTextureUsed, " which overshoots the minimum supported texture count guaranteed by the specification (from --min-descriptors argument currently set to ",
+                         options.m_minAvailableDescriptors.m_textures, ")");
+        }
+
+        int numViewUsed = bindInfo.m_untainted.GetAccumulated(BindingType::B) + bindInfo.m_untainted.GetAccumulated(BindingType::U);
+        if (options.m_minAvailableDescriptors.m_buffers >= 0
+            && numViewUsed > options.m_minAvailableDescriptors.m_buffers)
+        {
+            PrintWarning(Warn::W1, srgInfo->m_declNode->start,
+                         "SRG ", srgUid.m_name,
+                         " bumped data views to ", numViewUsed, " which overshoots the minimum supported data views count guaranteed by the specification (from --min-descriptors argument currently set to ",
+                         options.m_minAvailableDescriptors.m_buffers, ")");
+        }
+
+        int totalUsed = numSamplerUsed + numTextureUsed + numViewUsed;
+        if (options.m_minAvailableDescriptors.m_descriptorsTotal >= 0
+            && totalUsed > options.m_minAvailableDescriptors.m_descriptorsTotal)
+        {
+            PrintWarning(Warn::W1, srgInfo->m_declNode->start,
+                         "SRG ", srgUid.m_name,
+                         " bumped descriptors to ", totalUsed, " which overshoots the minimum supported descriptor count guaranteed by the specification (from --min-descriptors argument currently set to ",
+                         options.m_minAvailableDescriptors.m_descriptorsTotal, ")");
+        }
+    }
+
+    RootSigDesc::SrgParamDesc Backend::ReflectOneExternalResource(IdentifierUID id, MultiBindingLocationMaker& bindInfo, RootSigDesc& rootSig) const
+    {
+        Kind kind = m_ir->GetKind(id);
+        int count = 1;
+        RootParamType paramType = RootParamType::SrgConstantCB;  // this is the default because when "kind" is not "Variable", this function is used on symbols of "kind" "SRG"
+        bool isUnboundedArray = false;
+        if (kind == Kind::Variable)
+        {
+            const auto* memberInfo = m_ir->GetSymbolSubAs<VarInfo>(id.m_name);
+
+            // With this variable we track the need to validate improperly
+            // defined array sizes.
+            bool shouldCheckForValidArraySize = true;
+            isUnboundedArray = memberInfo->GetArrayDimensions().IsUnbounded();
+            if (isUnboundedArray)
+            {
+                TypeClass typeClass = memberInfo->GetTypeClass();
+                if ( CanBeDeclaredAsUnboundedArray(typeClass) )
+                {
+                    shouldCheckForValidArraySize = false;
+                }
+            }
+
+            if (shouldCheckForValidArraySize)
+            {
+                CheckHasOneFoldedDimensionOrThrow(memberInfo->GetArrayDimensions(), "CodeEmitter::BuildSignatureDescription");
+            }
+            count = isUnboundedArray ? 1 : memberInfo->GetArrayDimensions().GetDimensionAt_OrDefault(0, 1);
+            paramType = FindParamType(memberInfo->m_typeInfoExt);
+        }
+
+        auto regType = RootParamTypeToBindingType(paramType);
+        auto srgElementDesc = RootSigDesc::SrgParamDesc{ id, paramType, bindInfo.GetCurrent(regType), count, -1, isUnboundedArray};
+        rootSig.m_descriptorMap.emplace(id, srgElementDesc);
+        bindInfo.SignalRegisterIncrement(regType, count);
+        return srgElementDesc;
+    }
+
+    RootSigDesc::SrgParamDesc Backend::ReflectOneExternalResourceAndWrapWithUnifyIndices(IdentifierUID id, MultiBindingLocationMaker& bindInfo, RootSigDesc& rootSig) const
+    {
+        auto paramDesc = ReflectOneExternalResource(id, bindInfo, rootSig);
+        bindInfo.SignalUnifyIndices();
+        return paramDesc;
+    }
+
+    RootSigDesc Backend::BuildSignatureDescription(const Options& options, int num32BitConst) const
+    {
+        MultiBindingLocationMaker bindInfo{ options };
+        RootSigDesc rootSig;
+
+        auto allSrgs = m_ir->m_symbols.GetOrderedSymbolsOfSubType_2<SRGInfo>();
+        using Id_SrgInfo_Pair = decltype(allSrgs)::value_type;
+        // let's order them by frequency:
+        auto orderingFunction = [this](const Id_SrgInfo_Pair& srgSymbol1, const Id_SrgInfo_Pair& srgSymbol2)
+            {
+                auto srgSemantic1 = m_ir->GetSymbolSubAs<ClassInfo>(srgSymbol1.second->m_semantic->GetName())->Get<SRGSemanticInfo>();
+                auto srgSemantic2 = m_ir->GetSymbolSubAs<ClassInfo>(srgSymbol2.second->m_semantic->GetName())->Get<SRGSemanticInfo>();
+                return *srgSemantic1->m_frequencyId < *srgSemantic2->m_frequencyId;
+            };
+        std::sort(allSrgs.begin(), allSrgs.end(), orderingFunction);
+
+        const bool useUniqueIndices = options.m_useUniqueIndices;
+
+        // loop on SRGs since they're the containers of signature-inducing symbols
+        for (auto& [srgUid, srgInfo] : allSrgs)
+        {
+            RootSigDesc::SrgDesc srgDesc;
+            srgDesc.m_uid = srgUid;
+
+            for (const auto tId : srgInfo->m_srViews)
+            {
+                if (useUniqueIndices && !srgInfo->m_unboundedArrays.empty() && srgInfo->m_unboundedArrays[0] == tId)
+                {
+                    // This variable will be added to the end of the binding table.
+                    // See: "REMARK: --unique-idx" a few lines below.
+                    continue;
+                }
+                srgDesc.m_parameters.push_back(
+                    ReflectOneExternalResourceAndWrapWithUnifyIndices(tId, bindInfo, rootSig) );
+            }
+            for (const auto sId : srgInfo->m_samplers)
+            {
+                if (useUniqueIndices && !srgInfo->m_unboundedArrays.empty() && srgInfo->m_unboundedArrays[0] == sId)
+                {
+                    // This variable will be added to the end of the binding table.
+                    // See: "REMARK: --unique-idx" a few lines below.
+                    continue;
+                }
+                srgDesc.m_parameters.push_back(
+                    ReflectOneExternalResourceAndWrapWithUnifyIndices(sId, bindInfo, rootSig) );
+            }
+
+            bool hasSrgConstants = !srgInfo->m_implicitStruct.GetMemberFields().empty();
+            bool hasConstantBuffers = !srgInfo->m_CBs.empty();
+            if (hasSrgConstants || (hasConstantBuffers && options.m_emitConstantBufferBody))
+            {
+                srgDesc.m_parameters.push_back(
+                    ReflectOneExternalResourceAndWrapWithUnifyIndices(srgUid, bindInfo, rootSig));
+            }
+            if (!options.m_emitConstantBufferBody)  // emitCB is the SM5- "cbufer{}" block syntax. !emitCB is the "ConstantBuffer<>" SM5.1+ syntax
+            {
+                for (const auto cId : srgInfo->m_CBs)
+                {
+                    srgDesc.m_parameters.push_back(
+                        ReflectOneExternalResourceAndWrapWithUnifyIndices(cId, bindInfo, rootSig));
+                }
+            }
+
+            // REMARK: --unique-idx
+            // When using --unique-idx, it is imperative to reflect the unbounded array AFTER
+            // The SRG constant buffer and any constant buffer owned by the SRG because
+            // they are sharing the same register index range. Because an unbounded array
+            // takes ownership of the remaining register range within a register space, it always
+            // must be added after that last resource in the register space.
+            if (useUniqueIndices && !srgInfo->m_unboundedArrays.empty())
+            {
+                // Only srgInfo->m_unboundedArrays[0] is reflected because the "AzslcSemanticOrchestrator" already
+                // makes sure that only one unbounded array is declared inside the SRG when --unique-idx is enabled.
+                srgDesc.m_parameters.push_back(
+                    ReflectOneExternalResourceAndWrapWithUnifyIndices(srgInfo->m_unboundedArrays[0], bindInfo, rootSig));
+            }
+
+            bindInfo.SignalIncrementSpace(/*overshoot callback:*/[&, srgInfo = srgInfo, srgUid = srgUid](int numSpaces, int spacesAvailable)
+                {
+                    PrintWarning(Warn::W1, srgInfo->m_declNode->start,
+                                 "SRG ", srgUid.m_name, " on space ", numSpaces,
+                                 " overshoots the minimum supported logical register space count guaranteed by the specification (from --min-descriptors argument currently set to ",
+                                 spacesAvailable, ")");
+                });
+
+            bindInfo.SignalUnifyIndices();
+
+            // validate binding counts if options are activated for it
+            PrintWarningsRelatedToSpecOvershoots(bindInfo, options, srgInfo, srgUid);
+
+            rootSig.m_srGroups.push_back(srgDesc);
+        } // end for all SRGs
+
+        // and separately (not within SRGs), the root constants:
+        if (options.m_rootConstantsMaxSize > 0)
+        {
+            RootSigDesc::SrgDesc rootConstDesc;
+            rootConstDesc.m_uid = m_ir->m_rootConstantStructUID;
+
+            auto desc = RootSigDesc::SrgParamDesc{ rootConstDesc.m_uid, RootParamType::RootConstantCB,
+                bindInfo.GetCurrent(BindingType::B), 1, num32BitConst };
+
+            rootConstDesc.m_parameters.push_back(desc);
+            rootSig.m_descriptorMap.emplace(rootConstDesc.m_uid, desc);
+            rootSig.m_srGroups.push_back(rootConstDesc);
+        }
+
+        return rootSig;
+    }
+
+    const char* Backend::GetInputModifier(TypeQualifier typeQualifier)
+    {
+        const bool in = TypeHasStorageFlag(typeQualifier, StorageFlag::In);
+        const bool out = TypeHasStorageFlag(typeQualifier, StorageFlag::Out);
+        const bool inout = (in && out) || TypeHasStorageFlag(typeQualifier, StorageFlag::InOut);
+        return inout ? "inout"
+                     : (in ? "in"
+                           : (out ? "out" : ""));
+    }
+
+    string Backend::GetExtendedTypeInfo(const ExtendedTypeInfo& extTypeInfo, std::function<string(const TypeRefInfo&)> translator) const
+    {
+        string hlslString = "";
+
+        if (extTypeInfo.m_coreType.m_typeClass == TypeClass::Alias)
+        {
+            hlslString = GetExtendedTypeInfo(m_ir->GetSymbolSubAs<TypeAliasInfo>(extTypeInfo.m_coreType.m_typeId.GetName())->m_canonicalType, translator);
+        }
+        else if (HasGenericParameter(extTypeInfo.m_coreType.m_typeClass) || !extTypeInfo.m_genericParameter.IsEmpty())
+        {
+            hlslString = translator(extTypeInfo.m_coreType)
+                + "<" + translator(extTypeInfo.m_genericParameter);
+            if (extTypeInfo.m_genericDims.IsArray())
+            {
+                hlslString += extTypeInfo.m_genericDims.ToString(", ", ", ", "");
+            }
+            hlslString += ">";
+        }
+        else
+        {
+            hlslString = translator(extTypeInfo.m_coreType);
+        }
+
+        return hlslString;
+    }
+
+    uint32_t Backend::GetNumberOf32BitConstants(const Options& options, const IdentifierUID& uid) const
+    {
+        // Count the number of 32 bin constants
+        uint32_t numberOf32bitRootConstants = 0;
+        auto* rootConstInfo = m_ir->GetSymbolSubAs<ClassInfo>(uid.GetName());
+        if (options.m_rootConstantsMaxSize && rootConstInfo)
+        {
+            for (const auto& memberVar : rootConstInfo->GetMemberFields())
+            {
+                const auto& varInfo = *m_ir->GetSymbolSubAs<VarInfo>(memberVar.m_name);
+                assert(!IsChameleon(varInfo.GetTypeClass()));
+                auto exportedType = varInfo.m_typeInfoExt.m_coreType;
+
+                if (!exportedType.IsPackable())
+                {
+                    throw std::logic_error{ " internal error: unpackable type ("
+                        + exportedType.m_typeId.m_name
+                        + ") found its way in layout member "
+                        + memberVar.m_name };
+                }
+
+                // GetTotalSize of each member of the structure
+                uint32_t size = varInfo.m_typeInfoExt.GetTotalSize(options.m_packDataBuffers, options.m_emitRowMajor);
+
+                numberOf32bitRootConstants += (size / 4);
+            }
+            return numberOf32bitRootConstants;
+        }
+        return 0;
+    }
+}

+ 204 - 0
src/AzslcBackend.h

@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include "AzslcListener.h"
+
+#include "jsoncpp/dist/json/json.h"
+
+namespace AZ::ShaderCompiler
+{
+    struct PlatformEmitter;
+
+    struct DescriptorCountBounds
+    {
+        int m_descriptorsTotal = -1;   //!< negative values deactivate the check
+        int m_spaces = -1;
+        int m_samplers = -1;
+        int m_textures = -1;
+        int m_buffers = -1;
+    };
+
+    //! This structure is typically filled from parsed user settings from the command line
+    struct Options
+    {
+        bool m_useLogicalSpaces = false;
+        bool m_useUniqueIndices = false;
+        bool m_emitConstantBufferBody = false;
+        bool m_emitRootSig = false;
+        bool m_emitRowMajor = false;     //!< False by default (HLSL standard)
+        bool m_forceEmitMajor = false;   //!< True if either -Zpc or -Zpr was specified
+        bool m_padRootConstantCB = false; //!< If True, the emitted root constant CB will padded to 16-byte boundary.
+        DescriptorCountBounds m_minAvailableDescriptors;   //!< Hint about the targeted graphics API's minimal guaranteed usable descriptors
+        int m_maxSpaces = std::numeric_limits<int>::max();   //!< Maximum allocatable register logical space, after which register indexes will accumulate, but spaces will be capped
+        int m_rootConstantsMaxSize = std::numeric_limits<int>::max();   //!< Indicates the number of root constants to be allowed, 0 means root constants not enabled
+        Packing::Layout m_packConstantBuffers  = Packing::Layout::DirectXPacking; //!< Packing standard for constant buffers (uniform)
+        Packing::Layout m_packDataBuffers      = Packing::Layout::CStylePacking;  //!< Packing standard for data buffer views
+    };
+
+    struct Binding
+    {
+        int m_registerIndex = -1;
+        int m_logicalSpace = -1;
+    };
+
+    //! store 2 sets of binding points: one calculated purely (untainted), and one calculated with space compression (SRG-Merging)
+    struct BindingPair
+    {
+        MAKE_REFLECTABLE_ENUM(Set, Untainted, Merged);
+        std::array<Binding, Set::EndEnumeratorSentinel_> m_pair;
+    };
+
+    struct RootSigDesc
+    {
+        struct SrgParamDesc
+        {
+            IdentifierUID m_uid;
+            RootParamType m_type;
+            BindingPair m_registerBinding;
+            int m_registerRange = -1;
+            int m_num32BitConstants = -1;
+            // This flag is added so m_registerRange can take the value
+            // of 1 and at the same time We do not forget that m_uid refers
+            // to an unbounded array.
+            bool m_isUnboundedArray = false;
+        };
+
+        struct SrgDesc
+        {
+            IdentifierUID m_uid;
+            vector<SrgParamDesc> m_parameters;
+        };
+
+        const SrgParamDesc& Get(const IdentifierUID& uid) const
+        {
+            return m_descriptorMap.at(uid);
+        }
+
+        vector<SrgDesc> m_srGroups;
+        unordered_map<IdentifierUID, SrgParamDesc> m_descriptorMap;
+    };
+
+    //! Stateful tracker that helps to construct binding points, by counting up
+    struct SingleBindingLocationTracker
+    {
+        //! Increments logical space index and nullifies all register indices
+        void IncrementSpace();
+
+        //! Equalizes all register indices (important for platforms with no B,T,S,U distinction)
+        void UnifyIndices();
+
+        //! Access total burned up resources, e.g for hardware capacity checks
+        int GetAccumulated(BindingType r) const;
+
+        int m_space = 0;  //<! logical space
+        int m_registerPos[BindingType::EndEnumeratorSentinel_] = {0};   //!< register index, one by type.
+        int m_accumulated[BindingType::EndEnumeratorSentinel_] = {0};  //!< Counter for total usage independently from space increments
+        int m_accumulatedUnused[BindingType::EndEnumeratorSentinel_] = {0};  //!< Counter for holes created by indices unification
+    };
+
+    //! Because of space limitations on some devices, we needed to introduce SRG-merging.
+    //! For minimal engine impact, we need to be able to reflect the "untainted" (non-merged) binding scheme,
+    //!  and the merged scheme together. For that purpose, we will track 2 binding locations in that maker.
+    class MultiBindingLocationMaker
+    {
+    public:
+        MultiBindingLocationMaker(const Options& options)
+            : m_options(options)
+        {}
+
+        void SignalIncrementSpace(std::function<void(int, int)> warningMessageFunctionForMinDescOvershoot);
+
+        void SignalUnifyIndices();
+        
+        void SignalRegisterIncrement(BindingType regType, int count = 1);
+
+        BindingPair GetCurrent(BindingType regType);
+
+        SingleBindingLocationTracker m_untainted;
+        SingleBindingLocationTracker m_merged;
+        Options m_options;
+    };
+
+    //! This class intends to be a base umbrella for compiler back-end services.
+    //! A back-end service typically provides reflection or code emission.
+    //! Since there are re-used commonalities in such systems, it is helpful
+    //! to share their common data and methods in this base class.
+    class Backend
+    {
+    public:
+        Backend(IntermediateRepresentation* ir, TokenStream* tokens, std::ostream& out)
+        : m_ir(ir), m_tokens(tokens), m_out(out)
+        {}
+
+        //! Gets the IntermediateRepresentation object
+        const IntermediateRepresentation* GetIR() const { return m_ir; }
+
+    protected:
+        //! Obtains a supplement emitter which provides per-platform emission functionality.
+        const PlatformEmitter& GetPlatformEmitter() const;
+
+        //! Gets the next and increments tokenIndex. TokenIndex must be in the [misc::Interval.a, misc::Interval.b] range. Token cannot be nullptr.
+        auto GetNextToken(ssize_t& tokenIndex, size_t channel = Token::DEFAULT_CHANNEL) const -> antlr4::Token*;
+
+        //! Extract an interval of text out of the source token stream, and append it to @output
+        virtual void GetTextInStream(misc::Interval interval, std::ostream& output) const;
+
+        //! Extract an interval of text out of the source token stream
+        string GetTextAsString(misc::Interval interval) const;
+
+        string GetInitializerClause(const AZ::ShaderCompiler::VarInfo* varInfo) const;
+
+        uint32_t GetNumberOf32BitConstants(const Options& options, const IdentifierUID& uid) const;
+
+        RootSigDesc BuildSignatureDescription(const Options& options, int num32BitConst) const;
+
+        RootSigDesc::SrgParamDesc ReflectOneExternalResource(IdentifierUID id, MultiBindingLocationMaker& bindInfo, RootSigDesc& rootSig) const;
+
+        RootSigDesc::SrgParamDesc ReflectOneExternalResourceAndWrapWithUnifyIndices(IdentifierUID id, MultiBindingLocationMaker& bindInfo, RootSigDesc& rootSig) const;
+
+        void AppendOptionRange(Json::Value& varOption, const IdentifierUID& varUid, const AZ::ShaderCompiler::VarInfo* varInfo, const Options& options) const;
+
+        Json::Value GetVariantList(const Options& options, bool includeEmpty = false) const;
+
+        static const char* GetInputModifier(TypeQualifier typeQualifier);
+
+        string GetExtendedTypeInfo(const ExtendedTypeInfo& extTypeInfo, std::function<string(const TypeRefInfo&)> translator) const;
+
+        std::ostream&               m_out;
+        IntermediateRepresentation* m_ir;
+        TokenStream*                m_tokens;
+    };
+
+    // independent utility functions
+    bool IsReadWriteView(string_view viewName);
+
+    QualifiedName MakeSrgConstantsStructName(IdentifierUID srg);
+
+    QualifiedName MakeSrgConstantsCBName(IdentifierUID srg);
+
+    /// don't use for HLSL emission (this doesn't go through translation)
+    string UnmangleTrimedName(const QualifiedNameView name);
+
+    string JoinAllNestedNamesWithUnderscore(const QualifiedNameView name);
+
+    string GetGlobalRootConstantVarName(const QualifiedNameView name);
+
+    string GetShaderKeyFunctionName(const IdentifierUID& uid);
+
+    string GetRootConstFunctionName(const IdentifierUID& uid);
+
+    // don't use for HLSL emission (this doesn't go through translation)
+    string UnmangleTrimedName(const ExtendedTypeInfo& extTypeInfo);
+
+    std::ostream &operator << (std::ostream &out, const SamplerStateDesc::AddressMode& addressMode);
+    std::ostream &operator << (std::ostream &out, const SamplerStateDesc::ComparisonFunc& compFunc);
+    std::ostream &operator << (std::ostream &out, const SamplerStateDesc::BorderColor& borderColor);
+    std::ostream &operator << (std::ostream &out, const SamplerStateDesc& samplerDesc);
+    std::ostream &operator << (std::ostream &out, const SamplerStateDesc::ReductionType& redcType);
+}

+ 43 - 0
src/AzslcCodeEmissionMutator.h

@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include "AzslcUtils.h"
+
+namespace AZ::ShaderCompiler
+{
+    //! In this struct we store text mutations
+    //! for code emissions.
+    struct CodeMutation
+    {
+        //! If present, this string will be emitted first.
+        std::optional<std::string> m_prepend;
+
+        //! If present, the TokenIndex should not be looked up for its proper qualified symbol.
+        //! instead, it should be replaced with this text when it's time to emit.
+        std::optional<std::string> m_replace;
+
+        //! After the token text was emitted, this text will be emitted if present.
+        std::optional<std::string> m_append;
+    };
+
+    //! This is a companion interface for the CodeEmitter. The code emitter may hold a reference
+    //! to one of these.
+    //! During code emission the CodeEmitter will call GetMutation() for some tokens
+    //! and change the output accordingly.
+    class ICodeEmissionMutator
+    {
+    public:
+        virtual ~ICodeEmissionMutator() = default;
+
+        //! Returns nullptr if there's no mutation for the given tokenIndex.
+        //! tokenIndex refers to the exact same integral as returned by antlr4::Token::getTokenIndex().
+        virtual const CodeMutation* GetMutation(ssize_t tokenIndex) const = 0;
+    };
+
+} // namespace AZ::ShaderCompiler

+ 87 - 0
src/AzslcCommon.h

@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <stdint.h>
+
+namespace AZ::ShaderCompiler
+{
+    namespace Packing
+    {
+        enum class MatrixMajor : uint32_t
+        {
+            Default,            // If unspecified, it uses the default for the code emission (specified by -Zpc or -Zpr at build time)
+            ColumnMajor,        // Use column_major explicitly
+            RowMajor,           // Use row_major explicitly
+        };
+    }
+         
+    struct SamplerStateDesc
+    {
+        enum class FilterMode : uint32_t
+        {
+            Point = 0,
+            Linear
+        };
+
+        enum class ReductionType : uint32_t
+        {
+            Filter = 0,  /// Performs filtering on samples.
+            Comparison,  /// Performs comparison of samples using the supplied comparison function.
+            Minimum,     /// Returns minimum of samples.
+            Maximum      /// Returns maximum of samples.
+        };
+
+        enum class AddressMode : uint32_t
+        {
+            Wrap = 0,
+            Mirror,
+            Clamp,
+            Border,
+            MirrorOnce
+        };
+
+        enum class ComparisonFunc : uint32_t
+        {
+            Never = 0,
+            Less,
+            Equal,
+            LessEqual,
+            Greater,
+            NotEqual,
+            GreaterEqual,
+            Always
+        };
+
+        enum class BorderColor : uint32_t
+        {
+            OpaqueBlack = 0,
+            TransparentBlack,
+            OpaqueWhite
+        };
+
+        uint32_t          m_anisotropyMax = 0;                          /// Range [1 .. 16]
+        bool              m_anisotropyEnable = false;
+        FilterMode        m_filterMin = FilterMode::Point;
+        FilterMode        m_filterMag = FilterMode::Point;
+        FilterMode        m_filterMip = FilterMode::Point;
+        ComparisonFunc    m_comparisonFunc = ComparisonFunc::Always;
+        ReductionType     m_reductionType = ReductionType::Filter;
+        AddressMode       m_addressU = AddressMode::Wrap;
+        AddressMode       m_addressV = AddressMode::Wrap;
+        AddressMode       m_addressW = AddressMode::Wrap;
+        float             m_mipLodMin = 0.0f;
+        float             m_mipLodMax = 15.0f;     //On RHI side limit is set by Limits::Image::MipCountMax
+        float             m_mipLodBias = 0.0f;
+        BorderColor       m_borderColor = BorderColor::TransparentBlack;
+
+        // Metadata for emission
+        bool              m_isComparison = false;
+        bool              m_isDynamic = false;
+    };
+}

+ 1293 - 0
src/AzslcEmitter.cpp

@@ -0,0 +1,1293 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include "AzslcEmitter.h"
+
+#include <tuple>
+#include <cmath>
+#include <filesystem>
+namespace StdFs = std::filesystem;
+
+// We should only include the base platform emitter
+// Every specific implementation is supplied via a factory get method
+#include "AzslcPlatformEmitter.h"
+
+namespace AZ
+{
+    namespace
+    {
+        //  spec: https://github.com/microsoft/DirectXShaderCompiler/blob/master/docs/SPIR-V.rst#subpass-inputs
+        /// extract: "Subpasses are read through two new builtin resource types, available only in pixel shader"
+        //  because we have a unified file for all stages, we need to ensure the source remains buildable for other stages.
+        static constexpr char StubSubpassInputTypes[] = R"(
+#if !defined(AZ_USE_SUBPASSINPUT)
+  class SubpassInputStub
+  {
+    float4 SubpassLoad(){return (float4)0;}
+  };
+  class SubpassInputStubMS
+  {
+    float4 SubpassLoad(int sampleIndex){return (float4)0;}
+  };
+  #define SubpassInput SubpassInputStub
+  #define SubpassInputMS SubpassInputStubMS
+#endif
+)";
+    }
+}
+
+namespace AZ::ShaderCompiler
+{
+    // to activate argument dependent lookup from template utilities in AzslcUtils, this must be in a reachable namespace
+    std::ostream& operator << (std::ostream& out, const AttributeInfo::Argument& arg)
+    {
+        if (holds_alternative<string>(arg))
+        {
+            return out << get<string>(arg);
+        }
+
+        else if (holds_alternative<ConstNumericVal>(arg))
+        {
+            const auto& constVal = get<ConstNumericVal>(arg);
+
+            if (holds_alternative<int32_t>(constVal) || holds_alternative<uint32_t>(constVal))
+            {
+                return out << ExtractValueAsInt64(constVal, std::numeric_limits<int64_t>::min());
+            }
+            else if (holds_alternative<float>(constVal))
+            {
+                return out << ExtractValueAsFloat(constVal, std::numeric_limits<float>::infinity());
+            }
+        }
+
+        else if (holds_alternative<bool>(arg))
+        {
+            return out << get<bool>(arg);
+        }
+
+        return out;
+    }
+
+    std::ostream& operator << (std::ostream& out, const AttributeInfo& attr)
+    {
+        if (!attr.m_namespace.empty())
+        {
+            out << attr.m_namespace << "::";
+        }
+
+        out << attr.m_attribute;
+
+        if (!attr.m_argList.empty())
+        {
+            out << "(" << Join(attr.m_argList.begin(), attr.m_argList.end(), ", ") << ")";
+        }
+        return out;
+    }
+
+    using std::for_each;
+
+    void CodeEmitter::Run(const Options& options)
+    {
+        const uint32_t numOf32bitConst = GetNumberOf32BitConstants(options, m_ir->m_rootConstantStructUID);
+        const RootSigDesc rootSig = BuildSignatureDescription(options, numOf32bitConst);
+
+        SetupScopeMigrations(options);
+
+        // Emit global attributes
+        for (const auto& attr : m_ir->m_symbols.GetGlobalAttributeList())
+        {
+            EmitAttribute(attr);
+        }
+
+        if (m_ir->m_sema.m_subpassInputSeen)
+        {
+            m_out << StubSubpassInputTypes << "\n";
+        }
+        
+        EmitGetterFunctionDeclarationsForRootConstants(m_ir->m_rootConstantStructUID);
+
+        // loop on the (mostly) user-defined order of code entities, and emit
+        for (const IdentifierUID& iteratedSymbolUid : m_ir->m_symbols.GetOrderedSymbols())
+        {
+            const QualifiedNameView iteratedSymbolName = iteratedSymbolUid.GetName();
+            const Kind iteratedSymbolKind = m_ir->GetKind(iteratedSymbolUid);
+
+            EmitPreprocessorLineDirective(iteratedSymbolName);
+
+            switch (iteratedSymbolKind)
+            {
+                // top-level enums, structs and classes, as well as immediate-type-declaration enum/structs (`struct S{} s;`)
+            case Kind::Struct:
+            case Kind::Class:
+            case Kind::Enum:
+            {
+                if (IsTopLevelThroughTranslation(iteratedSymbolUid))
+                {
+                    auto* classInfo = m_ir->GetSymbolSubAs<ClassInfo>(iteratedSymbolName);
+                    iteratedSymbolKind == Kind::Enum ?
+                          EmitEnum(iteratedSymbolUid, *classInfo, options)
+                        : EmitStruct(*classInfo, iteratedSymbolName, options);
+                }
+                break;
+            }
+                // typedefs
+            case Kind::TypeAlias:
+            {
+                if (IsTopLevelThroughTranslation(iteratedSymbolUid))
+                {
+                    auto* aliasInfo = m_ir->GetSymbolSubAs<TypeAliasInfo>(iteratedSymbolName);
+                    EmitTypeAlias(iteratedSymbolUid, *aliasInfo);
+                }
+                break;
+            }
+                // global variables
+            case Kind::Variable:
+            {
+                if (IsTopLevelThroughTranslation(iteratedSymbolUid))
+                {
+                    auto* varInfo = m_ir->GetSymbolSubAs<VarInfo>(iteratedSymbolName);
+                    if (varInfo->CheckHasStorageFlag(StorageFlag::Enumerator))
+                    {   // Enumerators have already been emitted in the Kind::Enum case.
+                        // They act as static const, but no need to emit them here
+                        break;
+                    }
+
+                    // Option variables are emitted after the ShaderVariantFallback SRG
+                    if (varInfo->CheckHasStorageFlag(StorageFlag::Option))
+                    {
+                        EmitShaderVariantOptionVariableDeclaration(iteratedSymbolUid, options);
+                        break;
+                    }
+
+                    // a non-global extern is an SRG-variable. it should be emitted by EmitSRG
+                    bool global = IsGlobal(iteratedSymbolName);
+                    if (!global && !varInfo->StorageFlagIsLocalLinkage(global || varInfo->m_srgMember))
+                    {
+                        break;
+                    }
+
+                    EmitVariableDeclaration(*varInfo, iteratedSymbolUid, options, VarDeclHasFlag(VarDeclHas::Initializer));
+                    m_out << ";\n";
+                }
+                break;
+            }
+                // SRG
+            case Kind::ShaderResourceGroup:
+            {
+                auto* srgSub = m_ir->GetSymbolSubAs<SRGInfo>(iteratedSymbolName);
+                EmitSRG(*srgSub, iteratedSymbolUid, options, rootSig);
+                break;
+            }
+                // function
+            case Kind::Function:
+            {
+                auto* funcSub = m_ir->GetSymbolSubAs<FunctionInfo>(iteratedSymbolName);
+                const bool alreadyDeclared = AlreadyEmittedFunctionDeclaration(iteratedSymbolUid);
+                assert(!funcSub->IsEmpty());
+                const EmitFunctionAs form = funcSub->HasUniqueDeclarationThroughDefinition() || alreadyDeclared ?
+                    EmitFunctionAs::Definition : EmitFunctionAs::Declaration;
+                EmitFunction(*funcSub, iteratedSymbolUid, form, options);
+                break;
+            }
+            default: break;
+            } // end switch on code entities kind
+        } // end for all entities
+
+        EmitRootConstants(rootSig, options);
+
+        EmitShaderVariantOptionGetters(options);
+
+        if (options.m_emitRootSig)
+        {
+            m_out << GetPlatformEmitter().GetRootSig(*this, rootSig, options, BindingPair::Set::Merged);
+        }
+    }
+
+    //! azslSymbolName is going to be considered as a startup point of migration (e.g a scope)
+    //! and all symbols under that scope will be migrated equivalently.
+    void CodeEmitter::MigrateASTSubTree(const IdentifierUID& azslSymbol, QualifiedNameView landingScope)
+    {
+        m_translations.RegisterLandingScope(azslSymbol, landingScope);
+
+        // special case for non-scoped enums because they have dependent symbols that are not children
+        if (m_ir->GetKind(azslSymbol) == Kind::Enum)
+        {
+            auto* sub = m_ir->GetSymbolSubAs<ClassInfo>(azslSymbol.GetName());
+            bool scoped = sub->Get<EnumerationInfo>()->m_isScoped;
+            if (!scoped)
+            {
+                // migrate enumerators too
+                for (auto& enumerator : sub->GetMemberFields())
+                {
+                    m_translations.RegisterLandingScope(enumerator, landingScope);
+                }
+            }
+        }
+    }
+
+    bool CodeEmitter::IsTopLevelThroughTranslation(const IdentifierUID& uid) const
+    {
+        return m_translations.GetLandingScope(uid.GetName()) == QualifiedNameView{"/"};
+    }
+
+    //! setup all scope migrations (srg content to global, local structs to global)
+    void CodeEmitter::SetupScopeMigrations(const Options& options)
+    {
+        m_translations.SetAccessSymbolQueryFunctor([=](QualifiedNameView qnv){return m_ir->GetKindInfo(IdentifierUID{qnv});});
+        m_translations.SetGetSeenatFunctor([=](QualifiedNameView qnv) -> vector<Seenat>&
+                                            {
+                                                auto* uidkind = m_ir->GetIdAndKindInfo(qnv);
+                                                if (uidkind)
+                                                {
+                                                    return uidkind->second.GetSeenats();
+                                                }
+                                                static vector<Seenat> s_empty;
+                                                return s_empty;
+                                            });
+
+        auto globalScope = QualifiedNameView{ "/" };
+        
+        // Global root constant custom behavior
+        for (auto&[uid, info] : m_ir->GetOrderedSymbolsOfSubType_2<VarInfo>())
+        {
+            bool isAlreadyGlobal = IsGlobal(uid.GetName());
+            bool isNotContainedInType = !m_ir->IsNestedStructOrEnum(uid);
+            if (isAlreadyGlobal && isNotContainedInType)
+            {
+                auto* varInfo = m_ir->GetSymbolSubAs<VarInfo>(uid.GetName());
+                const auto& rootconstantClassInfo = *m_ir->GetSymbolSubAs<ClassInfo>(m_ir->m_rootConstantStructUID.m_name);
+
+                if (options.m_rootConstantsMaxSize && rootconstantClassInfo.HasMember(uid.GetNameLeaf())
+                    && varInfo->CheckHasAllStorageFlags({ StorageFlag::Rootconstant }))
+                {
+                    IdentifierUID constructRootconstantReference = *rootconstantClassInfo.FindMemberFromLeafName(uid.GetNameLeaf());
+                    m_translations.RegisterLandingScope(uid, m_ir->m_rootConstantStructUID.GetName());
+                    m_translations.AddCustomBehavior(uid.GetName(),
+                        BehaviorEvent::OnReference,
+                        [constructRootconstantReference](QualifiedNameView, UsageContext, string proposition, ssize_t)
+                    {
+                        // Construct the rootconstant member name which is declared as global variable with the use of get functions.
+                        // Type _g_MyRootConstVar = GetShaderRootConst_Member(); So now we just return _g_MyRootConstVar;
+                        return GetGlobalRootConstantVarName(constructRootconstantReference.GetName());
+                    });
+                }
+            }
+        }
+
+        // all SRGs -> erased. Migrate all their contents to global.
+        for (auto& [srgUID, srgInfo] : m_ir->GetOrderedSymbolsOfSubType_2<SRGInfo>())
+        {
+            array<decltype(srgInfo->m_structs)*, 6> allSrgMembersUidArrays = {&srgInfo->m_structs,
+                                                                              &srgInfo->m_srViews,
+                                                                              &srgInfo->m_samplers,
+                                                                              &srgInfo->m_CBs,
+                                                                              &srgInfo->m_nonexternVariables,
+                                                                              &srgInfo->m_functions};
+            for (auto& array : allSrgMembersUidArrays)
+            {
+                for (auto& member : *array)
+                {
+                    auto globalScope = QualifiedNameView{"/"};
+                    MigrateASTSubTree(member, globalScope);
+                }
+            }
+
+            // variables get special treatment in case of non-emitConstantBufferBody, because SRG-constants go in a generated-struct: <SRGNAME>_SRGConstantStruct
+            for (auto& member : srgInfo->m_implicitStruct.GetMemberFields())
+            {
+                auto globalScope = QualifiedNameView{"/"};
+                auto constantsStruct = MakeSrgConstantsStructName(srgUID);
+                MigrateASTSubTree(member, options.m_emitConstantBufferBody ? globalScope : QualifiedNameView{constantsStruct});
+            }
+
+            // add a special behavior for CB, because under cb-body switch, CBs are views, thus must be indexed.
+            if (options.m_emitConstantBufferBody)
+            {
+                for_each(srgInfo->m_CBs.begin(), srgInfo->m_CBs.end(), [this](const IdentifierUID& viewUid)
+                         {
+                             auto* varInfo = m_ir->GetSymbolSubAs<VarInfo>(viewUid.GetName());
+                             // all buffer types references must be mutated to [0]
+                             assert(varInfo->GetTypeClass() == TypeClass::ConstantBuffer);
+                             m_translations.AddCustomBehavior(viewUid.GetName(),
+                                                              BehaviorEvent::OnReference,
+                                                              [this](QualifiedNameView, UsageContext, const string& proposition, ssize_t tokenId)
+                                                              {
+                                                                  // in cb-body mode, CB are converted to views, a non-subscripted expression
+                                                                  // needs to be abstracted away (by adding [0]) to feel like a constant buffer access.
+                                                                  // but since we can have arrays of constant buffers in SRG, the user may have already subscripted
+                                                                  // the access, in that case we can't double the subscript or we'll emit an ill-formed program.
+                                                                  auto* ast = m_ir->m_tokenMap.GetNode(tokenId);
+                                                                  return proposition + (IsNextToArrayAccessExpression(ast) ? "" : "[0]");
+                                                              });
+                         });
+            }
+            else // in the else-case, we have dedicated structures for SRG constants, and references need a custom behavior
+            {
+                for_each(srgInfo->m_implicitStruct.GetMemberFields().begin(), srgInfo->m_implicitStruct.GetMemberFields().end(), [this, srgUID=srgUID](IdentifierUID fieldUid)
+                         {
+                            // the natural suggestion from the translation system is going to be
+                            // to mutate MyRsc::m_f4 to MyRsc_SRGConstantStruct::MyRsc_m_f4
+                            // which is the "address" of the variable declaration, but not where the instance reside.
+                            // we need to mutate this to MyRsc_SRGConstantBuffer.MyRsc_m_f4
+                            m_translations.AddCustomBehavior(fieldUid.GetName(),
+                                                            BehaviorEvent::OnReference,
+                                                            [this, srgUID=srgUID](QualifiedNameView, UsageContext, string proposition, ssize_t)
+                                                            {
+                                                                string constantBufferId = UnMangle(MakeSrgConstantsCBName(srgUID));
+                                                                string translatedFieldId = UnMangle(string{ExtractLeaf(ReMangle(proposition))});
+                                                                return constantBufferId + "." + translatedFieldId;
+                                                            });
+                         });
+            }
+        }
+
+        // structs/classes in functions/arguments/generic-parameters, may not be valid HLSL scopes to hold types -> migrate them to global
+        for (auto& [uid, info] : m_ir->GetOrderedSymbolsOfSubType_2<ClassInfo>())
+        {
+            bool isAlreadyGlobal = IsGlobal(uid.GetName());
+            bool isNotContainedInType = !m_ir->IsNestedStructOrEnum(uid);
+            if (!isAlreadyGlobal && isNotContainedInType)
+            {
+                MigrateASTSubTree(uid, globalScope);
+            }
+        }
+
+        // DXC has made an error the definition of typedef in classes -> move them all to global
+        for (auto& [typeAliasUid, typeAliasInfo] : m_ir->GetOrderedSymbolsOfSubType_2<TypeAliasInfo>())
+        {
+            if (!IsGlobal(typeAliasUid.GetName()))
+            {
+                MigrateASTSubTree(typeAliasUid, globalScope);
+            }
+        }
+    }
+
+    void CodeEmitter::EmitShaderVariantOptionVariableDeclaration(const IdentifierUID& symbolUid, const Options& options) const
+    {
+        assert(m_ir->GetKind(symbolUid) == Kind::Variable);
+        assert(IsTopLevelThroughTranslation(symbolUid));
+
+        auto* varInfo = m_ir->GetSymbolSubAs<VarInfo>(symbolUid.GetName());
+
+        EmitGetShaderKeyFunctionDeclaration(symbolUid, varInfo->GetTypeRefInfo());
+        m_out << ";\n\n";
+
+        m_out << "#if defined(" + JoinAllNestedNamesWithUnderscore(symbolUid.m_name) + "_OPTION_DEF)\n";
+        EmitVariableDeclaration(*varInfo, symbolUid, options, VarDeclHasFlag(VarDeclHas::OptionDefine));
+        m_out << "_OPTION_DEF ;\n#else\n";
+        EmitVariableDeclaration(*varInfo, symbolUid, options, VarDeclHasFlag(VarDeclHas::OptionDefine) | VarDeclHas::Initializer);
+        m_out << ";\n#endif\n";
+    }
+
+    void CodeEmitter::EmitShaderVariantOptionGetters(const Options& options) const
+    {
+        vector<pair<IdentifierUID, VarInfo*>> symbols;
+
+        // browse all variables
+        for (const auto& [uid, varInfo] : m_ir->m_symbols.GetOrderedSymbolsOfSubType_2<VarInfo>())
+        {
+            // For now only emit top level options
+            if (!IsTopLevelThroughTranslation(uid) || !varInfo->CheckHasStorageFlag(StorageFlag::Option))
+            {
+                continue;
+            }
+
+            symbols.emplace_back(uid, varInfo);
+        }
+
+        if (!symbols.empty())
+        {
+            m_out << "// Generated code: ShaderVariantOptions fallback value getters:\n";
+
+            auto shaderOptions = GetVariantList(options, true);
+            auto shaderOptionIndex = 0;
+
+            for (const auto& [uid, varInfo] : symbols)
+            {
+                const auto keySizeInBits = shaderOptions["ShaderOptions"][shaderOptionIndex]["keySize"].asUInt();
+                const auto keyOffsetBits = shaderOptions["ShaderOptions"][shaderOptionIndex]["keyOffset"].asUInt();
+                const auto defaultValue = shaderOptions["ShaderOptions"][shaderOptionIndex]["defaultValue"].asString();
+
+                shaderOptionIndex++;
+                EmitGetShaderKeyFunction(m_shaderVariantFallbackUid, uid, keySizeInBits, keyOffsetBits, defaultValue, varInfo->GetTypeRefInfo());
+            }
+        }
+    }
+
+    void CodeEmitter::EmitGetFunctionsForRootConstants(const ClassInfo& rootConstInfo, string_view bufferName) const
+    {
+        for (const auto& memberVar : rootConstInfo.GetMemberFields())
+        {
+            const auto* varInfo = m_ir->GetSymbolSubAs<VarInfo>(memberVar.m_name);
+
+            if (!varInfo->m_isPublic)
+            {
+                continue;
+            }
+
+            // Construct function return type and function name
+            m_out << GetTranslatedName(varInfo->GetTypeRefInfo(), UsageContext::ReferenceSite) << " ";
+            m_out << GetRootConstFunctionName(memberVar);
+
+            m_out << "\n{\n";
+            // The get function of root constants should return members of root ConstantBuffer object
+            // Ex: return rootCB.ExampleSRG_myRootCstfloat4;
+            m_out << "    return " << bufferName << "." << memberVar.GetNameLeaf() << ";\n";
+            m_out << "}\n\n";
+        }
+    }
+
+    void CodeEmitter::EmitRootConstants(const RootSigDesc& rootSig, const Options& options) const
+    {
+        if (const auto* rootConstInfo = m_ir->GetSymbolSubAs<ClassInfo>(m_ir->m_rootConstantStructUID.GetName()))
+        {
+            if (!rootConstInfo->GetMemberFields().empty())
+            {
+                m_out << GetPlatformEmitter().GetRootConstantsView(*this, rootSig, options, BindingPair::Set::Merged);
+
+                // Emit the get function definitions which returns root const member
+                EmitGetFunctionsForRootConstants(*rootConstInfo,
+                                                 GetTranslatedName(RootConstantsViewName, UsageContext::ReferenceSite));
+            }
+        }
+    }
+
+    void CodeEmitter::EmitPreprocessorLineDirective(size_t azslLineNumber)
+    {
+        const LineDirectiveInfo* lineDirectiveInfo = m_ir->GetNearestPreprocessorLineDirective(azslLineNumber);
+        if (!lineDirectiveInfo)
+        {
+            return;
+        }
+
+        const auto physicalTokenLine = lineDirectiveInfo->m_physicalTokenLine;
+        if (m_alreadyEmittedPreprocessorLineDirectives.find(physicalTokenLine) != m_alreadyEmittedPreprocessorLineDirectives.end())
+        {
+            return;
+        }
+
+        const string& originalFileName = StdFs::absolute(lineDirectiveInfo->m_containingFilename).lexically_normal().generic_string();
+        const auto lineNumber = lineDirectiveInfo->m_forcedLineNumber;
+        m_out << "#line " << lineNumber << " \"" << originalFileName << "\"\n";
+
+        m_alreadyEmittedPreprocessorLineDirectives.insert(physicalTokenLine);
+    }
+
+    void CodeEmitter::EmitPreprocessorLineDirective(QualifiedNameView symbolName)
+    {
+        IdAndKind* idAndkindInfo = m_ir->GetIdAndKindInfo(symbolName);
+        KindInfo& info = idAndkindInfo->second;
+        const auto origSourceLine = info.VisitSub(GetOrigSourceLine_Visitor{});
+        EmitPreprocessorLineDirective(origSourceLine);
+    }
+
+    void CodeEmitter::EmitStruct(const ClassInfo& classInfo, string_view structName, const Options& options)
+    {
+        const bool hasName = (structName.length() > 0);
+        const auto tabs = "    ";
+        if (hasName)
+        {
+            EmitAllAttachedAttributes(IdentifierUID { QualifiedNameView{structName} });
+
+            m_out << (classInfo.m_kind == Kind::Struct ? "struct " : "class ") << GetTranslatedName(QualifiedNameView{structName}, UsageContext::DeclarationSite) << "\n{\n";
+        }
+
+        for (const IdentifierUID& memberUid : classInfo.GetOrderedMembers())
+        {
+            if (structName.empty() || m_translations.GetLandingScope(memberUid.GetName()) == structName)
+            {
+                auto& [uid, info] = *m_ir->GetIdAndKindInfo(memberUid.GetName());
+                if (info.IsKindOneOf(Kind::Class, Kind::Struct, Kind::Interface))
+                {
+                    // recurse
+                    EmitStruct(info.GetSubRefAs<ClassInfo>(), uid.GetName(), options);
+                }
+                else if (info.IsKindOneOf(Kind::TypeAlias))
+                {
+                    EmitTypeAlias(uid, info.GetSubRefAs<TypeAliasInfo>());
+                }
+                else if (info.IsKindOneOf(Kind::Enum))
+                {
+                    EmitEnum(uid, info.GetSubRefAs<ClassInfo>(), options);
+                }
+                else if (info.IsKindOneOf(Kind::Variable))
+                {
+                    const auto* varInfo = m_ir->GetSymbolSubAs<VarInfo>(uid.m_name);
+                    assert(varInfo);
+
+                    m_out << tabs;
+                    EmitVariableDeclaration(*varInfo, uid, options, VarDeclHasFlag(VarDeclHas::HlslSemantics));
+                    m_out << ";\n";
+                }
+                else if (info.IsKindOneOf(Kind::Function))
+                {
+                    const auto* func = m_ir->GetSymbolSubAs<FunctionInfo>(uid.m_name);
+                    assert(func);
+
+                    m_out << tabs;
+                    EmitFunctionAs form = func->HasUniqueDeclarationThroughDefinition() ? EmitFunctionAs::Definition : EmitFunctionAs::Declaration;
+                    EmitFunction(*func, uid, form, options);
+                }
+                else
+                {
+                    assert(false);// "Unhandled KindInfo case in struct emission must be handled properly!"
+                }
+            }
+        }
+
+        if (hasName)
+        {
+            m_out << "};\n\n";
+        }
+    }
+
+    void CodeEmitter::EmitAllAttachedAttributes(const IdentifierUID& uid, Except omissionList/*= {}*/) const
+    {
+        if (auto attrList = m_ir->m_symbols.GetAttributeList(uid))
+        {
+            for_each(attrList->begin(), attrList->end(),
+                     [=](auto&& attrInfo)
+                     {
+                         if (!IsIn(attrInfo.m_attribute, omissionList))
+                         {
+                            EmitAttribute(attrInfo);
+                         }
+                     });
+        }
+    }
+
+    void CodeEmitter::EmitGetterFunctionDeclarationsForRootConstants(const IdentifierUID& uid) const
+    {
+        if (const auto* rootConstInfo = m_ir->GetSymbolSubAs<ClassInfo>(uid.GetName()))
+        {
+            for (const auto& memberVar : rootConstInfo->GetMemberFields())
+            {
+                const auto* varInfo = m_ir->GetSymbolSubAs<VarInfo>(memberVar.m_name);
+
+                if (!varInfo->m_isPublic)
+                {
+                    continue;
+                }
+
+                m_out << GetTranslatedName(varInfo->GetTypeRefInfo(), UsageContext::ReferenceSite) << " ";
+                m_out << GetRootConstFunctionName(memberVar);
+                m_out << ";\n\n";
+
+                // Emit a global variable to call the above declared function ex: static const float _gRootConstA = GetRootConst();
+                // The global variable will be the reference variable to access the root constant
+                m_out << "static const ";
+                m_out << GetTranslatedName(varInfo->GetTypeRefInfo(), UsageContext::ReferenceSite) << " ";
+                m_out << GetGlobalRootConstantVarName(memberVar.m_name) + " = ";
+                m_out << GetRootConstFunctionName(memberVar);
+                m_out << ";\n\n";
+            }
+        }
+    }
+
+    // special adapter version for AttributeInfo argument
+    static string Undecorate(string_view decoration, const AttributeInfo::Argument& arg)
+    {
+        std::stringstream ss;
+        ss << arg;
+        return string{AZ::Undecorate(decoration, ss.str())};
+    }
+
+    void CodeEmitter::EmitAttribute(const AttributeInfo& attrInfo) const
+    {
+        return EmitAttribute(attrInfo, m_out);
+    }
+
+    void CodeEmitter::EmitAttribute(const AttributeInfo& attrInfo, std::ostream& outstream)
+    {
+        if (attrInfo.m_attribute == "verbatim")
+        {
+            outstream << (attrInfo.m_argList.begin() == attrInfo.m_argList.end() ? "" : Unescape(Undecorate("\"", *attrInfo.m_argList.begin())));
+            auto outVer = [&](const AttributeInfo::Argument& arg) { outstream << " " << Unescape(Undecorate("\"", arg)); };
+            for_each(std::next(attrInfo.m_argList.begin()), attrInfo.m_argList.end(), outVer);
+            outstream << '\n';
+        }
+
+        else if (attrInfo.m_attribute == "output_format")
+        {
+            if (!attrInfo.m_argList.empty())
+            {
+                if (holds_alternative<string>(attrInfo.m_argList[0]))
+                {
+                    string poFormat{ Trim(get<string>(attrInfo.m_argList[0]), "\"") };
+                    OutputFormat hint = OutputFormat::FromStr(poFormat);
+                    outstream << "#pragma OutputFormatHint(default " << poFormat << ")\n";
+                }
+                else if (attrInfo.m_argList.size() > 1 &&
+                         holds_alternative<ConstNumericVal>(attrInfo.m_argList[0]) &&
+                         holds_alternative<string>(attrInfo.m_argList[1]))
+                {
+                    const auto rtIndex = ExtractValueAsInt64(get<ConstNumericVal>(attrInfo.m_argList[0]), std::numeric_limits<int64_t>::min());
+                    if (rtIndex >= 0 && rtIndex <= 7)
+                    {
+                        string poFormat{ Trim(get<string>(attrInfo.m_argList[1]), "\"") };
+                        OutputFormat hint = OutputFormat::FromStr(poFormat);
+                        outstream << "#pragma OutputFormatHint(target " << rtIndex << " " << poFormat << ")\n";
+                    }
+                }
+            }
+        }
+
+        else if (attrInfo.m_attribute == "partial")
+        {
+            // Reserved for ShaderResourceGroup use. Do not re-emit
+            outstream << "// Attribute not re-emitted by design - [[" << attrInfo << "]]\n ";
+        }
+
+        else if (attrInfo.m_attribute == "range")
+        {
+            // Reserved for integer type option variables. Do not re-emit
+            outstream << "// Attribute not re-emitted by design - [[" << attrInfo << "]]\n ";
+        }
+
+        else
+        {
+            // We don't block any attributes we don't understand - pass them through
+            outstream << ((attrInfo.m_category == AttributeCategory::Single) ? "[" : "[[")
+                  << attrInfo
+                  << ((attrInfo.m_category == AttributeCategory::Single) ? "]" : "]]")
+                  << "\n";
+        }
+    }
+
+    void CodeEmitter::EmitTypeAlias(const IdentifierUID& uid, const TypeAliasInfo& aliasInfo) const
+    {
+        m_out << "typedef " << GetTranslatedName(aliasInfo.m_canonicalType, UsageContext::ReferenceSite)
+              << " " << GetTranslatedName(uid, UsageContext::DeclarationSite) << ";\n";
+    }
+
+    void CodeEmitter::EmitEnum(const IdentifierUID& uid, const ClassInfo& classInfo, const Options& options)
+    {
+        const auto& enumInfo = get<EnumerationInfo>(classInfo.m_subInfo);
+
+        EmitAllAttachedAttributes(uid);
+
+        m_out << "enum " << (enumInfo.m_isScoped ? "class " : "") << GetTranslatedName(uid.m_name, UsageContext::DeclarationSite) << "\n";
+        m_out << "{\n";
+
+        for (const auto& memberUid : classInfo.GetMemberFields())
+        {
+            // The variable can potentially have an initialization declaration as well
+            const auto* varInfoPtr = m_ir->GetSymbolSubAs<VarInfo>(memberUid.m_name);
+            EmitVariableDeclaration(*varInfoPtr,
+                                    memberUid,
+                                    options,
+                                    VarDeclHasFlag(VarDeclHas::Initializer) | VarDeclHas::NoType | VarDeclHas::NoModifiers);
+            m_out << ",\n";
+        }
+        m_out << "};\n\n";
+    }
+
+    bool CodeEmitter::AlreadyEmittedFunctionDeclaration(const IdentifierUID& uid) const
+    {
+        return m_alreadyEmittedFunctionDeclarations.find(uid) != m_alreadyEmittedFunctionDeclarations.end();
+    }
+
+    bool CodeEmitter::AlreadyEmittedFunctionDefinition(const IdentifierUID& uid) const
+    {
+        return m_alreadyEmittedFunctionDefinitions.find(uid) != m_alreadyEmittedFunctionDefinitions.end();
+    }
+
+    void CodeEmitter::EmitFunction(const FunctionInfo& funcSub, const IdentifierUID& uid, EmitFunctionAs entityConfiguration, const Options& options)
+    {
+        // reproduce the signature `[attr] [modifiers] rettype [classnameFQN::] Name(params) [semantics]`
+
+        bool emitAsDeclaration = entityConfiguration == EmitFunctionAs::Declaration;
+        bool emitAsDefinition  = entityConfiguration == EmitFunctionAs::Definition;
+
+        bool riskDoubleEmission = emitAsDeclaration && AlreadyEmittedFunctionDeclaration(uid)
+                               || emitAsDefinition && AlreadyEmittedFunctionDefinition(uid);
+        bool undefinedFunction = funcSub.IsUndefinedFunction();
+        if (riskDoubleEmission
+            || funcSub.m_isVirtual   // interface's methods (isVirtual) are a declarative construct of AZSL and don't appear in HLSL.
+            || undefinedFunction && emitAsDefinition)
+        {
+            return;
+        }
+
+        AstFuncSig* node = funcSub.m_defNode ? funcSub.m_defNode : funcSub.m_declNode;
+
+        EmitAllAttachedAttributes(uid);
+
+        // emit modifiers in case of first declaration/definition
+        // because:    class C{ static vd A(); };
+        //             static vd C::A(){}    // ill-formed HLSL. we can't repeat static here.
+        if (!AlreadyEmittedFunctionDeclaration(uid))
+        {
+            m_out << GetTypeModifier(funcSub, options) << " ";
+        }
+
+        // emit return type:
+        m_out << GetTranslatedName(funcSub.m_returnType, UsageContext::ReferenceSite) << " ";
+        // emit Name
+        if (entityConfiguration == EmitFunctionAs::Definition && funcSub.HasDeportedDefinition())
+        {
+            // emit fully qualified function name (with classname prefix).
+            // surrounded by round braces because otherwise clang's greedy parsing will wreak havoc.
+            // indeed `RetType ::Class::Method() {}` will be parsed as `RetType::Class::Method(){}` one id-expr.
+            // since we can't use trailing return type in HLSL, the fix is to use round braces.
+            //  solution from https://stackoverflow.com/a/3185232/893406
+            m_out << "(" << GetTranslatedName(uid, UsageContext::ReferenceSite) << ")";
+        }
+        else
+        {
+            // emit leaf function name (in AZSL all declaration sites are Identifier and not idExpressions)
+            m_out << GetTranslatedName(uid, UsageContext::DeclarationSite);
+        }
+        // emit parameters:
+        m_out << "(";
+        bool inhibitInitializers = AlreadyEmittedFunctionDeclaration(uid) && !emitAsDeclaration;
+        EmitParameters(funcSub.GetParameters(emitAsDeclaration).begin(), funcSub.GetParameters(emitAsDeclaration).end(), options, !inhibitInitializers);
+        m_out << ")";
+
+        if (const auto hlslSemantic = node->hlslSemantic())
+        {
+            m_out << " " << hlslSemantic->getText();
+        }
+
+        if (entityConfiguration == EmitFunctionAs::Declaration)
+        {
+            m_out << ";\n";
+            m_alreadyEmittedFunctionDeclarations.insert(uid);
+        }
+        else
+        {
+            auto scopeInterval = m_ir->m_scope.m_scopeIntervals[uid];
+            auto astNode = m_ir->m_tokenMap.GetNode(scopeInterval.a);
+            auto funcDefNode = ExtractSpecificParent<azslParser::HlslFunctionDefinitionContext>(astNode);
+            auto blockInterval = funcDefNode->block()->getSourceInterval();
+            m_out << "\n";
+            EmitText(blockInterval);
+            m_out << "\n\n";
+            m_alreadyEmittedFunctionDefinitions.insert(uid);
+        }
+    }
+
+    // static
+    string CodeEmitter::GetTypeModifier(const VarInfo& var, const Options& options)
+    {
+        using namespace std::string_literals;
+        string modifiers;
+        bool isMatrix = IsArithmetic(var.GetTypeClass()) && var.m_typeInfoExt.m_coreType.m_arithmeticInfo.IsMatrix();
+        if (var.CheckHasStorageFlag(StorageFlag::ColumnMajor))
+        {
+            modifiers = "column_major";
+        }
+        else if (var.CheckHasStorageFlag(StorageFlag::RowMajor))
+        {
+            modifiers = "row_major";
+        }
+        else if (options.m_forceEmitMajor && isMatrix)
+        {
+            modifiers = options.m_emitRowMajor ? "row_major" : "column_major";
+        }
+
+        auto maybeSpace = [&modifiers](){ return modifiers.empty() ? "" : " "; };
+        if (var.CheckHasStorageFlag(StorageFlag::Volatile))
+        {
+            modifiers += maybeSpace() + "volatile"s;
+        }
+        if (var.CheckHasStorageFlag(StorageFlag::Precise))
+        {
+            modifiers += maybeSpace() + "precise"s;
+        }
+        if (var.CheckHasStorageFlag(StorageFlag::Groupshared))
+        {
+            modifiers += maybeSpace() + "groupshared"s;
+        }
+        if (var.CheckHasStorageFlag(StorageFlag::Extern))
+        {
+            assert(false); // should never get there. extern is banned from azsl
+        }
+        if (var.CheckHasStorageFlag(StorageFlag::Static))
+        {
+            modifiers += maybeSpace() + "static"s;
+        }
+        if (var.CheckHasStorageFlag(StorageFlag::Const))
+        {
+            modifiers += maybeSpace() + "const"s;
+        }
+        if (var.CheckHasStorageFlag(StorageFlag::Inline))
+        {
+            // inline should not be added as modifier for variables in HLSL. But in HLSL6 can be useful for functions
+        }
+        if (var.CheckHasStorageFlag(StorageFlag::Unknown))
+        {
+            for (const auto& flag : var.m_unknownQualifiers)
+            {
+                modifiers += " " + flag;
+            }
+        }
+        return modifiers;
+    }
+
+    // static
+    string CodeEmitter::GetTypeModifier(const FunctionInfo& func, const Options& options)
+    {
+        using namespace std::string_literals;
+        string modifiers;
+
+        auto maybeSpace = [&modifiers](){ return modifiers.empty() ? "" : " "; };
+        if (func.CheckHasStorageFlag(StorageFlag::Static))
+        {
+            modifiers += maybeSpace() + "static"s;
+        }
+        if (func.CheckHasStorageFlag(StorageFlag::Inline))
+        {
+            modifiers += maybeSpace() + "inline"s;
+        }
+        return modifiers;
+    }
+
+    void CodeEmitter::EmitVariableDeclaration(const VarInfo& varInfo, const IdentifierUID& uid, const Options& options, VarDeclHasFlag declOptions) const
+    {
+        // from MSDN: https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx-graphics-hlsl-variable-syntax
+        // [Storage_Class] [Type_Modifier] Type Name[Index] [: Semantic] [: Packoffset] [: Register]; [Annotations] [= Initial_Value]
+        // example of valid HLSL statement:
+        //    static const int dim = 2;
+        //    extern uniform bool stuff[dim][1] : Color0 : register(b0) <int blabla=27; string blacksheep="Hello There";> = {{false, true}};
+        const ICodeEmissionMutator* codeMutator = m_codeMutator;
+        const CodeMutation* codeMutation = nullptr;
+        if (codeMutator && varInfo.m_declNode)
+        {
+            const auto tokenIndex = varInfo.m_declNode->start->getTokenIndex();
+            codeMutation = codeMutator->GetMutation(tokenIndex);
+        }
+
+        if (codeMutation && codeMutation->m_prepend)
+        {
+            m_out << codeMutation->m_prepend.value();
+        }
+
+        if (codeMutation && codeMutation->m_replace)
+        {
+            m_out << codeMutation->m_replace.value();
+        }
+        else
+        {
+            EmitAllAttachedAttributes(uid);
+
+            // parameter in/out modifiers
+            if (declOptions & VarDeclHas::InOutModifiers)
+            {
+                m_out << GetInputModifier(varInfo.m_typeQualifier) << " ";
+            }
+            // type qualifiers (storage class, modifiers...)
+            if (!(declOptions & VarDeclHas::NoModifiers))
+            {
+                m_out << GetTypeModifier(varInfo, options) << " ";
+            }
+            // type
+            if (!(declOptions & VarDeclHas::NoType) || (declOptions & VarDeclHas::OptionDefine))
+            {
+                m_out << GetTranslatedName(varInfo.m_typeInfoExt, UsageContext::ReferenceSite) + " ";
+            }
+            // var name
+            m_out << GetTranslatedName(uid.m_name, UsageContext::DeclarationSite);
+            // array dimensions
+            if (varInfo.m_declNode && !varInfo.m_declNode->ArrayRankSpecifiers.empty())
+            {
+                for (auto* rankCtx : varInfo.m_declNode->ArrayRankSpecifiers)
+                {
+                    // the brackets are included by the rule arrayRankSpecifier
+                    EmitText(rankCtx->getSourceInterval());
+                }
+            }
+            else if (!varInfo.GetArrayDimensions().Empty())
+            {
+                m_out << varInfo.GetArrayDimensions().ToString();
+            }
+            // semantics
+            if (varInfo.m_declNode && (declOptions & VarDeclHas::HlslSemantics))
+            {
+                if (auto* semanticOption = varInfo.m_declNode->SemanticOpt)
+                {
+                    m_out << " " + semanticOption->getText();
+                }
+            }
+
+            // init clause
+            if (declOptions & VarDeclHas::OptionDefine)
+            {
+                if (declOptions & VarDeclHas::Initializer)
+                {
+                    m_out << " = " << GetShaderKeyFunctionName(uid);
+                }
+                else
+                {
+                    m_out << " = " << JoinAllNestedNamesWithUnderscore(uid.m_name);
+                }
+            }
+            else if (declOptions & VarDeclHas::Initializer)
+            {
+                auto* initClause = varInfo.m_declNode ? varInfo.m_declNode->variableInitializer() : nullptr;
+                if (initClause)
+                {
+                    m_out << " ";
+                    EmitText(initClause->getSourceInterval());
+                }
+                else  // fallback on a potentially folded value, that has chances to work for constants like enumerators
+                {
+                    int64_t constVal;
+                    if (TryGetConstExprValueAsInt64(varInfo.m_constVal, constVal))
+                    {
+                        m_out << " = " << constVal;
+                    }
+                }
+            }
+        }
+        if (codeMutation && codeMutation->m_append)
+        {
+            m_out << codeMutation->m_append.value();
+        }
+    }
+
+    void CodeEmitter::EmitSRGCBUnified(const SRGInfo& srgInfo, IdentifierUID srgId, const Options& options, const RootSigDesc& rootSig)
+    {
+        auto bindSet = BindingPair::Set::Merged;
+        // Use the uId of the SRG instead of a CBV, because we create a dummy placeholder CBV to hold the rest of the declarations:
+        if (!options.m_emitConstantBufferBody)
+        {
+            if (srgInfo.m_implicitStruct.GetMemberFields().size() > 0)
+            {
+                const auto& bindInfo = rootSig.Get(srgId);
+
+                const QualifiedName implicitStruct = MakeSrgConstantsStructName(srgId);
+                const QualifiedName implicitCB = MakeSrgConstantsCBName(srgId);
+                EmitStruct(srgInfo.m_implicitStruct, implicitStruct, options);
+
+                const auto spaceX = (options.m_useLogicalSpaces) ? ", space" + std::to_string(bindInfo.m_registerBinding.m_pair[bindSet].m_logicalSpace) : "";
+                const auto implicitStructForEmission = GetTranslatedName(implicitStruct, UsageContext::ReferenceSite);
+                const auto implicitCBForEmission = GetTranslatedName(implicitCB, UsageContext::DeclarationSite);
+                m_out << "ConstantBuffer<" << implicitStructForEmission << "> " << implicitCBForEmission << " : register(b" << bindInfo.m_registerBinding.m_pair[bindSet].m_registerIndex << spaceX << ");\n\n";
+            }
+
+            for (auto cId : srgInfo.m_CBs)
+            {
+                EmitSRGCB(cId, options, rootSig);
+            }
+
+            return;
+        }
+
+        if (srgInfo.m_CBs.empty() && srgInfo.m_implicitStruct.GetMemberFields().empty())
+        {
+            return;
+        }
+
+        const auto& bindInfo = rootSig.Get(srgId);
+        m_out << "ConstantBuffer " << srgInfo.m_declNode->Name->getText() << "_CBContainer : register(b" << bindInfo.m_registerBinding.m_pair[bindSet].m_registerIndex << ")\n{\n";
+
+        for (const auto& cId : srgInfo.m_CBs)
+        {
+            const auto& uqName = cId.GetNameLeaf();
+            m_out << "    CBVArrayView " << uqName << ";\n";
+        }
+
+        if (!srgInfo.m_implicitStruct.GetMemberFields().empty())
+        {
+            m_out << "\n";
+            EmitStruct(srgInfo.m_implicitStruct, "", options);
+        }
+
+        m_out << "\n};\n";
+
+        for (const auto& cId : srgInfo.m_CBs)
+        {
+            const auto* memberInfo = m_ir->GetSymbolSubAs<VarInfo>(cId.m_name);
+            const auto& cbName = ReplaceSeparators(cId.m_name, Underscore);
+            const auto& uqName = cId.GetNameLeaf();
+
+            if (memberInfo->IsConstantBuffer())
+            {
+                const auto& templatedCB = "RegularBuffer<" + GetTranslatedName(memberInfo->GetGenericParameterTypeId(), UsageContext::ReferenceSite) + "> ";
+
+                m_out << "static const " << templatedCB << cbName << " = " << templatedCB << "(" << uqName << ");\n";
+            }
+        }
+
+        m_out << "\n\n";
+    }
+
+    void CodeEmitter::EmitSRGCB(const IdentifierUID& cId, const Options& options, const RootSigDesc& rootSig) const
+    {
+        EmitAllAttachedAttributes(cId);
+        auto bindSet = BindingPair::Set::Merged;
+
+        assert(!options.m_emitConstantBufferBody);
+
+        const auto& bindInfo = rootSig.Get(cId);
+        const auto* varInfo = m_ir->GetSymbolSubAs<VarInfo>(cId.m_name);
+        auto cbName = ReplaceSeparators(cId.m_name, Underscore);
+
+        assert(varInfo->IsConstantBuffer());
+        // note: instead of redoing this work ad-hoc, EmitText could be used directly on the ext type.
+        const auto genericType = "<" + GetTranslatedName(varInfo->m_typeInfoExt.m_genericParameter, UsageContext::ReferenceSite) + ">";
+
+        const string spaceX = (options.m_useLogicalSpaces) ? ", space" + std::to_string(bindInfo.m_registerBinding.m_pair[bindSet].m_logicalSpace) : "";
+        m_out << "ConstantBuffer " << genericType << " " << cbName;
+        if (bindInfo.m_isUnboundedArray)
+        {
+            m_out << "[]";
+        }
+        else if (bindInfo.m_registerRange > 1)
+        {
+            m_out << "[" << bindInfo.m_registerRange << "]";
+        }
+        m_out << " : register(b" << bindInfo.m_registerBinding.m_pair[bindSet].m_registerIndex << spaceX << ");\n\n";
+    }
+
+    void CodeEmitter::EmitSRGSampler(const IdentifierUID& sId, const Options& options, const RootSigDesc& rootSig) const
+    {
+        EmitAllAttachedAttributes(sId);
+        auto bindSet = BindingPair::Set::Merged;
+
+        const auto& bindInfo = rootSig.Get(sId);
+        const auto* varInfo = m_ir->GetSymbolSubAs<VarInfo>(sId.m_name);
+
+        const string spaceX = (options.m_useLogicalSpaces) ? ", space" + std::to_string(bindInfo.m_registerBinding.m_pair[bindSet].m_logicalSpace) : "";
+        m_out << (varInfo->m_samplerState->m_isComparison ? "SamplerComparisonState " : "SamplerState ")
+              << ReplaceSeparators(sId.m_name, Underscore);
+        if (bindInfo.m_isUnboundedArray)
+        {
+            m_out << "[]";
+        }
+        else if (bindInfo.m_registerRange > 1)
+        {
+            m_out << "[" << bindInfo.m_registerRange << "]";
+        }
+        m_out << " : register(s" << bindInfo.m_registerBinding.m_pair[bindSet].m_registerIndex << spaceX << ")";
+        m_out << ";\n\n";
+    }
+
+    //! For scope-migration-aware name emission of symbol names
+    string CodeEmitter::GetTranslatedName(QualifiedNameView mangledName, UsageContext qualification, ssize_t tokenId /*= NotOverToken*/) const
+    {
+        return m_translations.GetTranslatedName(mangledName, qualification, tokenId);
+    }
+
+    string CodeEmitter::GetTranslatedName(const IdentifierUID& uid, UsageContext qualification, ssize_t tokenId /*= NotOverToken*/) const
+    {
+        return GetTranslatedName(uid.m_name, qualification, tokenId);
+    }
+
+    string CodeEmitter::GetTranslatedName(const TypeRefInfo& typeRef, UsageContext qualification, ssize_t tokenId /*= NotOverToken*/) const
+    {
+        return GetTranslatedName(typeRef.m_typeId, qualification, tokenId);
+    }
+
+    string CodeEmitter::GetTranslatedName(const ExtendedTypeInfo& extTypeInfo, UsageContext qualification, ssize_t tokenId /*= NotOverToken*/) const
+    {
+        return GetExtendedTypeInfo(extTypeInfo,
+                                   [this, qualification, tokenId](const TypeRefInfo& tri){ return GetTranslatedName(tri, qualification, tokenId); });
+    }
+
+    void CodeEmitter::EmitSRGDataView(const IdentifierUID& tId, const Options& options, const RootSigDesc& rootSig) const
+    {
+        EmitAllAttachedAttributes(tId, Except{ { "input_attachment_index" } });
+        auto   bindSet = BindingPair::Set::Merged;
+        auto&  bindInfo = rootSig.Get(tId);
+        auto*  varInfo = m_ir->GetSymbolSubAs<VarInfo>(tId.m_name);
+        string varType = GetTranslatedName(varInfo->m_typeInfoExt, UsageContext::DeclarationSite);
+        auto   registerTypeLetter = ToLower(BindingType::ToStr(RootParamTypeToBindingType(bindInfo.m_type)));
+        optional<string> stringifiedLogicalSpace;
+        if (options.m_useLogicalSpaces)
+        {
+            stringifiedLogicalSpace = std::to_string(bindInfo.m_registerBinding.m_pair[bindSet].m_logicalSpace);
+        }
+
+        // depending on platforms we may have supplementary attributes or/and type modifier.
+        auto [prefix, suffix] = GetPlatformEmitter().GetDataViewHeaderFooter(*this, tId, bindInfo.m_registerBinding.m_pair[bindSet].m_registerIndex, registerTypeLetter, stringifiedLogicalSpace);
+        m_out << prefix;
+        // declaration of the view variable on the global scope.
+        // type unmangled_path_to_symbol [optional_array_dimension] : optional_register_binding_as_suffix
+        m_out << varType << " " << ReplaceSeparators(tId.m_name, Underscore);
+
+        if (bindInfo.m_isUnboundedArray)
+        {
+            m_out << "[]";
+        }
+        else  if (bindInfo.m_registerRange > 1)
+        {
+            m_out << "[" << bindInfo.m_registerRange << "]";
+        }
+        m_out << suffix;
+
+        auto interval = m_ir->m_scope.m_scopeIntervals[tId];
+        EmitText(interval);
+        m_out << ";\n\n";
+    }
+
+    void CodeEmitter::EmitGetShaderKeyFunctionDeclaration(const IdentifierUID& getterUid, const TypeRefInfo& returnType) const
+    {
+        m_out << GetTranslatedName(returnType, UsageContext::ReferenceSite) << " ";
+        m_out << GetShaderKeyFunctionName(getterUid);
+    }
+
+    void CodeEmitter::EmitGetShaderKeyFunction(const IdentifierUID& shaderKeyUid, const IdentifierUID& getterUid, uint32_t keySizeInBits, uint32_t keyOffsetBits, string_view defaultValue, const TypeRefInfo& returnType) const
+    {
+        // Because we use uint on the shader source side no shader option can cross the 32-bit boundary
+        // This is already ensured by the emission side, in Json::Value CodeEmitter::GetVariantList(...)
+        uint32_t arraySlot = keyOffsetBits / AZ::ShaderCompiler::kShaderVariantKeyRegisterSize;
+        keyOffsetBits -= arraySlot * AZ::ShaderCompiler::kShaderVariantKeyRegisterSize;
+        uint32_t swizzle = keyOffsetBits / AZ::ShaderCompiler::kShaderVariantKeyElementSize;
+        keyOffsetBits -= swizzle * AZ::ShaderCompiler::kShaderVariantKeyElementSize;
+
+        // Intentional unnamed scope for error-checking
+        {
+            auto& varInfo = *m_ir->GetSymbolSubAs<VarInfo>(shaderKeyUid.m_name);
+            auto dims = varInfo.m_typeInfoExt.GetDimensions();
+            assert(dims.m_dimensions.size() == 1); // This is generated variable, it must have exactly 1 array dimension
+            if (arraySlot >= dims.m_dimensions[0])
+            {
+                const string errorMessage = ConcatString("The option {", UnmangleTrimedName(getterUid.m_name), "} exceeds the number of bits (",
+                    AZ::ShaderCompiler::kShaderVariantKeyRegisterSize * dims.m_dimensions[0], ") allowed by the ShaderVariantFallback.\n",
+                    "Either increase the limit or remove some options!");
+                throw AzslcEmitterException(EMITTER_OPTION_EXCEEDING_BITS_COUNT, errorMessage);
+            }
+        }
+
+        // The most significant bits are put in the first element, then the next, etc.
+        // The bit order within an element is swapped so the most significant option
+        // is at 0x00000001 and the least significant (32th bit) is at 0x80000000.
+        // This is tailored to the runtime to reduce CB compile times.
+        const char suffix[] = { 'x', 'y', 'z', 'w' };
+
+        m_out << "\n";
+
+        const auto mask = static_cast<uint32_t>(pow(2, keySizeInBits) - 1);
+
+        EmitGetShaderKeyFunctionDeclaration(getterUid, returnType);
+
+        m_out << "\n{\n";
+        if (keySizeInBits > 0)
+        {
+            m_out << "    uint shaderKey = (" << GetTranslatedName(shaderKeyUid, UsageContext::ReferenceSite) << "[" << arraySlot << "]." << suffix[swizzle] << " >> " << keyOffsetBits << ") & " << mask << ";\n";
+            m_out << "    return (" << GetTranslatedName(returnType, UsageContext::ReferenceSite) << ") shaderKey;\n";
+        }
+        else
+        {
+            m_out << "    " << GetTranslatedName(returnType, UsageContext::ReferenceSite) << " val = " << defaultValue << ";\n";
+            m_out << "    return val;\n";
+        }
+        m_out << "}\n\n";
+    }
+
+    void CodeEmitter::EmitSRG(const SRGInfo& srgInfo, const IdentifierUID& srgId, const Options& options, const RootSigDesc& rootSig)
+    {
+        RootSigDesc::SrgDesc srgDesc;
+        srgDesc.m_uid = srgId;
+
+        m_out << "/* Generated code from\n";
+        // We don't emit the SRG attributes (only as a comment), but they can be accessed by the srgId if needed
+        EmitAllAttachedAttributes(srgId);
+        m_out << "ShaderResourceGroup " << srgInfo.m_declNode->Name->getText() << "\n";
+        m_out << "*/\n";
+
+        for (const auto& t : srgInfo.m_srViews)
+        {
+            EmitSRGDataView(t, options, rootSig);
+        }
+
+        for (const auto& s : srgInfo.m_samplers)
+        {
+            EmitSRGSampler(s, options, rootSig);
+        }
+
+        EmitSRGCBUnified(srgInfo, srgId, options, rootSig);
+
+        if (srgInfo.m_shaderVariantFallback)
+        {
+            m_shaderVariantFallbackUid = *srgInfo.m_shaderVariantFallback;
+        }
+    }
+
+    // override of the base method, to incorporate symbol and expression mutations
+    void CodeEmitter::GetTextInStream(misc::Interval interval, std::ostream& output) const
+    {
+        const ICodeEmissionMutator* codeMutator = m_codeMutator;
+
+        ssize_t ii = interval.a;
+        bool wasInPreprocessorDirective = false;  // record a state to detect exit of directives, because they need to reside on their own lines
+        while (ii <= interval.b)
+        {
+            auto* token = GetNextToken(ii /*inout*/);
+            const auto tokenIndex = token->getTokenIndex();
+
+            const CodeMutation* codeMutation = codeMutator ? codeMutator->GetMutation(tokenIndex) : nullptr;
+            if (codeMutation && codeMutation->m_prepend)
+            {
+                output << codeMutation->m_prepend.value();
+            }
+
+            if (codeMutation && codeMutation->m_replace)
+            {
+                output << codeMutation->m_replace.value();
+            }
+            else
+            {
+                // Access the AST from the token. Note that any processing relying on this, amounts to a hacky shortcut.
+                // (taken to make the economy of having to produce a specific emitters, for each AST-node/code-construct in HLSL)
+                auto* astNode = m_ir->m_tokenMap.GetNode(tokenIndex);
+                // watch for potential mutations in the middle of the original source.
+                auto [originalSymbol, endToken] = m_translations.OverOriginalDefinitionOf(token->getTokenIndex());
+                bool emitAsIs = originalSymbol.empty();
+                if (!emitAsIs)
+                {
+                    auto* withinVarDecl = ExtractSpecificParent<azslParser::VariableDeclarationStatementContext>(astNode);
+                    // if we are over a variable declaration, then it's a more evolved statement than just a struct/class declaration
+                    // in such a case, the introduction of the type (declaration) is immediately used in a larger statement,
+                    // so we need to re-emit it as a reference.
+                    if (withinVarDecl && token == withinVarDecl->variableDeclaration()->type()->start)
+                    {
+                        output << GetTranslatedName(originalSymbol, UsageContext::ReferenceSite, ii) << " ";
+                    }
+                    // that and anything else: pure UDT declarations, or typeof/typealias may be jumped over completely
+                    ii = endToken + 1;
+                }
+                else
+                {
+                    auto idExpr = m_translations.GetIdExpression(token);
+                    if (!idExpr.IsEmpty())
+                    {
+                        auto getToken = [this](ssize_t& tokenId) -> string
+                        {
+                            assert(tokenId >= 0);
+                            auto* token = m_tokens->get(static_cast<size_t>(tokenId));
+                            return token->getChannel() == Token::DEFAULT_CHANNEL ? token->getText() : string{};
+                        };
+                        output << m_translations.TranslateIdExpression(idExpr, ii, getToken) << " ";
+                        ii += idExpr.m_span.length() - 1;
+                        emitAsIs = false;
+                    }
+                }
+                if (emitAsIs)
+                {   // do minimal reformatting to have a pseudo-readable emitted code
+                    auto str = token->getText();
+                    bool lineFeed = str == ";" || str == "{";
+                    output << str << (lineFeed ? '\n' : ' ');
+                }
+            }
+
+            if (codeMutation && codeMutation->m_append)
+            {
+                output << codeMutation->m_append.value();
+            }
+        }
+    }
+
+    void CodeEmitter::EmitText(misc::Interval interval) const
+    {
+        // extract interval (with necessary internal translations) and add it to m_out stream
+        GetTextInStream(interval, m_out);
+    }
+}

+ 203 - 0
src/AzslcEmitter.h

@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include "AzslcBackend.h"
+#include "AzslcSymbolTranslation.h"
+#include "AzslcCodeEmissionMutator.h"
+
+namespace Json
+{
+    class Value;
+}
+
+namespace AZ::ShaderCompiler
+{
+    enum class EmitFunctionAs
+    {
+        Declaration,
+        Definition
+    };
+
+    struct CodeEmitter : Backend
+    {
+        using Backend::Backend;
+
+        //! Create a companion database of mutations on the IR, through which the emitter backend can query symbols scope and names.
+        //! The state of changes is stored in the AZ::ShaderCompiler::SymbolTranslation class
+        //! @param options  user configuration parsed from command line
+        void SetupScopeMigrations(const Options& options);
+
+        //! Execute code emission
+        //! @param options  user configuration parsed from command line
+        void Run(const Options& options);
+
+        //! For scope-migration-aware name emission of symbol names
+        string GetTranslatedName(QualifiedNameView mangledName, UsageContext qualification, ssize_t tokenId = NotOverToken) const;
+
+        string GetTranslatedName(const IdentifierUID& uid, UsageContext qualification, ssize_t tokenId = NotOverToken) const;
+
+        string GetTranslatedName(const TypeRefInfo& typeRef, UsageContext qualification, ssize_t tokenId = NotOverToken) const;
+
+        string GetTranslatedName(const ExtendedTypeInfo& extType, UsageContext qualification, ssize_t tokenId = NotOverToken) const;
+
+        //! Write the HLSL formatted shape of an attribute into a stream
+        static void EmitAttribute(const AttributeInfo& attrInfo, std::ostream& outstream);
+
+        void SetCodeMutator(ICodeEmissionMutator* codeMutator) { m_codeMutator = codeMutator; }
+
+    protected:
+
+        //! Emits the closest preprocessor generated "#line <int> <filepath>" directive located before
+        //! @originalLineNumber. Keeps track of the best finds so the #line directives are not emitted more than once.
+        void EmitPreprocessorLineDirective(size_t originalLineNumber);
+
+        //! Emits the closest preprocessor generated "#line <int> <filepath>" directive located near
+        //! @symbolName. See above, EmitPreprocessorLineDirective (size_t), for more details.
+        void EmitPreprocessorLineDirective(QualifiedNameView symbolName);
+
+        void EmitStruct(const ClassInfo& classInfo, string_view structName, const Options& options);
+
+        void EmitAttribute(const AttributeInfo& attrInfo) const;
+
+        void EmitFunction(const FunctionInfo& funcSub, const IdentifierUID& id, EmitFunctionAs entityConfiguration, const Options& options);
+
+        void EmitTypeAlias(const IdentifierUID& uid, const TypeAliasInfo& aliasInfo) const;
+
+        void EmitEnum(const IdentifierUID& uid, const ClassInfo& classInfo, const Options& options);
+
+		//! Emits root constants
+        void EmitRootConstants(const RootSigDesc& rootSig, const Options& options) const;
+
+		//! Emits get function declarations for root constant members
+        void EmitGetterFunctionDeclarationsForRootConstants(const IdentifierUID& uid) const;
+		
+        struct Except : std::initializer_list<string>
+        {};
+        //! Emit all attributes accumulated over a symbol. Omit an optional list of attributes passed as 2nd argument.
+        void EmitAllAttachedAttributes(const IdentifierUID& uid, Except = {}) const;
+
+        //! Emits get function definitions for root constants
+        void EmitGetFunctionsForRootConstants(const ClassInfo& classInfo, string_view bufferName) const;
+
+        // for all sorts of const/static/groupshared/matrixmajor....
+        static string GetTypeModifier(const VarInfo&, const Options& options);
+
+        // for all sorts of inline/static/....
+        static string GetTypeModifier(const FunctionInfo&, const Options& options);
+
+        //! That is a list of code elements we possibly want to emit (e.g when we emit a variable declaration)
+        MAKE_REFLECTABLE_ENUM_POWER( VarDeclHas,
+            InOutModifiers,  // when we emit in the context of function parameters, HLSL in out keywords are important
+            HlslSemantics,   //  : TEXCOORD sort of element
+            Initializer,     // = val;  sort of element
+            OptionDefine,    // an option define is not a code element but an indicator that we're emitting a shader option
+            NoType,          // in the usual declaration 'Type varname', omit the type. this is used for enumerators
+            NoModifiers      // modifiers are storage flags (const, precise, rowmajor...)
+        );
+
+        using VarDeclHasFlag = Flag<VarDeclHas>;
+
+        void EmitVariableDeclaration(const VarInfo&, const IdentifierUID& uid, const Options& options, VarDeclHasFlag declOptions) const;
+
+        //! Iter must be an iterator over FunctionInfo::Parameter elements
+        template <typename Iter>
+        void EmitParameters(Iter begin, Iter end, const Options& options, bool withInitializer)
+        {
+            for (auto it = begin;
+                      it != end;
+                    ++it)
+            {
+                const FunctionInfo::Parameter& param = *it;
+                auto* varInfo = m_ir->GetSymbolSubAs<VarInfo>(param.m_varId.GetName());
+                if (varInfo)
+                {
+                    auto flag = VarDeclHasFlag(VarDeclHas::InOutModifiers) | VarDeclHas::HlslSemantics;
+                    flag |= withInitializer ? VarDeclHas::Initializer : VarDeclHas::EnumType(0);
+                    EmitVariableDeclaration(*varInfo, param.m_varId, options, flag);
+                }
+                else
+                {
+                    m_out << GetInputModifier(param.m_typeQualifier) << " ";
+
+                    m_out << GetTranslatedName(param.m_typeInfo, UsageContext::ReferenceSite);
+
+                    if (!param.m_arrayRankSpecifiers.empty())
+                    {
+                        for (auto* rankCtx : param.m_arrayRankSpecifiers)
+                        {
+                            EmitText(rankCtx->getSourceInterval());
+                        }
+                    }
+
+                    if (param.m_defaultValueExpression)
+                    {
+                        EmitText(param.m_defaultValueExpression->getSourceInterval());
+                    }
+                }
+
+                bool lastIteration = it + 1 == end;
+                if (!lastIteration)
+                {
+                    m_out << ", ";
+                }
+            }
+        }
+
+        //! NotOverToken is for procedurally generated code, that doesn't have an original source in terms of token.
+        static const ssize_t NotOverToken = -1;
+
+        void EmitSRGCBUnified(const SRGInfo& srgInfo, IdentifierUID srgId, const Options& options, const RootSigDesc& rootSig);
+
+        void EmitSRGCB(const IdentifierUID& cId, const Options& options, const RootSigDesc& rootSig) const;
+
+        void EmitSRGSampler(const IdentifierUID& sId, const Options& options, const RootSigDesc& rootSig) const;
+
+        void EmitSRGDataView(const IdentifierUID& tId, const Options& options, const RootSigDesc& rootSig) const;
+
+        void EmitGetShaderKeyFunctionDeclaration(const IdentifierUID& getterUid, const TypeRefInfo& returnType) const;
+
+        void EmitGetShaderKeyFunction(const IdentifierUID& shaderKeyUid, const IdentifierUID& getterUid, uint32_t size, uint32_t offset, string_view defaultValue, const TypeRefInfo& returnType) const;
+
+        //! Will emit SRG content in the shape of HLSL transformed resource, e.g a constant buffer struct for the SRG variables.
+        void EmitSRG(const SRGInfo& srgInfo, const IdentifierUID& srgId, const Options& options, const RootSigDesc& rootSig);
+
+        //! Advanced logic (targeted transpilation transforms included) interval-as-text extractor from source token stream
+        void GetTextInStream(misc::Interval interval, std::ostream& output) const override;
+
+        //! Will copy function body original tokens, skipping comments, reformatting if possible, and translating variable declarations when needed, as well as mutating reference names of migrated SRG contents.
+        void EmitText(misc::Interval interval) const;
+
+        //! Move a symbol to a different scope. Currently used to strip SRGs of their symbols, so that SRGs are effectively erased.
+        void MigrateASTSubTree(const IdentifierUID& azslSymbol, QualifiedNameView landingScope);
+
+        //! Verifies if a symbol is in the global scope (in the IR "viewed" through the translation)
+        bool IsTopLevelThroughTranslation(const IdentifierUID& uid) const;
+
+        //! Emits a single shader option variable as a static const
+        void EmitShaderVariantOptionVariableDeclaration(const IdentifierUID& symbolUid, const Options& options) const;
+
+        //! Emits all shader option variable fallback getters
+        void EmitShaderVariantOptionGetters(const Options& options) const;
+
+        //! Stateful check to normalize redundant declarations
+        bool AlreadyEmittedFunctionDeclaration(const IdentifierUID& uid) const;
+        bool AlreadyEmittedFunctionDefinition(const IdentifierUID& uid) const;
+
+        SymbolTranslation m_translations;
+        unordered_set<IdentifierUID> m_alreadyEmittedFunctionDeclarations;
+        unordered_set<IdentifierUID> m_alreadyEmittedFunctionDefinitions;
+        unordered_set<size_t> m_alreadyEmittedPreprocessorLineDirectives;
+
+        IdentifierUID m_shaderVariantFallbackUid;
+        
+        //! If not null it will be used during code emission to produce
+        //! the mutations. 
+        ICodeEmissionMutator* m_codeMutator = nullptr;
+    };
+}

+ 357 - 0
src/AzslcException.h

@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include "StdUtils.h"
+#include "GenericUtils.h"
+#include "PreprocessorLineDirectiveFinder.h"
+
+#include "antlr4-runtime.h"
+
+using namespace antlr4;
+using namespace antlrcpp;
+
+// NOTE: number the enum entries for quicker look-up
+namespace AZ::ShaderCompiler
+{
+    enum AzslcErrorCode : uint16_t
+    {
+        PARSER_SYNTAX_ERROR = 1u,
+
+        // orchestrator error codes
+        ORCHESTRATOR_DEPORTED_METHOD_DEFINITION = 2u,
+        ORCHESTRATOR_DEFINITION_FOREIGN_SCOPE = 3u,
+        ORCHESTRATOR_NO_DECLERATION = 4u,
+        ORCHESTRATOR_UNEXPECTED_KIND = 5u,
+        ORCHESTRATOR_OVERLY_QUALIFIED = 6u,
+        ORCHESTRATOR_DEPORTED_METHOD = 7u,
+        ORCHESTRATOR_FUNCTION_ALREADY_DEFINED = 8u,
+        ORCHESTRATOR_INVALID_INLINED_QUALIFIER = 9u,
+        ORCHESTRATOR_INVALID_NONGLOBAL_OPTION_OR_ROOTCONSTANT = 11u,
+        ORCHESTRATOR_ILLEGAL_GLOBAL_VARIABLE = 12u,
+        ORCHESTRATOR_ILLEGAL_MEMBER_VARIABLE_IN_INTERFACE = 13u,
+        ORCHESTRATOR_ILLEGAL_FOLDABLE_ARRAY_DIMENSIONS = 14u,
+        ORCHESTRATOR_INVALID_QUALIFIER_MIX = 15u,
+        ORCHESTRATOR_UNSPECIFIED_BASE_SYMBOL = 16u,
+        ORCHESTRATOR_INVALID_INTERFACE = 17u,
+        ORCHESTRATOR_CLASS_REDEFINE = 18u,
+        ORCHESTRATOR_UNREGISTERED_METHOD = 19u,
+        ORCHESTRATOR_HIDING_SYMBOL_BASE = 20u,
+        ORCHESTRATOR_INVALID_OVERRIDE_SPECIFIER_CLASS = 21u,
+        ORCHESTRATOR_INVALID_OVERRIDE_SPECIFIER_BASE = 22u,
+        ORCHESTRATOR_INVALID_SEMANTIC_DECLARATION = 23u,
+        ORCHESTRATOR_INVALID_SEMANTIC_DECLARATION_TYPE = 24u,
+        ORCHESTRATOR_INVALID_EXTERNAL_BOUND_RESOURCE_VIEW = 25u,
+        ORCHESTRATOR_INVALID_GENERIC_TYPE_CONSTANTBUFFER = 26u,
+        ORCHESTRATOR_UNDECLARED_GENERIC_TYPE_CONSTANTBUFFER = 27u,
+        ORCHESTRATOR_INVALID_GENERIC_TYPE_CONSTANTBUFFER_STRUCT = 28u,
+        ORCHESTRATOR_LITERAL_REQUIRED_SRG_SEMANTIC = 29u,
+        ORCHESTRATOR_INVALID_INTEGER_CONSTANT = 30u,
+        ORCHESTRATOR_INVALID_RANGE_FREQUENCY_ID = 31u,
+        ORCHESTRATOR_ODR_VIOLATION = 32u,  // One Definition Rule
+        ORCHESTRATOR_DISALLOWED_FUNCTION_MODIFIER = 33u,
+        ORCHESTRATOR_MULTIPLE_HIDDEN_SYMBOLS = 34u,
+        ORCHESTRATOR_SCOPE_NOT_FOUND = 35u,
+        ORCHESTRATOR_INVALID_TYPEALIAS_TARGET = 36u,
+        ORCHESTRATOR_NO_DOUBLE_DEFAULT_DECLARATION = 37u,
+        ORCHESTRATOR_NO_DEFAULT_PARAM_WITH_OVERLOADS = 38u,
+        ORCHESTRATOR_FUNCTION_INCONSISTENT_RETURN_TYPE = 39u,
+        ORCHESTRATOR_NO_INLINE_UDT_IN_PARAMETERS = 40u,
+        ORCHESTRATOR_OVERLOAD_RESOLUTION_HARD_FAILURE = 41u,
+        ORCHESTRATOR_EXTERNAL_VARIABLE_WITH_INITIALIZER = 42u,
+        ORCHESTRATOR_MEMBER_VARIABLE_WITH_INITIALIZER = 43u,
+        ORCHESTRATOR_SRG_REUSES_A_FREQUENCY = 44u,
+        ORCHESTRATOR_NON_PACKABLE_TYPE_IN_SRG_CONSTANT = 45u,
+        ORCHESTRATOR_TRYING_TO_EXTEND_NOT_PARTIAL_SRG = 46u,
+        ORCHESTRATOR_SRG_EXTENSION_HAS_DIFFERENT_SEMANTIC = 47u,
+        ORCHESTRATOR_UNBOUNDED_RESOURCE_ISSUE = 48u,
+
+
+        // Treat all compiler warnings as errors
+        WX_WARNINGS_AS_ERRORS = 127u,
+
+        // intermediate representation error codes
+        IR_MULTIPLE_SRG_FALLBACK  = 128u,
+        IR_NO_FALLBACK_ASSIGNED = 129u,
+        IR_SRG_WITHOUT_SEMANTIC = 130u,
+
+        // emitter error codes
+        EMITTER_INVALID_ARRAY_DIMENSIONS = 256u,
+        EMITTER_RECURSION_NOT_PERMITTED = 257u,
+        EMITTER_OVERFLOW_BIT_BOUNDARY = 258u,
+        EMITTER_OPTION_EXCEEDING_BITS_COUNT = 259u,
+        EMITTER_INTEGER_RANGE_NEEDS_ATTRIBUTE = 260u,
+        EMITTER_INTEGER_RANGE_MIN_IS_NOT_CONST = 261u,
+        EMITTER_INTEGER_RANGE_MAX_IS_NOT_CONST = 262u,
+        EMITTER_INTEGER_RANGE_MIN_IS_BIGGER_THAN_MAX = 263u,
+        EMITTER_INTEGER_HAS_NO_RANGE = 264u,
+        EMITTER_OPTION_HAS_UNSUPPORTED_TYPE = 265u,
+
+        // others
+        ADVANCED_SYNTAX_CONSTANT_BUFFER_RESTRICTION = 512u,
+        ADVANCED_SYNTAX_CONSTANT_BUFFER_ONLY_IN_SRG = 513u,
+        ADVANCED_SYNTAX_DOUBLE_SCOPE_RESOLUTION = 514u,
+        ADVANCED_RESERVED_NAME_USED = 515u,
+    };
+
+    class AzslcException : public antlr4::RuntimeException
+    {
+    public:
+        AzslcException(uint32_t errorCode, const char* const errorType, optional<size_t> line, optional<size_t> column, const string& message)
+            : antlr4::RuntimeException(message),
+                m_errorCode(errorCode),
+                m_errorType(errorType),
+                m_line(line),
+                m_column(column), 
+                m_errorMessage("")
+        {
+            BakeErrorMessage();
+        }
+
+        AzslcException(uint32_t errorCode, const char* const errorType, Token* token, const string& message)
+            : RuntimeException(message),
+                m_token(token),
+                m_errorCode(errorCode),
+                m_errorType(errorType),
+                m_errorMessage("")
+        {
+            if (m_token)
+            {
+                m_line = m_token->getLine();
+                m_column = m_token->getCharPositionInLine();
+            }
+            else
+            {
+                m_line = none;
+                m_column = none;
+            }
+
+            BakeErrorMessage();
+        }
+
+        AzslcException(uint32_t errorCode, const char* const errorType, const string& message)
+            : RuntimeException(message), 
+                m_token(nullptr), 
+                m_errorCode(errorCode), 
+                m_errorType(errorType),
+                m_line(none),
+                m_column(none),
+                m_errorMessage("")
+        {
+            BakeErrorMessage();
+        }
+
+        const char* what() const NOEXCEPT
+        {
+            return m_errorMessage.c_str();
+        }
+
+        inline uint16_t GetErrorCode() const
+        {
+            return m_errorCode;
+        }
+
+        static string MakeErrorMessage(string_view line, string_view column, string_view errorType, bool error, string_view code, string_view message)
+        {
+            return ConcatString(s_currentSourceFileName,
+                                "(", line, ",", column, ") : ",
+                                errorType,
+                                error ? " error" : " warning",
+                                code.empty() ? "" : " #", code, ": ",
+                                message);
+        }
+
+    protected:
+        void BakeErrorMessage()
+        {
+            if (s_sourceFileLineNumber)
+            {
+                m_line = *s_sourceFileLineNumber;
+            }
+            m_errorMessage = MakeErrorMessage(m_line ? ToString(*m_line) : "",
+                                              m_column ? ToString(*m_column) : "",
+                                              m_errorType ? m_errorType : "",
+                                              m_errorCode != WX_WARNINGS_AS_ERRORS,
+                                              ToString(m_errorCode),
+                                              RuntimeException::what());
+        }
+
+    public:
+        //! global filename for error messages. visual studio standard build-tool error format is:
+        //! {filename(line# [, column#]) | toolname} : [ any text ] {error | warning} code+number:localizable string [ any text ]
+        //! so to respect this, we're going to simplify the API by setting the report file here.
+        static inline string s_currentSourceFileName;
+
+        //! If set, it overrides @m_line.
+        static inline optional<size_t> s_sourceFileLineNumber;
+
+    protected:
+        const uint16_t m_errorCode;
+        const char* const m_errorType;
+        const Token* m_token;
+        string m_errorMessage;
+        optional<size_t> m_line;
+        optional<size_t> m_column;
+    };
+
+    class AzslcOrchestratorException final : public AzslcException
+    {
+    private:
+        inline static const char* const ErrorType = "Semantic";
+
+    public:
+        AzslcOrchestratorException(uint32_t errorCode, optional<size_t> line,optional<size_t> column, const string& message)
+            : AzslcException(errorCode,
+                ErrorType,
+                line,
+                column,
+                message)
+        {
+        }
+
+        AzslcOrchestratorException(uint32_t errorCode, Token* token, const string& message)
+            : AzslcException(errorCode, 
+                ErrorType,
+                token,
+                message)
+        {
+        }
+
+        AzslcOrchestratorException(uint32_t errorCode, const string& message)
+            : AzslcException(errorCode,
+                ErrorType,
+                message)
+        {
+        }
+    };
+
+    class AzslcIrException final : public AzslcException
+    {
+    private:
+        inline static const char* const ErrorType = "IR";
+
+    public:
+        AzslcIrException(uint32_t errorCode, const string& message)
+            : AzslcException(errorCode, 
+                ErrorType,
+                message)
+        {
+        }
+    };
+
+    class AzslcEmitterException final : public AzslcException
+    {
+    private:
+        inline static const char* const ErrorType = "Emitter";
+
+    public:
+        AzslcEmitterException(uint32_t errorCode, optional<size_t> line, optional<size_t> column, const string& message)
+            : AzslcException(errorCode,
+                ErrorType,
+                line,
+                column,
+                message)
+        {
+        }
+
+        AzslcEmitterException(uint32_t errorCode, Token* token, const string& message)
+            : AzslcException(errorCode, 
+                ErrorType,
+                token,
+                message)
+        {
+        }
+
+        AzslcEmitterException(uint32_t errorCode, const string& message)
+            : AzslcException(errorCode,
+                ErrorType,
+                message)
+        {
+        }
+    };
+
+    class AzslParserEventListener final : public antlr4::BaseErrorListener
+    {
+    public:
+        explicit AzslParserEventListener(PreprocessorLineDirectiveFinder& lineDirectiveFinder) : m_lineDirectiveFinder(lineDirectiveFinder) {}
+        AzslParserEventListener() = delete;
+
+        void syntaxError(antlr4::Recognizer* recognizer, antlr4::Token* offendingSymbol, size_t line,
+            size_t charPositionInLine, const string &msg, std::exception_ptr e) override
+        {
+            string errorMessage;
+            const LineDirectiveInfo* lineDirectiveInfo = m_lineDirectiveFinder.GetNearestPreprocessorLineDirective(line);
+            if (!lineDirectiveInfo)
+            {
+                errorMessage = std::move(AzslcException::MakeErrorMessage(ToString(line),
+                    ToString(charPositionInLine + 1),
+                    "syntax",
+                    true,
+                    ToString(static_cast<int>(PARSER_SYNTAX_ERROR)),
+                    msg));
+            }
+            else
+            {
+                auto absoluteLineNumberInIncludedFile = m_lineDirectiveFinder.GetLineNumberInOriginalSourceFile(*lineDirectiveInfo, line);
+                AzslcException::s_currentSourceFileName = lineDirectiveInfo->m_containingFilename;
+                errorMessage = std::move(AzslcException::MakeErrorMessage(ToString(absoluteLineNumberInIncludedFile),
+                    ToString(charPositionInLine + 1),
+                    "syntax",
+                    true,
+                    ToString(static_cast<int>(PARSER_SYNTAX_ERROR)),
+                    msg));
+            }
+
+            antlr4::ParseCancellationException parseException(errorMessage);
+            if (e)
+            {
+                throw_with_nested(parseException);
+            }
+            else
+            {
+                throw parseException;
+            }
+        }
+
+        void reportAmbiguity(antlr4::Parser *recognizer, const antlr4::dfa::DFA &dfa, size_t startIndex, size_t stopIndex, bool exact,
+            const antlrcpp::BitSet &ambigAlts, antlr4::atn::ATNConfigSet *configs) override
+        {
+        }
+
+        void reportAttemptingFullContext(antlr4::Parser *recognizer, const antlr4::dfa::DFA &dfa, size_t startIndex, size_t stopIndex,
+            const antlrcpp::BitSet &conflictingAlts, antlr4::atn::ATNConfigSet *configs) override
+        {
+        }
+
+        void reportContextSensitivity(antlr4::Parser *recognizer, const antlr4::dfa::DFA &dfa, size_t startIndex, size_t stopIndex,
+            size_t prediction, antlr4::atn::ATNConfigSet *configs) override
+        {
+        }
+
+    private:
+        PreprocessorLineDirectiveFinder& m_lineDirectiveFinder;
+    };
+
+    inline void OutputNestedAndException(const exception& e)
+    {
+        std::cerr << e.what() << std::endl;
+        try
+        {
+            rethrow_if_nested(e);
+        }
+        catch (const exception& ne)
+        {
+            OutputNestedAndException(ne);
+        }
+        catch (...)
+        {
+            std::cerr << "Unknown exception" << std::endl;
+        }
+    }
+};

+ 164 - 0
src/AzslcHomonymVisitor.h

@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include "AzslcKindInfo.h"
+
+namespace AZ::ShaderCompiler
+{
+    //! Strategy for the visitation extent of relationship
+    MAKE_REFLECTABLE_ENUM_POWER( RelationshipExtent
+                                ,Self                  // include the startup symbol
+                                ,Reference             // the seenat collection of a symbol
+                                ,Family                // up and down list of overrides
+                                ,OverloadSet           // group of overloads
+                                ,Recursive             // explore the entire homonymous graph, including e.g children's overloads's references. And e.g the base of an overload of a child
+    );
+    using RelationshipExtentFlag = Flag<RelationshipExtent>;
+
+    //! Facility to iterate over a graph of symbol appearances and relationships throughout the source.
+    //! as defined by the document image ../../Documentation/HomonymVisitation.png
+    class HomonymVisitor
+    {
+    public:
+        using SymbolGetterType = std::function< KindInfo* (QualifiedNameView) >;
+
+        //! @param symbolGetter     A closure callback that allows this object to be decoupled from other classes in this software.
+        //!                         Usually it would be bound to GetIdAndKindInfo of the SymbolAggregator.
+        //!                         Its signature is "takes a QualifiedNameView and return KindInfo*"
+        HomonymVisitor(SymbolGetterType symbolGetter)
+            : m_getInfo(symbolGetter)
+        {}
+
+        //! Start function. Clients call this.
+        //! @param symbol               Startup symbol of the discovery
+        //! @param functor              A callback function-object of any sort, of the signature "takes a Seenat, a RelationshipExtent and returns void"
+        //! @param walkConfiguration    The visitation strategy. You can add flag elements using operator |
+        //!                             e.g  RelationshipExtentFlag{ RelationshipExtent::Self } | RelationshipExtent::Reference
+        template< typename FunctorType >
+        void operator() (const IdentifierUID& symbol, FunctorType&& functor, RelationshipExtentFlag walkConfiguration) const
+        {
+            KindInfo* info = m_getInfo(symbol.GetName());
+            assert(info);
+            unordered_set<IdentifierUID> visited;
+            // visit self if requested
+            if (walkConfiguration & RelationshipExtent::Self)
+            {
+                Seenat at {symbol, GetDefinitionTokensLocation(*info)};
+                functor(at, RelationshipExtent::Self);
+            }
+            return Walk(symbol, std::forward<FunctorType&&>(functor), walkConfiguration, visited);
+        }
+    private:
+
+        template< typename FunctorType >
+        void Walk(const IdentifierUID& symbol, FunctorType&& functor, RelationshipExtentFlag walkConfiguration, unordered_set<IdentifierUID>& visitedSet) const
+        {
+            if (visitedSet.find(symbol) != visitedSet.end())
+            {
+                return;
+            }
+            // mark self as visited, immediately; to avoid re-entrancy.
+            visitedSet.insert(symbol);
+
+            // visit natural seenats if requested
+            if (walkConfiguration & RelationshipExtent::Reference)
+            {
+                VisitSeenats(symbol, functor);
+            }
+            // visit family if requested
+            if (walkConfiguration & RelationshipExtent::Family)
+            {
+                VisitFamily(symbol, functor, walkConfiguration, visitedSet);
+            }
+            // visit overload-set if requested
+            if (walkConfiguration & RelationshipExtent::OverloadSet)
+            {
+                VisitOverloadSet(symbol, functor, walkConfiguration, visitedSet);
+            }
+        }
+
+        template< typename FunctorType >
+        void VisitOverloadSet(const IdentifierUID &symbol, FunctorType&& functor, RelationshipExtentFlag walkConfiguration, unordered_set<IdentifierUID>& visitedSet) const
+        {
+            KindInfo& kind = *m_getInfo(symbol.GetName());
+            if (kind.GetKind() == Kind::Function)
+            {
+                string_view core = RemoveLastParenthesisGroup(symbol.GetName());  // strip the function decoration to find the symbol
+                // visit itself (it's interesting to report the naked set itself, and its references are all the unresolved call sites)
+                VisitDefinitionIdentifier(IdentifierUID{core}, functor, RelationshipExtent::OverloadSet, walkConfiguration, visitedSet);
+                KindInfo* overloadSet = m_getInfo(QualifiedNameView{core});
+                // visit the concrete members of that set:
+                auto& setInfo = overloadSet->GetSubRefAs<OverloadSetInfo>();
+                setInfo.ForEach([&](const IdentifierUID& brother)
+                                {
+                                    VisitDefinitionIdentifier(brother, functor, RelationshipExtent::OverloadSet, walkConfiguration, visitedSet);
+                                });
+            }
+        }
+
+        template< typename FunctorType >
+        void VisitFamily(const IdentifierUID& symbol, FunctorType&& functor, RelationshipExtentFlag walkConfiguration, unordered_set<IdentifierUID>& visitedSet) const
+        {
+            KindInfo& kind = *m_getInfo(symbol.GetName());
+            if (kind.GetKind() == Kind::Function)  // for now only functions have families. we'll have to abstract family access if we implement properties.
+            {
+                auto& fInfo = kind.GetSubRefAs<FunctionInfo>();
+                // children visit
+                for (const IdentifierUID& overrideUid : fInfo.m_overrides)
+                {
+                    VisitDefinitionIdentifier(overrideUid, functor, RelationshipExtent::Family, walkConfiguration, visitedSet);
+                }
+                // parent visit
+                if (fInfo.m_base)
+                {
+                    VisitDefinitionIdentifier(*fInfo.m_base, functor, RelationshipExtent::Family, walkConfiguration, visitedSet);
+                }
+            }
+        }
+
+        template< typename FunctorType >
+        void VisitSeenats(const IdentifierUID& symbol, FunctorType&& functor) const
+        {
+            auto& seenats = GetSeenats(symbol);
+            for (auto&& at : seenats)
+            {
+                functor(at, RelationshipExtent::Reference); // callback on reference. which are leaves. no need to add to visited set.
+            }
+        }
+
+        //! This function is named this way because of the opposition with "direct references" which already have seenats.
+        //! definition locations don't register a seenat, so we need to re-synthesize one.
+        template< typename FunctorType >
+        void VisitDefinitionIdentifier(const IdentifierUID& symbol, FunctorType&& functor, RelationshipExtent visitCategory, RelationshipExtentFlag walkConfiguration, unordered_set<IdentifierUID>& visitedSet) const
+        {
+            if (visitedSet.find(symbol) == visitedSet.end())
+            {
+                auto& info = *m_getInfo(symbol.GetName());
+                Seenat at{symbol, GetDefinitionTokensLocation(info)};
+                functor(at, visitCategory); // callback client
+                if (walkConfiguration & RelationshipExtent::Recursive)
+                {
+                    Walk(symbol, functor, walkConfiguration, visitedSet);  // walk will mark symbol as visited upon entrance
+                }
+                else
+                {
+                    visitedSet.insert(symbol); // mark as visited
+                }
+            }
+        }
+
+        const vector<Seenat>& GetSeenats(const IdentifierUID& uid) const
+        {
+            auto& kind = *m_getInfo(uid.GetName());
+            return kind.GetSeenats();
+        }
+
+        SymbolGetterType m_getInfo;
+    };
+}

+ 638 - 0
src/AzslcIntermediateRepresentation.cpp

@@ -0,0 +1,638 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include "AzslcIntermediateRepresentation.h"
+
+namespace AZ::ShaderCompiler
+{
+    using std::cout;
+    using std::endl;
+
+    void IntermediateRepresentation::RegisterAttributeSpecifier(AttributeScope scope,
+                                                                AttributeCategory category,
+                                                                size_t declarationLine,
+                                                                string_view space,
+                                                                string_view name,
+                                                                azslParser::AttributeArgumentListContext* argList)
+    {
+        AttributeInfo attrInfo;
+        attrInfo.m_scope      = scope;
+        attrInfo.m_category   = category;
+        attrInfo.m_lineNumber = declarationLine;
+        attrInfo.m_namespace  = space;
+        attrInfo.m_attribute  = name;
+
+        if (argList)
+        {
+            for (auto arg : argList->attributeArguments()->literal())
+            {
+                if (!arg->StringLiteral().empty())
+                {
+                    for (int i = 0; i < arg->StringLiteral().size(); ++i)
+                    {
+                        attrInfo.m_argList.push_back(arg->StringLiteral(i)->getText());
+                    }
+                }
+                else if (arg->IntegerLiteral())
+                {
+                    attrInfo.m_argList.push_back(m_sema.FoldEvalStaticConstExprNumericValue(arg->IntegerLiteral()));
+                }
+                else if (arg->FloatLiteral())
+                {
+                    attrInfo.m_argList.push_back(m_sema.FoldEvalStaticConstExprNumericValue(arg->FloatLiteral(), false));
+                }
+                else if (arg->True())
+                {
+                    attrInfo.m_argList.push_back(true);
+                }
+                else if (arg->False())
+                {
+                    attrInfo.m_argList.push_back(false);
+                }
+                else
+                {
+                    // A new case has been added - it should be resolved properly
+                    assert(false);
+                }
+            }
+        }
+
+        ProcessAttributeSpecifier(attrInfo);
+        m_symbols.PushPendingAttribute(attrInfo, scope);
+    }
+
+    void IntermediateRepresentation::ProcessAttributeSpecifier(const AttributeInfo& attrInfo)
+    {
+        // Hint for the pixel output format for all or one of the render targets
+        if (attrInfo.m_attribute == "output_format")
+        {
+            bool isDefault = (attrInfo.m_argList.size() == 1 && holds_alternative<string>(attrInfo.m_argList[0]));
+            bool isIndexed = (attrInfo.m_argList.size() == 2 && holds_alternative<string>(attrInfo.m_argList[1]) && holds_alternative<ConstNumericVal>(attrInfo.m_argList[0]));
+            if (!isDefault && !isIndexed)
+            {
+                return;
+            }
+
+            OutputFormat hint = OutputFormat::FromStr(Trim(get<string>(attrInfo.m_argList[isDefault ? 0 : 1]), "\""));
+
+            if (isDefault)
+            {
+                std::for_each(m_metaData.m_outputFormatHint, m_metaData.m_outputFormatHint + 8, [&](OutputFormat& fmt) { fmt = hint; });
+            }
+            else
+            {
+                auto rtIndex = ExtractValueAsInt64(get<ConstNumericVal>(attrInfo.m_argList[0]), std::numeric_limits<int64_t>::min());
+                if (rtIndex >= 0 && rtIndex <= 7)
+                {
+                    m_metaData.m_outputFormatHint[rtIndex] = hint;
+                }
+            }
+        }
+        // The attribute has no special meaning to AZSLc, just pass it
+    }
+
+    void IntermediateRepresentation::RegisterTokenToNodeAssociation(ssize_t tokenId, antlr4::ParserRuleContext* node)
+    {
+        m_tokenMap.SetAssociation(tokenId, node);
+    }
+
+    //! execute any logic that relates to intermediate treatment that would need to be done between front end and back end
+    void IntermediateRepresentation::MiddleEnd(const MiddleEndConfiguration& middleEndconfigration)
+    {
+        // At this point we have an order apparition vector that stores symbols in the immediate naive
+        // order in which they are first seen in the source code.
+        //
+        // However for code re-emission, it's not really the ordering we need. The ordering we need 
+        // corresponds exactly to the "head recursion visit of AST nodes".
+        // We're not going to transform our flat vector to tree and to vector again,
+        // instead we'll shift around the elements to respect that order.
+        // And to do so, we'll use a dependency DAG and a topological solver.
+
+        m_symbols.ReorderBySymbolDependency();
+
+        RegisterRootConstantStruct(middleEndconfigration);
+
+    }
+
+    bool IntermediateRepresentation::Validate()
+    {
+        auto hasOptions = false;
+        for (auto& [uid, varInfo] : m_symbols.GetOrderedSymbolsOfSubType_2<VarInfo>())
+        {
+            if (varInfo->CheckHasStorageFlag(StorageFlag::Option))
+            {
+                hasOptions = true;
+                break;
+            }
+        }
+
+        auto variantFallbackGroups = 0;
+        for (auto srgInfo : GetOrderedSubInfosOfSubType<SRGInfo>())
+        {
+            if (!srgInfo->m_semantic)
+            {
+                auto errorMsg = FormatString("Missing ShaderResourceGroupSemantic for ShaderResourceGroup [%s]", srgInfo->m_declNode->getText().c_str());
+                throw AzslcIrException(IR_SRG_WITHOUT_SEMANTIC, errorMsg);
+            }
+
+            auto* semanticAsClassInfo = GetSymbolSubAs<ClassInfo>(srgInfo->m_semantic->GetName());
+            optional<int64_t>& variantFallback = semanticAsClassInfo->Get<SRGSemanticInfo>()->m_variantFallback;
+            if (variantFallback)
+            {
+                variantFallbackGroups++;
+
+                if (!hasOptions)
+                {
+                    PrintWarning(Warn::W1, semanticAsClassInfo->GetDeclNode()->start,
+                                 "If you have no options, SRG do not need a ShaderVariantFallback");
+                    variantFallback = none;
+                }
+            }
+        }
+
+        if (variantFallbackGroups >= 2)
+        {
+            throw AzslcIrException(IR_MULTIPLE_SRG_FALLBACK, "Two or more SRGs cannot all be designated as the default ShaderVariantFallback");
+        }
+
+        if (hasOptions && variantFallbackGroups == 0)
+        {
+            throw AzslcIrException(IR_NO_FALLBACK_ASSIGNED, "If you have non-static options, one SRG must be designated as the default ShaderVariantFallback");
+        }
+
+        return true;
+    }
+
+    vector<IdentifierUID> IntermediateRepresentation::GetChildren(const IdentifierUID& parentUid) const
+    {
+        vector<IdentifierUID> results;
+        for (const auto& uid : m_symbols.GetOrderedSymbols())
+        {
+            if (uid.IsParent(parentUid))
+            {
+                results.push_back(uid);
+            }
+        }
+        return results;
+    }
+
+    void IntermediateRepresentation::RemoveUnusedSrgs()
+    {
+        // We use this function pointer to find SRGs that have no references.
+        auto unreferencedSymbolFilterFunc = +[](KindInfo* kindInfo) {
+            const SRGInfo* srgInfo = kindInfo->GetSubAs<SRGInfo>();
+            const bool isFallbackSrg = !!srgInfo->m_shaderVariantFallback;
+            return !isFallbackSrg && (kindInfo->GetSeenats().size() == 0);
+        };
+
+        vector<IdentifierUID> unusedSrgs = GetFilteredSymbolsOfSubType<SRGInfo>(unreferencedSymbolFilterFunc);
+        while (!unusedSrgs.empty())
+        {
+            // For any given symbol, collects all the scope intervals owned by the symbol or any of its children.
+            auto getSymbolScopesFunc = [&](const IdentifierUID& srgId) -> vector<misc::Interval>
+            {
+                vector<misc::Interval> intervals;
+                for (const auto & [symId, interval] : m_scope.m_scopeIntervals)
+                {
+                    if ((symId.m_name == srgId.m_name) || symId.IsParent(srgId))
+                    {
+                        intervals.push_back(interval);
+                    }
+                }
+                return intervals;
+            };
+
+            // With this filter we can extract symbols that are being referenced.
+            auto referencedSymbolFilterFunc = +[](KindInfo* kindInfo) {
+                return !kindInfo->GetSeenats().empty();
+            };
+
+            decltype(unusedSrgs) usedSrgs = GetFilteredSymbolsOfSubType<SRGInfo>(referencedSymbolFilterFunc);
+
+            for (const auto& srgUid : unusedSrgs)
+            {
+                // We will place here the scopes owned by the unused SRG.
+                // it must be a vector of intervals because thanks to "partial" SRGs
+                // the intervals may split. The key idea is that before an unused SRG is removed from
+                // the symbols table, we will loop through the used SRGs and check if their Seenats
+                // fall within an unused SRG scope interval. The matching Seenats will be removed from the used SRGs.
+                // In turn if all Seenats for any given used SRG are removed then the SRG becomes unused.
+                vector<misc::Interval> unusedSrgScopes = getSymbolScopesFunc(srgUid);
+
+                SRGInfo* srgInfo = GetSymbolSubAs<SRGInfo>(srgUid.GetName());
+
+                vector<IdentifierUID> symbolsToDelete = GetChildren(srgUid);
+                for (const auto& uid : symbolsToDelete)
+                {
+                    // Before removing the symbol let's remove it from the Seenat of the Srgs
+                    // that may have a reference to it.
+                    m_symbols.DeleteIdentifier(uid);
+                }
+
+                // In this loop used SRGs may become unused SRGs.
+                // The loop removes Seenats from used SRGs when they fall within the scope intervals
+                // of an unused SRG.
+                for (const auto& usedSrgId : usedSrgs)
+                {
+                    KindInfo* usedSrgKindInfo = GetKindInfo(usedSrgId);
+                    vector<Seenat>& seenAts = usedSrgKindInfo->GetSeenats();
+                    seenAts.erase(std::remove_if(seenAts.begin(), seenAts.end(), [&](const Seenat& seenAt) -> bool {
+                        return std::any_of(unusedSrgScopes.begin(), unusedSrgScopes.end(), [&](const misc::Interval& interval) -> bool {
+                            return (seenAt.m_where.m_focusedTokenId >= interval.a) && (seenAt.m_where.m_focusedTokenId <= interval.b);
+                            });
+                    }), seenAts.end());
+                }
+
+                //Finally, remove the SRG.
+                m_symbols.DeleteIdentifier(srgUid);
+            }
+
+            //Refresh, there could be new unused SRGs.
+            unusedSrgs = GetFilteredSymbolsOfSubType<SRGInfo>(unreferencedSymbolFilterFunc);
+        }
+
+    }
+
+    string ToYaml(const TypeRefInfo& tref, const IntermediateRepresentation& ir, string_view indent)
+    {
+        if (tref.IsEmpty())
+        {
+            return "<NA>";
+        }
+        string r;
+        r += "{name: \"" + tref.m_typeId.m_name + "\", ";
+        r += "validity: ";
+        r += ir.m_symbols.HasIdentifier(tref.m_typeId.m_name) ? "found, " : "undeclared, ";
+        r += "tclass: ";
+        r += TypeClass::ToStr(tref.m_typeClass);
+        r += ", ";
+        r += "underlying_scalar: " + tref.m_arithmeticInfo.UnderlyingScalarToStr() + "}";
+        return r;
+    }
+
+    string ToYaml(const ExtendedTypeInfo& ext, const IntermediateRepresentation& ir, string_view indent)
+    {
+        auto coreName = ext.m_coreType;
+        auto gnicName = ext.m_genericParameter;
+        string r{indent};
+        r += "core: ";
+        r += ToYaml(coreName, ir, string{indent} + "  ");
+        r += "\n";
+        r += indent.data();
+        r += "generic: ";
+        r += ToYaml(gnicName, ir, string{indent} + "  ");
+        return r;
+    }
+
+    void DumpSymbols(IntermediateRepresentation& ir)
+    {
+        // in YAML form
+        for (auto& uid : ir.m_symbols.GetOrderedSymbols())
+        {
+            auto& [_, sym] = *ir.m_symbols.GetIdAndKindInfo(uid.m_name);
+            assert(uid == _);
+            cout << "Symbol " << Decorate("'", uid.m_name) << ":\n";
+            cout << "  kind: " << Kind::ToStr(sym.GetKind()) << "\n";
+            cout << "  references:\n" << ToYaml(sym.GetSeenats().begin(), sym.GetSeenats().end(), "    ");
+            switch (sym.GetKind())
+            {
+            case Kind::Variable:
+            {
+                auto& sub = sym.GetSubRefAs<VarInfo>();
+                // We treat some attributes as static const-s for consistency with the grammar and rest of the code, 
+                //  but they don't have actual type or declaration line, so we skip them here.
+                cout << "  line: " << (sub.m_declNode ? std::to_string(sub.m_declNode->start->getLine()) : "NA") << "\n";
+                cout << "  type:\n" << ToYaml(sub.m_typeInfoExt, ir, "    ") << "\n";
+                cout << "  array dim: \"" << sub.m_typeInfoExt.m_arrayDims.ToString() << "\"\n";
+                cout << "  has sampler state: " << (sub.m_samplerState ? "yes\n" : "no\n");
+                cout << "  storage: ";
+                for (auto sf : StorageFlag::Enumerate{})
+                {
+                    auto flag = StorageFlag{sf};
+                    if (sub.CheckHasStorageFlag(flag))
+                    {
+                        cout << StorageFlag::ToStr(flag) << " ";
+                    }
+                }
+                cout << "\n";
+                if (!holds_alternative<monostate>(sub.m_constVal))
+                {
+                    cout << "  val: " << ExtractValueAsInt64(sub.m_constVal) << "\n";
+                }
+            }
+            break;
+            case Kind::Interface:
+            case Kind::Struct:
+            case Kind::Class:
+            case Kind::Enum:
+            {
+                auto& sub = sym.GetSubRefAs<ClassInfo>();
+                cout << "  line: " << sub.GetOrigSourceLine() << "\n";
+                cout << "  members:\n";
+                for (auto&& member : sub.GetOrderedMembers())
+                {
+                    auto* subFieldSym = ir.m_symbols.GetIdAndKindInfo(member.m_name);
+                    cout << "    - {kind: " << Kind::ToStr(subFieldSym->second.GetKind())
+                        << ", name: " << Decorate("'", member.m_name) << "}\n";
+                }
+            }
+            break;
+            case Kind::Function:
+            {
+                auto& sub = sym.GetSubRefAs<FunctionInfo>();
+                cout << "  line: " << sym.VisitSub(GetOrigSourceLine_Visitor{}) << "\n";
+                cout << "  def line: " << (sub.IsUndefinedFunction() ? "undef" : std::to_string(sub.m_defNode->start->getLine())) << "\n";
+                cout << "  must override: " << sub.m_mustOverride << "\n";
+                cout << "  is method: " << sub.m_isMethod << "\n";
+                cout << "  is virtual: " << sub.m_isVirtual << "\n";
+                cout << "  return type:\n" << ToYaml(sub.m_returnType, ir, "    ") << "\n";
+                cout << "  has overriding children:\n" << ToYaml(sub.m_overrides.begin(), sub.m_overrides.end(), "    ");
+                cout << "  is hiding base symbol: '" << (sub.m_base ? sub.m_base->m_name.c_str() : "") << "'\n";
+                cout << "  parameters:\n";
+                for (auto& param : sub.GetParameters(1))
+                {
+                    auto* varInfo = ir.GetSymbolSubAs<VarInfo>(param.m_varId.GetName());
+                    if (varInfo)
+                    {
+                        cout << "    - name: '" << (varInfo->m_identifier.empty() ? "<unnamed>" : static_cast<string&>(varInfo->m_identifier)) << "'\n"
+                             << "      type:\n" << ToYaml(varInfo->m_typeInfoExt, ir, "        ") << "\n";
+                    }
+                    else
+                    {
+                        cout << "    - non-var identifier (type?): '" << ToYaml(param.m_typeInfo, ir, "        ") << "'\n";
+                    }
+                }
+            }
+            break;
+            case Kind::OverloadSet:
+            {
+                auto& sub = sym.GetSubRefAs<OverloadSetInfo>();
+                cout << "  functions:\n";
+                sub.ForEach([](auto& uid2) {cout << "    - '" << uid2.GetName() << "'\n";});
+            }
+            break;
+            case Kind::ShaderResourceGroup:
+            {
+                auto& sub = sym.GetSubRefAs<SRGInfo>();
+                cout << "  line: " << sub.m_declNode->start->getLine() << "\n";
+                cout << "  structs: ";
+                std::for_each(sub.m_structs.begin(), sub.m_structs.end(), [](auto& uid2) {cout << uid2.GetNameLeaf() << ", ";});
+                cout << "\n";
+                cout << "  srViews: ";
+                std::for_each(sub.m_srViews.begin(), sub.m_srViews.end(), [](auto& uid2) {cout << uid2.GetNameLeaf() << ", ";});
+                cout << "\n";
+                cout << "  samplers: ";
+                std::for_each(sub.m_samplers.begin(), sub.m_samplers.end(), [](auto& uid2) {cout << uid2.GetNameLeaf() << ", ";});
+                cout << "\n";
+                cout << "  CBs: ";
+                std::for_each(sub.m_CBs.begin(), sub.m_CBs.end(), [](auto& uid2) {cout << uid2.GetNameLeaf() << ", ";});
+                cout << "\n";
+            }
+            break;
+            case Kind::Type:
+            {
+                auto& sub = sym.GetSubRefAs<TypeRefInfo>();
+                cout << ToYaml(sub, ir, "  ") << "\n";
+            }
+            break;
+            case Kind::TypeAlias:
+            {
+                auto& sub = sym.GetSubRefAs<TypeAliasInfo>();
+                cout << "  line: " << sym.VisitSub(GetOrigSourceLine_Visitor{}) << "\n";
+                cout << "  canonical type:\n" << ToYaml(sub.m_canonicalType, ir, "    ") << "\n";
+            }
+            break;
+            default:
+                break;
+            }
+        }
+    }
+
+    int IntermediateRepresentation::ValidateRootConstantStruct(const MiddleEndConfiguration& middleEndconfigration)
+    {
+        int rootConstantsSize = 0;
+        for (auto& [uid, varInfo] : m_symbols.GetOrderedSymbolsOfSubType_2<VarInfo>())
+        {
+            if (varInfo->CheckHasAnyStorageFlags({ StorageFlag::Rootconstant }) && IsGlobal(uid.GetName()))
+            {
+                assert(!IsChameleon(varInfo->GetTypeClass()));
+
+                auto exportedType = varInfo->m_typeInfoExt.m_coreType;
+                if (!exportedType.IsPackable())
+                {
+                    throw std::logic_error{ "Internal Error: The type '"
+                        + exportedType.m_typeId.m_name
+                        + "' in layout member '"
+                        + uid.m_name
+                        + "' cannot be packed." };
+                }
+
+                // GetTotalSize of each member of the structure
+                rootConstantsSize += varInfo->m_typeInfoExt.GetTotalSize(middleEndconfigration.m_packDataBuffers, middleEndconfigration.m_isRowMajor);
+            }
+        }
+
+        // We have a limited space for rootconstant (specified by --root-const=N) check that we're not going over
+        if (rootConstantsSize > middleEndconfigration.m_rootConstantsMaxSize)
+        {
+            throw std::runtime_error{ "The rootconstants size of "
+                + ToString(rootConstantsSize)
+                + " bytes exceeds the limit of "
+                + ToString(middleEndconfigration.m_rootConstantsMaxSize)
+                + "bytes supported on the target platform." };
+        }
+
+        // Spec requires at least 128 bytes of block, bigger values (>128 bytes) need to be checked against support by implementation
+        constexpr int MinByteSizeRequired = 128;
+        if (rootConstantsSize > MinByteSizeRequired)
+        {
+            PrintWarning(Warn::W2, none, "The root constant size ", rootConstantsSize, " is greater than 128 bytes. ",
+                "Large root constant size may not be supported by the implementation.");
+        }
+
+        return rootConstantsSize;
+    }
+
+    uint32_t IntermediateRepresentation::CalculateLayoutForRootConstantMember(
+        const IdentifierUID& memberId,
+        const bool isRowMajor,
+        const AZ::ShaderCompiler::Packing::Layout layoutPacking,
+        const uint32_t startingOffset,
+        uint32_t& nextMemberStartingOffset) const
+    {
+        const auto* varInfoPtr = GetSymbolSubAs<VarInfo>(memberId.m_name);
+        uint32_t size = 0;
+
+        if (varInfoPtr)
+        {
+            const auto& varInfo = *varInfoPtr;
+
+            // View types should only be called from GetViewStride until we decide to support them as struct constants
+            assert(!IsChameleon(varInfo.GetTypeClass()));
+
+            auto exportedType = varInfo.m_typeInfoExt.m_coreType;
+
+            if (!exportedType.IsPackable())
+            {
+                throw std::logic_error{ "error: unpackable type ("
+                    + exportedType.m_typeId.m_name
+                    + ") in layout member "
+                    + memberId.m_name };
+            }
+            TypeClass varClass = exportedType.m_typeClass;
+
+            size = varInfo.m_typeInfoExt.GetTotalSize(layoutPacking, isRowMajor);
+            auto startAt = startingOffset;
+            nextMemberStartingOffset = startingOffset;
+
+            // Alignment start
+            if (exportedType.m_arithmeticInfo.IsMatrix() || exportedType.m_arithmeticInfo.IsVector())
+            {
+                const auto rows = exportedType.m_arithmeticInfo.m_rows;
+                const auto cols = exportedType.m_arithmeticInfo.m_cols;
+                const auto packAlignment = exportedType.m_arithmeticInfo.IsMatrix() ? Packing::Alignment::asMatrixStart : Packing::Alignment::asVectorStart;
+                startAt = nextMemberStartingOffset = Packing::AlignOffset(layoutPacking, nextMemberStartingOffset, packAlignment, rows, cols);
+            }
+
+            // Compound types are not supported in root constants.
+            assert(!IsProductType(varClass));
+
+            if (varInfo.GetTypeClass() == TypeClass::Enum)
+            {
+                auto* asClassInfo = GetSymbolSubAs<ClassInfo>(varInfo.GetTypeId().GetName());
+                size = asClassInfo->Get<EnumerationInfo>()->m_underlyingType.m_arithmeticInfo.GetBaseSize();
+            }
+
+            nextMemberStartingOffset = Packing::PackNextChunk(layoutPacking, size, startAt);
+
+            // Alignment end
+            if (exportedType.m_arithmeticInfo.IsMatrix() || exportedType.m_arithmeticInfo.IsVector())
+            {
+                const auto rows = exportedType.m_arithmeticInfo.m_rows;
+                const auto cols = exportedType.m_arithmeticInfo.m_cols;
+                const auto packAlignment = exportedType.m_arithmeticInfo.IsMatrix() ? Packing::Alignment::asMatrixEnd : Packing::Alignment::asVectorEnd;
+                nextMemberStartingOffset = Packing::AlignOffset(layoutPacking, nextMemberStartingOffset, packAlignment, rows, cols);
+            }
+
+            size = nextMemberStartingOffset - startAt;
+        }
+
+        return size;
+    }
+
+    uint32_t IntermediateRepresentation::CalculateSizeOfRootConstantsCB(
+        const IdentifierUID& exportedTypeId,
+        const bool isRowMajor,
+        const AZ::ShaderCompiler::Packing::Layout layoutPacking) const
+    {
+        uint32_t memberOffset = 0;
+        uint32_t largestMemberSize = 0;
+
+        const auto* classInfo = GetSymbolSubAs<ClassInfo>(exportedTypeId.m_name);
+        for (const auto& memberField : classInfo->GetMemberFields())
+        {
+            const auto currentStride = memberOffset;
+            CalculateLayoutForRootConstantMember(memberField, isRowMajor, GetExtendedLayout(layoutPacking), currentStride, memberOffset);
+            largestMemberSize = std::max(largestMemberSize, memberOffset - currentStride);
+        }
+
+        memberOffset = Packing::AlignStructToLargestMember(layoutPacking, memberOffset, largestMemberSize);
+        return memberOffset;
+    }
+
+	void IntermediateRepresentation::RegisterRootConstantStruct(const MiddleEndConfiguration& middleEndconfigration)
+    {
+        int rootConstantByteSize = ValidateRootConstantStruct(middleEndconfigration);
+
+        QualifiedName name{ "/Root_Constants" };
+
+        assert(!m_symbols.HasIdentifier(name));  // reserved name semantic error must have prevented this state to happen.
+
+        if (rootConstantByteSize == 0)
+        {
+            return;
+        }
+
+        // Register a struct Root_Constants and its members to ir
+        auto& [rootConstantStructUid, rootConstantStructKindInfo] = m_symbols.AddIdentifier(name, Kind::Struct, none, AddIdentifierChecks::None);
+        rootConstantStructKindInfo.GetSubRefAs<ClassInfo>().m_kind = Kind::Struct;
+        
+        for (auto& [uid, varInfo] : m_symbols.GetOrderedSymbolsOfSubType_2<VarInfo>())
+        {
+            if (varInfo->CheckHasAnyStorageFlags({ StorageFlag::Rootconstant })
+                && IsGlobal(uid.GetName()))
+            {
+                assert(!IsChameleon(varInfo->GetTypeClass()));
+                auto exportedType = varInfo->m_typeInfoExt.m_coreType;
+
+                // copy the variable into a new symbol. so that its path can make sense
+                // as a child of the rootconstants struct.
+                QualifiedName copiedFieldName{ JoinPath(rootConstantStructUid.GetName(), ReplaceSeparators(uid.m_name, Underscore)) };
+                auto& [newVarUid, newVarKind] = m_symbols.AddIdentifier(copiedFieldName, Kind::Variable);
+                newVarKind.GetSubRefAs<VarInfo>() = *varInfo;
+                newVarKind.GetSubRefAs<VarInfo>().m_declNode = nullptr;  // the original declaration in the AST isn't inherited by a copied symbol.
+                // Add root constants to the registered struct as member
+                rootConstantStructKindInfo.GetSubRefAs<ClassInfo>().PushMember(newVarUid, Kind::Variable);
+            }
+        }
+
+        if (middleEndconfigration.m_padRootConstantCB)
+        {
+            const auto layoutPacking = middleEndconfigration.m_packConstantBuffers;
+            uint32_t startAt = Packing::AlignOffset(layoutPacking, 0, Packing::Alignment::asStructStart, 0, 0);
+            uint32_t strideSize = CalculateSizeOfRootConstantsCB(rootConstantStructUid, middleEndconfigration.m_isRowMajor, layoutPacking);
+            uint32_t endOffset = Packing::AlignOffset(layoutPacking, strideSize, Packing::Alignment::asStructEnd, 0, 0);
+
+            // If the structure is NOT aligned, we inject a 'pad' member to enforce that padding.
+            // There are 4 cases: uint1, uint2, uint3, or uint4.
+            if (endOffset > strideSize)
+            {
+                uint32_t padSize = endOffset - strideSize;
+
+                QualifiedName padFieldName{ JoinPath(rootConstantStructUid.GetName(), "__pad__") };
+                auto& [newVarUid, newVarKind] = m_symbols.AddIdentifier(padFieldName, Kind::Variable);
+
+                VarInfo varInfo;
+                varInfo.m_declNode = nullptr;
+                varInfo.m_isPublic = false;
+
+                assert(padSize == 16 || padSize == 12 || padSize == 8 || padSize == 4);
+                string typeName = FormatString("uint%d", padSize / 4);
+
+                ExtractedTypeExt padType = { UnqualifiedNameView(typeName), nullptr };
+                varInfo.m_typeInfoExt = ExtendedTypeInfo{ m_sema.CreateTypeRefInfo(padType),
+                                 {}, {}, {}, Packing::MatrixMajor::Default };
+
+                newVarKind.GetSubRefAs<VarInfo>() = varInfo;
+                rootConstantStructKindInfo.GetSubRefAs<ClassInfo>().PushMember(newVarUid, Kind::Variable);
+            }
+        }
+
+        m_rootConstantStructUID = rootConstantStructUid;
+    }
+
+    //////////////////////////////////////////////////////////////////////////
+    // PreprocessorLineDirective overrides...
+    const LineDirectiveInfo* IntermediateRepresentation::GetNearestPreprocessorLineDirective(size_t azslLineNumber) const
+    {
+        if (!azslLineNumber)
+        {
+            return nullptr;
+        }
+
+        auto lineBefore = m_lineMap.lower_bound(azslLineNumber);
+        if (lineBefore != m_lineMap.begin() && (lineBefore != m_lineMap.end() || m_lineMap.size() > 0))
+        {
+            lineBefore--;
+            return &lineBefore->second;
+        }
+        return nullptr;
+    }
+    ///////////////////////////////////////////////////////////////////////////
+
+}  // end of namespace AZ::ShaderCompiler

+ 253 - 0
src/AzslcIntermediateRepresentation.h

@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include "AzslcSemanticOrchestrator.h"
+#include "AzslcTokenToAst.h"
+#include "AzslcKindInfo.h"
+#include "PreprocessorLineDirectiveFinder.h"
+
+namespace AZ::ShaderCompiler
+{
+    //! We limit the maximum number of render targets to 8, with indices in the range [0..7]
+    static const uint32_t kMaxRenderTargets = 8;
+
+    struct IRMetaData
+    {
+        //! the input source file path
+        string m_insource = "Stdin";
+
+        //! the activated namespaces on the command line
+        unordered_set<string> m_attributeNamespaceFilters;
+
+        //! some platforms require source-coded target formats
+        OutputFormat m_outputFormatHint[kMaxRenderTargets] = { OutputFormat::R16G16B16A16_FLOAT };
+    };
+
+    // Holder responsible for owning post-parsed source data (in the form of an object graph).
+    // Note that this is not IL. e.g. not a serialized form of IR with op-codes (like DXIL).
+    struct IntermediateRepresentation : public PreprocessorLineDirectiveFinder
+    {
+        IntermediateRepresentation(azslLexer* lexer)
+            : m_scope{[&](QualifiedNameView sym)  // Initialize the scope object with a decoupled identifier getter. (SOLID's D.I.P)
+                      {
+                          return m_symbols.GetIdAndKindInfo(sym);
+                      }
+                     }
+            , m_lexer{ lexer }
+            , m_sema{&m_symbols, &m_scope, lexer, this}
+        {
+            // Default output format for all targets
+            std::fill_n(m_metaData.m_outputFormatHint, kMaxRenderTargets, m_metaData.m_outputFormatHint[0]);
+        }
+
+        //! Shortcut helper to symbols
+        auto GetIdAndKindInfo(QualifiedNameView symbol) const
+        {
+            return m_symbols.GetIdAndKindInfo(symbol);
+        }
+
+         //! Shortcut helper to symbols
+        auto GetIdAndKindInfo(QualifiedNameView symbol)
+        {
+            return m_symbols.GetIdAndKindInfo(symbol);
+        }
+
+        //! Only the second part of IdAndKindInfo, when we already have the UID
+        KindInfo* GetKindInfo(IdentifierUID uid)
+        {
+            auto* idKindPtr = m_symbols.GetIdAndKindInfo(uid.GetName());
+            return idKindPtr ? &idKindPtr->second : nullptr;
+        }
+
+        //! Just query the kind of a symbol
+        Kind GetKind(IdentifierUID symbol) const
+        {
+            auto* ptr = m_symbols.GetIdAndKindInfo(symbol.GetName());
+            return ptr ? ptr->second.GetKind() : Kind{Kind::EndEnumeratorSentinel_};
+        }
+
+        //! Query if a symbol is a structure/class/enum nested in a structure or class
+        bool IsNestedStruct(const IdentifierUID& symbol) const
+        {
+            return GetKind(symbol).IsOneOf(Kind::Class, Kind::Struct, Kind::Enum)
+                && GetKind({GetParentName(symbol.GetName())}).IsOneOf(Kind::Class, Kind::Struct);
+        }
+
+        //! overload from IdentifierUID
+        bool IsNestedStructOrEnum(const IdentifierUID& symbol) const
+        {
+            return IsNestedStruct(symbol);
+        }
+
+        //! Extracts the contained SubInfo from a symbol lookup
+        template<typename T>
+        const T* GetSymbolSubAs(QualifiedNameView symbol) const
+        {
+            const auto* symbolLookUp = GetIdAndKindInfo(symbol);
+            if (!symbolLookUp)
+            {
+                return nullptr;
+            }
+            return symbolLookUp->second.GetSubAs<T>();
+        }
+
+        template<typename T>
+        T* GetSymbolSubAs(QualifiedNameView symbol)
+        {
+            auto* symbolLookUp = GetIdAndKindInfo(symbol);
+            if (!symbolLookUp)
+            {
+                return nullptr;
+            }
+            return symbolLookUp->second.GetSubAs<T>();
+        }
+
+        //! Returns a copy of the Ordered list filtered by SubInfo (of type `Sub`) only. elements are only the subinfo.
+        template<typename Sub>
+        const vector<Sub*> GetOrderedSubInfosOfSubType()
+        {
+            return m_symbols.GetOrderedSubInfosOfSubType<Sub>();
+        }
+
+        //! Returns a copy of the Ordered list filtered by SubInfo (of type `Sub`) only. elements are pairs (uid, sub)
+        template<typename Sub>
+        vector<pair<IdentifierUID, Sub*>> GetOrderedSymbolsOfSubType_2()
+        {
+            return m_symbols.GetOrderedSymbolsOfSubType_2<Sub>();
+        }
+
+        const string& OriginalSource() const
+        {
+            return m_metaData.m_insource;
+        }
+
+        bool IsAttributeNamespaceActivated(const string& attr)
+        {
+            // while we don't have C++20, we can't benefit from heterogeneous key search in unordered container.
+            return (m_metaData.m_attributeNamespaceFilters.find(attr) != m_metaData.m_attributeNamespaceFilters.end());
+        }
+
+        void AddAttributeNamespaceFilter(string_view attr)
+        {
+            m_metaData.m_attributeNamespaceFilters.emplace(attr);
+        }
+
+        void RegisterAttributeSpecifier(AttributeScope scope,
+                                        AttributeCategory category,
+                                        size_t declarationLine,
+                                        string_view space,
+                                        string_view name,
+                                        azslParser::AttributeArgumentListContext* argList);
+
+        //! called internally after a new attribute is registered
+        void ProcessAttributeSpecifier(const AttributeInfo& attrInfo);
+
+        void RegisterTokenToNodeAssociation(ssize_t tokenId, antlr4::ParserRuleContext* node);
+
+        //! Validate the root variables & the size of the root constants structure (and return it)
+        int ValidateRootConstantStruct(const MiddleEndConfiguration& middleEndConfig);
+
+        //! Register a struct Root_Constants and its members to ir
+        void RegisterRootConstantStruct(const MiddleEndConfiguration& middleEndConfig);
+
+        //! Calculates the size of the given member of the RootConstant CB.
+        //! It also calculates the offset at which the next member of the RootConstant CB should start.
+        uint32_t CalculateLayoutForRootConstantMember(
+            const IdentifierUID& memberId,
+            const bool isRowMajor,
+            const AZ::ShaderCompiler::Packing::Layout layoutPacking,
+            const uint32_t startingOffset,
+            uint32_t& nextMemberStartingOffset) const;
+
+        //! The shader developer declares each rootconstant variable independently, but AZSLc
+        //! combines all those variables into a single ConstantBuffer. This function calculates
+        //! the total size of the generated ConstantBuffer.
+        //! @param exportedTypeId: This is the Uid created at runtime by AZSLc to identify the CB that contains
+        //!                        all the rootconstants.
+        uint32_t CalculateSizeOfRootConstantsCB(
+            const IdentifierUID& exportedTypeId,
+            const bool isRowMajor,
+            const AZ::ShaderCompiler::Packing::Layout layoutPacking) const;
+
+        //! execute any logic that relates to intermediate treatment that would need to be done between front end and back end
+        void MiddleEnd(const MiddleEndConfiguration& middleEndconfig);
+
+        bool Validate();
+
+        //! Returns a list with all symbols that have their QualifiedName starting with
+        //! the QualifiedName of parentIdUid.
+        vector<IdentifierUID> GetChildren(const IdentifierUID& parentUid) const;
+
+        //! Removes all unused SRGs from the symbol table.
+        void RemoveUnusedSrgs();
+
+        //! Helper function that returns a filtered list of identifiers for symbols
+        //! of a specific KindInfo Subtype. The LambdaFilter should return true when it finds a match.
+        template<typename Sub, typename LambdaFilter>
+        vector<IdentifierUID> GetFilteredSymbolsOfSubType(LambdaFilter filterFunc)
+        {
+            vector<IdentifierUID> filteredSymbols;
+
+            for (const auto& pairUidSym : GetOrderedSymbolsOfSubType_2<Sub>())
+            {
+                auto& uid = pairUidSym.first;
+                KindInfo* kindInfo = GetKindInfo(uid);
+                if (filterFunc(kindInfo))
+                {
+                    filteredSymbols.push_back(uid);
+                }
+            }
+            return filteredSymbols;
+        }
+
+        //! Same as above, only returns the first match.
+        template<typename Sub, typename LambdaFilter>
+        IdentifierUID GetFirstFilteredSymbolOfSubType(LambdaFilter filterFunc)
+        {
+            for (const auto& pairUidSym : GetOrderedSymbolsOfSubType_2<Sub>())
+            {
+                auto& uid = pairUidSym.first;
+                KindInfo* kindInfo = GetKindInfo(uid);
+                if (filterFunc(kindInfo))
+                {
+                    return uid;
+                }
+            }
+            return {};
+        }
+
+        //////////////////////////////////////////////////////////////////////////
+        // PreprocessorLineDirective overrides...
+        const LineDirectiveInfo* GetNearestPreprocessorLineDirective(size_t azslLineNumber) const override;
+        //////////////////////////////////////////////////////////////////////////
+
+        // the maps of all variables, functions, etc, from the source code (things with declarations and a name).
+        SymbolAggregator      m_symbols;
+        // stateful helper during parsing
+        ScopeTracker          m_scope;
+        // the orchestrator holds references to above objects, so for safety we can hold it in the same place.
+        // yielding better guarantees about its lifetime wrt its references.
+        SemanticOrchestrator  m_sema;
+        // object that allows reverse mapping of token pointers to AST rules
+        TokenToAst            m_tokenMap;
+        azslLexer*            m_lexer;
+        // the structure that holds root constants (it's a generated thing, and there is only one)
+        IdentifierUID         m_rootConstantStructUID;
+
+        map<size_t, LineDirectiveInfo> m_lineMap;
+
+        IRMetaData m_metaData;
+    };
+
+    string ToYaml(const TypeRefInfo& tref, const IntermediateRepresentation& ir, string_view indent);
+
+    string ToYaml(const ExtendedTypeInfo& ext, const IntermediateRepresentation& ir, string_view indent);
+
+    void DumpSymbols(IntermediateRepresentation& ir);
+}

+ 1109 - 0
src/AzslcKindInfo.h

@@ -0,0 +1,1109 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include "AzslcTypes.h"
+
+namespace AZ::ShaderCompiler
+{
+    // This randomly generated GUID has less chance to collide with a user-defined variable, for example m_shaderVariantKey
+    static constexpr char       kShaderVariantKeyFallbackVarName[] = "m_SHADER_VARIANT_KEY_NAME_";
+    static constexpr uint32_t   kShaderVariantKeyElementSize       =   32;
+    static constexpr uint32_t   kShaderVariantKeyRegisterSize      =  128;
+
+    // Maximum number of values for integer options (currently matched in Atom in ShaderAssetCreator.cpp and to be improved later).
+    static constexpr uint32_t   kIntegerMaxShaderVariantValues = 1000;
+
+    MAKE_REFLECTABLE_ENUM (Kind,
+        Namespace,
+        Type,    // non UDT only. Since UDT are struct/class/interface
+        TypeAlias,
+        Variable,
+        Function,
+        OverloadSet,
+        Struct,
+        Class,
+        Enum,
+        Interface,
+        ShaderResourceGroup,
+        ShaderResourceGroupSemantic
+    );
+
+    inline bool IsKindOneOfTypeRelated(Kind kind)
+    {
+        return kind.IsOneOf(Kind::Type, Kind::TypeAlias, Kind::Struct, Kind::Class, Kind::Enum, Kind::Interface);
+    }
+
+    struct SRGSemanticInfo
+    {
+        optional<int64_t> m_frequencyId;
+        optional<int64_t> m_variantFallback;
+    };
+
+    struct EnumerationInfo
+    {
+        bool         m_isScoped = false;
+        TypeRefInfo  m_underlyingType;
+    };
+
+    MAKE_REFLECTABLE_ENUM(AttributeScope,
+        Global,
+        Attached
+    );
+
+    MAKE_REFLECTABLE_ENUM(AttributeCategory,
+        Single,
+        Sequence
+    );
+
+    //! Store [[attributes(and, their, arguments)]]
+    struct AttributeInfo
+    {
+        AttributeScope    m_scope;
+        AttributeCategory m_category;
+        size_t            m_lineNumber;
+        string            m_namespace;
+        string            m_attribute;
+
+        struct Argument : variant< monostate, bool, ConstNumericVal, string >
+        {
+            using variant::variant;
+        };
+        vector<Argument>  m_argList;
+    };
+
+    //! store base list, member list...
+    struct ClassInfo
+    {
+        void PushMember(const IdentifierUID& uid, Kind kind)
+        {
+            if (kind == Kind::Variable)
+            {
+                m_memberFields.push_back(uid);
+            }
+            m_ordered.push_back(uid);
+            m_members.insert(uid);
+        }
+
+        const vector<IdentifierUID>& GetMemberFields() const
+        {
+            return m_memberFields;
+        }
+
+        const vector<IdentifierUID>& GetOrderedMembers() const
+        {
+            return m_ordered;
+        }
+
+        bool HasMember(const IdentifierUID& uid) const
+        {
+            return m_members.find(uid) != m_members.end();
+        }
+
+        bool HasMember(UnqualifiedNameView uqName) const
+        {
+            return FindMemberFromLeafName(uqName) != none;
+        }
+
+        //! To help for method name comparison (when doing overrides), only the leaf name needs to match
+        optional<IdentifierUID> FindMemberFromLeafName(UnqualifiedNameView uqName) const
+        {
+            assert(IsLeaf(uqName));
+            auto item = std::find_if(m_members.cbegin(), m_members.cend(), [&](decltype(*m_members.cbegin()) elem) {return elem.GetNameLeaf() == uqName; });
+            return (item != m_members.cend()) ? optional<IdentifierUID>{*item} : none;
+        }
+
+        //! upcast to get a generic declaration context
+        const antlr4::ParserRuleContext* GetDeclNode() const
+        {
+            // This might look like witchcraft to some, so allow me to explain.
+            // auto-parameter lambdas (C++14) are templates, so we can
+            // define a single generic visitor functor for the variant.
+            // We are lucky that all AST nodes have a start object.
+            // So this code compiles and work for all 5 types of nodes.
+            return StdUtils::visit([](auto&& arg)
+                                   {
+                                       return static_cast<antlr4::ParserRuleContext*>(arg);
+                                   }, m_declNodeVt);
+        }
+
+        //! non-const version
+        antlr4::ParserRuleContext* GetDeclNode()
+        {
+            return const_cast<antlr4::ParserRuleContext*>(const_cast<const ClassInfo*>(this)->GetDeclNode());
+        };
+
+        //! get Name token from declaration contexts
+        const Token* GetDeclNodeNameToken() const
+        {
+            return StdUtils::visit([](auto&& arg)
+                                   {
+                                       return arg->Name;
+                                   }, m_declNodeVt);
+        }
+
+        size_t GetOrigSourceLine() const
+        {
+            if (GetDeclNode())
+            {
+                return GetDeclNode()->start->getLine();
+            }
+            return 0;
+        }
+
+        //! Gets the concrete variant kind from SubInfo
+        template<typename T>
+        T* Get()
+        {
+            return (holds_alternative<T>(m_subInfo)) ? &get<T>(m_subInfo) : (T*)nullptr;
+        }
+
+        template<typename T>
+        const T* Get() const
+        {
+            return (holds_alternative<T>(m_subInfo)) ? &get<T>(m_subInfo) : (T*)nullptr;
+        }
+
+        Kind                           m_kind;   // which of class/struct/interface/srgsemantic ? (repetition of data in the upper KindInfo)
+        unordered_set< IdentifierUID > m_bases;
+
+        using DeclNode = variant< AstClassDeclNode*, AstStructDeclNode*, AstEnumDeclNode*, AstInterfaceDeclNode*, AstSRGSemanticDeclNode* >;
+        DeclNode                       m_declNodeVt;
+
+        using SubKind  = variant< monostate, SRGSemanticInfo, EnumerationInfo >;
+        SubKind                        m_subInfo;
+
+    private:
+        unordered_set< IdentifierUID > m_members;      //!< Fast lookup
+        vector< IdentifierUID >        m_memberFields; //!< Only the member fields, in order of declaration. All member fields are members.
+        vector< IdentifierUID >        m_ordered;      //!< Ordered. all contained symbols
+    };
+    
+    //! an extended type information gathers:
+    //!  + core type info (immutable and limited amount, all stored in the fixed symbol table)
+    //!  + array dimensions
+    //!  + matrix majorness storage flag
+    struct ExtendedTypeInfo
+    {
+        const auto& GetDimensions() const
+        {
+            return m_arrayDims;
+        }
+
+        //! True if the extended type is an array type.
+        const bool IsArray() const
+        {
+            return m_arrayDims.IsArray();
+        }
+
+        //! Get the size of a single element, ignoring array dimensions
+        const uint32_t GetSingleElementSize(Packing::Layout layout, bool defaultRowMajor) const
+        {
+            auto baseSize = m_coreType.m_arithmeticInfo.GetBaseSize();
+            bool isRowMajor = (m_mtxMajor == Packing::MatrixMajor::RowMajor ||
+                              (m_mtxMajor == Packing::MatrixMajor::Default && defaultRowMajor));
+            auto rows = m_coreType.m_arithmeticInfo.m_rows;
+            auto cols = m_coreType.m_arithmeticInfo.m_cols;
+            return PackAsVectorMatrix(layout, baseSize, rows, cols, isRowMajor);
+        }
+
+        //! Get the total size
+        const uint32_t GetTotalSize(Packing::Layout layout, bool defaultRowMajor) const
+        {
+            auto packSize = GetSingleElementSize(layout, defaultRowMajor);
+            return Packing::PackIntoArray(layout, packSize, m_arrayDims);
+        }
+
+        //! Set the storage flag for row major (true) or column major (false)
+        void SetMatrixMajor(Packing::MatrixMajor mtxMajor)
+        {
+            m_mtxMajor = mtxMajor;
+        }
+
+        //! True if the storage type is in row major. False if the storage type is column major (default).
+        const Packing::MatrixMajor GetMatrixMajor() const
+        {
+            return m_mtxMajor;
+        }
+
+        //! this is the secret of the whole typeof/reference-tracking system.
+        //! because this function is the leaf of any type query, we can transform types at will :)
+        QualifiedNameView GetMimickedType() const
+        {
+            return IsChameleon(m_coreType.m_typeClass) ? m_genericParameter.m_typeId.GetName() : m_coreType.m_typeId.GetName();
+        }
+
+        //! Check whether some type has been assigned or not (or plain not-initialized)
+        bool IsEmpty() const
+        {
+            return m_coreType.IsEmpty() && m_genericParameter.IsEmpty();
+        }
+
+        //! Verify that the extended type is fully understood.
+        bool IsClassFound() const
+        {
+            return !m_coreType.IsEmpty() && m_coreType.m_typeClass != TypeClass::IsNotType
+                && (m_genericParameter.IsEmpty() || m_genericParameter.m_typeClass != TypeClass::IsNotType);
+        }
+
+        friend bool operator == (const ExtendedTypeInfo& lhs, const ExtendedTypeInfo& rhs)
+        {
+            return lhs.m_coreType == rhs.m_coreType
+                && lhs.m_genericParameter == rhs.m_genericParameter
+                && lhs.m_arrayDims == rhs.m_arrayDims;
+        }
+        friend bool operator != (const ExtendedTypeInfo& lhs, const ExtendedTypeInfo& rhs)
+        {
+            return !operator==(lhs,rhs);
+        }
+
+        //! recreate a user-readable mangled name of the whole type, for diagnostic purposes
+        //! this is not rigorous and does not constitute a mangling. the full feature is better served at emission side by GetExtendedTypeInfo function.
+        string GetDisplayName() const
+        {
+             return m_coreType.m_typeId.m_name + (m_genericParameter.IsEmpty() ? "" : Decorate("<", m_genericParameter.m_typeId.m_name, ">"));
+        }
+
+        //! only use leaf form of names to compose a displayable reconstituted name
+        string GetDisplayShortName() const
+        {
+            string coreLeaf = m_coreType.m_typeId.GetNameLeaf();
+            return m_genericParameter.IsEmpty() ? coreLeaf : coreLeaf + Decorate("<", m_genericParameter.m_typeId.GetNameLeaf(), ">");
+        }
+
+        // Those can't be stored at the same place because of a problem of identity.
+        //  Types are identified by their core types.
+        //  i.e `int` and `int[2]` both have "?int" as a (mangled) qualified name.
+        //  This is because of a limitation called array collapsing. (which is kind of important for typeof and the seenat feature)
+        //  If we store the array dimensions directly in TypeRefInfo, it will not be possible
+        //  to support multiple different arrays of `int`s throughout the whole compilation unit.
+        // Finally m_mtxMajor is not part of the type; we only provide it here as a cache for convenience
+        TypeRefInfo          m_coreType;
+        TypeRefInfo          m_genericParameter;  // in case of Buffer<float> coreType is Buffer, genericParameter is float. note that genericParams can't be arrays since brackets are not part of the "type:" rule
+        ArrayDimensions      m_arrayDims;
+        ArrayDimensions      m_genericDims;
+        Packing::MatrixMajor m_mtxMajor = Packing::MatrixMajor::Default; // Can be changed by #pragmapack_matrix directive, or with the row_major or the column_major qualifiers.
+    };
+
+    //! Holds typealias or typedef data
+    struct TypeAliasInfo
+    {
+        azslParser::TypeAliasingDefinitionStatementContext* m_declNode = nullptr;
+        ExtendedTypeInfo m_canonicalType;  // ultimate (existing) target of the alias
+    };
+
+    static bool TypeHasStorageFlag(TypeQualifier typeQualifier, StorageFlag flag)
+    {
+        // The mask operation returns an rvalue and it isn't converted to bool
+        return static_cast<bool>(typeQualifier & flag);
+    }
+
+    struct VarInfo
+    {
+        inline bool                CheckHasStorageFlag(StorageFlag flag) const;
+        // variadic version for `all` (conjunction) check
+        inline bool                CheckHasAllStorageFlags(std::initializer_list<StorageFlag> vararg) const;
+        // variadic version for `any` (disjunction) check
+        inline bool                CheckHasAnyStorageFlags(std::initializer_list<StorageFlag> vararg) const;
+        // checks if the storage flag should be treated as local linkage
+        inline bool                StorageFlagIsLocalLinkage(bool isGlobalOrSrgScope) const;
+        // access variable's type's core identifier
+        inline IdentifierUID       GetTypeId() const;
+        // access variable's type's generic parameter if any (will return an empty identifier if not)
+        inline IdentifierUID       GetGenericParameterTypeId() const;
+        // get the variable's type's class
+        inline TypeClass           GetTypeClass() const;
+        // get the variable's type's generic parameter type's class
+        inline TypeClass           GetGenericParameterTypeClass() const;
+        // get the variable's type's ref info
+        inline TypeRefInfo         GetTypeRefInfo() const;
+        // is the variable's type a sampler ?
+        inline bool                IsSampler() const;
+        inline bool                IsConstantBuffer() const;
+        // is texture, sampler, buffer (all sorts except ConstantBuffer)
+        inline bool                IsViewType() const;
+        // access array dimensions. think of it as it they apply on the whole variable's type. (so not the generic type)
+        // returns an ArrayDimensions struct const ref.
+        inline const auto&         GetArrayDimensions() const;
+
+        AstUnnamedVarDecl*         m_declNode = nullptr;
+        UnqualifiedName            m_identifier;
+        TypeQualifier              m_typeQualifier;
+        vector<string>             m_unknownQualifiers;        // For qualifiers we didn't add to the enum
+        bool                       m_srgMember = false;
+        bool                       m_isPublic = true;
+        ConstNumericVal            m_constVal;   // (attempted folded) initializer value for simple scalars
+        optional<SamplerStateDesc> m_samplerState;
+        ExtendedTypeInfo           m_typeInfoExt;
+    };
+
+    // deported method definitions
+    bool VarInfo::CheckHasStorageFlag(StorageFlag flag) const
+    {
+       return TypeHasStorageFlag(m_typeQualifier, flag);
+    }
+
+    bool VarInfo::CheckHasAllStorageFlags(std::initializer_list<StorageFlag> vararg) const
+    {
+        return std::all_of(vararg.begin(), vararg.end(), [this](StorageFlag f) { return CheckHasStorageFlag(f); });
+    }
+
+    bool VarInfo::CheckHasAnyStorageFlags(std::initializer_list<StorageFlag> vararg) const
+    {
+        return std::any_of(vararg.begin(), vararg.end(), [this](StorageFlag f) { return CheckHasStorageFlag(f); });
+    }
+
+    bool VarInfo::StorageFlagIsLocalLinkage(bool isGlobalOrSrgScope) const
+    {
+        // Options are hybrid, but we consider them local. Because hard options are defined (by macro).
+        bool forcedLocal = CheckHasAnyStorageFlags({ StorageFlag::Static, StorageFlag::Groupshared, StorageFlag::Option });
+        bool impliedExtern = isGlobalOrSrgScope && !forcedLocal;  // [not-explicitely-local AND global-scope (or SRG-scope)] implies extern
+        bool isExtern = impliedExtern
+            || CheckHasAnyStorageFlags({ StorageFlag::Extern, StorageFlag::Rootconstant });
+        return !isExtern;
+    }
+
+    //! Get the type identifier of the core type
+    IdentifierUID VarInfo::GetTypeId() const
+    {
+        return m_typeInfoExt.m_coreType.m_typeId;
+    }
+
+    IdentifierUID VarInfo::GetGenericParameterTypeId() const
+    {
+        return m_typeInfoExt.m_genericParameter.m_typeId;
+    }
+
+    //! Get the type information of the core type
+    TypeRefInfo VarInfo::GetTypeRefInfo() const
+    {
+        return m_typeInfoExt.m_coreType;
+    }
+
+    //! Get the type class of the core type
+    TypeClass VarInfo::GetTypeClass() const
+    {
+        return m_typeInfoExt.m_coreType.m_typeClass;
+    }
+
+    TypeClass VarInfo::GetGenericParameterTypeClass() const
+    {
+        return m_typeInfoExt.m_genericParameter.m_typeClass;
+    }
+
+    bool VarInfo::IsSampler() const
+    {
+        return GetTypeClass() == TypeClass::Sampler;
+    }
+
+    bool VarInfo::IsConstantBuffer() const
+    {
+        return GetTypeClass() == TypeClass::ConstantBuffer;
+    }
+
+    bool VarInfo::IsViewType() const
+    {
+        return AZ::ShaderCompiler::IsViewType(GetTypeClass());
+    }
+
+    const auto& VarInfo::GetArrayDimensions() const
+    {
+        return m_typeInfoExt.GetDimensions();
+    }
+
+    struct OverloadSetInfo
+    {
+        //! Set the name of the set
+        void SetSetName(const IdentifierUID& myId)
+        {
+            m_setFullName = myId;
+        }
+
+        //! All functions belonging to this overload-set have
+        //! the same UDT return type, or all have return types of the predefined class.
+        bool HasHomogeneousReturnType() const
+        {
+            return m_returnTypeSet.m_state == ReturnTypeSet::Homogeneous
+                || m_returnTypeSet.m_state == ReturnTypeSet::NonInit;
+        }
+
+        string GetUniformReturnTypeDisplayName() const
+        {
+            if (!HasHomogeneousReturnType())
+            {
+                return "<non-homogeneous-type>";
+            }
+            if (!m_returnTypeSet.m_type)
+            {
+                return "<predefined-class-type>";
+            }
+            return m_returnTypeSet.m_type->GetDisplayName();
+        }
+
+        const ExtendedTypeInfo& GetUniformReturnType() const
+        {
+            assert(HasHomogeneousReturnType());
+            return *m_returnTypeSet.m_type;
+        }
+
+        //! if there is a unique function that corresponds to a given arity, return its UID. otherwise return an empty UID
+        IdentifierUID FindCandidateOfArity(size_t arity)
+        {
+            auto lookup = m_argCounts.find(arity);
+            return lookup != m_argCounts.end() ? lookup->second : IdentifierUID{};
+        }
+
+        //! attempt a lookup within
+        IdentifierUID GetConcreteFunctionThatMatchesArgumentList(string_view mangledArgumentList)
+        {
+            if (m_functions.size() == 1)
+            {
+                return *m_functions.begin();
+            }
+            auto reconstructedId = IdentifierUID{QualifiedName{ConcatString(m_setFullName.m_name, mangledArgumentList)}};
+            bool directMatch = m_functions.find(reconstructedId) != m_functions.end();
+            if (!directMatch)
+            {   // attempt a fallback matching logic using arity.
+                auto numArgs = CountParameters(mangledArgumentList);
+                return FindCandidateOfArity(numArgs);
+            }
+            return reconstructedId; // if found it in the set
+        }
+
+        //! queries whether this set has at least two functions registered to it.
+        bool HasOverloads() const
+        {
+            return m_functions.size() > 1;
+        }
+
+        //! queries whether this set contains a given candidate (in: fully decorated global unique identifiers only)
+        bool Has(const IdentifierUID& member) const
+        {
+            return m_functions.find(member) != m_functions.end();
+        }
+
+        //! construct your overload-set progressively, thanks to that adder.
+        void PushConcreteFunction(IdentifierUID functionBelongingToTheOverloadSet, ExtendedTypeInfo thatFunctionsReturnType)
+        {
+            assert(IsLeafDecoratedByArguments(functionBelongingToTheOverloadSet.GetName()));
+            string_view core = RemoveLastParenthesisGroup(functionBelongingToTheOverloadSet.GetName());
+            if (core != m_setFullName.m_name)
+            {
+                throw std::logic_error{ConcatString("Impossible to add function ", functionBelongingToTheOverloadSet.m_name,
+                                                    " to an overload-set that doesn't have the same core name or scope")};
+            }
+            if (m_returnTypeSet.m_state != ReturnTypeSet::HeterogeneousUserDefinedType)
+            {
+                bool ok = m_returnTypeSet.Merge(thatFunctionsReturnType);
+                if (!ok)
+                {
+                    verboseCout << "WARN: function " << functionBelongingToTheOverloadSet.m_name
+                                << " is introducing heterogeneity of return types to its overload-set\n";
+                }
+            }
+            m_functions.insert(functionBelongingToTheOverloadSet);
+            // cache the arguments count:
+            size_t argCount = CountParameters(functionBelongingToTheOverloadSet.GetName());
+            auto previousCandidateWithSameArgCount = m_argCounts.find(argCount);
+            if (previousCandidateWithSameArgCount == m_argCounts.end())
+            {
+                m_argCounts[argCount] = functionBelongingToTheOverloadSet;
+            }
+            else
+            {
+                previousCandidateWithSameArgCount->second.Clear();  // keep the count key in the map, but map to empty as a sentinel for "unusable arity"
+            }
+        }
+
+        //! run whatever you want on all overloads UID registered in the set
+        template< typename FunctorTakingUid >
+        void ForEach(FunctorTakingUid&& functor)
+        {
+            std::for_each(m_functions.begin(), m_functions.end(), functor);
+        }
+
+        //! queries whether any registered overloads has a positive result on the functor predicate
+        template< typename FunctorTakingUidReturningBool >
+        bool AnyOf(FunctorTakingUidReturningBool&& functor)
+        {
+            return std::any_of(m_functions.begin(), m_functions.end(), functor);
+        }
+
+    private:
+
+        //! helper to track their return type
+        struct ReturnTypeSet
+        {
+            //! validate that all functions return manageable types
+            bool Merge(ExtendedTypeInfo newType)
+            {
+                if (m_state == NonInit)   // first call case
+                {
+                    m_type = newType;
+                    m_state = Homogeneous;
+                }
+                else if (m_state == Homogeneous)
+                {
+                    if (newType != *m_type)  // overloaded functions must in principle have the same return type
+                    {                        // but we can tolerate differences in case of simple types.
+                                             // (because they can't have renamed members that should go through translation)
+                        bool newIsPredefined = IsPredefinedType(newType.m_coreType.m_typeClass);
+                        bool oldIsPredefined = IsPredefinedType(m_type->m_coreType.m_typeClass);
+                        if(!(newIsPredefined && oldIsPredefined))
+                        {
+                            m_state = HeterogeneousUserDefinedType;
+                        }
+                        m_type = none; // in case of a difference the cache becomes useless.
+                    }
+                }
+                return m_state == Homogeneous;
+            }
+            optional<ExtendedTypeInfo> m_type;
+            enum State
+            {
+                NonInit,
+                Homogeneous,
+                HeterogeneousUserDefinedType
+            } m_state = NonInit;
+        } m_returnTypeSet;                //!< if the returnType is uniform across all overloads, it'll be cached here.
+        IdentifierUID m_setFullName;      //!< stores our own ID to be able to check that added functions share the scope and name.
+        set<IdentifierUID> m_functions;   //!< IDs of functions participating to an overload set.
+        unordered_map<size_t, IdentifierUID> m_argCounts; //!< number of parameters as keys to a unique candidate that has this count.
+    };
+
+    struct FunctionInfo
+    {
+        //! tells if the FunctionInfo object hasn't been initialized
+        //!  note that this is an invalid state and would be illegal as an actual symbol
+        bool IsEmpty() const
+        {
+            return m_declNode == nullptr && m_declNode == nullptr;
+        }
+
+        //! tells if a function has no definition (it has a lone declaration)
+        bool IsUndefinedFunction() const
+        {
+            return !IsEmpty() && m_defNode == nullptr;
+        }
+
+        //! tells if a function has 2 apparition sites
+        bool HasPreDeclarationAndLaterDefinition() const
+        {
+            return m_declNode
+                && m_declNode != m_defNode
+                && !IsUndefinedFunction();
+        }
+
+        //! tells if a function has only a definition (acting as both declaration and definition)
+        bool HasUniqueDeclarationThroughDefinition() const
+        {
+            return m_defNode == m_declNode && !IsEmpty();
+        }
+
+        //! out-of-class definition exists
+        bool HasDeportedDefinition() const
+        {
+            return m_isMethod && HasPreDeclarationAndLaterDefinition();
+        }
+
+        bool CheckHasStorageFlag(StorageFlag flag) const
+        {
+            return (m_typeQualifier & flag).AsBool();
+        }
+
+        bool HasStorageFlags() const
+        {
+            return !m_typeQualifier.IsEmpty();
+        }
+
+        //! queries whether this function has a default value, registered for at least one of its parameters.
+        //! e.g such as in "f(int i = 2)"
+        bool HasAnyDefaultParameterValue() const
+        {
+            bool list0has = std::any_of(m_parameters[0].begin(), m_parameters[0].end(), [](auto&& p){return !!p.m_defaultValueExpression;});
+            assert(!std::any_of(m_parameters[1].begin(), m_parameters[1].end(), [](auto&& p){return !!p.m_defaultValueExpression;}));  // check that nobody runs this query in the middle of Stash() + MergeDefaultParameters()
+            return list0has;
+        }
+
+        //! add a parameter
+        void PushParameter(IdentifierUID varName, const ExtendedTypeInfo& typeInfo, TypeQualifier typeQualifier, const std::vector<azslParser::ArrayRankSpecifierContext*>& arrayRankSpecifiers, AstVarInitializer* initCtx)
+        {
+            Parameter param;
+            param.m_varId = varName;
+            param.m_typeInfo = typeInfo;
+            param.m_typeQualifier = typeQualifier;
+            param.m_arrayRankSpecifiers = arrayRankSpecifiers;
+            param.m_defaultValueExpression = initCtx;
+            m_parameters[m_currentList].push_back(param);
+        }
+
+        //! use during semantic parsing when a function signature is re-encountered
+        void StashParameters()
+        {
+            assert(m_currentList < 1);
+            ++m_currentList;
+            for (Parameter& p : m_parameters[0])
+            {
+                p.m_varId.Clear();  // mutate all variables to unnamed in the first list. because these symbols are getting deleted.
+            }
+        }
+
+        //! @param firstDeclaration get the list from the first signature site
+        auto& GetParameters(bool firstDeclaration) const
+        {
+            return m_parameters[std::min(m_currentList, firstDeclaration ? 0 : 1)];
+        }
+
+        bool DeleteParameter(const IdentifierUID& varName)
+        {
+            auto& paramList = m_parameters[m_currentList];
+            const auto originalSize = paramList.size();
+            paramList.erase(std::remove_if(paramList.begin(), paramList.end(), [&](const Parameter& param)
+                {
+                    return param.m_varId == varName;
+                }), paramList.end());
+            return paramList.size() != originalSize;
+        }
+
+        //! verify that if we went through a "stash", that:
+        //!    the redeclaration has the same number of parameter than the original
+        //!    there is no double definition of defaults arguments
+        //!    and merge all defaults to the first declaration. this way we can exploit default values even before they are declared. note that in the initializer expression, if the user calls an identifier that isn't declared yet at the first site, it will fail to build.
+        void MergeDefaultParameters()
+        {
+            if (m_currentList > 0) // we have 2 declaration sites (2 arg lists)
+            {
+                if (m_parameters[0].size() != m_parameters[1].size())
+                {   // not a mainstreamed EC diagnostic, because this is probably an internal error. because functions identity incorporate arguments.
+                    throw std::runtime_error{ConcatString(DiagLine(m_declNode->start), " function redeclaration not matching earlier appearance, during validation of parameters. Look for earlier warnings?")};
+                }
+                // merge all default values to first declaration
+                for (size_t i = 0; i < m_parameters[0].size(); ++i)
+                {
+                    Parameter& p0 = m_parameters[0][i];
+                    Parameter& p1 = m_parameters[1][i];
+                    if (p0.m_defaultValueExpression && p1.m_defaultValueExpression)
+                    {
+                        throw AzslcOrchestratorException{ORCHESTRATOR_NO_DOUBLE_DEFAULT_DECLARATION, p1.m_defaultValueExpression->start,
+                            ConcatString("default argument value declared twice. first seen at ",
+                                         p0.m_defaultValueExpression->start->getLine(), ":",
+                                         p1.m_defaultValueExpression->start->getCharPositionInLine() + 1)};
+                    }
+                    if (p1.m_defaultValueExpression)
+                    {
+                        using std::swap;
+                        swap(p0.m_defaultValueExpression, p1.m_defaultValueExpression);
+                    }
+                }
+            }
+        }
+
+        ExtendedTypeInfo          m_returnType;
+        AstFuncSig*               m_declNode     = nullptr;   //!< point to the AST node first declaring this function
+        AstFuncSig*               m_defNode      = nullptr;   //!< point to the AST node defining this function
+        bool                      m_isMethod     = false;
+        bool                      m_mustOverride = false;     //!< means is required to override, by override specifier keyword.
+        bool                      m_isVirtual    = false;     //!< is a method from an interface
+        vector< IdentifierUID >   m_overrides;                //!< list of implementing functions in child classes
+        optional< IdentifierUID > m_base;   //!< points to the overridden function in the base interface, if applies. only supports one base
+        TypeQualifier             m_typeQualifier;
+        struct Parameter
+        {
+            IdentifierUID m_varId;
+            ExtendedTypeInfo m_typeInfo;
+            TypeQualifier m_typeQualifier;
+            std::vector<azslParser::ArrayRankSpecifierContext*> m_arrayRankSpecifiers;
+            AstVarInitializer* m_defaultValueExpression = nullptr;
+        };
+        using ParameterList = vector<Parameter>;
+    private:
+        array<ParameterList, 2> m_parameters;  //!< two lists. one for the original declaration site, and one for an eventual second site for deported definition
+        int m_currentList = 0; //!< store index in m_parameters of currently edited list
+    };
+
+    struct SRGInfo
+    {
+        optional< IdentifierUID > FindMemberFromLeafName(UnqualifiedNameView member) const
+        {
+            auto inImplicit = m_implicitStruct.FindMemberFromLeafName(member);
+            if (inImplicit)
+            {
+                return inImplicit;
+            }
+            const vector< IdentifierUID >* memberArrays[] = {&m_structs, &m_srViews, &m_samplers, &m_CBs, &m_nonexternVariables, &m_functions};
+            for (auto* vector : memberArrays)
+            {
+                auto iterator = std::find_if(vector->begin(), vector->end(), [&member](const IdentifierUID& uid) { return uid.GetNameLeaf() == member; });
+                if (iterator != vector->end())
+                {
+                    return *iterator;
+                }
+            }
+            return none;
+        }
+
+        bool IsPartial() const { return m_declNode ? !!m_declNode->Partial() : false; }
+
+        AstSRGDeclNode*           m_declNode = nullptr;
+        ClassInfo                 m_implicitStruct;           // Implicit holding struct for SRG Constants
+        optional< IdentifierUID > m_shaderVariantFallback;
+        optional< IdentifierUID > m_semantic;
+        vector< IdentifierUID >   m_structs;
+        vector< IdentifierUID >   m_srViews;
+        vector< IdentifierUID >   m_samplers;
+        vector< IdentifierUID >   m_CBs;
+        vector< IdentifierUID >   m_nonexternVariables;
+        vector< IdentifierUID >   m_functions;
+        // We will cache here the unbounded arrays as the Semantic Orchestrator
+        // discovers them. We are using a vector, instead of a Set/Map because
+        // There can not be more than 4 unbounded arrays per SRG. For a small
+        // amount of items a linear search in a vector is faster than Set/Map searching.
+        // Later, during emission, we can easily change the order of appearance of these
+        // variables. 
+        vector< IdentifierUID >   m_unboundedArrays;
+    };
+
+    static const uint32_t SRGSemanticInfo_MaxAllowedFrequency = 15;  // Maximum CB slots for all graphics API we support (common minimum)
+
+    //! store data about code semantics for any sort of thing in the language. (a "sort of thing in the language" is a Kind)
+    struct KindInfo
+    {
+        using AnyKind = variant< monostate, VarInfo, FunctionInfo, OverloadSetInfo, ClassInfo, SRGInfo, TypeRefInfo, TypeAliasInfo >;
+
+        using MapKindToTypesT = TypeList<
+            ConstVal< Kind::Type >,                        TypeRefInfo,
+            ConstVal< Kind::TypeAlias >,                   TypeAliasInfo,
+            ConstVal< Kind::Variable >,                    VarInfo,
+            ConstVal< Kind::Function >,                    FunctionInfo,
+            ConstVal< Kind::OverloadSet >,                 OverloadSetInfo,
+            ConstVal< Kind::Struct >,                      ClassInfo,
+            ConstVal< Kind::Class >,                       ClassInfo,
+            ConstVal< Kind::Enum >,                        ClassInfo,
+            ConstVal< Kind::Interface >,                   ClassInfo,
+            ConstVal< Kind::ShaderResourceGroup >,         SRGInfo,
+            ConstVal< Kind::ShaderResourceGroupSemantic >, ClassInfo
+        >;
+
+        //! Helper-getter for the m_subInfo, directly accessed as its concrete type.
+        //! Returns nullptr in case of wrong template type (T) query, versus currently stored data type.
+        template<typename T>
+        const T* GetSubAs() const
+        {
+            return (holds_alternative<T>(m_subInfo)) ? &get<T>(m_subInfo) : (const T*)nullptr;
+        }
+
+        //! mutable version
+        template<typename T>
+        T* GetSubAs()
+        {
+            return (holds_alternative<T>(m_subInfo)) ? &get<T>(m_subInfo) : (T*)nullptr;
+        }
+
+        //! Throwing version (bad_access) of above helper, gets a reference on success
+        template<typename T>
+        const T& GetSubRefAs() const noexcept(false)
+        {
+            return get<T>(m_subInfo);
+        }
+
+        //! mutable version
+        template<typename T>
+        T& GetSubRefAs() noexcept(false)
+        {
+            return get<T>(m_subInfo);
+        }
+
+        //! Set the kind from a runtime value
+        void InitAs(Kind::EnumType k)
+        {
+            assert( OkToAssignKind(k) ); // don't try to recycle KindInfo. re-create from scratch, or you have a bug.
+            m_kind = k;
+            int typeIndex = -1;
+            ForEachType<MapKindToTypesT>([&typeIndex, k](auto inst, auto ii_c)
+                                         {
+                                             // the decltype(ii_c)::value is to prevent MSVC from crashing with an internal error. no kidding.
+                                             // using KeyAtii = At_t<ii_c, MapKindToTypesT>;  BOOM !
+                                             using KeyAtii = At_t<decltype(ii_c)::value, MapKindToTypesT>;
+                                             if constexpr (isIntegralConstant_v<KeyAtii>) // protection because we can't compare TypeRefInfo to Kind without a build error.
+                                             {
+                                                 if (KeyAtii{} == k) // found k in the maplist
+                                                 {
+                                                     using MappedType = At_t<ii_c + 1, MapKindToTypesT>;
+                                                     typeIndex = metaFind_v<MappedType, AnyKind>;
+                                                 }
+                                             }
+                                         });
+            // if typeIndex is sill -1, it's possibly a Namespace or a kind that has no subinfo.
+            IndexedFactory(m_subInfo, typeIndex);
+        }
+
+        //! For when you want to create a Kind that usually has no subinfo. mostly just Namespace.
+        template<Kind::EnumType k>
+        void InitAs()
+        {
+            constexpr auto index = metaFind_v< ConstVal<k>, MapKindToTypesT >;
+            if constexpr (index >= 0)
+            {   // k is applicable as a kind that has a subinfo, so we can use GetSubAfterInitAs to factorize.
+                GetSubAfterInitAs<k>();
+            }
+            else
+            {   // factorize with the runtime initializer
+                InitAs(k);
+            }
+        }
+
+        //! At registering time, this helps initialize in an atomic way and respects proper class invariants.
+        template<Kind::EnumType k>
+        auto& GetSubAfterInitAs()
+        {
+            static_assert(k != Kind::Namespace, "There is no subinfo for namespaces. just initialize the m_kind member.");
+
+            InitAs(k);
+
+            // lookup the concrete type from the kind since they can be mapped surjectively
+            using SubT = MetaFindValueNextToKey_t< ConstVal<k>, MapKindToTypesT >;
+            static_assert( !is_same_v<SubT, NotFoundT>, "did you pass a k value out of the Kind enum ?" );
+            // initialize the variant with an default constructed sub type:
+            m_subInfo = SubT{};
+            return GetSubRefAs<SubT>();
+        }
+
+        //! read-only access
+        Kind GetKind() const
+        {
+            return m_kind;
+        }
+
+        //! direct query for convenience
+        template<typename... Those>
+        bool IsKindOneOf(Those... kinds) const
+        {
+            return m_kind.IsOneOf(kinds...);
+        }
+
+        //! access the seenat vector directly
+        //! NOTE: careful, if you need to iterate references, check the more powerful HomonymVisitor facility.
+        auto& GetSeenats()
+        {
+            return m_seenAt;
+        }
+
+        //! reproduce the apply API to publicize visitation of subinfo variant
+        //! return: whatever visitation would return. normally deduced from return expressions of the visitor's operators().
+        template<typename Visitor>
+        auto VisitSub(Visitor&& v) const
+        {
+            return StdUtils::visit(std::forward<Visitor>(v), m_subInfo);
+        }
+
+    private:
+
+        // private method to check class invariants
+        bool OkToAssignKind(Kind k) const
+        {
+            // pre-init conditions, or non-changing re-assignment
+            return m_kind == k || m_kind == Kind::EndEnumeratorSentinel_;
+        }
+
+        // == Members ==
+
+        //! Precise kind of the symbol
+        Kind             m_kind;
+        //! Meat of the data for this symbol will have many specific attributes, so it goes in a variant
+        AnyKind          m_subInfo;
+        //! all reference (use) sites for this symbol
+        vector< Seenat > m_seenAt;
+
+    public:
+        // Meta API. query "type is alternative?" of subinfo variant
+        template<typename Alt>
+        static constexpr auto isSubAlternative_v = isContainedIn_v< Alt, decltype(KindInfo::m_subInfo) >;
+    };
+
+    // usings for data model. of particular relevance for the SymbolTable.
+    using IdToKindMap            = unordered_map< IdentifierUID, KindInfo >;
+    using IdAndKind              = IdToKindMap::value_type;  // nicely destructurable ! assign it like this: `auto& [uid, kind] = GetIdAndKind..`
+
+
+    struct GetSubKindInfoTypeName_Visitor
+    {
+        GetSubKindInfoTypeName_Visitor(IdentifierUID uid, bool forFunctionsGetReturnType)
+            : m_uid(uid),
+              m_forFunctionsGetReturnType(forFunctionsGetReturnType)
+        {}
+
+        QualifiedName operator()(monostate) const
+        {
+            return QualifiedName{"<fail>"};
+        }
+
+        QualifiedName operator()(const VarInfo& var) const
+        {   // for simplicity, note that arrays are collapsed to the underlying type (e.g. `int a[2]` has `int` type, not `int[]` type)
+            return var.m_typeInfoExt.GetMimickedType();
+        }
+
+        QualifiedName operator()(const FunctionInfo& func) const
+        {   // maintenance note: historical behavior here was to collapse to return type.
+            // today, in intermediate type-chains evaluations, we are going to distinguish between function and function call.
+            // this change is introduced to be able to make informed decisions on function-call-expression sites, amidst the bigger picture of overloads support.
+            return m_forFunctionsGetReturnType ? func.m_returnType.GetMimickedType() : m_uid.GetName();
+        }
+
+        QualifiedName operator()(const OverloadSetInfo& overloadSet) const
+        {
+            if (m_forFunctionsGetReturnType)
+            {
+                return overloadSet.HasHomogeneousReturnType() ? overloadSet.GetUniformReturnType().GetMimickedType()
+                                                              : QualifiedNameView{"<fail>"};
+            }
+            return m_uid.GetName();
+        }
+
+        QualifiedName operator()(const ClassInfo&) const
+        {   // a class/struct/interface IS a type
+            return m_uid.GetName();
+        }
+
+        // TODO: unify SRGInfo with ClassInfo. an SRG is a namespace. and a namespace is just a class where members are all static.
+        QualifiedName operator()(const SRGInfo&) const
+        {
+            return m_uid.GetName(); // this is not really a type, but whatever
+        }
+
+        QualifiedName operator()(const TypeRefInfo& tri) const
+        {
+            assert(tri.m_typeId == m_uid);
+            return m_uid.GetName();
+        }
+
+        QualifiedName operator()(const TypeAliasInfo& tai) const
+        {
+            return tai.m_canonicalType.GetMimickedType();
+        }
+
+        IdentifierUID m_uid;
+        bool m_forFunctionsGetReturnType;
+    };
+    enum class ForFunctionGetType
+    {
+        Returned,
+        SelfIdentity
+    };
+    //! Extract the: "canonical mimicked collapsed mangled" type name, from a symbol object. (the AZIR name)
+    //!   why ?
+    //!     canonical: vector<int,2> will be int2
+    //!     mimicked : ConstantBuffer<S> will be S
+    //!     collapsed: float4[10] will be float4
+    //!     mangled  : float is ?float  and  Srg::S is /Srg/S
+    //! @param getStrategy   set it to "Returned" to collapse functions to their return types. Can be useful for call expressions.
+    inline QualifiedName GetTypeName(const IdAndKind* ptr, ForFunctionGetType getStrategy = ForFunctionGetType::SelfIdentity)
+    {
+        auto& [uid, kind] = *ptr;
+        return kind.VisitSub(GetSubKindInfoTypeName_Visitor{uid, getStrategy == ForFunctionGetType::Returned});
+    }
+
+    static const size_t NoLine = 0_sz;
+    struct GetOrigSourceLine_Visitor
+    {
+        size_t operator()(const VarInfo& var) const
+        {
+            return var.m_declNode ? var.m_declNode->start->getLine() : NoLine;
+        }
+
+        size_t operator()(const FunctionInfo& func) const
+        {
+            return func.m_declNode ? func.m_declNode->start->getLine() : NoLine;
+        }
+
+        size_t operator()(const ClassInfo& clInfo) const
+        {
+            return clInfo.GetOrigSourceLine();
+        }
+
+        size_t operator()(const SRGInfo& srgInfo) const
+        {
+            return srgInfo.m_declNode ? srgInfo.m_declNode->start->getLine() : NoLine;
+        }
+
+        size_t operator()(const TypeAliasInfo& tai) const
+        {
+            return tai.m_declNode ? tai.m_declNode->start->getLine() : NoLine;
+        }
+
+        template<typename AnyNonCovered>
+        size_t operator()(AnyNonCovered) const
+        {
+            return NoLine;
+        }
+    };
+
+    inline string GetFirstSeenLineMessage(const KindInfo& kindInfo)
+    {
+        size_t firstSeen = kindInfo.VisitSub(GetOrigSourceLine_Visitor{});
+        if (firstSeen != NoLine)
+        {
+            return "first seen line " + std::to_string(firstSeen);
+        }
+        return {};
+    }
+
+    //! helper for fatal semantic error of ODR violation
+    inline void ThrowRedeclarationAsDifferentKind(string_view symbolName, Kind newKind, const KindInfo& kindInfo, optional<size_t> lineNumber = none)
+    {
+        const string errorMessage = ConcatString(
+            "redeclaration of ", symbolName, " with a different kind: ", Kind::ToStr(newKind),
+            " but was ", Kind::ToStr(kindInfo.GetKind()), GetFirstSeenLineMessage(kindInfo));
+        throw AzslcOrchestratorException(ORCHESTRATOR_ODR_VIOLATION, lineNumber, none, errorMessage);
+    }
+
+    struct GetDefinitionNodeTokensLocation_Visitor
+    {
+        const TokensLocation operator()(const VarInfo& var) const
+        {
+            ParserRuleContext* node = GetParentIfIsNamedVarDecl_OtherwiseIdentity(var.m_declNode);
+            return node ? MakeTokensLocation(node, ExtractVariableNameIdentifier(var.m_declNode))
+                        : TokensLocation{{}, -1, 0, 0};
+        }
+
+        const TokensLocation operator()(const FunctionInfo& func) const
+        {
+            return MakeTokensLocation(func.m_defNode ? func.m_defNode : func.m_declNode,
+                                      func.m_defNode ? func.m_defNode->Name : func.m_declNode->Name);
+        }
+
+        const TokensLocation operator()(const ClassInfo& clInfo) const
+        {
+            return MakeTokensLocation(clInfo.GetDeclNode(), clInfo.GetDeclNodeNameToken());
+        }
+
+        const TokensLocation operator()(const SRGInfo& srgInfo) const
+        {
+            return MakeTokensLocation(srgInfo.m_declNode, srgInfo.m_declNode->Name);
+        }
+
+        const TokensLocation operator()(const TypeAliasInfo taInfo) const
+        {
+            return MakeTokensLocation(taInfo.m_declNode, taInfo.m_declNode->typealiasStatement() ? taInfo.m_declNode->typealiasStatement()->NewTypeName
+                                                                                                 : taInfo.m_declNode->typedefStatement()->NewTypeName);
+        }
+
+        template<typename AnyNonCovered>
+        const TokensLocation operator()(AnyNonCovered) const
+        {
+            return {{}, -1, 0, 0};
+        }
+    };
+
+    inline TokensLocation GetDefinitionTokensLocation(const KindInfo& kindInfo)
+    {
+        return kindInfo.VisitSub(GetDefinitionNodeTokensLocation_Visitor{});
+    }
+
+    //! map SRV->T | UAV->U | Sampler->S | CBV,SrgConsant,RootConstant->B
+    BindingType GetBindingType(const ExtendedTypeInfo& extendedTypeInfo);
+}

+ 461 - 0
src/AzslcListener.cpp

@@ -0,0 +1,461 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include "AzslcListener.h"
+
+namespace AZ::ShaderCompiler
+{
+    void SemaCheckListener::enterIdExpression(azslParser::IdExpressionContext* ctx)
+    {
+        UnqualifiedName currentIdCtxUqName = ExtractNameFromIdExpression(ctx);
+        auto* memberAccessExpr = As<AstMemberAccess*>(ctx->parent);
+        assert(IsRHSOfMemberAccess(ctx) == !!memberAccessExpr);
+        if (memberAccessExpr)  // if direct parent is MAE, it's guaranteed this current idExpression is RHS. (because LHS of MAE is expression, not idExpression)
+        {
+            // we are in `a.b::c` (which can happen legally c.f. https://stackoverflow.com/q/56253767/893406)
+            bool isAbsolute = ctx->qualifiedId() && ctx->qualifiedId()->nestedNameSpecifier()->GlobalSROToken != nullptr;  // qualified and fully.
+            // in MAE (MemberAccessExpression) the LHS (left-hand-side) determines what scope we should look into, for this idExpression.
+            // in this context (MAE), a member accessed through a dot, `lhs.field` means the scope of interest is typeof(lhs).
+            QualifiedName startupScope = isAbsolute ? QualifiedName{"/"}  // no need to do any Join if the RHS is absolute
+                                                    : m_ir->m_sema.TypeofExpr(memberAccessExpr->LHSExpr); // relative to the scope of the type of LHS
+            if (isAbsolute || m_ir->m_sema.VerifyLHSExprOfMAExprIsValid(memberAccessExpr).first)
+            {
+                m_ir->m_sema.RegisterSeenat(ctx, startupScope);
+            }
+        }
+        else if (auto* typeofExpression = As<azslParser::TypeofExpressionContext*>(ctx->parent))
+        {
+            QualifiedName startupScope = typeofExpression->Expr ? m_ir->m_sema.TypeofExpr(typeofExpression->Expr)
+                                                                : m_ir->m_sema.TypeofExpr(typeofExpression->functionType());
+            if (m_ir->m_sema.VerifyTypeIsScopeComposable(startupScope).first)
+            {
+                m_ir->m_sema.RegisterSeenat(ctx, startupScope);
+            }
+        }
+        else
+        {   // simple direct reference registration. no outer context needs to be considered that can alter the symbol it refers to.
+            m_ir->m_sema.RegisterSeenat(ctx);
+        }
+    }
+
+    void SemaCheckListener::enterSrgSemantic(azslParser::SrgSemanticContext* ctx)
+    {
+        m_ir->m_sema.RegisterSRGSemantic(ctx);
+        m_ir->m_scope.EnterScope(ctx->Name->getText(), ctx->srgSemanticBodyDeclaration()->LeftBrace()->getSourceInterval().a);
+    }
+
+    void SemaCheckListener::enterInterfaceDefinition(azslParser::InterfaceDefinitionContext* ctx)
+    {
+        m_ir->m_sema.RegisterInterface(ctx);
+        m_ir->m_scope.EnterScope(ctx->Name->getText(), ctx->LeftBrace()->getSourceInterval().a);
+    }
+
+    void SemaCheckListener::enterStructDefinition(azslParser::StructDefinitionContext* ctx)
+    {
+        m_ir->m_sema.RegisterStruct(ctx);
+        m_ir->m_scope.EnterScope(ctx->Name->getText(), ctx->LeftBrace()->getSourceInterval().a);
+    }
+
+    void SemaCheckListener::enterClassDefinition(azslParser::ClassDefinitionContext* ctx)
+    {
+        m_ir->m_sema.RegisterClass(ctx);
+        m_ir->m_scope.EnterScope(ctx->Name->getText(), ctx->LeftBrace()->getSourceInterval().a);
+    }
+
+    void SemaCheckListener::enterEnumDefinition(azslParser::EnumDefinitionContext* ctx)
+    {        
+        m_ir->m_sema.RegisterEnum(ctx);
+        auto* scopedCtx = As<azslParser::ScopedEnumContext*>(ctx->enumKey());
+        if (scopedCtx != nullptr)
+        {
+            m_ir->m_scope.EnterScope(ctx->Name->getText(), ctx->LeftBrace()->getSourceInterval().a);
+        }
+    }
+
+    void SemaCheckListener::enterEnumeratorDeclarator(azslParser::EnumeratorDeclaratorContext* ctx)
+    {
+        m_ir->m_sema.RegisterEnumerator(ctx);
+    }
+
+    void SemaCheckListener::enterSamplerBodyDeclaration(azslParser::SamplerBodyDeclarationContext* ctx)
+    {
+        auto name = ExtractVariableNameSamplerBodyDeclaration(ctx);
+        m_ir->m_scope.EnterScope(name, ctx->LeftBrace()->getSourceInterval().a);
+    }
+
+    void SemaCheckListener::exitSamplerBodyDeclaration(azslParser::SamplerBodyDeclarationContext* ctx)
+    {
+        m_ir->m_scope.ExitScope(ctx->RightBrace()->getSourceInterval().b);
+    }
+
+    void SemaCheckListener::enterConstantBufferTemplated(azslParser::ConstantBufferTemplatedContext* ctx)
+    {
+        // Diagnose undue usage: any scope/contexts are not OK for constant buffers (which are not view types).
+        // Originally the grammar was strictly enforcing at the syntax level that CB appears only in SRG scope.
+        // But for canonicalization with predefined types, now their presence will parse correctly at any place a type rule can appear.
+
+        // the current rule has only one parent: predefinedType, itself has only one parent: type
+        auto* typeNode = As<azslParser::TypeContext*>(ctx->parent->parent);
+        // but the type rule has many parents, exhaustively:
+        //   streamOutputPredefinedType, structuredBufferPredefinedType  (as generics)
+        //   constantBufferTemplated (yes ! because it's generic it has an unrestricted type rule -> risk of recursion. let's forbid it semantically)
+        // and
+        //  functionType         not tolerable to return a constantbuffer, except if parent rule is typealias
+        //  functionParam        not tolerable to pass as argument
+        //  typedefStatement     tolerable anywhere
+        //  CastExpression       let's go the lenient route and accept it whatever it will end up meaning
+        //  variableDeclaration  tolerable depending on scope (SRG: ok, other: no)
+
+        bool isFunctionTypeInTypeAlias = Is<azslParser::FunctionTypeContext>(typeNode->parent) && Is3ParentRuleOfType<azslParser::TypeAliasingDefinitionStatementContext*>(typeNode);
+        if (DynamicTypeIsAnyOf<azslParser::StreamOutputPredefinedTypeContext,
+                               azslParser::StructuredBufferPredefinedTypeContext,
+                               azslParser::ConstantBufferTemplatedContext,
+                               azslParser::FunctionTypeContext,
+                               azslParser::FunctionParamContext>(typeNode->parent)
+            && !isFunctionTypeInTypeAlias)
+        {
+            throw AzslcException(ADVANCED_SYNTAX_CONSTANT_BUFFER_RESTRICTION,
+                                 "Syntax",
+                                 ctx->start,
+                                 "impossible to use ConstantBuffer in that context");
+        }
+
+        bool immediateScopeIsSRG = m_ir->m_sema.GetCurrentScopeIdAndKind().second.GetKind() == Kind::ShaderResourceGroup;
+        if (Is<azslParser::VariableDeclarationContext>(typeNode->parent) && !immediateScopeIsSRG)
+        {   // note that it's only OK in immediate scope, not "as one of the parents, however distant".
+            throw AzslcException(ADVANCED_SYNTAX_CONSTANT_BUFFER_ONLY_IN_SRG,
+                                 "Syntax",
+                                 ctx->start,
+                                 "ConstantBuffer variable declarations may only appear in ShaderResourceGroup");
+        }
+    }
+
+    void SemaCheckListener::enterSrgDefinition(azslParser::SrgDefinitionContext* ctx)
+    {
+        m_ir->m_sema.RegisterSRG(ctx);
+        m_ir->m_scope.EnterScope(ctx->Name->getText(), ctx->LeftBrace()->getSourceInterval().a);
+    }
+
+    void SemaCheckListener::exitSrgDefinition(azslParser::SrgDefinitionContext* ctx)
+    {
+        // Validate the content on scope exit
+        m_ir->m_sema.ValidateSrg(ctx);
+        m_ir->m_scope.ExitScope(ctx->RightBrace()->getSourceInterval().b);
+    }
+
+    void SemaCheckListener::enterSrgSemanticMemberDeclaration(azslParser::SrgSemanticMemberDeclarationContext* ctx)
+    {
+        m_ir->m_sema.RegisterSRGSemanticMember(ctx);
+    }
+
+    void SemaCheckListener::exitClassDefinition(azslParser::ClassDefinitionContext* ctx)
+    {
+        m_ir->m_sema.ValidateClass(ctx);
+        m_ir->m_scope.ExitScope(ctx->RightBrace()->getSourceInterval().b);
+    }
+
+    void SemaCheckListener::exitSrgSemantic(azslParser::SrgSemanticContext* ctx)
+    {
+        m_ir->m_sema.ValidateSrgSemantic(ctx);
+        m_ir->m_scope.ExitScope(ctx->srgSemanticBodyDeclaration()->RightBrace()->getSourceInterval().b);
+    }
+
+    void SemaCheckListener::exitInterfaceDefinition(azslParser::InterfaceDefinitionContext* ctx)
+    {
+        m_ir->m_scope.ExitScope(ctx->RightBrace()->getSourceInterval().b);
+    }
+
+    void SemaCheckListener::exitStructDefinition(azslParser::StructDefinitionContext* ctx)
+    {
+        m_ir->m_scope.ExitScope(ctx->RightBrace()->getSourceInterval().b);
+    }
+
+    void SemaCheckListener::exitEnumDefinition(azslParser::EnumDefinitionContext* ctx)
+    {
+        auto* scopedCtx = As<azslParser::ScopedEnumContext*>(ctx->enumKey());
+        if (scopedCtx != nullptr)
+        {
+            m_ir->m_scope.ExitScope(ctx->RightBrace()->getSourceInterval().b);
+        }
+    }
+
+    void SemaCheckListener::exitFunctionDefinition(azslParser::FunctionDefinitionContext* ctx)
+    {
+        m_ir->m_scope.ExitScope(ctx->hlslFunctionDefinition()->block()->RightBrace()->getSourceInterval().b);
+        // if it was a scoped definition, we entered a tiny scope to establish the function's identity correctly. exit that too.
+        if (ctx->hlslFunctionDefinition()->leadingTypeFunctionSignature()->ClassName)
+        {
+            m_ir->m_scope.ExitScope(ctx->hlslFunctionDefinition()->leadingTypeFunctionSignature()->Name->getTokenIndex());
+        }
+    }
+
+    void SemaCheckListener::enterFunctionDefinition(azslParser::FunctionDefinitionContext* ctx)
+    {
+        auto signature = ctx->hlslFunctionDefinition()->leadingTypeFunctionSignature();
+        auto uqName = ExtractNameFromAnyContextWithName(signature);
+
+        // extract the class type in case this is a deported method definition
+        azslParser::UserDefinedTypeContext* className = ctx->hlslFunctionDefinition()->leadingTypeFunctionSignature()->ClassName;
+        IdentifierUID newSymbol;
+        if (className)
+        {
+            newSymbol = m_ir->m_sema.RegisterDeportedMethod(uqName, className, signature).first;
+        }
+        else
+        {
+            newSymbol = m_ir->m_sema.RegisterFunction(uqName, signature, AsFunc::Definition).first;
+        }
+        // a function scope starts at the beginning of its parameter list
+        // use a variation of the EnterScope function that takes an absolute path (FQN)
+        // because entry into a deported method is not an accumulative scope, but a looked up one,
+        // so it can be arbitrarily unrelated to the current scope.
+        m_ir->m_scope.EnterScope(newSymbol.GetName(), ctx->hlslFunctionDefinition()->leadingTypeFunctionSignature()->LeftParen()->getSourceInterval().a);
+    }
+
+    void SemaCheckListener::exitFunctionDeclaration(azslParser::FunctionDeclarationContext* ctx)
+    {
+        // The scope interval will cover all trailing keywords (we are not mirroring the LeftParen with RightParen, but with end-of-statement)
+        // anyway don't confuse this token precision for actual scope activation. actual scope activation/deactivation is done by this call.
+        m_ir->m_scope.ExitScope(ctx->hlslFunctionDeclaration()->leadingTypeFunctionSignature()->stop->getTokenIndex());
+    }
+
+    void SemaCheckListener::enterFunctionDeclaration(azslParser::FunctionDeclarationContext* ctx)
+    {
+        auto signature = ctx->hlslFunctionDeclaration()->leadingTypeFunctionSignature();
+        auto uqName = ExtractNameFromAnyContextWithName(signature);
+        auto& [id, _] = m_ir->m_sema.RegisterFunctionDeclarationAndAddSeenat(uqName, signature);
+        // we will shortly enter the scope of the function, even for just a declaration, for the sake of canonicalizing validation.
+        // it also makes sense anyway since parameters (and any trailings) are defined in the function's scope.
+        m_ir->m_scope.EnterScope(id.GetNameLeaf(), signature->LeftParen()->getSourceInterval().a);
+    }
+
+    void SemaCheckListener::exitLeadingTypeFunctionSignature(azslParser::LeadingTypeFunctionSignatureContext* ctx)
+    {
+        // 'will run from function scope, be it declaration or definition.
+        m_ir->m_sema.ValidateFunctionAndRegisterFamilySeenat(ctx);
+    }
+
+    void SemaCheckListener::exitFunctionParam(azslParser::FunctionParamContext* ctx)
+    {   // we use the exit rule to let the time to inlined-UDT-decl to get registered through the type rule visit first.
+        if (!ctx->Name)
+        {
+            m_ir->m_sema.RegisterNamelessFunctionParameter(ctx);
+        }
+        else
+        {
+            m_ir->m_sema.RegisterVar(ctx->Name, ctx->unnamedVariableDeclarator());
+        }
+    }
+
+    void SemaCheckListener::enterNamedVariableDeclarator(azslParser::NamedVariableDeclaratorContext* ctx)
+    {
+        // unlike in versions prior and equal to 1.4.x.x, this rule is now only taken by standalone variable declaration statements, and not function arguments anymore
+        m_ir->m_sema.RegisterVar(ctx->Name, ctx->unnamedVariableDeclarator());
+    }
+
+    void SemaCheckListener::enterBaseList(azslParser::BaseListContext* ctx)
+    {
+        m_ir->m_sema.RegisterBases(ctx);
+    }
+
+    void SemaCheckListener::enterCompilerExtensionStatement(azslParser::CompilerExtensionStatementContext* ctx)
+    {
+        if (m_silentPrintExtensions)
+        {
+            return;
+        }
+
+        if (ctx->KW_ext_print_message())
+        {
+            // [GFX TODO]: use a log channel that makes sense. e.g. INFORMATION ?
+            std::cout << Unescape(ctx->Message->getText());
+        }
+        else if (ctx->KW_ext_print_symbol())
+        {
+            if (ctx->idExpression())
+            {
+                auto name = ExtractNameFromIdExpression(ctx->idExpression());
+                auto maybeSym = m_ir->m_sema.LookupSymbol(name);
+                if (!maybeSym)
+                {
+                    std::cout << "<not_found>";
+                }
+                else
+                {
+                    auto& [symId, sym] = *maybeSym;
+                    if (ctx->KW_ext_prtsym_fully_qualified())
+                    {
+                        std::cout << symId.m_name;
+                    }
+                    else if (ctx->KW_ext_prtsym_least_qualified())
+                    {
+                        std::cout << m_ir->m_symbols.FindLeastQualifiedName(m_ir->m_scope.m_currentScopePath, symId);
+                    }
+                    else if (ctx->KW_ext_prtsym_constint_value())
+                    {
+                        if (sym.GetKind() != Kind::Variable)
+                        {
+                            std::cout << "<not_a_var>";
+                        }
+                        else
+                        {
+                            auto& var = sym.GetSubRefAs<VarInfo>();
+                            try
+                            {
+                                int64_t asInt = ExtractValueAsInt64(var.m_constVal);
+                                std::cout << asInt;
+                            }
+                            catch (exception e)
+                            {
+                                std::cout << "<folding_failed>";
+                            }
+                        }
+                    }
+                }
+            }
+            else if (ctx->typeofExpression())
+            {
+                QualifiedName resolved = m_ir->m_sema.TypeofExpr(ctx->typeofExpression());
+                if (ctx->KW_ext_prtsym_fully_qualified())
+                {
+                    std::cout << resolved;
+                }
+                else if (ctx->KW_ext_prtsym_least_qualified())
+                {
+                    const auto* idKind = m_ir->m_symbols.GetIdAndKindInfo(resolved);
+                    if (idKind)
+                    {
+                        std::cout << m_ir->m_symbols.FindLeastQualifiedName(m_ir->m_scope.m_currentScopePath, idKind->first);
+                    }
+                    else
+                    {
+                        std::cout << resolved;
+                    }
+                }
+            }
+        }
+    }
+
+    static AttributeCategory GetAttributeCategory(antlr4::ParserRuleContext* ctx)
+    {
+        if (Is<azslParser::AttributeSpecifierContext*>(ctx->parent))
+        {
+            return AttributeCategory::Single;
+        }
+
+        if (Is<azslParser::AttributeSpecifierSequenceContext*>(ctx->parent))
+        {
+            return AttributeCategory::Sequence;
+        }
+
+        throw std::runtime_error{"Attribute specifier declared from unhandled parent type!"};
+    }
+
+    template< typename AttributeContextT >
+    static void DoAttributeRegistration(AttributeContextT* ctx, AttributeScope scope, IntermediateRepresentation* ir)
+    {
+        // Attributes can be filtered out by namespace.
+        // Attributes without a namespace are always valid and attributes in the 'void' namespace are always filtered out
+        auto Namespace = (ctx->Namespace) ? ctx->Namespace->getText() : "";
+        if (!Namespace.empty() && (Namespace == "void" || !ir->IsAttributeNamespaceActivated(Namespace)))
+        {
+            return;
+        }
+
+        ir->RegisterAttributeSpecifier(scope,
+                                       GetAttributeCategory(ctx),
+                                       ctx->getStart()->getLine(),
+                                       Namespace,
+                                       ctx->Name->getText(),
+                                       ctx->attributeArgumentList());
+    }
+
+    void SemaCheckListener::enterGlobalAttribute(azslParser::GlobalAttributeContext* ctx)
+    {
+        DoAttributeRegistration(ctx, AttributeScope::Global, m_ir);
+    }
+
+    void SemaCheckListener::enterAttachedAttribute(azslParser::AttachedAttributeContext* ctx)
+    {
+        DoAttributeRegistration(ctx, AttributeScope::Attached, m_ir);
+    }
+
+    void SemaCheckListener::exitTypedefStatement(azslParser::TypedefStatementContext* ctx)
+    {
+        m_ir->m_sema.RegisterTypeAlias(ctx->NewTypeName->getText(), ctx->ExistingType, polymorphic_downcast<azslParser::TypeAliasingDefinitionStatementContext*>(ctx->parent));
+    }
+
+    void SemaCheckListener::exitTypealiasStatement(azslParser::TypealiasStatementContext* ctx)
+    {
+        m_ir->m_sema.RegisterTypeAlias(ctx->NewTypeName->getText(), ctx->ExistingType, polymorphic_downcast<azslParser::TypeAliasingDefinitionStatementContext*>(ctx->parent));
+    }
+
+    void SemaCheckListener::exitTypeofExpression(azslParser::TypeofExpressionContext* ctx)
+    {
+        if (ctx->SubQualification
+            && ctx->SubQualification->qualifiedId()
+            && ctx->SubQualification->qualifiedId()->nestedNameSpecifier()->GlobalSROToken)
+        {
+            throw AzslcException(ADVANCED_SYNTAX_DOUBLE_SCOPE_RESOLUTION, "Syntax", ctx->SubQualification->qualifiedId()->nestedNameSpecifier()->GlobalSROToken,
+                                 "double scope resolution operator forbidden at this position");
+        }
+    }
+
+    void SemaCheckListener::enterForStatement(azslParser::ForStatementContext* ctx)
+    {
+        // for (a;b;c) block is special, because it has a special power of symbol definition in the 'a' block.
+        //             therefore we enter the anonymous scope right before 'a'
+        m_ir->m_sema.MakeAndEnterAnonymousScope("for", ctx->LeftParen()->getSymbol());
+    }
+
+    void SemaCheckListener::exitForStatement(azslParser::ForStatementContext* ctx)
+    {
+        m_ir->m_scope.ExitScope(ctx->stop->getTokenIndex());
+    }
+
+    void SemaCheckListener::enterBlockStatement(azslParser::BlockStatementContext* ctx)
+    {
+        if (!Is< azslParser::HlslFunctionDefinitionContext >(ctx->parent)  // not function because we already entered
+            && !Is< azslParser::ForStatementContext >(ctx->parent))  // not for statement because we already entered
+        {
+            m_ir->m_sema.MakeAndEnterAnonymousScope("bk", ctx->start);
+        }
+    }
+
+    void SemaCheckListener::exitBlockStatement(azslParser::BlockStatementContext* ctx)
+    {
+        if (!Is< azslParser::HlslFunctionDefinitionContext >(ctx->parent)  // not function, because we already exited
+            && !Is< azslParser::ForStatementContext >(ctx->parent))  // not for statement, because we exited in exitForStatement
+        {
+            m_ir->m_scope.ExitScope(ctx->stop->getTokenIndex());
+        }
+    }
+
+    void SemaCheckListener::enterSwitchBlock(azslParser::SwitchBlockContext* ctx)
+    {
+        m_ir->m_sema.MakeAndEnterAnonymousScope("sw", ctx->start);
+    }
+
+    void SemaCheckListener::exitSwitchBlock(azslParser::SwitchBlockContext* ctx)
+    {
+        m_ir->m_scope.ExitScope(ctx->stop->getTokenIndex());
+    }
+
+    void SemaCheckListener::enterFunctionCallExpression(azslParser::FunctionCallExpressionContext* ctx)
+    {
+        if (m_functionCallMutator)
+        {
+            m_functionCallMutator->enterFunctionCallExpression(ctx);
+        }
+    }
+
+    void SemaCheckListener::visitTerminal(tree::TerminalNode* node)
+    {
+        m_ir->RegisterTokenToNodeAssociation(node->getSymbol()->getTokenIndex(), polymorphic_downcast<ParserRuleContext*>(node->parent));
+    }
+}

+ 74 - 0
src/AzslcListener.h

@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include "AzslcIntermediateRepresentation.h"
+
+namespace AZ::ShaderCompiler
+{
+    struct SemaCheckListener : azslParserBaseListener
+    {
+        using Self = SemaCheckListener;
+        using Base = azslParserBaseListener;
+
+        SemaCheckListener(IntermediateRepresentation* ir)
+            : m_ir(ir)
+        {}
+
+        void enterIdExpression(azslParser::IdExpressionContext* ctx) override;
+        void enterSrgSemantic(azslParser::SrgSemanticContext* ctx) override;
+        void enterInterfaceDefinition(azslParser::InterfaceDefinitionContext* ctx) override;
+        void enterStructDefinition(azslParser::StructDefinitionContext* ctx) override;
+        void enterClassDefinition(azslParser::ClassDefinitionContext* ctx) override;
+        void enterEnumDefinition(azslParser::EnumDefinitionContext* ctx) override;
+        void enterEnumeratorDeclarator(azslParser::EnumeratorDeclaratorContext* ctx) override;
+        void enterSamplerBodyDeclaration(azslParser::SamplerBodyDeclarationContext* ctx) override;
+        void exitSamplerBodyDeclaration(azslParser::SamplerBodyDeclarationContext* ctx) override;
+        void enterConstantBufferTemplated(azslParser::ConstantBufferTemplatedContext* ctx) override;
+        void enterSrgDefinition(azslParser::SrgDefinitionContext* ctx) override;
+        void exitSrgDefinition(azslParser::SrgDefinitionContext* ctx) override;
+        void enterSrgSemanticMemberDeclaration(azslParser::SrgSemanticMemberDeclarationContext* ctx) override;
+        void exitClassDefinition(azslParser::ClassDefinitionContext* ctx) override;
+        void exitSrgSemantic(azslParser::SrgSemanticContext* ctx) override;
+        void exitInterfaceDefinition(azslParser::InterfaceDefinitionContext* ctx) override;
+        void exitStructDefinition(azslParser::StructDefinitionContext* ctx) override;
+        void exitEnumDefinition(azslParser::EnumDefinitionContext* ctx) override;
+        void exitFunctionDefinition(azslParser::FunctionDefinitionContext* ctx) override;
+        void enterFunctionDefinition(azslParser::FunctionDefinitionContext* ctx) override;
+        void exitFunctionDeclaration(azslParser::FunctionDeclarationContext* ctx) override;
+        void enterFunctionDeclaration(azslParser::FunctionDeclarationContext* ctx) override;
+        void exitLeadingTypeFunctionSignature(azslParser::LeadingTypeFunctionSignatureContext* ctx) override;
+        void exitFunctionParam(azslParser::FunctionParamContext* ctx) override;
+        void enterNamedVariableDeclarator(azslParser::NamedVariableDeclaratorContext* ctx) override;
+        void enterBaseList(azslParser::BaseListContext* ctx) override;
+        void enterCompilerExtensionStatement(azslParser::CompilerExtensionStatementContext* ctx) override;
+        void enterGlobalAttribute(azslParser::GlobalAttributeContext* ctx) override;
+        void enterAttachedAttribute(azslParser::AttachedAttributeContext* ctx) override;
+        void exitTypedefStatement(azslParser::TypedefStatementContext* ctx) override;
+        void exitTypealiasStatement(azslParser::TypealiasStatementContext* ctx) override;
+        void exitTypeofExpression(azslParser::TypeofExpressionContext* ctx) override;
+        void enterForStatement(azslParser::ForStatementContext* ctx) override;
+        void exitForStatement(azslParser::ForStatementContext* ctx) override;
+        void enterBlockStatement(azslParser::BlockStatementContext* ctx) override;
+        void exitBlockStatement(azslParser::BlockStatementContext* ctx) override;
+        void enterSwitchBlock(azslParser::SwitchBlockContext* ctx) override;
+        void exitSwitchBlock(azslParser::SwitchBlockContext* ctx) override;
+        void enterFunctionCallExpression(azslParser::FunctionCallExpressionContext* ctx) override;
+
+        void visitTerminal(tree::TerminalNode* node) override;
+
+        IntermediateRepresentation* m_ir;
+        bool m_silentPrintExtensions = false;
+
+        //! If not null, this other parser listener will be used during
+        //! enterFunctionCallExpression(), its data type is azslParserBaseListener because
+        //! in the future it can be convenient to do mutations at different stages of parsing
+        //! the AST.
+        azslParserBaseListener* m_functionCallMutator = nullptr;
+    };
+}

+ 729 - 0
src/AzslcMain.cpp

@@ -0,0 +1,729 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include "docopt/docopt.h"
+
+#include "AzslcReflection.h"
+#include "AzslcEmitter.h"
+#include "AzslcHomonymVisitor.h"
+#include "Texture2DMSto2DCodeMutator.h"
+
+#include <cstddef>
+#include <filesystem>
+namespace StdFs = std::filesystem;
+
+// versioning
+// Correspond to the supported version of the AZSL language.
+#define AZSLC_MAJOR    "1"
+// For large features or milestones. Minor version allows for breaking changes. Existing tests can change.
+#define AZSLC_MINOR    "7"
+// For small features or bug fixes. They cannot introduce breaking changes. Existing tests shouldn't change.
+#define AZSLC_REVISION "23" // ATOM-15801
+
+namespace AZ::ShaderCompiler
+{
+    DiagnosticStream verboseCout;
+    DiagnosticStream warningCout(std::cerr);
+    Endl             azEndl;
+
+    using MapOfStringViewToSetOfString = map< string_view, set<string> >;
+
+    // out argument: classifiedTokens
+    // filter: is a predicate for condition to check to pass registration
+    template <typename FilterFunction>
+    void ClassifyAllTokens(const azslLexer* lexer, MapOfStringViewToSetOfString& classifiedTokens, FilterFunction&& filter = nullptr)
+    {
+        // loop over all keywords
+        const auto& vocabulary = lexer->getVocabulary();
+        size_t maxToken = vocabulary.getMaxTokenType();
+        set<string> notTypes1;
+        for (size_t ii = 0; ii < maxToken; ++ii)
+        {
+            string token = vocabulary.getLiteralName(ii);
+            token = Trim(token, "\"'");  // because AntlR gives us e.g "'float'"
+            if (!token.empty()) // empty when there is a complex rule (not a straight unconditional keyword)
+            {
+                TypeClass tc = AnalyzeTypeClass(TentativeName{token});
+                bool accept = true;
+                if constexpr (!is_same_v<std::nullptr_t, std::remove_reference_t<decltype(filter)>>)
+                {
+                    accept = filter(tc);
+                }
+
+                if (tc == TypeClass::IsNotType)
+                {
+                    notTypes1.insert(token);
+                }
+
+                if (accept)
+                {
+                    classifiedTokens[ TypeClass::ToStr(tc) ].emplace( std::move(token) );
+                }
+            }
+        }
+
+        // now. because of names such as StructuredBuffer or matrix, need to have a generic appendix to mean something,
+        // they will be classified as IsNotType. So we need to re-attempt analysis by appending something parseable.
+
+        // get a scalar typename from the scalar class:
+        string someScalar = *classifiedTokens[TypeClass::ToStr(TypeClass::Scalar)].begin();
+
+        // now we'll use it to construct parseable generic type expressions
+        enum class RetryStateMachine
+        {
+            OneTypeGenericParameter,
+            OneTypeAndDimensionGenericParameters,
+            OneTypeAndTwoDimensionsGenericParameters,
+            End,
+        };
+
+        constexpr auto isNotTypeKey = TypeClass::ToStr(TypeClass::IsNotType);
+        // completely delete all IsNotType from the classification, and redo it all over.
+        set<string> notTypes = std::move(classifiedTokens[isNotTypeKey]);
+        classifiedTokens.erase(isNotTypeKey);
+        SetMerge(notTypes, notTypes1);
+
+        for (const string& token : notTypes)
+        {
+            RetryStateMachine state = RetryStateMachine::OneTypeGenericParameter;
+            TypeClass tc;
+            do
+            {
+                string attemptedTypeName = token;
+                switch (state)
+                {
+                case RetryStateMachine::OneTypeGenericParameter:
+                    attemptedTypeName  += "<" + someScalar + ">";
+                    state = RetryStateMachine::OneTypeAndDimensionGenericParameters;
+                    break;
+                case RetryStateMachine::OneTypeAndDimensionGenericParameters:
+                    attemptedTypeName  += "<" + someScalar + ",1>";
+                    state = RetryStateMachine::OneTypeAndTwoDimensionsGenericParameters;
+                    break;
+                case RetryStateMachine::OneTypeAndTwoDimensionsGenericParameters:
+                    attemptedTypeName  += "<" + someScalar + "1,1>";
+                    state = RetryStateMachine::End;
+                    break;
+                case RetryStateMachine::End:
+                default:
+                    // save as is
+                    break;
+                }
+                tc = AnalyzeTypeClass(TentativeName{attemptedTypeName});
+            } while (tc == TypeClass::IsNotType && state != RetryStateMachine::End);
+
+            // re-register at the correct place (if passes filter):
+            bool accept = true;
+            if constexpr (!is_same_v<std::nullptr_t, std::remove_reference_t<decltype(filter)>>)
+            {
+                accept = filter(tc);
+            }
+
+            if (accept)
+            {
+                classifiedTokens[TypeClass::ToStr(tc)].insert(token);
+            }
+        } // end for
+    }
+
+    void DumpClassifiedTokensToYaml(const MapOfStringViewToSetOfString& classifiedTokens)
+    {
+        for (const auto& [category, tokens] : classifiedTokens)
+        {
+            std::cout << category << ":\n";
+            for (const string& tokenName : tokens)
+            {
+                std::cout << "  - \"" << tokenName << "\"\n";
+            }
+        }
+    }
+
+    // outputs information about language keywords related to predefined types
+    void DumpPredefinedVocabulary(const azslLexer* lexer)
+    {
+        MapOfStringViewToSetOfString classifiedTokens;
+        ClassifyAllTokens(lexer, classifiedTokens /*out*/, [](TypeClass tc) { return IsPredefinedType(tc); });
+        DumpClassifiedTokensToYaml(classifiedTokens);
+    }
+
+    //! iterates on tokens and build the line number mapping (from preprocessor line directives)
+    void ConstructLineMap(vector<std::unique_ptr<Token>>* allTokens, IntermediateRepresentation* irOut)
+    {
+        string lastNonEmptyFileName = AzslcException::s_currentSourceFileName;
+        for (auto& token : *allTokens)  // auto& because each element is a unique_ptr we can't copy
+        {
+            if (token->getType() == azslLexer::LineDirective)
+            {
+                LineDirectiveInfo directiveInfo{0,0};
+                const auto lineText = token->getText();
+                //                    the sharp
+                //                        |  any whitespaces
+                //                        | /   optional line token
+                //                        | |     |       decimal
+                // custom raw string      | |     |          |  optional filename between quotes
+                //         delimiter ──┐  | |     |          |        |
+                std::regex lineRegex(R"__(#\s*(line\s+)?\s*(\d+)\s*("(.*)")?)__");
+                auto matchBegin = std::sregex_iterator(lineText.begin(), lineText.end(), lineRegex);
+                // there can be only 1 match, and it HAS to match since AntlR lexer already matched.
+                auto& groups = *matchBegin; // 4 groups: [0] is the whole line. [1] is the first parenthesized group, [2] the 2nd etc
+                directiveInfo.m_physicalTokenLine = token->getLine();
+                directiveInfo.m_forcedLineNumber = std::atoi(groups[2].str().c_str());
+                directiveInfo.m_containingFilename = groups[4];
+                if (directiveInfo.m_containingFilename.empty())
+                {
+                    // if we don't have a filename specified on the line, it means the last seen filename is still active.
+                    // storing it this way simplifies the lookup algorithm using this data.
+                    directiveInfo.m_containingFilename = lastNonEmptyFileName;
+                }
+                else
+                {
+                    lastNonEmptyFileName = directiveInfo.m_containingFilename;
+                }
+                irOut->m_lineMap[token->getLine()] = directiveInfo;
+            }
+        }
+        if (irOut->m_lineMap.find(1) == irOut->m_lineMap.end())
+        {
+            // if we have no line directives, add one that can be found by lower_bound
+            LineDirectiveInfo catchAllDirective{1, 1, AzslcException::s_currentSourceFileName};
+            irOut->m_lineMap[1] = catchAllDirective;
+        }
+    }
+}
+
+namespace AZ::ShaderCompiler::Main
+{
+    using namespace AZ::ShaderCompiler;
+
+    static const char USAGE[] =
+    R"(Amazon Shader Language Compiler
+
+        Usage:
+          azslc (- | FILE) [--use-spaces] [--unique-idx] [--cb-body] [--root-sig] [--root-const=<size>] [--pad-root-const] [--Zpc] [--Zpr] [--namespace=<nspc>] [--strip-unused-srgs] [--no-ms] [-o OUTFILE]
+                           [--W0|--W1|--W2|--W3] [--Wx|--Wx1|--Wx2|--Wx3] [--min-descriptors=<set,spaces,samplers,textures,buffers>] [--max-spaces=<count>]
+          azslc (- | FILE) --full [--use-spaces] [--unique-idx] [--cb-body] [--root-sig] [--root-const=<size>] [--pad-root-const] [--Zpc] [--Zpr] [--pack-dx12] [--pack-vulkan] [--pack-opengl] [--namespace=<nspc>] [--strip-unused-srgs] [--no-ms] [-o OUTFILE]
+                           [--W0|--W1|--W2|--W3] [--Wx|--Wx1|--Wx2|--Wx3] [--min-descriptors=<set,spaces,samplers,textures,buffers>] [--max-spaces=<count>]
+          azslc (- | FILE) --ia [--use-spaces] [--unique-idx] [--cb-body] [--root-sig] [--Zpc] [--Zpr] [--pack-dx12] [--pack-vulkan] [--pack-opengl] [--namespace=<nspc>] [--strip-unused-srgs] [-o OUTFILE]
+          azslc (- | FILE) --om [--use-spaces] [--unique-idx] [--cb-body] [--root-sig] [--Zpc] [--Zpr] [--pack-dx12] [--pack-vulkan] [--pack-opengl] [--namespace=<nspc>] [--strip-unused-srgs] [-o OUTFILE]
+          azslc (- | FILE) --srg [--use-spaces] [--unique-idx] [--cb-body] [--root-sig] [--root-const=<size>] [--pad-root-const] [--Zpc] [--Zpr] [--pack-dx12] [--pack-vulkan] [--pack-opengl] [--namespace=<nspc>] [--min-descriptors=<set,spaces,samplers,textures,buffers>] [--max-spaces=<count>] [--strip-unused-srgs] [--no-ms] [-o OUTFILE]
+          azslc (- | FILE) --options [--use-spaces] [--unique-idx] [--cb-body] [--root-sig] [--Zpc] [--Zpr] [--pack-dx12] [--pack-vulkan] [--pack-opengl] [--namespace=<nspc>] [--strip-unused-srgs] [-o OUTFILE]
+          azslc (- | FILE) --semantic [--verbose] [--W0|--W1|--W2|--W3] [--Wx|--Wx1|--Wx2|--Wx3] [--root-const=<size>] [--pad-root-const] [--strip-unused-srgs]
+          azslc (- | FILE) --syntax
+          azslc (- | FILE) --dumpsym [--strip-unused-srgs] [--no-ms]
+          azslc (- | FILE) --ast [--strip-unused-srgs]
+          azslc (- | FILE) --bindingdep [--use-spaces] [--unique-idx] [--cb-body] [--Zpc] [--Zpr] [--pack-dx12] [--pack-vulkan] [--pack-opengl] [--namespace=<nspc>] [--max-spaces=<count>] [--strip-unused-srgs] [--no-ms] [-o OUTFILE]
+          azslc (- | FILE) --visitsym MQNAME [-d] [-v] [-f] [-r]
+          azslc --listpredefined
+          azslc -h | --help | --version
+
+        Arguments:
+          FILE              Input file (optional if use of stdin)
+
+        Options:
+          -o OUTFILE                Output file (optional if use of stdout).
+          --use-spaces              Use logical space index per SRG.
+          --unique-idx              Use unique indices for all registers. e.g. b0, t0, u0, s0 becomes b0, t1, u2, s3.
+          --cb-body                 Emit ConstantBuffer body rather than using <T>
+          --root-sig                Emit RootSignature for parameter binding in the shader.
+          --root-const=<size>       Maximum size in bytes, of the root constants buffer.
+          --pad-root-const          Automatically append padding data to the root constant CB to keep it aligned to 16-byte boundary.
+          --Zpc                     Pack matrices in column-major order (default). Cannot be specified together with -Zpr
+          --Zpr                     Pack matrices in row-major order. Cannot be specified together with -Zpc
+          --pack-dx12               Pack buffers using strict DX12 packing rules. If not specified it will use relaxed packing rules.
+          --pack-vulkan             Pack buffers using strict Vulkan packing rules. That's vector-relaxed std140 for uniform and std430 for storage buffers.
+          --pack-opengl             Pack buffers using strict OpenGL packing rules. That's vector-strict std140 for uniform and std430 for storage buffers.
+          --namespace=<nspc>        The list of namespaces (comma separated, no white spaces) indicates which attribute namespaces are active.
+          --ia                      Output a list of vs entries with their Input Assembler layout *and* a list of cs entries with their numthreads.
+          --om                      Output the Output Merger layout, not the shader code.
+          --srg                     Output the Shader Resource Group layout, not the shader code.
+          --options                 Output the list of available shader options for this shader.
+          --dumpsym                 Dump symbols
+          --syntax                  Check syntax    (no output means no complaints)
+          --semantic                Check semantics (no output means no complaints)
+          --ast                     Output the syntax tree.
+          --bindingdep              Output binding dependencies (what entry points access what external resource).
+          --visitsym MQNAME         Output the locations of all relationships of the symbol MQNAME.
+          --full                    Output the shader code, Input Assembler layout, Output Merger layout, Shader Resource Group layout,
+                                    the list of available shader options, and the binding dependencies.
+          --strip-unused-srgs       Strips the unused SRGs.
+          --no-ms                   Transforms usage of Texture2DMS/Texture2DMSArray and related functions and semantics into plain Texture2D/Texture2DArray equivalents.
+                                    This is useful for allowing shader authors to easily write AZSL code that can be compiled into alternatives
+                                    to work with both a multisample render pipeline and a non-MS render pipeline.
+          -d                        (Option of --visitsym) Visit direct references
+          -v                        (Option of --visitsym) Visit overload-set
+          -f                        (Option of --visitsym) Visit family
+          -r                        (Option of --visitsym) Visit recursively
+          --listpredefined          Output a list of all predefined types in AZSLang.
+          --max-spaces=<count>      Will choose register spaces that do not extend past this limit.
+
+        Diagnostic Options:
+          --W0                      Suppresses all warnings.
+          --W1                      Severe warnings activated. (default)
+          --W2                      Maybe significant warnings activated.
+          --W3                      Low-confidence-diagnostic warnings activated.
+          --Wx                      Treat any currently activated warning as error.
+          --Wx1                     Treat level-1 warning as error.
+          --Wx2                     Treat up to level-2 warning as error.
+          --Wx3                     Treat up to level-3 warning as error.
+          --min-descriptors=<set,spaces,samplers,textures,buffers>  Emits a warning if a count overshoots the limit. Use -1 for "no limit".
+
+        Example:
+            azslc.exe myshader.azsl --W3 -o out.hlsl --namespace=dx --min-descriptors=-1,10,16,128,-1
+            azslc.exe myshader.azsl --namespace=vk --min-descriptors=-1,4 --max-spaces=4
+)";
+// Note: for now, the docopt C++ port, uses a regexp internally, that crashes the MSVC version of std <regex>.
+//       We need a dependence to boost to fix that (or build with GCC-cygwin or clang, or give-up docopt).
+//       https://github.com/docopt/docopt.cpp/issues/67
+
+    /// This function will support the --ast option. It uses an AntlR facility and prettifies it.
+    void PrintAst(tree::ParseTree* tree, azslParser& parser)
+    {
+        // not sure why wstring, but I'm going along the AntlR's doc example.
+        std::wstring s = antlrcpp::s2ws(tree->toStringTree(&parser));
+        // hopefully easy to read indentator
+        std::wstring curindent = L"";
+        for (wchar_t c : s)
+        {
+            if (c == L'(')
+            {
+                std::wcout << "\n";
+                curindent += L"  ";
+                std::wcout << curindent << c;
+            }
+            else if (c == L')')
+            {
+                std::wcout << c << "\n";
+                curindent = curindent.substr(0, std::max(2_sz, curindent.size()) - 2);
+                std::wcout << curindent;
+            }
+            else
+            {
+                std::wcout << c;
+            }
+        }
+        std::wcout << std::endl; // flush
+    }
+
+    /// this function supports the --visitsym option
+    // @symbolMqn   starting point of symbol homonyms graph discovery. Mqn: mangled qualified name
+    void PrintVisitSymbol(IntermediateRepresentation& ir, string_view symbolMqn, RelationshipExtentFlag visitOptions)
+    {
+        IdAndKind* symbol = ir.GetIdAndKindInfo(QualifiedNameView{symbolMqn});
+        if (!symbol)
+        {
+            std::cerr << "Error: symbol " << symbolMqn.data() << " not found. To list all symbols use --dumpsym option.\n";
+            return;
+        }
+        std::cout << "Symbol found. kind: " << Kind::ToStr(ir.GetKind(symbol->first)) << ". Homonyms list:\n";
+        HomonymVisitor hv{[&ir](QualifiedNameView qnv) { return ir.GetKindInfo({{qnv}}); }};
+        hv(symbol->first,
+           [](const Seenat& at, RelationshipExtent category)
+             {
+                 std::cout << "- {categ: " << RelationshipExtent::ToStr(category)
+                      << ", id: " << Decorate("'", at.m_referredDefinition.GetName())
+                      << ", at: ':" << at.m_where.m_line << ":" << at.m_where.m_charPos + 1 << "'"
+                      << ", token#: " << at.m_where.m_focusedTokenId << "}\n";
+             },
+           visitOptions);
+    }
+
+    void ParseWarningLevel(const map<string, docopt::value>& args, DiagnosticStream& warningConfig)
+    {
+        for (auto level : Warn::Enumerate{})
+        {
+            auto optionString = "--" + string{Warn::ToStr(level)};  // example "--W0"
+            auto lookup = args.find(optionString);
+            if (lookup != args.end() && lookup->second.asBool())
+            {
+                if (level >= Warn::Wx)
+                {
+                    warningConfig.SetAsErrorLevel(level);
+                }
+                else
+                {
+                    warningConfig.SetRevealedWarningLevel(level);
+                }
+            }
+        }
+    }
+}
+
+namespace AZ
+{
+    string_view GetFileLeafName(string_view path)
+    {
+        return Slice(path, path.find_last_of("/\\") + 1, -1);
+    }
+
+    inline void DoAsserts()
+    {
+#ifndef NDEBUG
+        using namespace AZ::Tests;
+
+        DoAsserts2();
+        DoAsserts4();
+        DoAsserts5();
+        DoAsserts6();
+#endif
+    }
+}
+
+int main(int argc, const char* argv[])
+{
+    using namespace AZ;
+    using namespace AZ::ShaderCompiler::Main;
+
+    DoAsserts();
+
+    int processReturnCode = 0;
+    try
+    {
+        bool constexpr showHelpIfRequested = true;
+        // Major.Minor.Revision
+        auto versionString = string{"AZSL Compiler " AZSLC_MAJOR "." AZSLC_MINOR "." AZSLC_REVISION " "} + GetCurrentOsName().data();
+        map<string, docopt::value> args
+            = docopt::docopt(USAGE, { argv + 1, argv + argc }, showHelpIfRequested, versionString);
+
+        auto parseLongCommandLineArgument = [&args](const string& name, string_view errorMessage) -> long
+        {
+            try
+            {
+                return args[name].asLong();
+            }
+            catch (...)
+            {
+                throw std::runtime_error(errorMessage.data());
+            }
+        };
+
+        if (args["--listpredefined"].asBool())
+        {
+            verboseCout.m_on = false;
+            ANTLRInputStream is;
+            azslLexer lexer{&is};
+            DumpPredefinedVocabulary(&lexer);
+            return 0;
+        }
+
+        verboseCout.m_on = args["--verbose"].asBool();
+
+        bool useStdin = args["-"].asBool();
+        // we need to scope a stream object here, to be able to bind a polymorphic reference to it
+        std::ifstream ifs;
+        if (!useStdin)
+        {
+            ifs = std::ifstream(args["FILE"].asString()); // try to open as file
+        }
+
+        std::istream& in{ useStdin ? std::cin : ifs };
+        if (!in.good())
+        {
+            throw std::runtime_error("input file could not be opened");
+        }
+
+        const string inputFileName = useStdin ? "" : args["FILE"].asString();
+        AzslcException::s_currentSourceFileName = useStdin ? "stdin" : args["FILE"].asString();
+
+        bool useOutputFile = !!args["-o"];
+        const string outputFileName = useOutputFile ? args["-o"].asString() : "";
+
+        ANTLRInputStream input(in);
+        azslLexer lexer(&input);
+        CommonTokenStream tokens(&lexer);
+        IntermediateRepresentation ir(&lexer);
+        auto allTokens = lexer.getAllTokens();
+        if (lexer.getNumberOfSyntaxErrors() > 0)
+        {
+            throw std::runtime_error("syntax errors present");
+        }
+        ConstructLineMap(&allTokens, &ir);
+        lexer.reset();
+        auto azslParserEventListener = AzslParserEventListener(ir);
+        azslParser parser(&tokens);
+        parser.removeErrorListeners();
+        parser.addErrorListener(&azslParserEventListener);
+        tree::ParseTree* tree = parser.compilationUnit();
+
+        if (parser.getNumberOfSyntaxErrors() > 0)
+        {
+            throw std::runtime_error("grammatic errors present");
+        }
+
+        if (args["--syntax"].asBool())
+        {  // if we are here with no exception then the syntax pass is valid.
+        }
+        else  // continue with semantic, and later emission
+        {
+            if (!useStdin)
+            {
+                StdFs::path inSource{ args["FILE"].asString() };
+                ir.m_metaData.m_insource = StdFs::absolute(inSource).lexically_normal().generic_string();
+            }
+            tree::ParseTreeWalker walker;
+            Texture2DMSto2DCodeMutator texture2DMSto2DCodeMutator(&ir);
+            SemaCheckListener semanticListener{&ir};
+            warningCout.m_onErrorCallback = [](string_view message)
+                                            {
+                                                throw AzslcException{WX_WARNINGS_AS_ERRORS, "as-error", string{message}};
+                                            };
+            ParseWarningLevel(args, warningCout);
+            const char* nonValidativeOptions[] = { "--full", "--ia", "--om", "--srg", "--options", "--dumpsym", "--ast", "--bindingdep", "--visitsym", "--strip-unused-srgs" };
+            bool anyNonValidativeOption = std::any_of(std::begin(nonValidativeOptions), std::end(nonValidativeOptions),
+                                                      [&](auto opt)
+                                                      {
+                                                          return args[opt].isBool() ? args[opt].asBool() : !!args[opt];
+                                                      });
+            semanticListener.m_silentPrintExtensions = !args["--semantic"].asBool() || args["--verbose"].asBool(); // print-extensions are useful for interested parties; but not normal operation.
+            const bool convertToNoMS = args["--no-ms"].asBool();
+            if (convertToNoMS)
+            {
+                semanticListener.m_functionCallMutator = &texture2DMSto2DCodeMutator;
+            }
+            warningCout.m_on = !anyNonValidativeOption;  // warnings are interesting for emission, and explicit semantic check modes.
+
+            // Enable attribute namespaces
+            if (args["--namespace"])
+            {
+                auto spaces = Split<vector<string>>(args["--namespace"].asString(), ",");
+                std::for_each(spaces.begin(), spaces.end(),
+                    [&](const string& space) { ir.AddAttributeNamespaceFilter(space); });
+            }
+
+            UnboundedArraysValidator::Options unboundedArraysValidationOptions = {
+                args["--use-spaces"].asBool(), args["--unique-idx"].asBool()
+            };
+            if (args["--max-spaces"])
+            {
+                unboundedArraysValidationOptions.m_maxSpaces = parseLongCommandLineArgument("--max-spaces", "--max-spaces requires a number. eg --max-spaces=4");
+            }
+            ir.m_sema.m_unboundedArraysValidator.SetOptions(unboundedArraysValidationOptions);
+
+            // semantic logic and validation
+            walker.walk(&semanticListener, tree);
+
+            Options emitOptions;
+            emitOptions.m_useLogicalSpaces = args["--use-spaces"].asBool();
+            emitOptions.m_useUniqueIndices = args["--unique-idx"].asBool();
+            emitOptions.m_emitConstantBufferBody = args["--cb-body"].asBool();
+            emitOptions.m_emitRootSig = args["--root-sig"].asBool();
+            emitOptions.m_padRootConstantCB = args["--pad-root-const"].asBool();
+
+            if (args["--root-const"])
+            {
+                emitOptions.m_rootConstantsMaxSize = parseLongCommandLineArgument("--root-const", "--root-const requires a number of bytes. eg --root-const=128");
+            }
+
+            if (args["--min-descriptors"])
+            {
+                sscanf(args["--min-descriptors"].asString().c_str(), "%d,%d,%d,%d,%d",
+                       &emitOptions.m_minAvailableDescriptors.m_descriptorsTotal,
+                       &emitOptions.m_minAvailableDescriptors.m_spaces,
+                       &emitOptions.m_minAvailableDescriptors.m_samplers,
+                       &emitOptions.m_minAvailableDescriptors.m_textures,
+                       &emitOptions.m_minAvailableDescriptors.m_buffers);
+            }
+
+            if (args["--max-spaces"])
+            {
+                emitOptions.m_maxSpaces = parseLongCommandLineArgument("--max-spaces", "--max-spaces requires a number. eg --max-spaces=4");
+            }
+
+            auto isZpc = args["--Zpc"].asBool();
+            auto isZpr = args["--Zpr"].asBool();
+            if (isZpc && isZpr)
+            {
+                throw std::runtime_error("Cannot specify --Zpr and --Zpc together, use --help to get usage information");
+            }
+            else if (isZpr)
+            {
+                emitOptions.m_emitRowMajor = true;
+                emitOptions.m_forceEmitMajor = true;
+            }
+            else if (isZpc)
+            {
+                emitOptions.m_emitRowMajor = false; // Default
+                emitOptions.m_forceEmitMajor = true;
+            }
+
+            if (args["--pack-dx12"].asBool())
+            {
+                emitOptions.m_packConstantBuffers = AZ::ShaderCompiler::Packing::Layout::DirectXPacking;
+                emitOptions.m_packDataBuffers = AZ::ShaderCompiler::Packing::Layout::DirectXStoragePacking;
+            }
+
+            if (args["--pack-vulkan"].asBool())
+            {
+                emitOptions.m_packConstantBuffers = AZ::ShaderCompiler::Packing::Layout::RelaxedStd140Packing;
+                emitOptions.m_packDataBuffers = AZ::ShaderCompiler::Packing::Layout::RelaxedStd430Packing;
+            }
+
+            if (args["--pack-opengl"].asBool())
+            {
+                emitOptions.m_packConstantBuffers = AZ::ShaderCompiler::Packing::Layout::StrictStd140Packing;
+                emitOptions.m_packDataBuffers = AZ::ShaderCompiler::Packing::Layout::StrictStd430Packing;
+            }
+
+            // middle end logic
+            MiddleEndConfiguration middleEndConfigration{ emitOptions.m_rootConstantsMaxSize, emitOptions.m_packConstantBuffers, emitOptions.m_packDataBuffers, emitOptions.m_emitRowMajor, emitOptions.m_padRootConstantCB };
+            ir.MiddleEnd(middleEndConfigration);
+            if (convertToNoMS)
+            {
+                texture2DMSto2DCodeMutator.RunMiddleEndMutations();
+            }
+
+            // If ir fails to find any root members in the source, overwrite the m_numOfRootConstants to 0
+            if (ir.m_rootConstantStructUID.m_name == "")
+            {
+                emitOptions.m_rootConstantsMaxSize = 0;
+            }
+            // intermediate state validation
+            ir.Validate();
+
+            bool doEmission = true;
+
+            if (args["--strip-unused-srgs"].asBool())
+            {
+                ir.RemoveUnusedSrgs();
+            }
+
+            if (args["--dumpsym"].asBool())
+            {
+                DumpSymbols(ir);
+                doEmission = false;
+            }
+            else if (args["--ast"].asBool())
+            {
+                PrintAst(tree, parser);
+                doEmission = false;
+            }
+            else if (args["--visitsym"])
+            {
+                using RE = RelationshipExtent;
+                RelationshipExtentFlag visitOptions{RE::Self};  // at least self.  + optional things as listed here-under
+                static constexpr array<pair<const char*, RE::EnumType>, 4> optToRelation = {{ {"-d", RE::Reference},
+                                                                                              {"-f", RE::Family},
+                                                                                              {"-v", RE::OverloadSet},
+                                                                                              {"-r", RE::Recursive} }};
+                for (auto&& possibleOption : optToRelation)
+                {
+                    visitOptions |= args[possibleOption.first].asBool() ? possibleOption.second : RE::EnumType(0);
+                }
+                PrintVisitSymbol(ir, args["--visitsym"].asString(), visitOptions);
+                doEmission = false;
+            }
+            else
+            {
+                bool checkerFlagsPresent = args["--semantic"].asBool() || args["--verbose"].asBool(); // or --syntax but we already exited by now.
+                doEmission = !checkerFlagsPresent;
+            }
+
+            if (doEmission)
+            {
+                std::ofstream mainOutFile;
+
+                if (useOutputFile)
+                {
+                    mainOutFile = std::ofstream(outputFileName);
+                    if (!mainOutFile.good())
+                    {
+                        throw std::runtime_error("output file could not be opened");
+                    }
+                }
+
+                std::ostream& out{ useOutputFile ? mainOutFile : std::cout };
+
+                CodeReflection reflecter{ &ir, &tokens, out };
+
+                // Lambda to create an output stream and perform an output action
+                auto prepareOutputAndCall = [&](const string& suffix, std::function<void(CodeReflection&)> action)
+                {
+                    string outputName;
+                    if (useOutputFile)
+                    {
+                        outputName = string(GetFileNameWithoutExtension(outputFileName));
+                    }
+                    else
+                    {
+                        outputName = string(GetFileNameWithoutExtension(inputFileName));
+                    }
+                    outputName = outputName + "." + suffix + ".json";
+
+                    std::ofstream outFile = std::ofstream(outputName);
+                    if (!outFile.good())
+                    {
+                        throw std::runtime_error("output file '" + outputName + "' could not be opened");
+                    }
+
+                    std::ostream& out{ outFile };
+                    CodeReflection reflecter{ &ir, &tokens, out };
+
+                    action(reflecter);
+                };
+
+                if (args["--full"].asBool())
+                {   // Combine the default emission and the ia, om, srg, options, bindingdep commands
+                    CodeEmitter emitter{ &ir, &tokens, out };
+                    if (convertToNoMS)
+                    {
+                        emitter.SetCodeMutator(&texture2DMSto2DCodeMutator);
+                    }
+                    out << "// HLSL emission by " << versionString << "\n";
+                    emitter.Run(emitOptions);
+
+                    prepareOutputAndCall("ia", [&](CodeReflection& r){r.DumpShaderEntries();});
+                    prepareOutputAndCall("om", [&](CodeReflection& r) {r.DumpOutputMergerLayout(); });
+                    prepareOutputAndCall("srg", [&](CodeReflection& r) {r.DumpSRGLayout(emitOptions); });
+                    prepareOutputAndCall("options", [&](CodeReflection& r) {r.DumpVariantList(emitOptions); });
+                    prepareOutputAndCall("bindingdep", [&](CodeReflection& r) {r.DumpResourceBindingDependencies(emitOptions); });
+                }
+                else if (args["--ia"].asBool())
+                {   // Reflect the Input Assembler layout and the Compute shader entries
+                    reflecter.DumpShaderEntries();
+                }
+                else if (args["--om"].asBool())
+                {   // Reflect the Input Assembler layout
+                    reflecter.DumpOutputMergerLayout();
+                }
+                else if (args["--srg"].asBool())
+                {   // Reflect the Shader Resource Groups layout
+                    reflecter.DumpSRGLayout(emitOptions);
+                }
+                else if (args["--options"].asBool())
+                {   // Reflect the list of available variant options for this shader
+                    reflecter.DumpVariantList(emitOptions);
+                }
+                else if (args["--bindingdep"].asBool())
+                {
+                    reflecter.DumpResourceBindingDependencies(emitOptions);
+                }
+                else
+                {   // Emit the shader source code
+                    CodeEmitter emitter{ &ir, &tokens, out };
+                    if (convertToNoMS)
+                    {
+                        emitter.SetCodeMutator(&texture2DMSto2DCodeMutator);
+                    }
+                    out << "// HLSL emission by " << versionString << "\n";
+                    emitter.Run(emitOptions);
+                }
+            }
+        }
+    }
+    catch (const exception& e)
+    {
+        OutputNestedAndException(e);
+        processReturnCode = 1;
+    }
+    catch (...)
+    {
+        std::cerr << "Unknown exception" << std::endl;
+        processReturnCode = 1;
+    }
+
+    return processReturnCode;
+}

+ 627 - 0
src/AzslcMangling.h

@@ -0,0 +1,627 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include "StdUtils.h"
+
+namespace AZ::ShaderCompiler
+{
+    //! In AZSL Qualified means fully qualified.
+    //! Let's make strong types for those, to get build-time incompatibility safety.
+    //! Instead of using strings for naked name storage, we use these structures to store symbols.
+    //! This way, we have a sort of "tainted string" concept, that will prevent us to break contract invariants in many places.
+    //! Picture a sort of BOOST_STRONG_TYPEDEF. But that keeps compatibility with a 'view' type
+    struct QualifiedName : string
+    {
+        // I need to declare explicitly a default constructor, since the declaration of a copy constructor hereunder would otherwise delete it.
+        QualifiedName() = default;
+
+        // Provide implicit compatibility from string_view for sheer convenience
+        QualifiedName(string_view sv) : string(sv) {}
+
+        friend size_t hash_value(const QualifiedName& arg) { return std::hash<string>{}(arg); }
+    };
+
+    //! In AZSL, Unqualified is a relative symbol. (can have some scope resolution operator, but not starting from global)
+    //! Please refer to the comments in QualifiedName struct. They apply the same.
+    struct UnqualifiedName : string
+    {
+        UnqualifiedName() = default;
+
+        UnqualifiedName(string_view sv) : string(sv) {}
+
+        friend size_t hash_value(const UnqualifiedName& arg) { return std::hash<string>{}(arg); }
+    };
+
+    // These 2 asserts verify that the whole exercise is functioning
+    static_assert( !std::is_convertible< QualifiedName, UnqualifiedName >::value );
+    static_assert( !std::is_convertible< UnqualifiedName, QualifiedName >::value );
+    // Hereunder is the problem underlined by the nonexplicit conversion operator, we created a path,
+    // that the compiler can take from one to another during explicit construction.
+    // Ideally I want these asserts to verify. But we need to create the painful unordered requirements.
+    // This "weakness" is not a massive problem in practice, since wrong-typed argument passing is still protected.
+    //static_assert( !std::is_constructible< QualifiedName, UnqualifiedName >::value );
+    //static_assert( !std::is_constructible< UnqualifiedName, QualifiedName >::value );
+}
+
+namespace std
+{
+    template<> struct hash<AZ::ShaderCompiler::QualifiedName>
+    {
+        size_t operator()(const AZ::ShaderCompiler::QualifiedName& qn) const
+        {
+            return hash_value(qn);
+        }
+    };
+
+    template<> struct hash<AZ::ShaderCompiler::UnqualifiedName>
+    {
+        size_t operator()(const AZ::ShaderCompiler::UnqualifiedName& qn) const
+        {
+            return hash_value(qn);
+        }
+    };
+}
+
+namespace AZ::ShaderCompiler
+{
+    // Please refer to the comments around the struct QualifiedName, for explanation about its raison d'etre.
+    struct QualifiedNameView : string_view
+    {
+        constexpr QualifiedNameView() = default;
+
+        template <typename... Args>
+        explicit constexpr QualifiedNameView(Args&&... args) : string_view(std::forward<Args>(args)...) {}
+
+        QualifiedNameView(const QualifiedName& qn) : string_view(static_cast<const string&>(qn)) {}
+
+        friend size_t hash_value(const QualifiedNameView& arg) { return std::hash<string_view>{}(arg); }
+    };
+
+    struct UnqualifiedNameView : string_view
+    {
+        constexpr UnqualifiedNameView() = default;
+
+        template <typename... Args>
+        explicit constexpr UnqualifiedNameView(Args&&... args) : string_view(std::forward<Args>(args)...) {}
+
+        UnqualifiedNameView(const UnqualifiedName& qn) : string_view(static_cast<const string&>(qn)) {}
+
+        friend size_t hash_value(const UnqualifiedNameView& arg) { return std::hash<string_view>{}(arg); }
+    };
+
+    static_assert(!std::is_convertible< QualifiedNameView, UnqualifiedNameView >::value);
+    static_assert(!std::is_convertible< UnqualifiedNameView, QualifiedNameView >::value);
+}
+
+namespace std
+{
+    template<> struct hash<AZ::ShaderCompiler::QualifiedNameView>
+    {
+        size_t operator()(const AZ::ShaderCompiler::QualifiedNameView& qn) const
+        {
+            return hash_value(qn);
+        }
+    };
+
+    template<> struct hash<AZ::ShaderCompiler::UnqualifiedNameView>
+    {
+        size_t operator()(const AZ::ShaderCompiler::UnqualifiedNameView& qn) const
+        {
+            return hash_value(qn);
+        }
+    };
+}
+
+namespace AZ::ShaderCompiler
+{
+    inline bool IsRooted(string_view path)
+    {
+        return StartsWith(path, "/") || StartsWith(path, "?");
+    }
+
+    //! from "/func(/f2(?int), ?float)" return "/func"
+    inline string RemoveMatchedParenthesis(string_view name)
+    {
+        string result;
+        int level = 0;
+        for (auto character : name)
+        {
+            if (character == '(')
+            {
+                ++level;
+            }
+            if (level == 0)
+            {
+                result += character;
+            }
+            if (level > 0 && character == ')')
+            {
+                --level;
+            }
+        }
+        return result;
+    }
+
+    //! "thing()more" will be untouched.
+    //! "thing()" will become "thing"
+    inline string_view RemoveLastParenthesisGroup(string_view name)
+    {
+        int level = 0;
+        size_t startOfLastGroup = name.size();
+        // find matched parenthesis going backward
+        for (auto iter = name.rbegin(); iter != name.rend(); ++iter)
+        {
+            bool close = *iter == '(';  // since we go backward ( is the end of a group
+            bool open = *iter == ')';
+            level = open ? level + 1 : (close ? level - 1 : level);
+            if (level <= 0)
+            {
+                startOfLastGroup = name.size() - std::distance(name.rbegin(), iter) - (close ? 1 : 0); // eat the closing '('
+                break;
+            }
+        }
+        return Slice(name, 0, startOfLastGroup);
+    }
+
+    //! Mutate "stuff()" to "stuff_vd_"
+    //"        "thing(?int)" to "thing_?int_"
+    inline string FlattenParenthesisGroups(string_view name)
+    {
+        auto ret = std::regex_replace(string{name}, std::regex("\\(\\)"), "_vd");
+        ret = std::regex_replace(ret, std::regex("[\\(\\)]"), "_");
+        return ret;
+    }
+
+    inline bool IsLeaf(string_view name)
+    {
+        return RemoveMatchedParenthesis(name).find("/") == string::npos;
+    }
+
+    //! String replace of AZIR separators (mangled scope separators)
+    inline string ReplaceSeparators(string name, string_view replacement)
+    {
+        if (IsRooted(name))
+        {   // remove leading slash/questionmark to lighten output
+            name = Slice(name, 1, -1);
+        }
+        name = std::regex_replace(name, std::regex("\\/"), replacement.data());
+        return name;
+    }
+
+    static inline const char* Underscore = "_";
+
+    enum FlattenStrategy
+    {
+        PreserveArgumentsUnicity,  //!< x(a) will be x_a
+        CollapseArguments          //!< x(a) will be x
+    };
+    //! can be used at emission to flatten symbols into the global scope.
+    //! symbol names are stored with path separators. like such:
+    //! name = "/MyStruct/m_myVar"
+    //! returns: "MyStruct_m_myVar"
+    inline string Flatten(string name, FlattenStrategy strategy)
+    {
+        name = std::regex_replace(name, std::regex("\\?"), "");
+        return ReplaceSeparators(strategy == PreserveArgumentsUnicity ? FlattenParenthesisGroups(name) : RemoveMatchedParenthesis(name), Underscore);
+    }
+
+    //! "?int" to "int" (partial unmangling)
+    inline string_view RemoveFloatingMark(string_view name)
+    {
+        return StartsWith(name, "?") ? Slice(name, 1, -1) : name;
+    }
+
+    //! Change from AZIR form to AZSLang form
+    //! eg "/SRG/mem" to "::SRG::mem"
+    inline string UnMangle(string name)
+    {
+        name = std::regex_replace(name, std::regex("/"), "::");
+        name = std::regex_replace(name, std::regex("\\?"), "");
+        name = RemoveMatchedParenthesis(name);
+        return name;
+    }
+
+    //! Attempt to re-mangle a text-form idExpression but no guarantee about validity as an IdentifierUID. Notably predefined won't get their '?'
+    //! If you have a proper AST context, use ExtractNameFromIdExpression instead.
+    //! eg "SRG::mem" to "SRG/mem"
+    inline string ReMangle(string name)
+    {
+        name = std::regex_replace(name, std::regex("::"), "/");
+        return name;
+    }
+
+    //! in: `anyname` in AzIR format
+    //! split a qualified name and return the last name element
+    //! e.g. "/Stem/Nested/Leaf" returns "Leaf"
+    //! Important note: This does not provide a valid unqualified name, lookup-able from a given scope.
+    //!                 It is merely a string based chopping, that can be used for leaf name comparisons when useful.
+    //! If you want the correct context-valid unqualifying feature, use the SymbolAggregator's FindLeastQualifiedName
+    inline UnqualifiedNameView ExtractLeaf(string_view anyname)
+    {
+        auto lastSlash = anyname.rfind("/");
+        while (WithinMatchedParentheses(anyname, lastSlash))
+        {
+            lastSlash = anyname.rfind("/", lastSlash-1);
+        }
+        if (lastSlash == string::npos)
+        {   // if there is no slash at all, or is not rooted
+            return UnqualifiedNameView{RemoveFloatingMark(anyname)};
+        }
+        return UnqualifiedNameView{RemoveFloatingMark(Slice(anyname, lastSlash + 1, -1))};
+    }
+
+    //! true  if anywhere in the input, a parenthesis appear
+    //!       even in the middle, e.g:  "/A/f(/?int)/b"
+    inline bool ArgumentDecorationExists(string_view name)
+    {
+        return name.find("(") != string::npos;
+    }
+
+    //! true  if the leaf of the input has this sort of form "fun(/T,?int)"
+    //! false if e.g "/X"; e.g "/F(/T)/a"
+    inline bool IsLeafDecoratedByArguments(string_view name)
+    {
+        return !name.empty() && (name.back() == ')' || ArgumentDecorationExists({ExtractLeaf(name)}));
+    }
+
+    inline size_t CountParameters(string_view mangledFunctionName)
+    {
+        auto leaf = ExtractLeaf(mangledFunctionName);
+        return EndsWith(leaf, "()") || !EndsWith(leaf, ")")  // no-arg function or not-a-function
+            ? 0
+            : 1 + std::count(leaf.begin(), leaf.end(), ',');  // this isn't robust if types may hold comma (and it's the case for generics and function-types)
+    }
+
+    enum class JoinPolicy
+    {
+        EmptyMeansRoot,  // empty stem is joined as a root
+        EmptyMeansEmpty  // empty stem is nil and doesn't participate in join
+    };
+    //! return "A/B" if passed stem="A/" and leaf="B"
+    //!        "A/B" if passed stem="A"  and leaf="B"
+    //!        "/A"  if passed stem=""   and leaf="A" (if policy is EmptyMeansRoot)
+    //!        "A"   if passed stem=""   and leaf="A" (if policy is EmptyMeansEmpty)
+    inline string JoinPath(string_view stem, string_view leaf, JoinPolicy policy = JoinPolicy::EmptyMeansRoot)
+    {
+        if ((stem.empty() || leaf.empty() ) && policy == JoinPolicy::EmptyMeansEmpty)
+        {
+            return string{stem.empty() ? leaf : stem};
+        }
+
+        assert(!IsRooted(leaf)); // violation of contract that leaf must not be rooted.
+        if (EndsWith(stem, "/"))
+        {
+            return string{stem}.append(leaf);
+        }
+        else
+        {
+            return string{stem}.append("/").append(leaf);
+        }
+    }
+
+    //! will return "/"     from "/dir"
+    //! or          "/dir/" from "/dir/leaf"
+    //! or          "/dir/" from "/dir/leaf/"
+    //! or          ""      from "dir"
+    //! or          "/X/"   from "/X/Get(/?int)"
+    //! Note:       "/"     from "/"  (bump against the root)
+    //! this function is fundamentally the dual of ExtractLeaf in the sense that it gets you the other side.
+    inline string_view LevelUp(string_view path)
+    {
+        if (path == "/")
+        {   // special case
+            return "/";
+        }
+
+        if (EndsWith(path, "/"))
+        {   // remove this nuisance early, to canonicalize input.
+            // -1 is the end, -2 is 1 earlier than end. (think python slices)
+            path = Slice(path, 0, -2);
+        }
+        size_t lastSlash = path.rfind("/");
+        while (WithinMatchedParentheses(path, lastSlash))
+        {
+            lastSlash = path.rfind("/", lastSlash - 1);
+        }
+        if (lastSlash == string::npos)
+        {   // no slash at all
+            return "";
+        }
+        return Slice(path, 0, lastSlash + 1);
+    }
+
+    //! Has the same semantics than LevelUp but cleans up the trailing slash when there is a name left other than root.
+    //! example: "/dir" from "/dir/leaf"
+    inline string_view GetParentName(string_view path)
+    {
+        auto oneUp = LevelUp(path);
+        if (oneUp != "/" && EndsWith(oneUp, "/"))
+        {
+            return Slice(oneUp, 0, oneUp.length() - 1);
+        }
+        return oneUp;
+    }
+
+    //! Overload for when you work with QualifiedNameView type
+    inline QualifiedNameView GetParentName(QualifiedNameView path)
+    {
+        return QualifiedNameView{GetParentName(string_view{path})};
+    }
+
+    struct PathPart
+    {
+        string_view m_slice;
+        size_t      m_sliceBegin;
+        size_t      m_sliceLen;
+    };
+
+    inline bool IsGlobal(QualifiedNameView sym)
+    {
+        return GetParentName(sym) == "/";
+    }
+
+    //! When there is no need to create the split path in memory
+    //! FunctionObject will be fed a PathPart as parameter, and called for each part.
+    //! The behavior is the same as SplitPath function, please refer to it for behavior document.
+    template <typename FunctionObject>
+    void ForEachPathPart(string_view path, FunctionObject action)
+    {
+        size_t start = 0;
+        size_t pathLen = path.length();
+        // character by character. cp=character position
+        for (size_t cp = 0; cp < pathLen; ++cp)
+        {
+            bool isLast = cp + 1 == path.length();
+            bool isSlash = path[cp] == '/' && !WithinMatchedParentheses(path, cp);
+            if (isSlash || isLast)
+            {
+                size_t count = cp - start + (isLast && !isSlash ? 1 : 0);
+                action(PathPart{path.substr(start, count), start, count});
+                start = cp + 1;
+                if (isSlash && isLast) // we need to call again for the end part, to signal it exists but is empty
+                {
+                    action(PathPart{path.substr(start, 0), start, 0});
+                }
+            }
+        }
+    }
+
+    //! returns true if supposedChild is contained in supposedParent
+    //! example: IsParent("/ns/bag", "/ns/bag/member") == true
+    inline bool IsParent(string_view supposedParent, string_view supposedChild)
+    {
+        return supposedChild.size() > supposedParent.size() && supposedParent == supposedChild.substr(0, supposedParent.size());
+    }
+
+    //! examples ["", "A", "Leaf"] from "/A/Leaf"
+    //!          ["A", "Leaf"] from "A/Leaf"
+    //!          ["A", "Leaf", ""] from "A/Leaf/"
+    inline vector<string_view> SplitPath(string_view path)
+    {
+        const size_t numSlashes = count(path.begin(), path.end(), '/'); // in the case of "/X(/a)" numSlashes overshoots, so we only use it for reservation
+        vector<string_view> split;
+        split.reserve(numSlashes);
+        ForEachPathPart(path, [&split](const PathPart& ppart)
+                        {
+                            split.push_back(ppart.m_slice);
+                        });
+        return split;
+    }
+
+    //! counts the number of slashes.
+    //! so "" is depth 0, "/sym" is depth 1, "/ns/x" is depth 2
+    //! however "/" and "/ns/" are invalid input. because they are ambiguous. fix your input first.
+    inline size_t GetSymbolDepth(string_view mangledName)
+    {
+        assert(!EndsWith(mangledName, "/"));  // principle of least astonishment -> make an error of this case
+        return std::count(mangledName.begin(), mangledName.end(), '/');
+    }
+
+    //! Simple function joining scope and leaf. example: returns "/gfx/device" if you pass "/gfx" and "device"
+    //! the only advantage over directly using JoinPath, is input validation (asserts), explicit verbosity, and helped casting.
+    //! This is not the function you need if you are doing a lookup for a symbol, from a site that references something already declared.
+    //! In that case you need the SymbolTable::LookupSymbol function.
+    //! This function is for constructing new names from declaration sites.
+    inline QualifiedName MakeFullyQualified(QualifiedNameView scope, UnqualifiedNameView name)
+    {
+        assert(IsRooted(scope));
+        assert(IsLeaf(name));
+        return QualifiedName{JoinPath(scope, name)};
+    }
+
+    //! produce a string of the form "(/t1,/t2,/t3)"
+    //! where `begin` and `end` represents a range over resolved types fully qualified names (FQN)
+    template< typename IteratorType >
+    inline string CreateDecorationOfFunction(IteratorType begin, IteratorType end)
+    {
+        static_assert(std::is_same_v<typename IteratorType::value_type, QualifiedNameView> || std::is_same_v<typename IteratorType::value_type, QualifiedName>);
+        return Decorate("(", Join(begin, end, ","), ")");
+    }
+
+    //! The key to any symbol
+    struct IdentifierUID
+    {
+        UnqualifiedName GetNameLeaf() const
+        {
+            return ExtractLeaf(m_name);
+        }
+
+        QualifiedNameView GetName() const
+        {
+            return m_name;
+        }
+
+        bool IsEmpty() const
+        {
+            return m_name.empty();
+        }
+
+        void Clear()
+        {
+            m_name.clear();
+        }
+
+        //! Returns true if @otherUid is parent symbol of @this.
+        //! e.g: "/MySrg" is parent of "/MySrg/m_color"
+        bool IsParent(const IdentifierUID& otherUid) const
+        {
+            if (m_name.find(otherUid.m_name) != 0)
+            {
+                return false;
+            }
+            const size_t separatorPosition = otherUid.m_name.size();
+            return (m_name.find("/", separatorPosition, 1) == separatorPosition);
+        }
+
+        // for hash table keying:
+        friend bool operator == (const IdentifierUID& lhs, const IdentifierUID& rhs)
+        {
+            return lhs.m_name == rhs.m_name;
+        }
+
+        friend bool operator != (const IdentifierUID& lhs, const IdentifierUID& rhs)
+        {
+            return !operator==(lhs, rhs);
+        }
+
+        friend size_t hash_value(const IdentifierUID& arg)
+        {
+            return hash_value(arg.m_name); /*ADL*/
+        }
+
+        // for ordered container insertion:
+        friend bool operator < (const IdentifierUID& lhs, const IdentifierUID& rhs)
+        {
+            return lhs.m_name < rhs.m_name;
+        }
+
+        friend std::ostream& operator << (std::ostream& stream, const IdentifierUID& id)
+        {
+            stream << id.m_name;
+            return stream;
+        }
+
+        QualifiedName m_name;  // mangled symbol's absolute unique name. example: "/sdk/gfx/Device" or "?float"
+    };
+}
+
+namespace std
+{  // to fulfill unordered requirements
+    template<> struct hash<AZ::ShaderCompiler::IdentifierUID>
+    {
+        size_t operator()(const AZ::ShaderCompiler::IdentifierUID& arg) const
+        {
+            return hash_value(arg);
+        }
+    };
+}
+
+namespace AZ::ShaderCompiler
+{
+    // helper function to search over a collection, for a match on the leaf of a symbol.
+    template <typename ContainerOfIdUID>
+    bool ContainsSameLeafName(const ContainerOfIdUID& ctr, UnqualifiedNameView uqName)
+    {
+        assert(!IsRooted(uqName));
+        return Contains(ctr.cbegin(), ctr.cend(), [&](decltype(*ctr.cbegin()) elem) {return elem.GetNameLeaf() == uqName; });
+    }
+}
+
+#ifndef NDEBUG
+namespace AZ::Tests
+{
+    inline void Func(AZ::ShaderCompiler::UnqualifiedName un) {}
+
+    inline void DoAsserts5()
+    {
+        using namespace AZ::ShaderCompiler;
+
+        assert(ExtractLeaf("/") == "");
+        assert(ExtractLeaf("/A/B") == "B");
+        assert(ExtractLeaf("/A/B/") == "");
+        assert(ExtractLeaf("A") == "A");
+        //assert(ExtractLeaf("A/B") == "A/B");
+        assert(ExtractLeaf("A/B") == "B");
+        assert(ExtractLeaf("?int") == "int");
+        assert(ExtractLeaf("/X/func(/A)") == "func(/A)");
+        assert(ExtractLeaf("func(/A)") == "func(/A)");
+        assert(ExtractLeaf("func(/A)/param") == "param");
+        assert(ExtractLeaf("/func(/A)/param") == "param");
+
+        assert(LevelUp("/dir") == "/");
+        assert(LevelUp("/dir/leaf") == "/dir/");
+        assert(LevelUp("/dir/leaf/") == "/dir/");
+        assert(LevelUp("dir") == "");
+        assert(LevelUp("/") == "/");
+        assert(LevelUp("/X/Get(/?int, /func())") == "/X/");
+        assert(LevelUp("/X/Get(/?int, /func())/lambda()") == "/X/Get(/?int, /func())/");
+
+        assert(GetParentName("/dir/leaf") == "/dir");
+        assert(GetParentName("/dir/leaf/") == "/dir");
+        assert(GetParentName("/global") == "/");
+        assert(GetParentName("/") == "/");
+        assert(GetParentName("leaf") == "");
+        assert(GetParentName("?leaf") == "");
+
+        using namespace std::literals::string_view_literals;
+
+#if defined(_MSC_VER) && _MSC_VER >= 1910
+        // Visual Studio passes the string views here, other compilers will not
+        assert(SplitPath("/A/Leaf") == (vector{ ""sv, "A"sv, "Leaf"sv }));
+        assert(SplitPath("A/Leaf") == (vector{ "A"sv, "Leaf"sv }));
+        assert(SplitPath("Leaf") == (vector{ "Leaf"sv }));
+        assert(SplitPath("A/Leaf/") == (vector{ "A"sv, "Leaf"sv, ""sv }));
+        assert(SplitPath("/") == (vector{ ""sv, ""sv }));
+        assert(SplitPath("/X(/a)/f") == (vector{ ""sv, "X(/a)"sv, "f"sv }));
+        assert(SplitPath("X(/f(/t))/g") == (vector{ "X(/f(/t))"sv, "g"sv }));
+#endif
+
+        assert(JoinPath("A/", "B") == "A/B");
+        assert(JoinPath("A", "B") == "A/B");
+        assert(JoinPath("", "A") == "/A");
+        assert(JoinPath("", "A", JoinPolicy::EmptyMeansEmpty) == "A");
+
+        assert(UnMangle("/func(?int, f2())") == "::func");
+        assert(UnMangle("/class/member") == "::class::member");
+        assert(UnMangle("?matrix4x4") == "matrix4x4");
+
+        assert(IsLeafDecoratedByArguments("/func()"));
+        assert(IsLeafDecoratedByArguments("/func(?int)"));
+        assert(!IsLeafDecoratedByArguments("/func"));
+        assert(!IsLeafDecoratedByArguments("func"));
+        assert(!IsLeafDecoratedByArguments("/A/func(/T)/b"));
+        assert(!IsLeafDecoratedByArguments("A/func(/T)/b"));
+        assert(IsLeafDecoratedByArguments("A/func(/T)/g(?int)"));
+
+        assert(RemoveLastParenthesisGroup("func()more") == "func()more");
+        assert(RemoveLastParenthesisGroup("func()") == "func");
+        assert(RemoveLastParenthesisGroup("func()()") == "func()");
+        assert(RemoveLastParenthesisGroup("func(()())") == "func");
+        assert(RemoveLastParenthesisGroup("func(blah)") == "func");
+        assert(RemoveLastParenthesisGroup("func(blah)/more") == "func(blah)/more");
+        assert(RemoveLastParenthesisGroup("func(blah())/more") == "func(blah())/more");
+        assert(RemoveLastParenthesisGroup("func(blah())") == "func");
+
+        assert(CountParameters("/f(?int)") == 1);
+        assert(CountParameters("/f()") == 0);
+        assert(CountParameters("/f(?int, ?float)") == 2);
+        assert(CountParameters("/f(/A/B/C, /D/E/F, ?half)") == 3);
+        assert(CountParameters("?vector<half,3>") == 0);
+
+        QualifiedName qn;
+        // moderately shameful that this compiles:
+        UnqualifiedName uqn{ qn };  // mitigation c.f. comment around the is_constructible earlier in this file
+        // desired:   (uncomment to verify)
+        // UnqualifiedName uqn2 = qn;  Error C2440 'initializing': cannot convert from 'AZ::ShaderCompiler::QualifiedName' to 'AZ::ShaderCompiler::UnqualifiedName'
+
+        // desired:   (uncomment to verify)
+        // Func(qn);  Error C2664: cannot convert argument 1 from 'AZ::ShaderCompiler::QualifiedName' to 'AZ::ShaderCompiler::UnqualifiedName'
+    }
+}
+#endif

+ 100 - 0
src/AzslcPlatformEmitter.cpp

@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <mutex>
+
+#include "AzslcEmitter.h"
+#include "AzslcPlatformEmitter.h"
+
+namespace AZ::ShaderCompiler
+{
+    const PlatformEmitter* PlatformEmitter::GetDefaultEmitter() noexcept(true)
+    {
+        // The default platform emitter is never registered and will never be a result in PlatformEmitter::GetEmitter()
+        static PlatformEmitter platformEmitter; // Static linkage, will be destroyed
+        return &platformEmitter;
+    }
+
+    unordered_map<string, const PlatformEmitter*>* s_emitters = nullptr;
+    std::mutex emitterListMutex;
+
+    const PlatformEmitter* PlatformEmitter::GetEmitter(const string& key) noexcept(true)
+    {
+        std::lock_guard<std::mutex> lock(emitterListMutex);
+
+        try
+        {
+            return (*s_emitters)[key];            
+        }
+        catch (...)
+        {   // We should never return a default emitter here. This method searches by name only
+            return nullptr;
+        }        
+    }
+
+    void PlatformEmitter::SetEmitter(const string& key, const PlatformEmitter* const platformEmitter) noexcept(false)
+    {
+        std::lock_guard<std::mutex> lock(emitterListMutex);
+
+        if (!s_emitters)
+        {
+            s_emitters = new unordered_map<string, const PlatformEmitter*>();
+        }
+
+        if (s_emitters->find(key) != s_emitters->end())
+        {
+            throw std::runtime_error{ "PlatformEmitter::RegisterEmitter cannot register two platforms with the same key!" };            
+        }
+        s_emitters->try_emplace(key, platformEmitter);
+    }
+
+    // Virtual methods to be overridden
+
+    string PlatformEmitter::GetRootSig(const CodeEmitter&, const RootSigDesc&, const Options&, BindingPair::Set) const
+    {
+        // The default implementation of most emission methods does nothing
+        return "";
+    }
+
+    string PlatformEmitter::GetRootConstantsView(const CodeEmitter& codeEmitter, const RootSigDesc& rootSig, const Options& options, BindingPair::Set signatureQuery) const
+    {
+        std::stringstream strOut;
+
+        const auto& structUid = codeEmitter.GetIR()->m_rootConstantStructUID;
+        const auto& bindInfo = rootSig.Get(structUid);
+        assert(structUid == bindInfo.m_uid);
+        const auto& rootCBForEmission = codeEmitter.GetTranslatedName(RootConstantsViewName, UsageContext::DeclarationSite);
+        const auto& rootConstClassForEmission = codeEmitter.GetTranslatedName(structUid.GetName(), UsageContext::ReferenceSite);
+        const auto& spaceX = (options.m_useLogicalSpaces) ? ", space" + std::to_string(bindInfo.m_registerBinding.m_pair[signatureQuery].m_logicalSpace) : "";
+        strOut << "ConstantBuffer<" << rootConstClassForEmission << "> " << rootCBForEmission << " : register(b" << bindInfo.m_registerBinding.m_pair[signatureQuery].m_registerIndex << spaceX << ");\n\n";
+
+        return strOut.str();
+    }
+
+    std::pair<string, string> PlatformEmitter::GetDataViewHeaderFooter(const CodeEmitter& codeEmitter, const IdentifierUID& symbol, uint32_t bindInfoRegisterIndex, string_view registerTypeLetter, optional<string> stringifiedLogicalSpace) const
+    {
+        // there is an exception for subpassinput variables since they are not yet supported on our DX12 emission
+        // we need to neutralize it (otherwise DXC will complain with "register space cannot be specified on global constants")
+        // example result: [azslc: SubpassInput var;   hlsl: SubpassInputStub srg_var : register(t0); ]
+        optional<AttributeInfo> inputAttachmentIndexAttribute = codeEmitter.GetIR()->m_symbols.GetAttribute(symbol, "input_attachment_index");
+        if (inputAttachmentIndexAttribute)
+        {
+            return { "static ", {} };
+        }
+        // in the general case, we output normal HLSL `var decl : register(b0, space0);`
+        // no special header, but the post colon part is the footer
+        string bindingSpaceStringlet { stringifiedLogicalSpace ? ", space" + *stringifiedLogicalSpace : "" };
+        string footer {ConcatString(" : register(", registerTypeLetter, bindInfoRegisterIndex, bindingSpaceStringlet, ")")};
+        return { {}, footer };
+    }
+
+    uint32_t PlatformEmitter::AlignRootConstants(uint32_t size) const
+    {
+        return size;
+    }
+}

+ 78 - 0
src/AzslcPlatformEmitter.h

@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <string>
+
+#include "AzslcBackend.h"
+
+namespace AZ::ShaderCompiler
+{
+    struct CodeEmitter;
+
+    // PlatformEmitter is not a Backend by design. It's a supplement to CodeEmitter, not a replacement.
+    struct PlatformEmitter 
+    {
+        using SrgParamDesc = RootSigDesc::SrgParamDesc;
+
+        //! Returns the default platform emitter. It's always guaranteed to exist.
+        static const PlatformEmitter* GetDefaultEmitter() noexcept(true);
+
+        //! Returns a platform emitter registered with the specified key.
+        //! If no such key is registered, returns nullptr instead.
+        //! It never returns the default platform emitter.
+        //! @param key  The key used to search the platform emitter
+        static const PlatformEmitter* GetEmitter(const string& key) noexcept(true);
+
+        PlatformEmitter(PlatformEmitter const&) = delete;
+        void operator=(PlatformEmitter const&)  = delete;
+        void operator=(PlatformEmitter const&& other)
+        {
+            return *this = std::move(other);
+        }
+
+    protected:
+        PlatformEmitter() {};
+        virtual ~PlatformEmitter() {}
+
+        //! Registers a new platform emitter with the specified name.
+        //! In order to provide robust assertion it throws an exception if more than one emitters try to use the same key.
+        //! The only intended use of this method is by the platform emitter itself. No other entity should register emitters.
+        //! @param key  The key used to search. The platform emitter will be registered under this key.
+        //! @param platformEmitter  An emitter to register, which must be of a class derived from this PlatformEmitter
+        static void SetEmitter(const string& key, const PlatformEmitter* const platformEmitter) noexcept(false);
+
+    public:
+        //! Gets the string emission for the root signature for this platform 
+        //! @param codeEmitter  Reference to the calling code emitter
+        //! @param rootSig      Root signature description which should be emitted as shader code
+        //! @param options      Emission options
+        [[nodiscard]]
+        virtual string GetRootSig(const CodeEmitter& codeEmitter, const RootSigDesc& rootSig, const Options& options, BindingPair::Set signatureQuery) const;
+
+        //! Gets the string emission for the data view containing the root constants
+        //! @param codeEmitter  Reference to the calling code emitter
+        //! @param rootSig      Root signature description which should be emitted as shader code
+        //! @param options      Emission options
+        [[nodiscard]]
+        virtual string GetRootConstantsView(const CodeEmitter& codeEmitter, const RootSigDesc& rootSig, const Options& options, BindingPair::Set signatureQuery) const;
+
+        //! Gets the string emission for the surroundings of an extern data view variable
+        //! @param codeEmitter              Reference to the calling code emitter
+        //! @param symbol                   The symbol path of the original variable we are emitting as an extern data view
+        //! @param bindInfoRegisterIndex    Register index of the resource
+        //! @param stringifiedLogicalSpace  Optional register space
+        //! \return                         first: header to emit before the dataview declaration.   second: footer to emit after the dataview declaration
+        [[nodiscard]]
+        virtual std::pair<string, string> GetDataViewHeaderFooter(const CodeEmitter& codeEmitter, const IdentifierUID& symbol, uint32_t bindInfoRegisterIndex, string_view registerTypeLetter, optional<string> stringifiedLogicalSpace) const;
+
+        //! Aligns the size for the data containing the root constants.
+        //! @param size  The size of stride
+        virtual uint32_t AlignRootConstants(uint32_t size) const;
+    };
+}

+ 269 - 0
src/AzslcPredefinedTypes.h

@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+// THIS FILE IS AUTO-GENERATED BY exportKeywords.py
+
+#include <array>
+#include <tuple>
+
+namespace AZ::ShaderCompiler::Predefined
+{
+static constexpr std::array<const char*, 3> Buffer = {
+"Buffer",
+"RWBuffer",
+"RasterizerOrderedBuffer"};
+
+static constexpr std::array<const char*, 3> ByteAddressBuffer = {
+"ByteAddressBuffer",
+"RWByteAddressBuffer",
+"RasterizerOrderedByteAddressBuffer"};
+
+static constexpr std::array<const char*, 2> ConstantBuffer = {
+"ConstantBuffer",
+"constantbuffer"};
+
+static constexpr std::array<const char*, 9> LibrarySubobject = {
+"GlobalRootSignature",
+"LocalRootSignature",
+"ProceduralPrimitiveHitGroup",
+"RaytracingPipelineConfig",
+"RaytracingPipelineConfig1",
+"RaytracingShaderConfig",
+"StateObjectConfig",
+"SubobjectToExportsAssociation",
+"TriangleHitGroup"};
+
+static constexpr std::array<const char*, 113> Matrix = {
+"bool1x1",
+"bool1x2",
+"bool1x3",
+"bool1x4",
+"bool2x1",
+"bool2x2",
+"bool2x3",
+"bool2x4",
+"bool3x1",
+"bool3x2",
+"bool3x3",
+"bool3x4",
+"bool4x1",
+"bool4x2",
+"bool4x3",
+"bool4x4",
+"double1x1",
+"double1x2",
+"double1x3",
+"double1x4",
+"double2x1",
+"double2x2",
+"double2x3",
+"double2x4",
+"double3x1",
+"double3x2",
+"double3x3",
+"double3x4",
+"double4x1",
+"double4x2",
+"double4x3",
+"double4x4",
+"dword1x1",
+"dword1x2",
+"dword1x3",
+"dword1x4",
+"dword2x1",
+"dword2x2",
+"dword2x3",
+"dword2x4",
+"dword3x1",
+"dword3x2",
+"dword3x3",
+"dword3x4",
+"dword4x1",
+"dword4x2",
+"dword4x3",
+"dword4x4",
+"float1x1",
+"float1x2",
+"float1x3",
+"float1x4",
+"float2x1",
+"float2x2",
+"float2x3",
+"float2x4",
+"float3x1",
+"float3x2",
+"float3x3",
+"float3x4",
+"float4x1",
+"float4x2",
+"float4x3",
+"float4x4",
+"half1x1",
+"half1x2",
+"half1x3",
+"half1x4",
+"half2x1",
+"half2x2",
+"half2x3",
+"half2x4",
+"half3x1",
+"half3x2",
+"half3x3",
+"half3x4",
+"half4x1",
+"half4x2",
+"half4x3",
+"half4x4",
+"int1x1",
+"int1x2",
+"int1x3",
+"int1x4",
+"int2x1",
+"int2x2",
+"int2x3",
+"int2x4",
+"int3x1",
+"int3x2",
+"int3x3",
+"int3x4",
+"int4x1",
+"int4x2",
+"int4x3",
+"int4x4",
+"matrix",
+"uint1x1",
+"uint1x2",
+"uint1x3",
+"uint1x4",
+"uint2x1",
+"uint2x2",
+"uint2x3",
+"uint2x4",
+"uint3x1",
+"uint3x2",
+"uint3x3",
+"uint3x4",
+"uint4x1",
+"uint4x2",
+"uint4x3",
+"uint4x4"};
+
+static constexpr std::array<const char*, 2> MultisampledTexture = {
+"Texture2DMS",
+"Texture2DMSArray"};
+
+static constexpr std::array<const char*, 2> OtherPredefined = {
+"BuiltInTriangleIntersectionAttributes",
+"RayDesc"};
+
+static constexpr std::array<const char*, 1> OtherViewBufferType = {
+"RaytracingAccelerationStructure"};
+
+static constexpr std::array<const char*, 4> Sampler = {
+"Sampler",
+"SamplerComparisonState",
+"SamplerState",
+"sampler"};
+
+static constexpr std::array<const char*, 8> Scalar = {
+"bool",
+"double",
+"dword",
+"float",
+"half",
+"int",
+"uint",
+"unsigned int"};
+
+static constexpr std::array<const char*, 3> StreamOutput = {
+"LineStream",
+"PointStream",
+"TriangleStream"};
+
+static constexpr std::array<const char*, 5> StructuredBuffer = {
+"AppendStructuredBuffer",
+"ConsumeStructuredBuffer",
+"RWStructuredBuffer",
+"RasterizerOrderedStructuredBuffer",
+"StructuredBuffer"};
+
+static constexpr std::array<const char*, 19> Texture = {
+"RWTexture1D",
+"RWTexture1DArray",
+"RWTexture2D",
+"RWTexture2DArray",
+"RWTexture3D",
+"RasterizerOrderedTexture1D",
+"RasterizerOrderedTexture1DArray",
+"RasterizerOrderedTexture2D",
+"RasterizerOrderedTexture2DArray",
+"RasterizerOrderedTexture3D",
+"SubpassInput",
+"SubpassInputMS",
+"Texture1D",
+"Texture1DArray",
+"Texture2D",
+"Texture2DArray",
+"Texture3D",
+"TextureCube",
+"TextureCubeArray"};
+
+static constexpr std::array<const char*, 29> Vector = {
+"bool1",
+"bool2",
+"bool3",
+"bool4",
+"double1",
+"double2",
+"double3",
+"double4",
+"dword1",
+"dword2",
+"dword3",
+"dword4",
+"float1",
+"float2",
+"float3",
+"float4",
+"half1",
+"half2",
+"half3",
+"half4",
+"int1",
+"int2",
+"int3",
+"int4",
+"uint1",
+"uint2",
+"uint3",
+"uint4",
+"vector"};
+
+static constexpr std::array<const char*, 1> Void = {
+"void"};
+
+template<size_t N> struct Bag { const char* m_name = nullptr; const std::array<const char*, N> m_bag; };
+
+static constexpr auto All = std::make_tuple(Bag<3>{"Buffer", Buffer},
+                                            Bag<3>{"ByteAddressBuffer", ByteAddressBuffer},
+                                            Bag<2>{"ConstantBuffer", ConstantBuffer},
+                                            Bag<9>{"LibrarySubobject", LibrarySubobject},
+                                            Bag<113>{"Matrix", Matrix},
+                                            Bag<2>{"MultisampledTexture", MultisampledTexture},
+                                            Bag<2>{"OtherPredefined", OtherPredefined},
+                                            Bag<1>{"OtherViewBufferType", OtherViewBufferType},
+                                            Bag<4>{"Sampler", Sampler},
+                                            Bag<8>{"Scalar", Scalar},
+                                            Bag<3>{"StreamOutput", StreamOutput},
+                                            Bag<5>{"StructuredBuffer", StructuredBuffer},
+                                            Bag<19>{"Texture", Texture},
+                                            Bag<29>{"Vector", Vector},
+                                            Bag<1>{"Void", Void});
+
+};

+ 1010 - 0
src/AzslcReflection.cpp

@@ -0,0 +1,1010 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include "AzslcReflection.h"
+#include "AzslcPlatformEmitter.h"
+
+#include <tuple>
+#include <cmath>
+
+#include <filesystem>
+namespace StdFs = std::filesystem;
+
+namespace AZ::ShaderCompiler
+{
+    template <typename OStreamable>
+    Json::Value ToJson(const OStreamable& streamableObj)
+    {
+        std::ostringstream baseStr;
+        baseStr << streamableObj;
+        return baseStr.str();
+    }
+
+    bool CodeReflection::BuildIAElement(Json::Value& jsonVal, const IdentifierUID& uid, bool allowStruct) const
+    {
+        auto& varInfo = *m_ir->GetSymbolSubAs<VarInfo>(uid.m_name);
+
+        if (IsPredefinedType(varInfo.GetTypeClass()))
+        {
+            if (const auto semOpt = varInfo.m_declNode->SemanticOpt)
+            {
+                // Semantic name and index
+                auto semanticName = semOpt->Name->getText();
+                const size_t index = semanticName.find_last_not_of("0123456789") + 1;
+                const uint32_t semanticIndex = (index == semanticName.length()) ?
+                    0 : static_cast<uint32_t>(std::stoi(semanticName.substr(index)));
+                semanticName = semanticName.substr(0, index);
+                bool isSystemValue = (semOpt->Name->HLSLSemanticSystem() != nullptr);
+
+                // Array dimensions
+                Json::Value varArrayDimensions(Json::arrayValue);
+                for (const auto dim : varInfo.m_typeInfoExt.GetDimensions())
+                {
+                    varArrayDimensions.append(Json::Value(dim));
+                }
+
+                Json::Value semStream(Json::objectValue);
+                semStream["name"]           = ExtractLeaf(uid.m_name).data();
+                semStream["fullType"]       = varInfo.GetTypeId().m_name;
+                semStream["baseType"]       = varInfo.m_typeInfoExt.m_coreType.m_arithmeticInfo.UnderlyingScalarToStr();
+                semStream["semanticName"]   = semanticName;
+                semStream["semanticIndex"]  = semanticIndex;
+                semStream["systemValue"]    = isSystemValue;
+                semStream["dimensions"]     = varArrayDimensions;
+                semStream["rows"]           = varInfo.m_typeInfoExt.m_coreType.m_arithmeticInfo.m_rows;
+                semStream["cols"]           = varInfo.m_typeInfoExt.m_coreType.m_arithmeticInfo.m_cols;
+
+                jsonVal.append(semStream);
+
+                return true;
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+        if (!allowStruct)
+        {
+            return false;
+        }
+
+        // From the non-predefineds we only support POD (struct) as valid element for the Input Assembler
+        auto idKind = m_ir->GetIdAndKindInfo(varInfo.GetTypeId().GetName());
+        if (!idKind)
+        {
+            return false;
+        }
+
+        auto& [typeId, typeKind] = *idKind;
+        if (typeKind.GetKind() != Kind::Struct)
+        {   // It's ok not to have POD attributes in other methods, but those are not valid vs entries so return false here
+            return false;
+        }
+
+        // Structs are PODs, so all member fields can only be variables
+        auto& classInfo = typeKind.GetSubRefAs<ClassInfo>();
+        for (const auto& memberVar : classInfo.GetMemberFields())
+        {
+            if (!BuildIAElement(jsonVal, memberVar, false)) // Second pass prevents structs in structs
+            {
+                return false;
+            }
+        }
+
+        return jsonVal.size() > 0;
+    }
+
+    bool CodeReflection::BuildOMStruct(const ExtendedTypeInfo& returnTypeRef, string_view semanticOverride, Json::Value& jsonVal, int& semanticIndex) const
+    {
+        auto idKind = m_ir->GetIdAndKindInfo(returnTypeRef.m_coreType.m_typeId.GetName());
+        if (!idKind)
+        {
+            return false;
+        }
+
+        auto&[typeId, typeKind] = *idKind;
+        if (typeKind.GetKind() != Kind::Struct)
+        {   // It's ok not to have POD attributes in other methods, but those are not valid ps entries so return false here
+            return false;
+        }
+
+        // Structs are PODs, so all member fields can only be variables
+        auto& classInfo = typeKind.GetSubRefAs<ClassInfo>();
+        for (const auto& memberVar : classInfo.GetMemberFields())
+        {
+            auto& attrVarInfo = *m_ir->GetSymbolSubAs<VarInfo>(memberVar.m_name);
+
+            bool buildOMResult = false;
+            if (!semanticOverride.empty())
+            {
+                if (!BuildOMElement(jsonVal, attrVarInfo.m_typeInfoExt, semanticOverride, semanticIndex, false))
+                {
+                    return false;
+                }
+            }
+            else
+            {
+                // Semantic name and index
+                auto hlslSemantic = attrVarInfo.m_declNode->hlslSemantic();
+                if (hlslSemantic == nullptr || hlslSemantic->Name->HLSLSemanticSystem() == nullptr)
+                {
+                    return false;
+                }
+                auto[semanticName, semanticIndex, isSystemValue] = ExtractHlslSemantic(hlslSemantic);
+                if (!BuildOMElement(jsonVal, attrVarInfo.m_typeInfoExt, semanticName.c_str(), semanticIndex, isSystemValue))
+                {
+                    return false;
+                }
+            }
+        }
+
+        return jsonVal.size() > 0;
+    }
+
+    bool ValidOutputSemanticIndex(string_view semanticName, const int semanticIndex)
+    {
+        if (EqualNoCase(semanticName, "SV_Target"))
+        {
+            return (semanticIndex >= 0 && semanticIndex < kMaxRenderTargets);
+        }
+
+        return (semanticIndex == 0);
+    }
+
+    bool IsValidPSOutput(string_view &semanticOverride, int& semanticIndex, Json::Value& jsonVal, const ExtendedTypeInfo &returnTypeRef)
+    {
+        if (semanticOverride.empty())
+        {   // dxc error: Semantic must be defined for all parameters of an entry function or patch constant function
+            return false;
+        }
+
+        if (!ValidOutputSemanticIndex(semanticOverride, semanticIndex))
+        {   // dxc error: [semanticName] semantic index exceeds maximum (7)
+            return false;
+        }
+
+        for (auto existingSemantic : jsonVal)
+        {
+            if (semanticOverride.find(existingSemantic["semanticName"].asCString()) != string::npos &&
+                semanticIndex == existingSemantic["semanticIndex"].asInt())
+            {   // dxc error: Parameter with semantic [semanticName] has overlapping semantic index at [semanticIndex]
+                return false;
+            }
+        }
+
+        if (!IsArithmetic(returnTypeRef.m_coreType.m_typeClass))
+        {   // fxc internal error: compilation aborted unexpectedly
+            // dxc hangs
+            return false;
+        }
+
+        if (returnTypeRef.m_coreType.m_arithmeticInfo.m_rows > 0)
+        {   // dxc error: Pixel shader output registers are not indexable.
+            return false;
+        }
+
+        return true;
+    }
+
+    bool CodeReflection::BuildOMElement(Json::Value& jsonVal, const ExtendedTypeInfo& returnTypeRef, string_view semanticOverride, int& semanticIndex, bool isSystemValue) const
+    {
+        // Structs are allowed as pixel shader output, if the type is registered walk over its attributes here
+        if (!IsPredefinedType(returnTypeRef.m_coreType.m_typeClass))
+        {
+            return BuildOMStruct(returnTypeRef, semanticOverride, jsonVal, semanticIndex);
+        }
+
+        if (!IsValidPSOutput(semanticOverride, semanticIndex, jsonVal, returnTypeRef))
+        {   // We only want to emit a list of potential pixel shader entry points
+            //  so give up here if any
+            return false;
+        }
+
+        Json::Value semStream (Json::objectValue);
+
+        bool isVoid = returnTypeRef.m_coreType.m_typeClass == TypeClass::Void;
+
+        semStream["baseType"] = isVoid ? "void" : returnTypeRef.m_coreType.m_arithmeticInfo.UnderlyingScalarToStr();
+
+        auto numElements = returnTypeRef.m_coreType.m_arithmeticInfo.m_cols;
+        semStream["cols"] = numElements;
+        semStream["semanticName"] = semanticOverride.data();
+
+        if (EqualNoCase(semanticOverride, "SV_Target"))
+        {
+            semStream["format"] = isVoid ? "None" : OutputFormat::ToStr(m_ir->m_metaData.m_outputFormatHint[semanticIndex]).data();;
+        }
+        else if (StartsWithNoCase(semanticOverride, "SV_Depth"))
+        {
+            semStream["format"] = OutputFormat::ToStr(OutputFormat::R32).data();
+        }
+        else if (EqualNoCase(semanticOverride, "SV_Coverage") || EqualNoCase(semanticOverride, "SV_StencilRef"))
+        {
+            semStream["format"] = OutputFormat::ToStr(OutputFormat::R32).data();
+        }
+        else if (!(StartsWithNoCase(semanticOverride, "SV_")))
+        {
+            // Can't end up here, which means there is newly added grammar - it should be handled properly
+            assert(false);
+        }
+
+        // This behavior conforms to how dxc and fxc layout output semantics.
+        // Any output semantic declaration in a structure will override any member variable declarations recursively.
+        // The end result is semantic index in order of declaration rather than what is specified per member variable.
+        semStream["semanticIndex"] = semanticIndex++;
+
+        semStream["systemValue"] = isSystemValue;
+
+        jsonVal.append(semStream);
+
+        return true;
+    }
+
+    Json::Value CodeReflection::GetShaderEntries(const char * const sEntry) const
+    {
+        Json::Value inputLayouts(Json::arrayValue);
+
+        // Emit all vertex and compute shader entry functions
+        for (const auto& symId : m_ir->m_symbols.GetOrderedSymbols())
+        {
+            const auto& [uid, sym] = *m_ir->m_symbols.GetIdAndKindInfo(symId.m_name);
+            const UnqualifiedName funcName{ExtractLeaf(uid.m_name)};
+
+            bool validFunc = ((sym.GetKind() == Kind::Function) && IsGlobal(uid.m_name) &&
+                (sEntry == nullptr || funcName == sEntry));
+            if (!validFunc)
+            {   // It's not a function or at least not the function we are looking for
+                continue;
+            }
+
+            Json::Value functionEntry(Json::objectValue);
+            functionEntry["entry"] = string{RemoveLastParenthesisGroup(funcName)};
+
+            Json::Value semanticStreams(Json::arrayValue);
+            const auto& funcSubInfo = sym.GetSubRefAs<FunctionInfo>();
+            for (const auto it : funcSubInfo.GetParameters(false))
+            {
+                if (!it.m_varId.IsEmpty())
+                {
+                    validFunc &= BuildIAElement(semanticStreams, it.m_varId, true/*Allow structs on the first level*/);
+                }
+            }
+
+            // Optional - compute shader entries also have [numthreads] attribute
+            auto attrInfo = m_ir->m_symbols.GetAttribute(uid, "numthreads");
+            if (attrInfo)
+            {
+                Json::Value numThreads(Json::arrayValue);
+                for (auto arg : attrInfo->m_argList)
+                {
+                    if (!holds_alternative<ConstNumericVal>(arg))
+                    {
+                        continue;                        
+                    }
+
+                    const auto& constVal = get<ConstNumericVal>(arg);
+                    if (!holds_alternative<int32_t>(constVal) && !holds_alternative<uint32_t>(constVal))
+                    {
+                        continue;
+                    }
+
+                    auto threadCount = ExtractValueAsInt64(constVal, std::numeric_limits<int64_t>::min());
+                    if (threadCount <= 0)
+                    {
+                        continue;
+                    }
+
+                    numThreads.append(Json::Value(static_cast<int>(threadCount)));
+                }
+
+                if (numThreads.size() == 3)
+                {   // We expect exactly 3 numbers that specify the ThreadGroup dimensions
+                    functionEntry["numthreads"] = numThreads;
+                }
+            }
+
+            // the shader attribute is the way to declare raytracing entrypoints so they are valid entries
+            if (validFunc || m_ir->m_symbols.GetAttribute(uid, "shader"))
+            {
+                functionEntry["streams"] = semanticStreams;
+                inputLayouts.append(functionEntry);
+            }
+        }
+
+        return inputLayouts;        
+    }
+
+    Json::Value CodeReflection::GetOutputMergerLayout(const char * const psEntry) const
+    {
+        Json::Value outputLayouts(Json::arrayValue);
+
+        // Emit all pixel shader entry functions
+        for (const auto& [uid, sym] : m_ir->GetOrderedSymbolsOfSubType_2<FunctionInfo>())
+        {
+            UnqualifiedNameView funcName = ExtractLeaf(uid.m_name);
+
+            bool validFunc = (IsGlobal(uid.m_name)
+                              && (psEntry == nullptr || strcmp(psEntry, funcName.data()) == 0));
+            if (!validFunc)
+            {   // It's not the function we are looking for
+                continue;
+            }
+
+            Json::Value functionEntry(Json::objectValue);
+            functionEntry["entry"] = string{RemoveLastParenthesisGroup(funcName)};
+
+            Json::Value renderTargets(Json::arrayValue);
+            const auto& funcSubInfo = *sym;
+
+            string semanticName("");
+            int semanticIndex = 0;
+            bool isSystemValue = false;
+            if (funcSubInfo.m_defNode->hlslSemantic())
+            {
+                tie (semanticName, semanticIndex, isSystemValue) = ExtractHlslSemantic(funcSubInfo.m_defNode->hlslSemantic());
+            }
+
+            validFunc &= BuildOMElement(renderTargets, funcSubInfo.m_returnType, semanticName, semanticIndex, isSystemValue);
+
+            int depthFoundNTimes = 0;
+            for (auto existingSemantic : renderTargets)
+            {
+                if (existingSemantic["semanticName"].asString().find("Depth") != string::npos)
+                {
+                    depthFoundNTimes++;
+                }
+            }
+            // dxc error: Pixel Shader only allows one type of depth semantic to be declared
+            validFunc &= (depthFoundNTimes <= 1);
+
+            if (validFunc)
+            {
+                functionEntry["renderTargets"] = renderTargets;
+                outputLayouts.append(functionEntry);
+            }
+        }
+
+        return outputLayouts;        
+    }
+
+    void CodeReflection::DumpOutputMergerLayout(const char * const psEntry) const
+    {
+        Json::Value iaRoot(Json::objectValue);
+        iaRoot["meta"] = "Output Merger Layout exported by AZSLc";
+        iaRoot["source"] = m_ir->OriginalSource();
+        iaRoot["material"] = "Output Merger Layout exported by AZSLc";
+
+        iaRoot["outputLayouts"] = GetOutputMergerLayout(psEntry);
+        m_out << iaRoot;
+    }
+
+    void CodeReflection::DumpShaderEntries() const
+    {
+        Json::Value entries(Json::objectValue);
+        entries["meta"] = "Shader entries exported by AZSLc";
+        entries["source"] = m_ir->OriginalSource();
+        entries["material"] = "Unknown material";
+
+        entries["inputLayouts"] = GetShaderEntries(nullptr);
+        m_out << entries;                
+    }
+
+    uint32_t CodeReflection::GetViewStride(const IdentifierUID& memberId, const AZ::ShaderCompiler::Packing::Layout& layoutPacking, const Options& options) const
+    {
+        const auto* varInfoPtr = m_ir->GetSymbolSubAs<VarInfo>(memberId.m_name);
+        if (!varInfoPtr)
+        {
+            return 0;
+        }
+
+        assert(IsViewType(varInfoPtr->GetTypeClass()));
+
+        if (varInfoPtr->m_typeInfoExt.m_genericParameter.IsEmpty())
+        {
+            if (varInfoPtr->GetTypeClass() == TypeClass::ByteAddressBuffer)
+            {
+                return 4;
+            }
+            else
+            {
+                // Unspecified generic types default to float4
+                return 16;
+            }
+        }
+
+        const auto genericType = varInfoPtr->m_typeInfoExt.m_genericParameter;
+        if (!IsUserDefined(genericType.m_typeClass))
+        {
+            assert(!genericType.m_arithmeticInfo.IsEmpty());
+            return genericType.m_arithmeticInfo.GetTotalSize();
+        }
+
+        Json::Value emptyLayout(Json::arrayValue);
+        const auto& structMember = genericType.m_typeId;
+        uint32_t startAt = 0;
+        return BuildUserDefinedMemberLayout(emptyLayout, structMember, options, layoutPacking, startAt, "");
+    }
+
+    uint32_t CodeReflection::BuildUserDefinedMemberLayout(Json::Value& membersContainer,
+                                                          const IdentifierUID& exportedTypeId,
+                                                          const Options& options,
+                                                          const AZ::ShaderCompiler::Packing::Layout layoutPacking,
+                                                          uint32_t& startAt, string_view namePrefix) const
+    {
+        // Alignment start
+        uint32_t tempOffset = startAt = Packing::AlignOffset(layoutPacking, startAt, Packing::Alignment::asStructStart, 0, 0);
+
+        uint32_t largestMemberSize = 0;
+
+        const auto* classInfo = m_ir->GetSymbolSubAs<ClassInfo>(exportedTypeId.m_name);
+        for (const auto& memberField : classInfo->GetMemberFields())
+        {
+            const auto currentStride = tempOffset;
+            BuildMemberLayout(membersContainer, namePrefix, memberField, false, options, GetExtendedLayout(layoutPacking), tempOffset);
+            largestMemberSize = std::max(largestMemberSize, tempOffset - currentStride);
+        }
+
+        tempOffset = Packing::AlignStructToLargestMember(layoutPacking, tempOffset, largestMemberSize);
+
+        // Alignment end
+        tempOffset = Packing::AlignOffset(layoutPacking, tempOffset, Packing::Alignment::asStructEnd, 0, 0);
+
+        // Total size equals the end offset less the starting address
+        return tempOffset - startAt;   
+    }
+
+    uint32_t CodeReflection::BuildMemberLayout(Json::Value& membersContainer,
+                                               string_view namePrefix,
+                                               const IdentifierUID& memberId,
+                                               const bool isArrayItr,
+                                               const Options& options,
+                                               const AZ::ShaderCompiler::Packing::Layout layoutPacking,
+                                               uint32_t& offset) const
+    {
+        const auto* varInfoPtr = m_ir->GetSymbolSubAs<VarInfo>(memberId.m_name);
+        uint32_t size = 0;
+
+        if (varInfoPtr)
+        {
+            const auto& varInfo = *varInfoPtr;
+
+            // View types should only be called from GetViewStride until we decide to support them as struct constants
+            assert(!IsChameleon(varInfo.GetTypeClass()));
+
+            auto exportedType = varInfo.m_typeInfoExt.m_coreType;
+
+            if (!exportedType.IsPackable())
+            {
+                throw std::logic_error{"reflection error: unpackable type ("
+                    + exportedType.m_typeId.m_name
+                    + ") in layout member "
+                    + memberId.m_name};
+            }
+            TypeClass varClass = exportedType.m_typeClass;
+            bool isPrefedined  = IsPredefinedType(varClass);
+            Json::Value memberLayout(Json::objectValue);
+            const auto& shortName = memberId.GetNameLeaf();
+            memberLayout["constantId"]       = isArrayItr ? namePrefix.data() : namePrefix.data() + shortName;
+            memberLayout["qualifiedName"]    = memberId.m_name;
+            memberLayout["typeKind"]         = isPrefedined ? "Predefined" :
+                IsProductType(varClass) ? "Struct"    : TypeClass::ToStr(varClass).data();
+            memberLayout["typeName"]         = varInfo.GetTypeId().m_name;         
+
+            size = varInfo.m_typeInfoExt.GetTotalSize(layoutPacking, options.m_emitRowMajor);
+            auto startAt = offset;
+
+            // Alignment start
+            if (exportedType.m_arithmeticInfo.IsMatrix() || exportedType.m_arithmeticInfo.IsVector())
+            {
+                const auto rows = exportedType.m_arithmeticInfo.m_rows;
+                const auto cols = exportedType.m_arithmeticInfo.m_cols;
+                const auto packAlignment = exportedType.m_arithmeticInfo.IsMatrix() ? Packing::Alignment::asMatrixStart : Packing::Alignment::asVectorStart;
+                startAt = offset = Packing::AlignOffset(layoutPacking, offset, packAlignment, rows, cols);
+            }
+
+            uint32_t totalArraySize = 1;
+            ArrayDimensions listOfArrayDim = varInfo.m_typeInfoExt.GetDimensions();
+            std::reverse(listOfArrayDim.m_dimensions.begin(), listOfArrayDim.m_dimensions.end());
+            for (const auto dim : varInfo.m_typeInfoExt.GetDimensions())
+            {
+                totalArraySize *= dim;
+            }
+
+            if (varInfo.m_typeInfoExt.IsArray() && !isArrayItr)
+            {
+                startAt = offset = Packing::AlignOffset(layoutPacking, offset, Packing::Alignment::asArrayStart, 0, 0);
+                uint32_t arrayOffset = startAt;
+                for (uint32_t i = 0; i < totalArraySize; i++)
+                {
+                    // Construct dimension, [m][n], [p][q][r], etc
+                    string strDimIndex = "";
+                    int prevDim = 1;
+                    for (const auto dim : listOfArrayDim)
+                    {
+                        // If the array dimension is 3, construct index for an element i in array [x][y][z]
+                        strDimIndex = "[" + std::to_string((i / prevDim) % dim) + "]" + strDimIndex;
+                        prevDim *= dim;
+                    }
+
+                    if (!m_ir->GetIdAndKindInfo(varInfo.GetTypeId().m_name))
+                    {
+                        continue;
+                    }
+
+                    // If array is a structure
+                    if (IsProductType(varClass))
+                    {
+                        size = BuildUserDefinedMemberLayout(membersContainer, exportedType.m_typeId, options, layoutPacking, startAt = offset,
+                            (namePrefix.data() + shortName + strDimIndex + "."));
+
+                        offset = Packing::PackNextChunk(layoutPacking, size, startAt);
+
+                        // Add packing into array
+                        size = Packing::PackIntoArray(layoutPacking, size, varInfo.m_typeInfoExt.GetDimensions());
+                    }
+                    else
+                    {
+                        // Alignment start
+                        startAt = offset = Packing::AlignOffset(layoutPacking, offset, Packing::Alignment::asArrayStart, 0, 0);
+
+                        // We want to calculate the offset for each array element
+                        uint32_t tempOffset = startAt;
+
+                        BuildMemberLayout(membersContainer, (namePrefix.data() + shortName + strDimIndex), memberId, true, options, layoutPacking, tempOffset);
+
+                        // Alignment end
+                        tempOffset = Packing::AlignOffset(layoutPacking, tempOffset, Packing::Alignment::asArrayEnd, 0, 0);
+                        size = tempOffset - startAt;
+
+                        offset = Packing::PackNextChunk(layoutPacking, size, startAt);
+
+                        // Add packing into array
+                        size = Packing::PackIntoArray(layoutPacking, size, varInfo.m_typeInfoExt.GetDimensions());
+                    }
+                }
+                startAt = arrayOffset;
+            }
+            else if (IsProductType(varClass))
+            {
+                size = BuildUserDefinedMemberLayout(membersContainer, exportedType.m_typeId, options, layoutPacking, startAt, 
+                                          (namePrefix.data() + shortName + "."));
+
+                // Add packing into array
+                size = Packing::PackIntoArray(layoutPacking, size, varInfo.m_typeInfoExt.GetDimensions());
+            }
+            else if (varInfo.m_typeInfoExt.IsArray())
+            {
+                // Get the size of one element from total size
+                size = varInfo.m_typeInfoExt.GetSingleElementSize(layoutPacking, options.m_emitRowMajor);
+            }
+            else if (varInfo.GetTypeClass() == TypeClass::Enum)
+            {
+                auto* asClassInfo = m_ir->GetSymbolSubAs<ClassInfo>(varInfo.GetTypeId().GetName());
+                size = asClassInfo->Get<EnumerationInfo>()->m_underlyingType.m_arithmeticInfo.GetBaseSize();
+            }
+
+            offset = Packing::PackNextChunk(layoutPacking, size, startAt);
+
+            // Alignment end
+            if (exportedType.m_arithmeticInfo.IsMatrix() || exportedType.m_arithmeticInfo.IsVector())
+            {
+                const auto rows = exportedType.m_arithmeticInfo.m_rows;
+                const auto cols = exportedType.m_arithmeticInfo.m_cols;
+                const auto packAlignment = exportedType.m_arithmeticInfo.IsMatrix() ? Packing::Alignment::asMatrixEnd : Packing::Alignment::asVectorEnd;
+                offset = Packing::AlignOffset(layoutPacking, offset, packAlignment, rows, cols);
+            }
+
+            if (varInfo.m_typeInfoExt.IsArray())
+            {
+                offset = Packing::AlignOffset(layoutPacking, offset, Packing::Alignment::asArrayEnd, 0, 0);
+            }
+
+            size = offset - startAt;
+
+            // Array dimensions
+            Json::Value varArrayDimensions(Json::arrayValue);
+            for (const auto dim : varInfo.m_typeInfoExt.GetDimensions())
+            {
+                varArrayDimensions.append(Json::Value(dim));
+            }
+
+            memberLayout["constantByteOffset"]   = startAt;
+            memberLayout["constantByteSize"]     = size;
+            memberLayout["typeDimensions"]       = varArrayDimensions;
+
+            membersContainer.append(memberLayout);
+        }
+
+        return size;
+    }
+
+    void CodeReflection::DumpVariantList(const Options& options) const
+    {
+        m_out << GetVariantList(options);
+    }
+
+    static void ReflectBinding(Json::Value& output, const RootSigDesc::SrgParamDesc& bindInfo)
+    {
+        output["count"] = (bindInfo.m_isUnboundedArray) ? -1 : bindInfo.m_registerRange;
+        output["index"] = bindInfo.m_registerBinding.m_pair[BindingPair::Set::Untainted].m_registerIndex;
+        output["space"] = bindInfo.m_registerBinding.m_pair[BindingPair::Set::Untainted].m_logicalSpace;
+        output["index-merged"] = bindInfo.m_registerBinding.m_pair[BindingPair::Set::Merged].m_registerIndex;
+        output["space-merged"] = bindInfo.m_registerBinding.m_pair[BindingPair::Set::Merged].m_logicalSpace;
+    }
+
+    void CodeReflection::DumpSRGLayout(const Options& options) const
+    {
+        uint32_t numOf32bitConst = GetNumberOf32BitConstants(options, m_ir->m_rootConstantStructUID);
+        RootSigDesc rootSig = BuildSignatureDescription(options, numOf32bitConst);
+
+        Json::Value srgRoot(Json::objectValue);
+        srgRoot["meta"] = "SRGs layout exported by AZSLc";
+        srgRoot["source"] = m_ir->OriginalSource();
+
+        Json::Value srgLayouts(Json::arrayValue);
+
+        // Reflect all SRGs
+        for (auto& [srgUid, srgInfo] : m_ir->GetOrderedSymbolsOfSubType_2<SRGInfo>())
+        {
+            Json::Value srgLayout(Json::objectValue);
+            srgLayout["id"] = srgInfo->m_declNode->Name->getText();
+
+            // Try to locate the original filename where this SRG is declared
+            auto lineBefore = m_ir->m_lineMap.lower_bound(srgInfo->m_declNode->getStart()->getLine());
+            if (lineBefore != m_ir->m_lineMap.begin() && (lineBefore != m_ir->m_lineMap.end() || m_ir->m_lineMap.size() > 0))
+            {
+                lineBefore--;
+                const string& containing = lineBefore->second.m_containingFilename;
+                srgLayout["originalFileName"]    = StdFs::absolute(containing).lexically_normal().generic_string();
+                srgLayout["originalLineNumber"]  = static_cast<Json::Value::UInt64>(lineBefore->second.m_forcedLineNumber); // TODO: fix according to following concept:
+                /*auto nearestLineDirective = lineBefore->second.m_forcedLineNumber;
+                srgLayout["originalLineNumber"]  =  CurrentLine - nearestLineDirective;*/
+            }
+            auto semantic = m_ir->GetSymbolSubAs<ClassInfo>(srgInfo->m_semantic->GetName())->Get<SRGSemanticInfo>();
+
+            srgLayout["bindingSlot"] = *semantic->m_frequencyId; // Semantic check has asserted that we have it, so we can dereference here.
+
+            if (srgInfo->m_shaderVariantFallback)
+            {
+                srgLayout["fallbackSize"] = *semantic->m_variantFallback;
+                srgLayout["fallbackName"] = srgInfo->m_shaderVariantFallback->GetNameLeaf();
+            }
+
+            Json::Value buffersList(Json::arrayValue);
+            Json::Value imagesList(Json::arrayValue);
+
+            // External CBVs
+            for (const auto& cId : srgInfo->m_CBs)
+            {
+                const auto& bindInfo = rootSig.Get(cId);
+
+                uint32_t strideSize = GetViewStride(cId, options.m_packConstantBuffers, options);
+
+                auto& srgMemberInfo = *m_ir->GetSymbolSubAs<VarInfo>(cId.m_name);
+                assert(srgMemberInfo.IsConstantBuffer());
+                string typeNameLeaf = srgMemberInfo.m_typeInfoExt.GetDisplayShortName();
+
+                Json::Value dataView(Json::objectValue);
+                dataView["id"] = ExtractLeaf(cId.m_name).data();
+                dataView["type"] = typeNameLeaf;
+                dataView["usage"] = "Read";
+                dataView["stride"] = strideSize;
+                ReflectBinding(dataView, bindInfo);
+
+                buffersList.append(dataView);
+            }
+
+            // SRVs and UAVs
+            for (const auto& tId : srgInfo->m_srViews)
+            {
+                const auto& bindInfo = rootSig.Get(tId);
+
+                uint32_t strideSize = GetViewStride(tId, options.m_packDataBuffers, options);
+
+                auto& srgMemberInfo = *m_ir->GetSymbolSubAs<VarInfo>(tId.m_name);
+                auto viewName = srgMemberInfo.m_typeInfoExt.GetDisplayShortName();
+                bool isBufferView = IsViewTypeBuffer(srgMemberInfo.GetTypeClass());
+                bool isReadWriteView = IsReadWriteView(viewName);
+
+                // Relaxed vector std140 and std430 packing rules
+                if (isBufferView && options.m_packConstantBuffers == AZ::ShaderCompiler::Packing::Layout::RelaxedStd140Packing)
+                {
+                    strideSize = Packing::AlignUp(strideSize, Packing::s_bytesPerRegister);
+                }
+
+                Json::Value dataView(Json::objectValue);
+                dataView["id"]     = ExtractLeaf(tId.m_name).data();
+                dataView["type"]   = viewName;
+                dataView["usage"]  = (isReadWriteView) ? "ReadWrite" : "Read";
+                ReflectBinding(dataView, bindInfo);
+                dataView["stride"] = strideSize;
+
+                if (isBufferView)
+                {
+                    buffersList.append(dataView);
+                }
+                else
+                {
+                    imagesList.append(dataView);
+                }
+            }
+
+            srgLayout["inputsForBufferViews"] = buffersList;
+            srgLayout["inputsForImageViews"] = imagesList;
+
+            Json::Value samplersList(Json::arrayValue);
+            for (const auto& sId : srgInfo->m_samplers)
+            {
+                const auto& bindInfo = rootSig.Get(sId);
+
+                const auto* srgMemberInfo = m_ir->GetSymbolSubAs<VarInfo>(sId.m_name);
+                const auto& samplerInfo = *srgMemberInfo->m_samplerState;
+
+                Json::Value samplerJson(Json::objectValue);
+                samplerJson["Id"] = sId.GetNameLeaf();
+                samplerJson["isDynamic"] = samplerInfo.m_isDynamic;
+                ReflectBinding(samplerJson, bindInfo);
+
+                if (!samplerInfo.m_isDynamic)
+                {
+                    // Emit predefined enums for static sampler
+                    samplerJson["anisotropyMax"] = samplerInfo.m_anisotropyMax;
+                    samplerJson["anisotropyEnable"] = samplerInfo.m_anisotropyEnable;
+                    samplerJson["filterMin"] = (samplerInfo.m_filterMin == SamplerStateDesc::FilterMode::Linear) ? "Linear" : "Point";
+                    samplerJson["filterMag"] = (samplerInfo.m_filterMag == SamplerStateDesc::FilterMode::Linear) ? "Linear" : "Point";
+                    samplerJson["filterMip"] = (samplerInfo.m_filterMip == SamplerStateDesc::FilterMode::Linear) ? "Linear" : "Point";
+                    samplerJson["reductionType"] = ToJson(samplerInfo.m_reductionType);
+                    samplerJson["comparisonFunc"] = ToJson(samplerInfo.m_comparisonFunc);
+                    samplerJson["addressU"] = ToJson(samplerInfo.m_addressU);
+                    samplerJson["addressV"] = ToJson(samplerInfo.m_addressV);
+                    samplerJson["addressW"] = ToJson(samplerInfo.m_addressW);
+                    samplerJson["mipLodMin"] = samplerInfo.m_mipLodMin;
+                    samplerJson["mipLodMax"] = samplerInfo.m_mipLodMax;
+                    samplerJson["mipLodBias"] = samplerInfo.m_mipLodBias;
+                    samplerJson["borderColor"] = ToJson(samplerInfo.m_borderColor);
+                    samplerJson["isComparison"] = samplerInfo.m_isComparison;
+                }
+
+                samplersList.append(samplerJson);
+            }
+            srgLayout["inputsForSamplers"] = samplersList;
+
+            // SRG Constants
+            // Call extracted func for rgInfo->m_implicitStruct
+            if (!srgInfo->m_implicitStruct.GetMemberFields().empty())
+            {
+                const auto& layoutPacking = options.m_packConstantBuffers;
+                uint32_t offset = 0;
+                Json::Value structLayout(Json::arrayValue);
+
+                for (const auto& srgConst : srgInfo->m_implicitStruct.GetMemberFields())
+                {
+                    BuildMemberLayout(structLayout, "", srgConst, false, options, layoutPacking, offset);
+                }
+
+                srgLayout["inputsForSRGConstants"] = structLayout;
+
+                // CBuffer for SRG Constants
+                {
+                    const auto& bindInfo = rootSig.Get(srgUid);
+
+                    Json::Value dataView(Json::objectValue);
+                    dataView["id"] = ExtractLeaf(srgUid.m_name).data();
+                    dataView["usage"] = "Read";
+                    ReflectBinding(dataView, bindInfo);
+
+                    srgLayout["bufferForSRGConstants"] = dataView;
+                }
+            }
+
+            srgLayouts.append(srgLayout);
+
+        }
+        srgRoot["ShaderResourceGroups"] = srgLayouts;
+
+        Json::Value rootConstantLayout(Json::objectValue);
+
+        if (options.m_rootConstantsMaxSize > 0)
+        {
+            const auto& layoutPacking = options.m_packConstantBuffers;
+            Json::Value structLayout(Json::arrayValue);
+            uint32_t startAt = 0;
+            uint32_t strideSize = BuildUserDefinedMemberLayout(structLayout, m_ir->m_rootConstantStructUID, options, options.m_packConstantBuffers, startAt, "");
+            strideSize = GetPlatformEmitter().AlignRootConstants(strideSize);
+
+            rootConstantLayout["inputsForInlineConstants"] = structLayout;
+
+            const auto& bindInfo = rootSig.Get(m_ir->m_rootConstantStructUID);
+            Json::Value dataView(Json::objectValue);
+            dataView["id"] = ExtractLeaf(m_ir->m_rootConstantStructUID.m_name).data();
+            dataView["usage"] = "Read";
+            ReflectBinding(dataView, bindInfo);
+            dataView["sizeInBytes"] = strideSize;
+
+            rootConstantLayout["bufferForInlineConstants"] = dataView;
+
+            // Add the layout to Root node
+            srgRoot["InlineConstantBuffer"] = rootConstantLayout;
+        }
+
+        m_out << srgRoot;
+    }
+
+    bool CodeReflection::IsNonOverloaded(const IdentifierUID& uid) const
+    {                      // func() to func
+                           // because in AZIR mangling scheme, the parenthesized name is the concrete function name, and the core name is the overload-set name.
+        string_view core = RemoveLastParenthesisGroup(uid.GetName());
+        auto* coreSymbol = m_ir->GetSymbolSubAs<OverloadSetInfo>(QualifiedNameView{core});
+        return !coreSymbol || !coreSymbol->HasOverloads();
+    }
+
+    bool CodeReflection::IsPotentialEntryPoint(const IdentifierUID& uid) const
+    {
+        // only global and non overloaded, potentially qualifies. we can also add --ia detectors.
+        return m_ir->GetKind(uid) == Kind::Function && IsGlobal(uid.GetName()) && IsNonOverloaded(uid);
+    }
+
+    void CodeReflection::DiscoverTopLevelFunctionDependencies(const IdentifierUID& uid,
+                                                              set<IdentifierUID>& output,
+                                                              const MapOfBeginToSpanAndUid& scopes,
+                                                              set<IdentifierUID>&& funcStack_ = {}) const
+    {
+        // discover references:
+        auto* kindInfo = m_ir->GetKindInfo(uid);
+        for (auto& seenat : kindInfo->GetSeenats())
+        {
+            assert(uid == seenat.m_referredDefinition);
+            // TODO: the assumption that intervals where distinct doesnt hold anymore now that we have unnamed scopes
+            auto intervalIter = FindInterval(scopes, seenat.m_where.m_focusedTokenId, [](ssize_t key, auto& value)
+                                             {
+                                                 return value.first.properlyContains({key, key});
+                                             });
+            if (intervalIter != scopes.cend())
+            {
+                const IdentifierUID& encloser = intervalIter->second.second;
+                if (m_ir->GetKind(encloser) == Kind::Function)
+                {
+                    // detect cycles:
+                    if (funcStack_.find(encloser) != funcStack_.end())
+                    {
+                        throw AzslcEmitterException(EMITTER_RECURSION_NOT_PERMITTED,
+                                                    seenat.m_where.m_line, seenat.m_where.m_charPos,
+                                                    ConcatString("Recursion not permitted in AZSL. ", uid.m_name, " in ", encloser.m_name));
+                    }
+                    if (IsPotentialEntryPoint(encloser))  // reduce a bit the amount of unused data by filtering by potential entry points
+                    {
+                        output.insert(encloser);
+                    }
+                    // recurse
+                    auto it = funcStack_.insert(encloser).first;  // push
+                    DiscoverTopLevelFunctionDependencies(encloser, output, scopes, CastToRValueReference(funcStack_));
+                    funcStack_.erase(it);  // pop
+                }
+            }
+            else // no interval found -> assume global scope
+            {
+                // the only sort of reference that can appear outside of any scope are within global variable initializers.
+                antlr4::ParserRuleContext* syntaxContext = m_ir->m_tokenMap.GetNode(seenat.m_where.m_focusedTokenId);
+                auto* varInitializerContext = ExtractInitializerParent(syntaxContext);
+                if (varInitializerContext) // if null, it's highly suspicious though. what syntax construct allows that ?
+                {
+                    auto* declarator = polymorphic_downcast<AstUnnamedVarDecl*>(varInitializerContext->parent);
+                    // reconstruct the name to get the UID
+                    UnqualifiedName declaredIdentifier{ExtractVariableNameIdentifier(declarator)->getText()};
+                    QualifiedNameView globalScope{"/"};
+                    QualifiedName variable = MakeFullyQualified(globalScope, declaredIdentifier);
+                    auto& [identified, kind] = *m_ir->GetIdAndKindInfo(variable);  // unchecked dereference here, because symbol presence is an invariant
+                    assert(kind.GetKind() == Kind::Variable);  // what else
+                    DiscoverTopLevelFunctionDependencies(identified, output, scopes, CastToRValueReference(funcStack_)); // continue tracking
+                }
+            }
+        }
+    }
+
+    void CodeReflection::DumpResourceBindingDependencies(const Options& options) const
+    {
+        uint32_t numOf32bitConst  = GetNumberOf32BitConstants(options, m_ir->m_rootConstantStructUID);
+        RootSigDesc rootSignature = BuildSignatureDescription(options, numOf32bitConst);
+
+        // prepare a lookup acceleration data structure for reverse mapping tokens to scopes.
+        MapOfBeginToSpanAndUid scopeStartToFunctionIntervals;
+        for (auto& [uid, interval] : m_ir->m_scope.m_scopeIntervals)
+        {
+            if (m_ir->GetKind(uid) == Kind::Function)  // Filter out unnamed blocs and types. We need a set of disjoint intervals as an invariant for the next algorithm.
+            {
+                // the reason to choose .a as the key is so we can query using Infimum (sort of lower_bound)
+                scopeStartToFunctionIntervals[interval.a] = std::make_pair(interval, uid);
+            }
+        }
+
+        Json::Value srgRoot(Json::objectValue);
+        // Order the reflection by SRG for convenience
+        for (const RootSigDesc::SrgDesc& srgDesc : rootSignature.m_srGroups)
+        {
+            auto uid = srgDesc.m_uid;
+            if (uid.m_name != m_ir->m_rootConstantStructUID.m_name) // Since root constant isn't part of SRG resource, and just an alias of SRG members, skip for now.
+            {
+                auto* srgInfo = m_ir->GetKindInfo(uid)->GetSubAs<SRGInfo>();
+                Json::Value srgMember(Json::objectValue);
+
+                // functions to factorize node output
+                auto dependencyListToJson = [](const set<IdentifierUID>& dependencyList) -> Json::Value
+                {
+                    Json::Value allEntriesJson(Json::arrayValue);
+                    for_each(dependencyList.begin(), dependencyList.end(), [&allEntriesJson](const IdentifierUID& id)
+                                                                           {
+                                                                               allEntriesJson.append(string{RemoveLastParenthesisGroup(id.GetNameLeaf())});
+                                                                           });
+                    return allEntriesJson;
+                };
+
+                auto makeJsonNodeForOneResource = [&dependencyListToJson](const set<IdentifierUID>& dependencyList,
+                    const RootSigDesc::SrgParamDesc& binding,
+                    const Json::Value& allConstants)
+                {
+                    Json::Value resourceJsonValue(Json::objectValue);
+                    resourceJsonValue["dependentFunctions"] = dependencyListToJson(dependencyList);
+                    if (!allConstants.empty())
+                    {
+                        resourceJsonValue["participantConstants"] = allConstants;
+                    }
+                    BindingPair::Set bindingQuery = BindingPair::Set::Untainted;
+                    resourceJsonValue["binding"]["type"] = RootParamType::ToStr(binding.m_type).data();
+                    ReflectBinding(resourceJsonValue["binding"], binding);
+                    return resourceJsonValue;
+                };
+
+                optional<RootSigDesc::SrgParamDesc> srgConstants;  // if we have SRG Constants we treat them later
+                for (auto& srgParam : srgDesc.m_parameters)
+                {
+                    if (srgParam.m_type == RootParamType::SrgConstantCB)
+                    {
+                        assert(!srgConstants); // can't have multiple of those per SRG.
+                        srgConstants = srgParam;
+                    }
+                    else
+                    {
+                        set<IdentifierUID> dependencyList;
+                        DiscoverTopLevelFunctionDependencies(srgParam.m_uid, dependencyList, scopeStartToFunctionIntervals);
+                        srgMember[srgParam.m_uid.GetNameLeaf()] = makeJsonNodeForOneResource(dependencyList, srgParam, {});
+                    }
+                }
+                // SRG constants (and the variant-fallback) are in one special constant buffer
+                if (srgConstants || srgInfo->m_shaderVariantFallback)
+                {
+                    Json::Value allConstants(Json::arrayValue);
+                    // make a merged list of dependencies, shared by all individual constants.
+                    set<IdentifierUID> dependencyList;
+                    for (auto& srgConstant : srgInfo->m_implicitStruct.GetMemberFields())
+                    {
+                        allConstants.append({ srgConstant.GetNameLeaf() });
+                        DiscoverTopLevelFunctionDependencies(srgConstant, dependencyList, scopeStartToFunctionIntervals);
+                    }
+                    // variant fallback support
+                    if (srgInfo->m_shaderVariantFallback)
+                    {
+                        // no need to append m_shaderVariantFallback to allConstants because the fallback already exists in the memberFields
+                        // all option variables converge to the fallback
+                        for (auto& [varUid, varSub] : m_ir->GetOrderedSymbolsOfSubType_2<VarInfo>())
+                        {
+                            if (varSub->CheckHasStorageFlag(StorageFlag::Option))
+                            {
+                                DiscoverTopLevelFunctionDependencies(varUid, dependencyList, scopeStartToFunctionIntervals);
+                            }
+                        }
+                    }
+                    srgMember[string{ ExtractLeaf(MakeSrgConstantsCBName(uid)) }] = makeJsonNodeForOneResource(dependencyList, *srgConstants, allConstants);
+                }
+                srgRoot[uid.GetNameLeaf()] = srgMember;
+            }
+        }
+
+        m_out << srgRoot;
+    }
+}

+ 75 - 0
src/AzslcReflection.h

@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include "AzslcBackend.h"
+
+namespace AZ::ShaderCompiler
+{
+    struct CodeReflection : Backend
+    {
+        using Backend::Backend;
+
+        // bring all non code emitting features here.
+
+        //! Reflect information about vertex and compute stage inputs
+        Json::Value GetShaderEntries(const char * const sEntry = nullptr) const;
+
+        //! Reflect information about fragment stage outputs
+        Json::Value GetOutputMergerLayout(const char * const psEntry = nullptr) const;
+
+        //! Dumps reflected information about fragment stage outputs
+        void DumpOutputMergerLayout(const char * const psEntry = nullptr) const;
+
+        //! Dumps reflected information about vertex and compute shader entries
+        void DumpShaderEntries() const;
+
+        //! Reflect resource groups layout
+        //! @param options  user configuration parsed from command line
+        void DumpSRGLayout(const Options& options) const;
+
+        //! Reflect shader options
+        //! @param options  user configuration parsed from command line
+        void DumpVariantList(const Options& options) const;
+
+        //! Reflect extended resource binding information.
+        //! Such as which stage function is client of a resource; and what are the registers of the descriptors.
+        //! @param options  user configuration parsed from command line
+        void DumpResourceBindingDependencies(const Options& options) const;
+
+    private:
+
+        //! Builds member variable packing information and adds it to the membersContainer
+        uint32_t BuildMemberLayout(Json::Value& membersContainer, string_view namePrefix, const IdentifierUID& memberId, const bool isArrayItr, const Options& options, const AZ::ShaderCompiler::Packing::Layout layoutPacking, uint32_t& offset) const;
+
+        //! Gets the stride for a resource view based on its generic type
+        uint32_t GetViewStride(const IdentifierUID& memberId, const AZ::ShaderCompiler::Packing::Layout& layoutPacking, const Options& options) const;
+
+        //! Gets the stride for a user defined type containing member variables
+        [[nodiscard]]
+        uint32_t BuildUserDefinedMemberLayout(Json::Value& membersContainer, const IdentifierUID& exportedTypeId, const Options& options, const AZ::ShaderCompiler::Packing::Layout layoutPacking, uint32_t& startAt, string_view namePrefix) const;
+
+        bool BuildIAElement(Json::Value& jsonVal, const IdentifierUID& uid, bool allowStruct) const;
+
+        bool BuildOMElement(Json::Value& jsonVal, const ExtendedTypeInfo& returnTypeRef, string_view semanticOverride, int& semanticIndex, bool systemValue) const;
+
+        bool BuildOMStruct(const ExtendedTypeInfo& returnTypeRef, string_view semanticOverride, Json::Value& jsonVal, int& semanticIndex) const;
+
+        using MapOfBeginToSpanAndUid = map<ssize_t, pair< misc::Interval, IdentifierUID> >;
+        //! Populate a list of functions where a symbol appear as potentially used
+        //! @param uid      The symbol to start the dependency analysis on
+        //! @param output   Any dependency symbol will be appended to this set
+        //! @scopes         Code reflecting structure storing a map of intervals of function scopes
+        //!                 The last parameter is for internal recursion tracking.
+        void DiscoverTopLevelFunctionDependencies(const IdentifierUID& uid, set<IdentifierUID>& output, const MapOfBeginToSpanAndUid& scopes, set<IdentifierUID>&&) const;
+
+        bool IsNonOverloaded(const IdentifierUID& uid) const;
+
+        bool IsPotentialEntryPoint(const IdentifierUID& uid) const;
+    };
+}

+ 80 - 0
src/AzslcScopeTracker.cpp

@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include "AzslcScopeTracker.h"
+
+namespace AZ::ShaderCompiler
+{
+    ScopeTracker::ScopeTracker(SymbolGetterFunctorT symbolFinder)
+        : m_symFinder{symbolFinder}
+    {
+        // global scope is represented as "/"
+        // and it is an invariant of this program that m_currentScopePath will never be empty (always at least "/")
+        EnterScope("/", 0);
+        assert(IsRooted(m_currentScopePath));
+    }
+
+    // variation that takes an absolute path in (used by entry into deported method definitions, they can jump scope)
+    void ScopeTracker::EnterScope(QualifiedNameView scopeName, ssize_t tokenStreamPosition)
+    {
+        m_oldScopePaths.push(m_currentScopePath);
+        m_currentScopePath = scopeName;
+        UpdateCurScopeUID();
+        // keep track of the scope begin:
+        m_scopeIntervals[m_currentScopeUID].a = tokenStreamPosition;
+        verboseCout << "Scope Entry. new current path is " << m_currentScopePath << azEndl;
+    }
+
+    // classic variation that takes a cumulative leaf
+    void ScopeTracker::EnterScope(string_view scopeName, ssize_t tokenStreamPosition)
+    {
+        auto accumulatedPath = JoinPath(m_currentScopePath, scopeName, JoinPolicy::EmptyMeansEmpty);
+        EnterScope(QualifiedNameView{accumulatedPath}, tokenStreamPosition);
+    }
+
+    void ScopeTracker::ExitScope(ssize_t tokenStreamPosition)
+    {
+        // keep track of the scope end token:
+        m_scopeIntervals[m_currentScopeUID].b = tokenStreamPosition;
+        m_currentScopePath = m_oldScopePaths.top();
+        m_oldScopePaths.pop();
+        verboseCout << "Exit scope. new current path is " << m_currentScopePath << azEndl;
+
+        UpdateCurScopeUID();
+    }
+
+    void ScopeTracker::UpdateCurScopeUID()
+    {
+        auto& [uid, Kind] = *m_symFinder(m_currentScopePath);
+        // ↓ in this case please make sure you register the scope identifier before calling EnterScope. yes it is a sequential coupling antipattern, sorry for now
+        assert(IsRooted(m_currentScopePath));
+        m_currentScopeUID = uid;
+    }
+
+    QualifiedNameView ScopeTracker::GetNameOfCurScope() const
+    {
+        return m_currentScopeUID.m_name;
+    }
+
+    QualifiedNameView ScopeTracker::GetNameOfCurParentScope() const
+    {
+        auto nameOfParentScope = QualifiedNameView{GetParentName(GetNameOfCurScope())};
+        return nameOfParentScope;
+    }
+
+    // For debugging
+    void ScopeTracker::DumpScopeIntervals() const
+    {
+        std::cout << "\nScopeTracker::DumpScopeIntervals:\n";
+        for (auto const& [symId, interval] : m_scopeIntervals)
+        {
+            std::cout << symId.GetName() << ": a=" << interval.a << ", b=" << interval.b << "\n";
+        }
+        std::cout << "\n";
+    }
+}

+ 46 - 0
src/AzslcScopeTracker.h

@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include "AzslcSymbolAggregator.h"
+
+namespace AZ::ShaderCompiler
+{
+    using SymbolGetterFunctorT = std::function< IdAndKind* (QualifiedNameView) >;
+
+    struct ScopeTracker
+    {
+        // please pass me a closure that has access to the symbol database, at construction. (RAII principle: no halfass initialized state)
+        explicit ScopeTracker(SymbolGetterFunctorT symbolFinder);
+
+        /// relative descent into a normal scope tree
+        void EnterScope(string_view scopeName, ssize_t tokenStreamPosition);
+
+        /// jump to unrelated scope
+        void EnterScope(QualifiedNameView scopeName, ssize_t tokenStreamPosition);
+
+        void ExitScope(ssize_t tokenStreamPosition);
+
+        void UpdateCurScopeUID();
+
+        // For debugging
+        void DumpScopeIntervals() const;
+
+        QualifiedNameView GetNameOfCurScope() const;
+
+        QualifiedNameView GetNameOfCurParentScope() const;
+
+        SymbolGetterFunctorT  m_symFinder;
+        QualifiedName         m_currentScopePath; // example: "/sisdk3/gfx/"
+        IdentifierUID         m_currentScopeUID;
+        stack<QualifiedName>  m_oldScopePaths;
+        // Store each scopes begin/end in the original source
+        using MapOfScopeIntervals = unordered_map<IdentifierUID, misc::Interval>;
+        MapOfScopeIntervals   m_scopeIntervals;
+    };
+}

+ 1969 - 0
src/AzslcSemanticOrchestrator.cpp

@@ -0,0 +1,1969 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include "AzslcSemanticOrchestrator.h"
+
+namespace AZ::ShaderCompiler
+{
+    namespace  // utility helpers to raise the abstraction level of SemanticOrchestrator's methods.
+    {
+        Packing::MatrixMajor ExtractMatrixMajorness(VarInfo& varInfo)
+        {
+            Packing::MatrixMajor major = Packing::MatrixMajor::Default;
+            if (varInfo.CheckHasStorageFlag(StorageFlag::RowMajor))
+            {
+                major = Packing::MatrixMajor::RowMajor;
+            }
+            else if (varInfo.CheckHasStorageFlag(StorageFlag::ColumnMajor))
+            {
+                major = Packing::MatrixMajor::ColumnMajor;
+            }
+            return major;
+        }
+
+        TypeQualifier ExtractTypeQualifiers(azslParser::StorageFlagsContext* flags, vector<string>* unknownQualifiers = nullptr)
+        {
+            TypeQualifier qualifiers;
+            for (auto* flagCtx : flags->storageFlag())
+            {
+                const auto& newFlag = AsFlag(flagCtx);
+                qualifiers |= newFlag;
+                if (newFlag == StorageFlag::Unknown && unknownQualifiers)
+                {
+                    unknownQualifiers->push_back(flagCtx->getText());
+                }
+            }
+            return qualifiers;
+        }
+
+        TypeQualifier ExtractTypeQualifiers(AstUnnamedVarDecl* ctx, vector<string>* unknownQualifiers = nullptr)
+        {
+            azslParser::StorageFlagsContext* flags = ExtractStorageFlagsFromVariableDeclarator(ctx);
+            return flags ? ExtractTypeQualifiers(flags, unknownQualifiers) : TypeQualifier{};
+        }
+    }
+
+    SemanticOrchestrator::SemanticOrchestrator(SymbolAggregator* sema, ScopeTracker* scope, azslLexer* lexer,
+        PreprocessorLineDirectiveFinder* preprocessorLineDirectiveFinder /*= nullptr*/)
+        : m_symbols{ sema },
+          m_scope{ scope },
+          m_lexer{ lexer },
+          m_anonymousCounter{ 0 },
+          m_preprocessorLineDirectiveFinder { preprocessorLineDirectiveFinder }
+    {
+        assert(sema != nullptr && scope != nullptr);
+    }
+
+    IdAndKind& SemanticOrchestrator::GetCurrentScopeIdAndKind()
+    {
+        auto nameOfScope = m_scope->GetNameOfCurScope();
+        auto* idAndKindPtr = m_symbols->GetIdAndKindInfo(nameOfScope);
+        if (!idAndKindPtr)
+        {
+            throw std::logic_error("Internal error: current scope not registered");
+        }
+        return *idAndKindPtr;
+    }
+
+    // Note that to factorize code between above and below, one could pass a runtime (or template integer), N-levels up as an argument.
+    IdAndKind& SemanticOrchestrator::GetCurrentParentScopeIdAndKind()
+    {
+        auto nameOfParentScope = m_scope->GetNameOfCurParentScope();
+        auto idAndKindPtr = m_symbols->GetIdAndKindInfo(nameOfParentScope);
+        if (!idAndKindPtr)
+        {   // even parent scope will invariantly be registered. and the Levelup of '/' is '/'
+            throw std::logic_error("Internal error: parent scope not registered");
+        }
+        return *idAndKindPtr;
+    }
+
+    // special high level entry of "RegisterFunction" feature but with pre-treatments related to scope resolution and semantic checks specifics to methods
+    IdAndKind& SemanticOrchestrator::RegisterDeportedMethod(UnqualifiedNameView uqName, azslParser::UserDefinedTypeContext* className, AstFuncSig* ctx)
+    {
+        // extract the type name from the type rule. remember that it can be a reference to an existing UDT, or the inline definition of a new UDT.
+        //  note: in the latter case, the syntax is contrived but valid (surprising for C++/java people)
+        //        e.g.   bool struct MyS{}::Method() {return true;}
+        //        however, the semantics of that expression are not supported. And that's because "struct MyS" is visited
+        //        AFTER the method registration, so MyS doesn't exist as a registered symbol yet.
+        ExtractedComposedType extracted = ExtractComposedTypeNamesFromAstContext(className);
+        verboseCout << ctx->start->getLine() << ": register method: " << className->getText() << "::" << uqName << "\n";
+        // lookup for the symbol of the extracted scope
+        IdAndKind* scopeIdKind = LookupSymbol(extracted.m_core.m_name);
+        if (!scopeIdKind)
+        {
+            ThrowAzslcOrchestratorException(ORCHESTRATOR_SCOPE_NOT_FOUND,
+                ctx->start, ConcatString("scope ", extracted.m_core.m_name,
+                    " for method ", uqName, " not found"));
+        }
+        auto [scopeUid, scopeKind] = *scopeIdKind;
+        if (!scopeKind.IsKindOneOf(Kind::Class, Kind::ShaderResourceGroup))
+        {
+            ThrowAzslcOrchestratorException(ORCHESTRATOR_DEPORTED_METHOD_DEFINITION,
+                ctx->start, "Only class and SRG may have deported method definitions");
+        }
+        IdentifierUID holdingScope = GetCurrentScopeIdAndKind().first;
+        if (! (holdingScope.m_name == "/" || holdingScope == scopeUid))
+        {
+            ThrowAzslcOrchestratorException(ORCHESTRATOR_DEFINITION_FOREIGN_SCOPE,
+                ctx->start, ConcatString("definition of (", uqName,
+            ") method of ", scopeUid.m_name, " in foreign scope ", holdingScope.m_name, " is forbidden"));
+        }
+        // between function name and the entrance into the function scope, the class scope is briefly activated.
+        // this way, parameters may be specified as if in the scope of their holding function. e.g `ObjectOfCurrentScope MyClass::MyMethod(ObjectOfMyClass)`
+        m_scope->EnterScope(scopeUid.GetName(), ctx->Name->getTokenIndex());
+        // now that we're in scope, we can establish the decorated (leaf) identity of this function:
+        auto decoratedUqName = UnqualifiedName{ConcatString(uqName, CreateDecorationOfFunction(ctx->functionParams()))};
+
+        bool scopeIsClass = scopeKind.GetKind() == Kind::Class;
+        // now let's check if that method was pre-declared in the class/SRG:
+        optional<IdentifierUID> optionalIdentifier = scopeIsClass ? scopeKind.GetSubRefAs<ClassInfo>().FindMemberFromLeafName(decoratedUqName)
+                                                                  : scopeKind.GetSubRefAs<SRGInfo>().FindMemberFromLeafName(decoratedUqName);
+        // note that if the method is not found, we could accept to add it anyway to provide an extension-method feature a la C#.
+        // it would be great to require an attribute or a keyword for that though (like [[extends]])
+        // for now, it will be forbidden to inject methods in classes from outside.
+        if (!optionalIdentifier)
+        {
+            ThrowAzslcOrchestratorException(ORCHESTRATOR_NO_DECLERATION, ctx->start,
+                ConcatString((scopeIsClass ? "class " : "SRG "), scopeUid.m_name, " doesn't have a declaration for ", decoratedUqName));
+        }
+        // verify also the kind of the member
+        auto* originalDeclarationAsFunc = m_symbols->GetAsSub<FunctionInfo>(*optionalIdentifier);
+        if (!originalDeclarationAsFunc)
+        {
+            Kind realKindOfOriginallyDeclaredMember = m_symbols->GetIdAndKindInfo(optionalIdentifier->GetName())->second.GetKind();
+            ThrowAzslcOrchestratorException(ORCHESTRATOR_UNEXPECTED_KIND,
+                ctx->start, ConcatString((scopeIsClass ? "class " : "SRG "), scopeUid.m_name,
+                     " holds a member ", optionalIdentifier->m_name,
+                     " but it is of kind ", string{ Kind::ToStr(realKindOfOriginallyDeclaredMember) },
+                     " instead of expected ", string{ Kind::ToStr(Kind::Function) }));
+        }
+        // now we're good.
+        // merge the type and the method name and call the classic register. RegisterFunction is going to re-run the decoration so just pass the naked name.
+        string nakedJoined = JoinPath(scopeUid.GetName(), uqName, JoinPolicy::EmptyMeansRoot);
+        return RegisterFunction(QualifiedNameView{nakedJoined}, ctx, AsFunc::Definition);
+    }
+
+    // unqualified-name (UQN) taking version. (expect a relative name to current scope)
+    IdAndKind& SemanticOrchestrator::RegisterFunction(UnqualifiedNameView name, AstFuncSig* ctx, AsFunc statementGenre)
+    {
+        auto fqName = MakeFullyQualified(name);
+        return RegisterFunction(fqName, ctx, statementGenre);
+    }
+
+    string SemanticOrchestrator::CreateDecorationOfFunction(azslParser::FunctionParamsContext* parametersContext) const
+    {
+        if (parametersContext == nullptr || parametersContext->Void())
+        {
+            return "()";
+        }
+        vector<QualifiedName> typeList;
+        auto vectorOfFunctionParams = parametersContext->functionParam();
+        typeList.reserve(vectorOfFunctionParams.size());
+        // transform the collection into looked-up type names
+        for (auto functionParamContext : vectorOfFunctionParams)
+        {
+            IdentifierUID paramType = LookupType(functionParamContext->type()); // [TODO-GFX][ATOM2627]: change this to CreateExtendedTypeInfo
+            typeList.push_back(paramType.GetName());
+        }
+        return ::AZ::ShaderCompiler::CreateDecorationOfFunction(typeList.begin(), typeList.end());
+    }
+
+    QualifiedName SemanticOrchestrator::CreateDecoratedIdentityOfFunction(QualifiedNameView name, azslParser::FunctionParamsContext* parametersContext) const
+    {
+        return QualifiedName{ConcatString(name, CreateDecorationOfFunction(parametersContext))};
+    }
+
+    // qualified-name (FQN) taking version. (pre-resolved scope)
+    IdAndKind& SemanticOrchestrator::RegisterFunction(QualifiedNameView fqUndecoratedName, AstFuncSig* ctx, AsFunc statementGenre)
+    {
+        // parameter validation: check the claim of the caller
+        assert((statementGenre == AsFunc::Declaration && Is<azslParser::HlslFunctionDeclarationContext*>(ctx->parent))
+            || (statementGenre == AsFunc::Definition  && Is<azslParser::HlslFunctionDefinitionContext*>(ctx->parent)));
+
+        auto line = ctx->Name->getLine();
+        verboseCout << line << ": register func: " << fqUndecoratedName;
+
+        // `/f` is undecorated. `/f(?int)` is decorated
+        QualifiedName decoratedName = CreateDecoratedIdentityOfFunction(fqUndecoratedName, ctx->functionParams());
+
+        verboseCout << " full identity: " << decoratedName << "\n";
+
+        // validation
+        bool isScopeCompositeType = IsScopeStructClassInterface();
+        if (statementGenre == AsFunc::Declaration && ctx->ClassName)
+        {
+            if (isScopeCompositeType)
+            {
+                ThrowAzslcOrchestratorException(ORCHESTRATOR_OVERLY_QUALIFIED, ctx->Name,
+                    ConcatString(ctx->getText(), " is overly qualified. In-class declarations spawn new identifiers, and don't have to refer to existing symbols."));
+            }
+            else
+            {
+                ThrowAzslcOrchestratorException(ORCHESTRATOR_DEPORTED_METHOD, ctx->Name,
+                    ConcatString(ctx->getText(), "is a deported method declaration, which is considered ill-formed. You can make it a definition (with a body), or delete that statement."));
+            }
+        }
+        IdAndKind* symbol = m_symbols->GetIdAndKindInfo(decoratedName);
+        auto* funcInfo = symbol ? symbol->second.GetSubAs<FunctionInfo>() : nullptr;
+
+        bool alreadyDeclared = !!symbol;
+        bool alreadyDefined = funcInfo ? !!funcInfo->m_defNode : false;
+        if (!alreadyDeclared)  // brand new function
+        {
+            symbol = &m_symbols->AddIdentifier(decoratedName, Kind::Function, line);
+            // prepare a virgin subinfo
+            funcInfo = &symbol->second.GetSubAfterInitAs<Kind::Function>();
+        }
+        else
+        {
+            if (statementGenre == AsFunc::Declaration)
+            {
+                PrintWarning(Warn::W1, ctx->start, "ignored redundant redeclaration of function ", decoratedName,
+                             ", ", GetFirstSeenLineMessage(symbol->second));
+                return *symbol;
+            }
+            auto originalKind = symbol->second.GetKind();
+            if (originalKind != Kind::Function)  // verify that we're not transforming a lychee into a melon
+            {
+                ThrowRedeclarationAsDifferentKind(decoratedName, Kind::Function, symbol->second, line);
+            }
+
+            if (alreadyDefined)  // verify that it's not a second definition
+            {
+                ThrowAzslcOrchestratorException(ORCHESTRATOR_FUNCTION_ALREADY_DEFINED, ctx->Name,
+                    ConcatString("One Definition Rule: function ", symbol->first.m_name,
+                         " is already defined ", GetFirstSeenLineMessage(symbol->second)));
+            }
+
+            // this function was already declared before. (like a forward)
+            // imagine this scenario:
+            //   void f(int i);
+            //
+            //   void f(int num) {...}
+            //
+            // If we do nothing, the symbol table will have "f, f/i and f/num" but f/i will be unusable.
+            // and worse, f will register two parameters i and num. (because by chance they have different names)
+            // Also, recall that
+            //   void f(int);
+            // Is a legal declaration, arguments don't need to be named, and the declarative functionality is still the same.
+            // so for canonicalization we can consider that we never saw the arguments in declarations; but only as soon as we see a definition.
+            // if we never store them, we won't be able to emit the declaration in the backend if it is the only thing we ever see.
+            for (auto dependent : symbol->second.GetSubAs<FunctionInfo>()->GetParameters(true))
+            {   // delete AST-dependents (children) : the arguments.
+                if (!dependent.m_varId.IsEmpty())
+                {
+                    m_symbols->DeleteIdentifier(dependent.m_varId);
+                }
+            }
+            // don't delete the old symbol because it has the seenat table, important to keep track of all occurrences of forward declarations.
+            // just delete references to the deleted parameters.
+            symbol->second.GetSubRefAs<FunctionInfo>().StashParameters();
+            // also some attributes are only parsed at declaration, like override, or static.
+            // and inversely, HLSL semantics are only considered at the definition site.
+
+            // push a second apparition record in the ordered elastic symbol list
+            // this way, the emitter can emit 2 entities: a declaration at first apparition, and the definition on the second apparition
+            m_symbols->m_elastic.m_order.push_back(symbol->first);
+        }
+        // decompose the Id and Kind of this function
+        auto& [newUID, newKind] = *symbol;
+
+        // Add storage flags
+        funcInfo->m_typeQualifier |= ExtractTypeQualifiers(ctx->storageFlags());
+        CheckQualifersAreOnlyInlineOrStatic(funcInfo->m_typeQualifier, line); // throws a diagnostic if needed
+
+        // keep track of original AST node
+        if (statementGenre == AsFunc::Definition)
+        {
+            funcInfo->m_defNode = ctx;
+        }
+        if (!funcInfo->m_declNode)
+        {
+            funcInfo->m_declNode = ctx;
+        }
+        // OR fusion between decl and def sites
+        funcInfo->m_mustOverride = funcInfo->m_mustOverride || ctx->Override() != nullptr;
+        // return types must match (between redeclaration of this concrete function)
+        ExtendedTypeInfo returnType = CreateExtendedTypeInfo(ctx->functionType(), {}, Packing::MatrixMajor::Default);
+        if (alreadyDeclared && funcInfo->m_returnType != returnType)
+        {
+            ThrowAzslcOrchestratorException(ORCHESTRATOR_FUNCTION_INCONSISTENT_RETURN_TYPE, ctx->functionType()->start,
+                                             ConcatString("function definition ",  decoratedName, ", ",
+                                                          GetFirstSeenLineMessage(symbol->second), ", had a different return type: ",
+                                                          funcInfo->m_returnType.GetDisplayName(), ", versus now seen: ", returnType.GetDisplayName()));
+        }
+        funcInfo->m_returnType = returnType;
+        assert(!funcInfo->m_returnType.IsEmpty());
+        if (!funcInfo->m_returnType.IsClassFound())
+        {
+            PrintWarning(Warn::W2, ctx->functionType()->start, "return type ", ctx->functionType()->getText(), " not understood.",
+                         " (for function ", decoratedName, ")");
+        }
+
+        // try to fetch the overload-set:
+        IdAndKind* overloadSetIdKind = m_symbols->GetIdAndKindInfo(fqUndecoratedName);
+        OverloadSetInfo* overloadSet = overloadSetIdKind ? overloadSetIdKind->second.GetSubAs<OverloadSetInfo>() : nullptr;
+        if (!overloadSetIdKind)  // don't exist yet. it must be the first occurrence of this function's core name.
+        {
+            // create and prepare a brand new overload-set
+            overloadSetIdKind = &m_symbols->AddIdentifier(fqUndecoratedName, Kind::OverloadSet, line);
+            overloadSet = overloadSetIdKind->second.GetSubAs<OverloadSetInfo>();
+            overloadSet->SetSetName(overloadSetIdKind->first); // set own id on it for logical independence (decoupling) in the methods of this object
+        }
+        // add this concrete function occurrence to the overload-set:
+        overloadSet->PushConcreteFunction(newUID, returnType);
+
+        // don't register the parameters here because of two reasons:
+        //  - the listener will naturally enter variableDeclarator rule which calls RegisterVar
+        //  - the scope needs to be the function scope, and we will enter the scope only after function registration
+        if (isScopeCompositeType)    // vocabulary reminder: composite-type = product or sum type = class/struct/union/interface/enum
+        {   // we are now in a class-kind scope -> so this function is a method
+            funcInfo->m_isMethod = true;
+            // access the class kindinfo and add a member:
+            auto& classInfo = GetCurrentScopeSubInfoAs<ClassInfo>();
+            classInfo.PushMember(newUID, Kind::Function);
+            if (classInfo.m_kind == Kind::Interface)
+            {
+                funcInfo->m_isVirtual = true;
+            }
+        }
+        else if (GetCurrentScopeIdAndKind().second.GetKind() == Kind::ShaderResourceGroup)
+        {
+            auto& srgInfo = GetCurrentScopeSubInfoAs<SRGInfo>();
+            srgInfo.m_functions.push_back(newUID);
+        }
+        return *symbol;
+    }
+
+    IdAndKind& SemanticOrchestrator::RegisterFunctionDeclarationAndAddSeenat(UnqualifiedNameView uqName, AstFuncSig* signature)
+    {
+        // join scope and uqName (no unqualified lookup here, declarations are authoritative in scope):
+        auto qName = MakeFullyQualified(uqName);
+        // check existence, because re-declarations are innocent and should not take priority over ones higher up.
+        // especially definition, which must be the winning occurrence for registration of identifiers.
+        IdAndKind& symbol = RegisterFunction(uqName, signature, AsFunc::Declaration);
+        // register a seenat to avoid missing redeclarations as references, because they don't trigger the idExpression rule.
+        // (because a new declaration is always a direct Identifier lexer token)
+        // a function's reference is incarnated by its name. like its call sites.
+        auto location = MakeTokensLocation(signature->Name);
+        RegisterSeenat(symbol, location);
+        return symbol;
+    }
+
+    void SemanticOrchestrator::RegisterEnumerator(azslParser::EnumeratorDeclaratorContext* ctx)
+    {
+        auto* enumDefinitionCtx = As<azslParser::EnumDefinitionContext*>(ctx->parent->parent);
+        bool isScopedEnum = Is<azslParser::ScopedEnumContext*>(enumDefinitionCtx->enumKey());
+        UnqualifiedName parentName{(enumDefinitionCtx)->Name->getText()};
+        QualifiedName enumQn;
+        // reconstruct the identifier of the enumInfo (since current scope may or may not be it, we can't rely on it)
+        if (isScopedEnum)
+        {
+            enumQn = GetCurrentScopeIdAndKind().first.m_name;  // the current scope IS the enum
+            assert(enumQn == QualifiedName{JoinPath(m_scope->GetNameOfCurParentScope(), parentName)});  // verify that we can reconstruct it
+        }
+        else
+        {
+            enumQn = MakeFullyQualified(parentName); // uses current scope (because the current scope encloses the enum)
+        }
+        auto& [enumId, parentKindInfo] = *m_symbols->GetIdAndKindInfo(enumQn);
+        auto& enumInfo = parentKindInfo.GetSubRefAs<ClassInfo>();
+
+        size_t line           = ctx->Name->getLine();
+        auto enumeratorName   = UnqualifiedName{ctx->Name->getText()};
+        auto& [uid, var]      = AddIdentifier(enumeratorName, Kind::Variable, line);
+        auto& varInfo         = var.GetSubAfterInitAs<Kind::Variable>();
+        if (ctx->Value)
+        {
+            varInfo.m_constVal = FoldEvalStaticConstExprNumericValue(ctx->Value);
+        }
+        varInfo.m_declNode    = nullptr;
+        varInfo.m_typeQualifier |= StorageFlag::Static;
+        varInfo.m_typeQualifier |= StorageFlag::Const;
+        varInfo.m_typeQualifier |= StorageFlag::Enumerator;
+
+        varInfo.m_typeInfoExt = ExtendedTypeInfo{CreateTypeRefInfo(UnqualifiedNameView{enumQn}, OnNotFoundOrWrongKind::Diagnose),
+                                                 {}, {}, {}, Packing::MatrixMajor::Default };
+        enumInfo.PushMember(uid, Kind::Variable);
+    }
+
+    void SemanticOrchestrator::RegisterSRGSemanticMember(AstSRGSemanticMemberDeclNode* ctx)
+    {
+        // access SRGSemanticInfo and add a member:
+        auto& srgSemanticInfo = GetCurrentScopeSubInfoAs<ClassInfo>();
+
+        if (ctx->Frequency && ctx->FrequencyValue)
+        {
+            if (auto* intLit = ctx->FrequencyValue->IntegerLiteral())
+            {
+                size_t line           = ctx->Frequency->getLine();
+                auto frequencyId      = ctx->Frequency->getText();
+                auto uqNameView       = UnqualifiedNameView{ frequencyId };
+                auto& [uid, var]      = AddIdentifier(uqNameView, Kind::Variable, line);
+                auto& varInfo         = var.GetSubAfterInitAs<Kind::Variable>();
+                varInfo.m_constVal    = FoldEvalStaticConstExprNumericValue(intLit);
+                varInfo.m_declNode    = nullptr;
+                varInfo.m_typeInfoExt = ExtendedTypeInfo{CreateTypeRefInfo(UnqualifiedNameView{"int"}, OnNotFoundOrWrongKind::Diagnose),
+                                                         {}, {}, {}, Packing::MatrixMajor::Default };
+                srgSemanticInfo.PushMember(uid, Kind::Variable);
+            }
+        }
+        else if (ctx->VariantFallback && ctx->VariantFallbackValue)
+        {
+            if (auto* intLit = ctx->VariantFallbackValue->IntegerLiteral())
+            {
+                size_t line           = ctx->VariantFallback->getLine();
+                auto variantFallback  = ctx->VariantFallback->getText();
+                auto uqNameView       = UnqualifiedNameView{ variantFallback };
+                auto& [uid, var]      = AddIdentifier(uqNameView, Kind::Variable, line);
+                auto& varInfo         = var.GetSubAfterInitAs<Kind::Variable>();
+                varInfo.m_constVal    = FoldEvalStaticConstExprNumericValue(intLit);
+                varInfo.m_declNode    = nullptr;
+                varInfo.m_typeInfoExt = ExtendedTypeInfo{CreateTypeRefInfo(UnqualifiedNameView{"int"}, OnNotFoundOrWrongKind::Diagnose),
+                                                         {}, {}, {}, Packing::MatrixMajor::Default };
+                srgSemanticInfo.PushMember(uid, Kind::Variable);
+            }
+        }
+    }
+
+    IdAndKind& SemanticOrchestrator::RegisterTypeAlias(string_view newIdentifier, AstFuncType* existingTypeCtx, azslParser::TypeAliasingDefinitionStatementContext* ctx)
+    {
+        UnqualifiedNameView newId { newIdentifier };
+        auto& idKind = AddIdentifier(newId, Kind::TypeAlias, ctx->start->getLine());
+        auto& [uid, kinfo]  = idKind;
+        TypeAliasInfo& aliasInfo  = kinfo.GetSubAfterInitAs<Kind::TypeAlias>();
+        aliasInfo.m_declNode      = ctx;
+        aliasInfo.m_canonicalType = CreateExtendedTypeInfo(existingTypeCtx, {}, Packing::MatrixMajor::Default);
+        if (!aliasInfo.m_canonicalType.IsClassFound())
+        {
+            ThrowAzslcOrchestratorException(ORCHESTRATOR_INVALID_TYPEALIAS_TARGET,
+                                             existingTypeCtx->start,
+                                             ConcatString("target type ", existingTypeCtx->getText() + " not understood in typealias expression"));
+        }
+        assert(aliasInfo.m_canonicalType.m_coreType.m_typeClass != TypeClass::Alias);
+        // further registration in containing scopes
+        auto& [curScopeId, curScopeKind] = GetCurrentScopeIdAndKind();
+        if (curScopeKind.IsKindOneOf(Kind::Struct, Kind::Class))
+        {   // we are now in a struct/class-kind scope -> so this typealias is a member object.
+            // access the class kindinfo and add a member:
+            // future: check protocol validation with associatedtype
+            auto& classInfo = GetCurrentScopeSubInfoAs<ClassInfo>();
+            classInfo.PushMember(uid, Kind::TypeAlias);
+        }
+        return idKind;
+    }
+
+    IdAndKind& SemanticOrchestrator::RegisterSRGSemantic(AstSRGSemanticDeclNode* ctx)
+    {
+        return RegisterStructuredType(ctx, Kind::ShaderResourceGroupSemantic);
+    }
+
+    IdAndKind& SemanticOrchestrator::RegisterInterface(AstInterfaceDeclNode* ctx)
+    {
+        return RegisterStructuredType(ctx, Kind::Interface);
+    }
+
+    IdAndKind& SemanticOrchestrator::RegisterClass(AstClassDeclNode* ctx)
+    {
+        return RegisterStructuredType(ctx, Kind::Class);
+    }
+
+    IdAndKind& SemanticOrchestrator::RegisterStruct(AstStructDeclNode* ctx)
+    {
+        return RegisterStructuredType(ctx, Kind::Struct);
+    }
+
+    IdAndKind& SemanticOrchestrator::RegisterEnum(AstEnumDeclNode* ctx)
+    {
+        return RegisterStructuredType(ctx, Kind::Enum);
+    }
+
+    IdAndKind* SemanticOrchestrator::RegisterVar(Token* nameIdentifier, AstUnnamedVarDecl* ctx)
+    {
+        azslParser::FunctionParamContext* paramCtx = nullptr;
+        auto typeCtx           = ExtractTypeFromVariableDeclarator(ctx, &paramCtx);
+        auto&& idText          = nameIdentifier->getText();
+        size_t line            = nameIdentifier->getLine();
+        const string verboseMessage = ConcatString(line, ": var decl: ", idText, "\n");
+        verboseCout << verboseMessage;
+        auto uqNameView        = UnqualifiedNameView{idText};
+        auto& varSymbol        = AddIdentifier(uqNameView, Kind::Variable, line);
+        auto& [uid, info]      = varSymbol;
+        // now fillup what we can about that variable in the IR:
+        VarInfo& varInfo       = info.GetSubRefAs<VarInfo>();
+        // discover the storage flags:
+        varInfo.m_typeQualifier = ExtractTypeQualifiers(ctx, &varInfo.m_unknownQualifiers);
+        varInfo.m_declNode   = ctx;
+        varInfo.m_identifier = uqNameView;
+        // discover array dimensions
+        ArrayDimensions arrayDims;
+        TryFoldArrayDimensions(ctx, arrayDims);
+        // discover matrix majorness
+        Packing::MatrixMajor major = ExtractMatrixMajorness(varInfo);
+        // finally make the structure to hold all type information from the type context (will lookup/resolve type/typeof and compose the data)
+        varInfo.m_typeInfoExt = CreateExtendedTypeInfo(typeCtx, arrayDims, major);
+        assert(!varInfo.m_typeInfoExt.IsEmpty());
+        if (!varInfo.m_typeInfoExt.IsClassFound())
+        {
+            PrintWarning(Warn::W2, typeCtx->start, "variable type ", typeCtx->getText(), " not understood.",
+                         " (for variable ", idText, ")");
+        }
+        if (varInfo.GetTypeRefInfo().IsInputAttachment(m_lexer))
+        {
+            if (!varInfo.GetGenericParameterTypeId().IsEmpty()
+                && varInfo.GetGenericParameterTypeId().GetNameLeaf() != "float4")
+            {
+                PrintWarning(Warn::W1, typeCtx->start, typeCtx->getText(), " only float4 is supported on SubpassInput. Mutated to implicit float4 form.");
+            }
+            // erase the generic type, until the SubpassInputStub type can be templated:
+            varInfo.m_typeInfoExt.m_genericParameter = TypeRefInfo{};
+            m_subpassInputSeen = true;
+        }
+        // get enclosing scope:
+        auto& [curScopeId, curScopeKind] = GetCurrentScopeIdAndKind();
+        bool enclosedBySRG = curScopeKind.GetKind() == Kind::ShaderResourceGroup;
+
+        assert(!curScopeKind.IsKindOneOf(Kind::Enum)); // should use RegisterEnumerator
+
+        // Some semantic checks
+        if (varInfo.CheckHasStorageFlag(StorageFlag::Inline))
+        {
+            ThrowAzslcOrchestratorException(ORCHESTRATOR_INVALID_INLINED_QUALIFIER, ctx->start,
+                                             "inline qualification on variables is ill-formed");
+        }
+        bool global = curScopeId.GetName() == "/";
+        bool isOption = varInfo.CheckHasStorageFlag(StorageFlag::Option);
+        bool isRootconstant = varInfo.CheckHasStorageFlag(StorageFlag::Rootconstant);
+        bool hasExplicitLocalFlag = varInfo.CheckHasAnyStorageFlags({ StorageFlag::Static, StorageFlag::Groupshared });
+
+        if (isRootconstant || isOption)
+        {
+            if (arrayDims.IsArray())
+            {
+                ThrowAzslcOrchestratorException(ORCHESTRATOR_INVALID_NONGLOBAL_OPTION_OR_ROOTCONSTANT, ctx->start,
+                    "arrays can not be declared as rootconstants.");
+            }
+            if (!global)
+            {
+                ThrowAzslcOrchestratorException(ORCHESTRATOR_INVALID_NONGLOBAL_OPTION_OR_ROOTCONSTANT, ctx->start,
+                                                 "rootconstant or option qualifier is only accepted at top-level scope");
+            }
+            if (hasExplicitLocalFlag)
+            {
+                ThrowAzslcOrchestratorException(ORCHESTRATOR_INVALID_QUALIFIER_MIX, ctx->start,
+                                                 "static, groupshared qualifiers cannot be used with the rootconstant or option qualifier");
+            }
+            if (varInfo.CheckHasStorageFlag(StorageFlag::Const))
+            {
+                PrintWarning(Warn::W2, ctx->start, "const ignored in conjunction with rootconstant or option (because already immutable).");
+                varInfo.m_typeQualifier &= StorageFlag::EnumType(~StorageFlag::Const);
+            }
+        }
+
+        if (isOption)
+        {
+            // we'll set that here, an option is better flagged as static const for simplicity during emission
+            varInfo.m_typeQualifier |= StorageFlag::Static;
+            varInfo.m_typeQualifier |= StorageFlag::Const;
+            // we don't do the same for rootconstant because they exist through a ConstantBuffer<STRUCT>
+        }
+
+        TypeClass typeClass = varInfo.GetTypeClass();
+        if (typeClass == TypeClass::Sampler)
+        {
+            // let's extract any potential sampler information
+            varInfo.m_samplerState = ExtractSamplerState(ctx->variableInitializer());
+
+            // Emit a warning to the user in case we have upgraded the Sampler to ComparisonSampler implicitly
+            bool declaredAsSamplerComparison = TypeIsSamplerComparisonState(ctx);
+            if (varInfo.m_samplerState->m_isComparison && !declaredAsSamplerComparison)
+            {
+                PrintWarning(Warn::W3, ctx->start, "Comparison function found, sampler will be upgraded to SamplerComparisonState.\n",
+                             "Please use SamplerComparisonState if the use is intended, or remove the comparison function if not.");
+            }
+            varInfo.m_samplerState->m_isComparison |= declaredAsSamplerComparison;
+        }
+
+        if (ctx->packOffsetNode())
+        {
+            PrintWarning(Warn::W1, ctx->packOffsetNode()->start, "packoffset information ignored");
+        }
+
+        bool parentIsFuncDecl = IsParentRuleAFunctionDeclaration(paramCtx);
+        bool parentIsFuncDef  = IsParentRuleAFunctionDefinition(paramCtx);
+        if (parentIsFuncDef || parentIsFuncDecl)
+        {
+            // We need to register each newly registered parameter variable ID, in the list of the function subinfo too:
+            auto& funcSub = GetCurrentScopeSubInfoAs<FunctionInfo>();
+            funcSub.PushParameter(uid, varInfo.m_typeInfoExt, varInfo.m_typeQualifier, varInfo.m_declNode->ArrayRankSpecifiers, ctx->variableInitializer());
+        }
+
+        bool isExtern = !varInfo.StorageFlagIsLocalLinkage(global || enclosedBySRG);
+        bool dxilLibraryFlagType = typeClass == TypeClass::LibrarySubobject;  // No need to check any semantic for subobjects. They just enrich the dxil metadata but don't participate in the program.
+        if (isExtern && !dxilLibraryFlagType)
+        {
+            if (global && !varInfo.CheckHasStorageFlag(StorageFlag::Rootconstant))
+            {
+                ThrowAzslcOrchestratorException(ORCHESTRATOR_ILLEGAL_GLOBAL_VARIABLE, nameIdentifier,
+                    ConcatString(Decorate("'", idText), " extern global variables are ill-formed in AZSL. You might want an internal variable (static or groupshared), a rootconstant, an option, or to put your resource in a ShaderResourceGroup.") );
+            }
+            if (HasStandardInitializer(ctx))
+            {
+                ThrowAzslcOrchestratorException(ORCHESTRATOR_EXTERNAL_VARIABLE_WITH_INITIALIZER, nameIdentifier,
+                    ConcatString(Decorate("'", idText), " extern variables can't be initialized from the shader side, since their values are set by bindings.") );
+            }
+        }
+
+        bool isStaticConst = varInfo.CheckHasAllStorageFlags({ StorageFlag::Static, StorageFlag::Const });
+        if (curScopeKind.IsKindOneOf(Kind::Struct, Kind::Class))
+        {   // we are now in a struct/class-kind scope -> so this variable is a member object.
+            // access the class kindinfo and add a member:
+            auto& classInfo = GetCurrentScopeSubInfoAs<ClassInfo>();
+            classInfo.PushMember(uid, Kind::Variable);
+
+            if (HasStandardInitializer(ctx) && !isStaticConst)
+            {
+                ThrowAzslcOrchestratorException( ORCHESTRATOR_MEMBER_VARIABLE_WITH_INITIALIZER, nameIdentifier,
+                    ConcatString(idText, " default-member-initializers are not supported.") );
+            }
+        }
+        if (curScopeKind.GetKind() == Kind::Interface)
+        {
+            // this is an impossible case because the parser doesn't accept these constructs.
+            // but let's say one day we have an API that allows constructing AST programmatically.
+            ThrowAzslcOrchestratorException(ORCHESTRATOR_ILLEGAL_MEMBER_VARIABLE_IN_INTERFACE,
+                nameIdentifier, "member variables in interfaces are forbidden.");
+        }
+        if (enclosedBySRG)
+        {
+            FillOutSrgField(polymorphic_downcast<AstNamedVarDecl*>(ctx->parent), varInfo, uid, arrayDims);
+        }
+        // attempt some level of constant folding from initializers:
+        //  note to maintainers: do NOT try to avoid bloat in the verbose stream, by protecting this in `if (ctx->variableInitializer())`
+        //                       it will result in the "static no-init-assignment zero initialization" case being <failed> instead of 0.
+        varInfo.m_constVal = FoldEvalStaticConstExprNumericValue(varInfo);
+        return &varSymbol;
+    }
+
+    void SemanticOrchestrator::RegisterNamelessFunctionParameter(azslParser::FunctionParamContext* ctx)
+    {
+        TypeQualifier typeQualifier = ExtractTypeQualifiers(ctx->storageFlags());
+        CheckQualifersAreOnlyInlineOrStatic(typeQualifier, ctx->start->getLine());  // throws a diagnostic if needed
+
+        ArrayDimensions arrayDims;
+        TryFoldArrayDimensions(ctx->unnamedVariableDeclarator(), arrayDims);
+        auto paramType = CreateExtendedTypeInfo(ctx->type(), arrayDims, Packing::MatrixMajor::Default);
+        GetCurrentScopeSubInfoAs<FunctionInfo>().PushParameter({}, paramType, typeQualifier, ctx->unnamedVariableDeclarator()->ArrayRankSpecifiers, ctx->unnamedVariableDeclarator()->variableInitializer());
+    }
+
+    // Helper to avoid code redundancy for a message that is used in three different places.
+    static string GetNonEasyToFoldMessage(const ArrayDimensions& arrayDims)
+    {
+        return ConcatString("array dimensions must be an easy-to-fold build time constant ( ",
+            arrayDims.ToString(),
+            " ) in external resource declaration");
+    }
+
+    void SemanticOrchestrator::FillOutSrgField(AstNamedVarDecl* ctx, VarInfo& varInfo, IdentifierUID varUid, ArrayDimensions& arrayDims)
+    {
+        const bool isUnboundedArray = arrayDims.IsUnbounded();
+        if (!isUnboundedArray && !arrayDims.AreAllDimsFullyConstantFolded())
+        {
+            ThrowAzslcOrchestratorException(ORCHESTRATOR_ILLEGAL_FOLDABLE_ARRAY_DIMENSIONS, ctx->start
+                , GetNonEasyToFoldMessage(arrayDims));
+        }
+
+        auto& [srgUid, srgKind] = GetCurrentScopeIdAndKind();
+        auto& srgInfo = srgKind.GetSubRefAs<SRGInfo>();
+
+        TypeClass typeClass = varInfo.GetTypeClass();
+        assert(typeClass != TypeClass::Alias);
+
+        string errorMessage;
+        if (!m_unboundedArraysValidator.CheckFieldCanBeAddedToSrg(isUnboundedArray, srgUid, srgInfo, varUid, varInfo, typeClass, &errorMessage))
+        {
+            ThrowAzslcOrchestratorException(ORCHESTRATOR_UNBOUNDED_RESOURCE_ISSUE, ctx->start, errorMessage);
+        }
+
+        if (typeClass == TypeClass::ConstantBuffer)
+        {
+            srgInfo.m_CBs.push_back(varUid);
+        }
+        else if (typeClass == TypeClass::Sampler)
+        {
+            srgInfo.m_samplers.push_back(varUid);
+        }
+        else if (IsViewType(typeClass))
+        {
+            srgInfo.m_srViews.push_back(varUid);
+        }
+        else if (!varInfo.StorageFlagIsLocalLinkage(true))
+        {
+            if (isUnboundedArray)
+            {
+                ThrowAzslcOrchestratorException(ORCHESTRATOR_ILLEGAL_FOLDABLE_ARRAY_DIMENSIONS, ctx->start
+                    , GetNonEasyToFoldMessage(arrayDims));
+            }
+            if (!varInfo.GetTypeRefInfo().IsPackable())
+            {
+                ThrowAzslcOrchestratorException(ORCHESTRATOR_NON_PACKABLE_TYPE_IN_SRG_CONSTANT, ctx->start,
+                                                 ConcatString(varInfo.GetTypeId().m_name,
+                                                              " is of kind ",
+                                                              TypeClass::ToStr(varInfo.GetTypeClass()),
+                                                              " which is a non packable kind of type."));
+            }
+            auto& classInfo = srgInfo.m_implicitStruct;
+            classInfo.PushMember(varUid, Kind::Variable);
+        }
+        else
+        {
+            if (isUnboundedArray)
+            {
+                ThrowAzslcOrchestratorException(ORCHESTRATOR_ILLEGAL_FOLDABLE_ARRAY_DIMENSIONS, ctx->start
+                    , GetNonEasyToFoldMessage(arrayDims));
+            }
+            assert(varInfo.StorageFlagIsLocalLinkage(true));
+            srgInfo.m_nonexternVariables.push_back(varUid);
+        }
+        varInfo.m_srgMember = true;
+    }
+
+    SamplerStateDesc SemanticOrchestrator::ExtractSamplerState(AstVarInitializer* ctx)
+    {
+        if (ctx == nullptr || ctx->standardVariableInitializer())
+        {
+            SamplerStateDesc defaultState;
+            defaultState.m_isDynamic = true;  // no initializer, or variable assignation both denotes a dynamic sampler.
+            return defaultState;
+        }
+        // Antlr auto-generates tons of methods for every rule, so we have to walk and parse them here.
+        // We declare a couple of helper methods to simplify the rest of the resolve function:
+
+        auto GetAddressMode = [](azslParser::AddressModeEnumContext* ctx) -> SamplerStateDesc::AddressMode
+        {
+            return ctx->ADDRESS_MODE_WRAP()   ? SamplerStateDesc::AddressMode::Wrap
+                 : ctx->ADDRESS_MODE_CLAMP()  ? SamplerStateDesc::AddressMode::Clamp
+                 : ctx->ADDRESS_MODE_MIRROR() ? SamplerStateDesc::AddressMode::Mirror
+                 : ctx->ADDRESS_MODE_BORDER() ? SamplerStateDesc::AddressMode::Border
+                 :                              SamplerStateDesc::AddressMode::MirrorOnce;
+        };
+
+        auto GetCompFunc = [](azslParser::ComparisonFunctionEnumContext* ctx) -> SamplerStateDesc::ComparisonFunc
+        {
+            return ctx->COMPARISON_FUNCTION_NEVER()         ? SamplerStateDesc::ComparisonFunc::Never
+                 : ctx->COMPARISON_FUNCTION_NEVER()         ? SamplerStateDesc::ComparisonFunc::Never
+                 : ctx->COMPARISON_FUNCTION_LESS()          ? SamplerStateDesc::ComparisonFunc::Less
+                 : ctx->COMPARISON_FUNCTION_EQUAL()         ? SamplerStateDesc::ComparisonFunc::Equal
+                 : ctx->COMPARISON_FUNCTION_LESS_EQUAL()    ? SamplerStateDesc::ComparisonFunc::LessEqual
+                 : ctx->COMPARISON_FUNCTION_GREATER()       ? SamplerStateDesc::ComparisonFunc::Greater
+                 : ctx->COMPARISON_FUNCTION_NOT_EQUAL()     ? SamplerStateDesc::ComparisonFunc::NotEqual
+                 : ctx->COMPARISON_FUNCTION_GREATER_EQUAL() ? SamplerStateDesc::ComparisonFunc::GreaterEqual
+                 :                                            SamplerStateDesc::ComparisonFunc::Always;
+        };
+
+        auto GetRedcType = [](azslParser::ReductionTypeEnumContext* ctx) -> SamplerStateDesc::ReductionType
+        {
+            return ctx->REDUCTION_TYPE_FILTER()     ? SamplerStateDesc::ReductionType::Filter
+                 : ctx->REDUCTION_TYPE_COMPARISON() ? SamplerStateDesc::ReductionType::Comparison
+                 : ctx->REDUCTION_TYPE_MINIMUM()    ? SamplerStateDesc::ReductionType::Minimum
+                 :                                    SamplerStateDesc::ReductionType::Maximum;
+        };
+
+        auto GetFilterType = [](azslParser::FilterModeEnumContext* ctx) -> SamplerStateDesc::FilterMode
+        {
+            return ctx->FILTER_MODE_LINEAR() ? SamplerStateDesc::FilterMode::Linear : SamplerStateDesc::FilterMode::Point;
+        };
+
+        auto GetBorderColor = [](azslParser::BorderColorEnumContext* ctx) -> SamplerStateDesc::BorderColor
+        {
+            return ctx->BORDER_COLOR_OPAQUE_BLACK()      ? SamplerStateDesc::BorderColor::OpaqueBlack
+                 : ctx->BORDER_COLOR_TRANSPARENT_BLACK() ? SamplerStateDesc::BorderColor::TransparentBlack
+                 :                                         SamplerStateDesc::BorderColor::OpaqueWhite;
+        };
+
+        // Now proceed with resolving the sampler state
+        SamplerStateDesc desc;
+        auto samplerOpts = ctx->samplerBodyDeclaration()->samplerMemberDeclaration();
+        for (auto samplerOption : samplerOpts)
+        {
+            if (auto opt = samplerOption->maxAnisotropyOption())
+            {
+                auto maxAnisoVal = FoldEvalStaticConstExprNumericValue(opt->IntegerLiteral());
+                desc.m_anisotropyMax = static_cast<uint32_t>(ExtractValueAsInt64(maxAnisoVal));
+                desc.m_anisotropyEnable = true;
+            }
+
+            else if (auto opt = samplerOption->minLodOption())
+            {
+                auto minLODVal = FoldEvalStaticConstExprNumericValue(opt->FloatLiteral(), false);
+                desc.m_mipLodMin = ExtractValueAsFloat(minLODVal);
+            }
+
+            else if (auto opt = samplerOption->maxLodOption())
+            {
+                auto maxLODVal = FoldEvalStaticConstExprNumericValue(opt->FloatLiteral(), false);
+                desc.m_mipLodMax = ExtractValueAsFloat(maxLODVal);
+            }
+
+            else if (auto opt = samplerOption->mipLodBiasOption())
+            {
+                auto biasVal = FoldEvalStaticConstExprNumericValue(opt->FloatLiteral(), false);
+                desc.m_mipLodBias = ExtractValueAsFloat(biasVal);
+            }
+
+            else if (auto opt = samplerOption->minFilterOption())
+            {
+                desc.m_filterMin = GetFilterType(opt->filterModeEnum());
+            }
+
+            else if (auto opt = samplerOption->magFilterOption())
+            {
+                desc.m_filterMag = GetFilterType(opt->filterModeEnum());
+            }
+
+            else if (auto opt = samplerOption->mipFilterOption())
+            {
+                desc.m_filterMip = GetFilterType(opt->filterModeEnum());
+            }
+
+            else if (auto opt = samplerOption->reductionTypeOption())
+            {
+                desc.m_reductionType = GetRedcType(opt->reductionTypeEnum());
+            }
+
+            else if (auto opt = samplerOption->comparisonFunctionOption())
+            {
+                desc.m_comparisonFunc = GetCompFunc(opt->comparisonFunctionEnum());
+                desc.m_isComparison = true;
+            }
+
+            else if (auto opt = samplerOption->addressUOption())
+            {
+                desc.m_addressU = GetAddressMode(opt->addressModeEnum());
+            }
+
+            else if (auto opt = samplerOption->addressVOption())
+            {
+                desc.m_addressV = GetAddressMode(opt->addressModeEnum());
+            }
+
+            else if (auto opt = samplerOption->addressWOption())
+            {
+                desc.m_addressW = GetAddressMode(opt->addressModeEnum());
+            }
+
+            else if (auto opt = samplerOption->borderColorOption())
+            {
+                desc.m_borderColor = GetBorderColor(opt->borderColorEnum());
+            }
+        }
+        return desc;
+    }
+
+    IdAndKind& SemanticOrchestrator::RegisterSRG(AstSRGDeclNode* ctx)
+    {
+        auto const& idText = ctx->Name->getText();
+        size_t line        = ctx->Name->getLine();
+        verboseCout << line << ": srg decl: " << idText << "\n";
+        auto uqNameView    = UnqualifiedNameView{ idText };
+        IdAndKind* srgSym  = LookupSymbol(uqNameView);
+        if (srgSym) // already exists
+        {
+            if (ctx->Partial())  // case of a syntactically valid extension
+            {
+                SRGInfo& srgInfo = srgSym->second.GetSubRefAs<SRGInfo>();
+                if (!srgInfo.IsPartial())
+                {
+                    ThrowAzslcOrchestratorException(ORCHESTRATOR_TRYING_TO_EXTEND_NOT_PARTIAL_SRG,
+                        ctx->Partial()->getSymbol(), ConcatString("Cannot extend ShaderResourceGroup ", uqNameView, " ",
+                                                                  GetFirstSeenLineMessage(srgSym->second),
+                                                                  " because its original declaration isn't 'partial'"));
+                }
+                return *srgSym;  // valid: both original and current SRG declaration statements carry partial.
+            }
+            ThrowAzslcOrchestratorException(ORCHESTRATOR_ODR_VIOLATION,
+                ctx->Name, ConcatString("ShaderResourceGroup ", uqNameView, " already exists, ", GetFirstSeenLineMessage(srgSym->second),
+                                        ". Consider using the 'partial' keyword (on both declaration sites) to extend a ShaderResourceGroup."));
+        }
+        if (!ctx->Partial() && !ctx->Semantic)
+        {
+            ThrowAzslcOrchestratorException(ORCHESTRATOR_INVALID_SEMANTIC_DECLARATION,
+                ctx->Name, "A semantic is mandatory on the declaration of a non-partial ShaderResourceGroup.");
+        }
+        auto& symbol       = AddIdentifier(uqNameView, Kind::ShaderResourceGroup, line);
+        // now fillup what we can about the kindinfo:
+        auto& [uid, info]  = symbol;
+        SRGInfo& srgInfo   = info.GetSubAfterInitAs<Kind::ShaderResourceGroup>();
+        srgInfo.m_declNode = ctx;
+        srgInfo.m_implicitStruct.m_kind = Kind::Struct;
+        return symbol;
+    }
+
+    void SemanticOrchestrator::RegisterBases(azslParser::BaseListContext* ctx)
+    {
+        using namespace std::string_literals;
+
+        for (auto& idexpr : ctx->idExpression())
+        {
+            UnqualifiedName baseName = ExtractNameFromIdExpression(idexpr);
+            auto baseSymbol = LookupSymbol(baseName);
+            if (!baseSymbol)
+            {
+                ThrowAzslcOrchestratorException(ORCHESTRATOR_UNSPECIFIED_BASE_SYMBOL,
+                    ctx->start, ConcatString("Base symbol "s, baseName, " not found"));
+            }
+            auto& curClassInfo = GetCurrentScopeSubInfoAs<ClassInfo>();
+            curClassInfo.m_bases.emplace(baseSymbol->first);
+        }
+    }
+
+    void SemanticOrchestrator::RegisterSeenat(IdAndKind& idPair, const TokensLocation& location)
+    {
+        auto& [uid, info] = idPair;
+        Seenat seenat;
+        seenat.m_referredDefinition = uid;
+        seenat.m_where              = location;
+        info.GetSeenats().emplace_back(seenat);
+
+        const string verboseMessage = ConcatString(seenat.m_where.m_line, ": seenat registered for ", uid.m_name, " at col ", seenat.m_where.m_charPos + 1, "\n");
+        verboseCout << verboseMessage;
+    }
+
+    void SemanticOrchestrator::RegisterSeenat(AstIdExpr* ctx)
+    {
+        RegisterSeenat(ctx, GetCurrentScopeIdAndKind().first.GetName());
+    }
+
+    // e.g. provided "int a; X GetX(int);" then from expression "(a, GetX(2), true)" MangleArgumentList returns "(?int,/X,?bool)"
+    string SemanticOrchestrator::MangleArgumentList(azslParser::ArgumentListContext* ctx) const
+    {
+        // resolve all argument types.
+        vector<QualifiedName> resolvedArguments;
+        auto vectorOfExpressions = ctx->arguments() ? ctx->arguments()->expression() : vector<AstExpr*>{};
+        resolvedArguments.reserve(vectorOfExpressions.size());
+        for (AstExpr* expression : vectorOfExpressions)
+        {
+            QualifiedName typeName = TypeofExpr(expression);
+            resolvedArguments.push_back(typeName);
+        }
+        return ::AZ::ShaderCompiler::CreateDecorationOfFunction(resolvedArguments.begin(), resolvedArguments.end());
+    }
+
+    bool SemanticOrchestrator::HasAnyDefaultParameterValue(const IdentifierUID& functionUid) const
+    {
+        auto* funcInfo = m_symbols->GetAsSub<FunctionInfo>(functionUid);
+        return funcInfo ? funcInfo->HasAnyDefaultParameterValue() : false;
+    }
+
+    void SemanticOrchestrator::OverrideAzslcExceptionFileAndLine(size_t azslLineNumber) const
+    {
+        if (!m_preprocessorLineDirectiveFinder)
+        {
+            return;
+        }
+        const LineDirectiveInfo* lineInfo = m_preprocessorLineDirectiveFinder->GetNearestPreprocessorLineDirective(azslLineNumber);
+        if (!lineInfo)
+        {
+            return;
+        }
+        AzslcException::s_currentSourceFileName = lineInfo->m_containingFilename;
+        AzslcException::s_sourceFileLineNumber = m_preprocessorLineDirectiveFinder->GetLineNumberInOriginalSourceFile(*lineInfo, azslLineNumber);
+    }
+
+    IdAndKind* SemanticOrchestrator::ResolveOverload(IdAndKind* maybeOverloadSet, azslParser::ArgumentListContext* argumentListCtx) const
+    {
+        IdAndKind* toReturn = maybeOverloadSet;
+        if (maybeOverloadSet && maybeOverloadSet->second.GetKind() == Kind::OverloadSet)
+        {
+            string mangledArgList = argumentListCtx ? MangleArgumentList(argumentListCtx) : "";
+            auto& setInfo = maybeOverloadSet->second.GetSubRefAs<OverloadSetInfo>();
+            // attempt direct matching or arity matching
+            IdentifierUID concrete = setInfo.GetConcreteFunctionThatMatchesArgumentList(mangledArgList);
+            if (concrete.IsEmpty())  // failure case
+            {
+                std::stringstream message;
+                message << " unable to match arguments " << mangledArgList << " to a registered overload. candidates are:\n";
+                setInfo.ForEach([&](auto&& uid){ message << uid.GetName() << "\n"; });
+                if (setInfo.HasHomogeneousReturnType())
+                {
+                    verboseCout << (argumentListCtx ? std::to_string(argumentListCtx->start->getLine()) : "")
+                                << message.str() << " It is not an error since that overload-set has homogeneous return type\n";  // at this point of the source. further declaration can change that.
+                }
+                else
+                {
+                    ThrowAzslcOrchestratorException(ORCHESTRATOR_OVERLOAD_RESOLUTION_HARD_FAILURE, argumentListCtx ? argumentListCtx->start : nullptr,
+                                                     ConcatString(message.str(), " This is an error because functions belonging to this overload-set have heterogeneous return types.\n",
+                                                                  "Consider using type-casts to help type resolution."));
+                }
+            }
+            else
+            {
+                toReturn = m_symbols->GetIdAndKindInfo(concrete.GetName());
+            }
+        }
+        return toReturn;
+    }
+
+    void SemanticOrchestrator::RegisterSeenat(AstIdExpr* ctx, QualifiedNameView startupScope)
+    {
+        auto* argumentList = GetArgumentListIfBelongsToFunctionCall(ctx);  // no need to execute that in the loop body, extracted up-here.
+
+        // an id-expression is made of nested parts: stuff::thing::leaf
+        // for each part we need to register a reference to the symbol the nested name refers to.
+        // so we loop over the expression, reconstructing by appending part by part so correctly qualify each nested part for lookup.
+        // the startupScope is not the first path of the path of each element, but the CONTEXT from which we start the lookup.
+        // the lookup mechanism must sill execute. Just not necessarily from the current scope as startup context.
+        string partialExpression;
+        ForEachIdExpressionPart(ctx, [&](const IdExpressionPart& part)
+                                    {   // this is not Schlemiel the painter
+                                        partialExpression += part.GetAZIRMangledText();
+                                        if (!part.IsScopeToken()) // scope token is the SRO "::"
+                                        {
+                                            auto* idToKind = ResolveOverload(m_symbols->LookupSymbol(startupScope, UnqualifiedNameView{partialExpression}),
+                                                                             argumentList);
+                                            if (idToKind)
+                                            {
+                                                auto tl = MakeTokensLocation(ctx, part.m_token);
+                                                RegisterSeenat(*idToKind, tl);
+                                            }
+                                            else
+                                            {
+                                                DiagnoseUndeclaredSub(ctx->start, startupScope, partialExpression);
+                                            }
+                                        }
+                                    });
+    }
+
+    void SemanticOrchestrator::DiagnoseUndeclaredSub(Token* atToken, QualifiedNameView startupScope, string partialName) const
+    {
+        // check if we can help the user a bit, that will avoid long pondering when DXC refuses to build something that broke through translation.
+        auto parent = GetParentName(partialName);
+        bool hasParent = !parent.empty();
+        // try to get the parent to see if we can say something special.
+        auto* idToKind = m_symbols->LookupSymbol(startupScope, UnqualifiedNameView{parent});
+        bool parentFound = hasParent && idToKind != nullptr;
+        if (parentFound)
+        {
+            auto& [id, kind] = *idToKind;
+            if (kind.GetKind() == Kind::Enum)
+            {
+                bool isScopedEnum = kind.GetSubRefAs<ClassInfo>().Get<EnumerationInfo>()->m_isScoped;
+                if (!isScopedEnum)
+                {
+                    PrintWarning(Warn::W1, atToken, "in AZSL, non-class enumeration ", parent, " can't qualify names.");
+                    return; // job done
+                }
+            }
+            PrintWarning(Warn::W3, atToken, "undeclared sub-symbol in idexpression: ", parent, " was found, but not ", partialName);
+        }
+        else
+        {   // that warning is probably going to fire a tad too much. repeated for all left-over elements on the right of an expression that failed early.
+            // eg  i_did_a_typo_here_omg::not_found::not_found::not_found... you see the point.
+            // but being here is our only chance to grab lone identifiers (completely unqualified).
+            // so protect it for only that case.
+            if (!hasParent)
+            {
+                PrintWarning(Warn::W3, atToken, "undeclared sub-symbol in idexpression: ", partialName);
+            }
+        }
+    }
+
+    pair<bool, QualifiedName> SemanticOrchestrator::VerifyLHSExprOfMAExprIsValid(azslParser::MemberAccessExpressionContext* ctx) const
+    {
+        return VerifyTypeIsScopeComposable(ctx->LHSExpr);
+    }
+
+    //! Member Access Expression (MAE) such as A.B is a scoped lookup. (A is the scope and B is the composition)
+    //! Typeof Expression such as typeof(A)::B is a scoped lookup. (typeof(A) is the scope and B is the composition)
+    //! returns the looked up scope
+    pair<bool, QualifiedName> SemanticOrchestrator::VerifyTypeIsScopeComposable(azslParser::ExpressionContext* typeScopeAnyExpression) const
+    {
+         return VerifyTypeIsScopeComposable(TypeofExpr(typeScopeAnyExpression), typeScopeAnyExpression->getText(), typeScopeAnyExpression->start->getLine());
+    }
+
+    //! same function as above for already resolved typeof
+    pair<bool, QualifiedName> SemanticOrchestrator::VerifyTypeIsScopeComposable(QualifiedNameView lhsTypeName, optional<string> lhsExpressionText/*= none*/, optional<size_t> line/*= none*/) const
+    {
+        // (generalized) member-access-expressions can only work on types with members:
+        // any UDT: struct, enum, class, interface. Any scope; srg, function. Any type-like: typeof, typedef (because they get collapsed)
+        auto lhsSymbol = m_symbols->GetIdAndKindInfo(lhsTypeName);
+        bool valid = true;
+        if (!lhsSymbol)
+        {
+            PrintWarning(Warn::W2, line, "unresolved member access ",
+                         "on undeclared type ", lhsTypeName,
+                         (lhsExpressionText ? " (of expression " + *lhsExpressionText + ")" : ""), ")");
+            valid = false;
+        }
+        else // left side symbol exists
+        {
+            const KindInfo& lhsKindInfo = lhsSymbol->second;
+            if (!lhsKindInfo.IsKindOneOf(Kind::Namespace, Kind::Class, Kind::Struct, Kind::Enum, Kind::Interface, Kind::ShaderResourceGroup, Kind::Function))
+            {
+                auto outputStream = lhsKindInfo.GetKind() == Kind::Type ? verboseCout : warningCout;
+                // registered predefined types can have members, but we don't know them -> not important. But anything else is very likely an ill-formed source.
+                PrintWarning(outputStream, Warn::W1, line, none, " warning: ill-formed semantics: access of member ",
+                             " on an unsupported kind ", Kind::ToStr(lhsKindInfo.GetKind()),
+                             " (of believed type ", lhsSymbol->first.GetName(),
+                             (lhsExpressionText ? " from expression " + *lhsExpressionText : ""), ")");
+                valid = false;
+            }
+        }
+        return {valid, lhsTypeName};
+    }
+
+    QualifiedName SemanticOrchestrator::ComposeMemberNameWithScopeAndGetType(QualifiedName scopingType, AstIdExpr* rhsMember) const
+    {
+        // get the symbol name we want to lookup on the right hand side:
+        UnqualifiedName rhsName = ExtractNameFromIdExpression(rhsMember);
+        // look it up from the scope of the lhs's type: (this behavior is explained in https://stackoverflow.com/questions/56253767)
+        auto fullyResolved = m_symbols->LookupSymbol(scopingType, rhsName);
+        if (!fullyResolved)
+        {
+            PrintWarning(Warn::W3, rhsMember->start, "type tracking fail: ", rhsName, " is not a member of ", scopingType);
+        }
+        else // rhs symbol exists
+        {   // let's extract its type and return that.
+            return GetTypeName(fullyResolved);
+        }
+        return {"<fail>"};
+    }
+
+    QualifiedName SemanticOrchestrator::TypeofExpr(azslParser::ExpressionContext* ctx) const
+    {
+        try
+        {
+            return VisitFirstNonNull([this](auto* ctx) { return TypeofExpr(ctx); },
+                                     As<AstIdExpr*>(ctx),
+                                     As<azslParser::IdentifierExpressionContext*>(ctx),
+                                     As<azslParser::ParenthesizedExpressionContext*>(ctx),
+                                     As<azslParser::CastExpressionContext*>(ctx),
+                                     As<azslParser::MemberAccessExpressionContext*>(ctx),
+                                     As<azslParser::FunctionCallExpressionContext*>(ctx),
+                                     As<azslParser::ArrayAccessExpressionContext*>(ctx),
+                                     As<azslParser::ConditionalExpressionContext*>(ctx),
+                                     As<azslParser::AssignmentExpressionContext*>(ctx),
+                                     As<azslParser::NumericConstructorExpressionContext*>(ctx),
+                                     As<azslParser::LiteralExpressionContext*>(ctx),
+                                     As<azslParser::LiteralContext*>(ctx));
+        }
+        catch (AllNull&)
+        {
+            verboseCout << ctx->start->getLine() << ": unsupported expression in typeof: " << typeid(ctx).name() << "\n";
+            return {"<fail>"};
+        }
+    }
+
+    QualifiedName SemanticOrchestrator::TypeofExpr(azslParser::ExpressionExtContext* ctx) const
+    {
+        return VisitFirstNonNull([this](auto* ctx) { return TypeofExpr(ctx); },
+                                 As<azslParser::OtherExpressionContext*>(ctx),
+                                 As<azslParser::CommaExpressionContext*>(ctx));
+    }
+
+    QualifiedName SemanticOrchestrator::TypeofExpr(azslParser::OtherExpressionContext* ctx) const
+    {
+        return TypeofExpr(ctx->Expr);
+    }
+
+    QualifiedName SemanticOrchestrator::TypeofExpr(AstType* ctx) const
+    {
+        return LookupType(ctx).GetName();
+    }
+
+    QualifiedName SemanticOrchestrator::TypeofExpr(AstFuncType* ctx) const
+    {
+        return LookupType(ctx).GetName();
+    }
+
+    QualifiedName SemanticOrchestrator::TypeofExpr(AstIdExpr* ctx) const
+    {
+        // idExpression will represent registered symbol. if not, it's a fail.
+        UnqualifiedName uqName = ExtractNameFromIdExpression(ctx);
+        auto lookup = ResolveOverload(LookupSymbol(uqName), GetArgumentListIfBelongsToFunctionCall(ctx));
+        if (!lookup)
+        {
+            verboseCout << ctx->start->getLine() << ": can't find typeof " << uqName << "\n";
+            return {"<fail>"};
+        }
+        else
+        {
+            return GetTypeName(lookup);  // this call is often the leaf of the TypeofExpr call tree.
+        }
+    }
+
+    QualifiedName SemanticOrchestrator::TypeofExpr(azslParser::IdentifierExpressionContext* ctx) const
+    {
+        return TypeofExpr(ctx->idExpression());
+    }
+
+    QualifiedName SemanticOrchestrator::TypeofExpr(azslParser::MemberAccessExpressionContext* ctx) const
+    {
+        // "LHS.RHS"
+        // typeof(LHS.RHS) is the type of RHS
+        // start with a simple case -> rhs is absolute (e.g. "stuff.::A::f()" ::A::f is absolute)
+        bool rhsIsAbsolute = ctx->Member->qualifiedId() && ctx->Member->qualifiedId()->nestedNameSpecifier()->GlobalSROToken;
+        if (rhsIsAbsolute)
+        {
+            return TypeofExpr(ctx->Member /*idExpression*/);
+        }
+        // otherwise, RHS is relative to LHS -> it's a member of typeof(LHS)
+        auto [valid, lhsType] = VerifyLHSExprOfMAExprIsValid(ctx);
+        return valid ? ComposeMemberNameWithScopeAndGetType(lhsType, ctx->Member)
+                     : QualifiedName{"<fail>"};
+    }
+
+    QualifiedName SemanticOrchestrator::TypeofExpr(azslParser::FunctionCallExpressionContext* ctx) const
+    {
+        // Function call grammar is "Expr arglist" so we need to extract the typeof Expr.
+        // which must be a function. (a concrete well resolved function, or an overload set under some conditions)
+        // then access its return type and we can return that.
+        // it can get complicated in case of overloads.
+        auto function{TypeofExpr(ctx->Expr)};
+        IdAndKind* symbol = m_symbols->GetIdAndKindInfo(function);
+        symbol = ResolveOverload(symbol, ctx->argumentList());
+        if (!symbol || !symbol->second.IsKindOneOf(Kind::Function, Kind::OverloadSet))
+        {
+            return {"<fail>"};
+        }
+        return GetTypeName(symbol, ForFunctionGetType::Returned);
+    }
+
+    QualifiedName SemanticOrchestrator::TypeofExpr(azslParser::ArrayAccessExpressionContext* ctx) const
+    {   // typeof(myvariable[3]) is typeof(myvariable) minus brackets.  (e.g. int[] -> int)
+        // But because of a limitation, today the type of array variable is not described as type[], but only type
+        // which means it is enough today to just return typeof(myvariable).
+        // Again, this is loose since array access is not going to be semantically validated.
+        //  (if you call it on a function for example, it should fail validation; but that's not the mandate of azslc)
+        return TypeofExpr(ctx->Expr);
+    }
+
+    QualifiedName SemanticOrchestrator::TypeofExpr(azslParser::ParenthesizedExpressionContext* ctx) const
+    {   // parenthesis don't change the type, it's just a syntax precedence thing. so just forward to the inner expression.
+        return TypeofExpr(ctx->Expr);
+    }
+
+    QualifiedName SemanticOrchestrator::TypeofExpr(azslParser::CastExpressionContext* ctx) const
+    {   // typeof( (type)expr ) is typeof(type) so just forward to that sub rule.
+        // Note that the cast-expression can optionally specify array-ranks, but they are ignored today.
+        return TypeofExpr(ctx->type());
+    }
+
+    QualifiedName SemanticOrchestrator::TypeofExpr(azslParser::ConditionalExpressionContext* ctx) const
+    {   // typeof(a ? b : c) is typeof(b) because b and c are forbidden to differ in type. DXC/clang is going to see to it.
+        return TypeofExpr(ctx->TrueExpr);
+    }
+
+    QualifiedName SemanticOrchestrator::TypeofExpr(azslParser::AssignmentExpressionContext* ctx) const
+    {   // typeof(a = b) is typeof(a) because an assignment returns a reference to the left-hand-side as rvalue for the enclosing expression.
+        return TypeofExpr(ctx->Left);
+    }
+
+    QualifiedName SemanticOrchestrator::TypeofExpr(azslParser::NumericConstructorExpressionContext* ctx) const
+    {   // typeof(float2(0,0)) is float2
+        return LookupType(ctx->scalarOrVectorOrMatrixType()).GetName();
+    }
+
+    QualifiedName SemanticOrchestrator::TypeofExpr(azslParser::TypeofExpressionContext* ctx) const
+    {   // typeof(typeof(..)) is typeof(..) | and typeof(A)::id is type of the symbol composed by `lookup-of-A`/id
+        auto leftType = ctx->Expr ? TypeofExpr(ctx->Expr)
+                                  : TypeofExpr(ctx->functionType());
+        if (ctx->SubQualification)
+        {
+            auto [valid, lhsType] = VerifyTypeIsScopeComposable(leftType,
+                                                                ctx->Expr ? ctx->Expr->getText()
+                                                                          : ctx->functionType()->getText(),
+                                                                ctx->start->getLine());
+            return valid ? ComposeMemberNameWithScopeAndGetType(lhsType, ctx->SubQualification)
+                         : QualifiedName{"<fail>"};
+        }
+        return leftType;
+    }
+
+    QualifiedName SemanticOrchestrator::TypeofExpr(azslParser::LiteralExpressionContext* ctx) const
+    {
+        return TypeofExpr(ctx->literal());
+    }
+
+    QualifiedName SemanticOrchestrator::TypeofExpr(azslParser::LiteralContext* ctx) const
+    {
+        // verifies that our hardcoded strings don't have typo, by checking against the lexer-extracted keywords stored in the Scalar array.
+        auto checkExistType = [](string_view scalarName){return std::find(AZ::ShaderCompiler::Predefined::Scalar.begin(), AZ::ShaderCompiler::Predefined::Scalar.end(), scalarName) != AZ::ShaderCompiler::Predefined::Scalar.end();};
+        // verifies that last or 1-before-last characters are a particular literal suffix. like in "56ul"
+        auto hasSuffix = [](auto node, char s){return tolower(node->getText().back()) == s || tolower(Slice(node->getText(), -3, -2) == s);};
+        auto checkAndReturn = [&](string_view typeName)
+                              {
+                                  assert(checkExistType(typeName));
+                                  return QualifiedName{"?" + string{typeName}};
+                              };
+        if (ctx->True() || ctx->False())
+        {
+            return checkAndReturn("bool");
+        }
+        else if (auto* literal = ctx->IntegerLiteral())
+        {
+            return hasSuffix(literal, 'u') ? checkAndReturn("uint")
+                 : checkAndReturn("int");
+        }
+        else if (auto* literal = ctx->FloatLiteral())
+        {
+            return hasSuffix(literal, 'h') ? checkAndReturn("half")
+                 : hasSuffix(literal, 'l') ? checkAndReturn("double")
+                 : checkAndReturn("float");
+        }
+        return {"<fail>"};
+    }
+
+    QualifiedName SemanticOrchestrator::TypeofExpr(azslParser::CommaExpressionContext* ctx) const
+    {
+        // comma returns whatever is last
+        return TypeofExpr(ctx->Right);
+    }
+
+    bool SemanticOrchestrator::TryFoldArrayDimensions(AstUnnamedVarDecl* ctx, ArrayDimensions& arrayDimensions)
+    {
+        for (auto arrayDecl : ctx->ArrayRankSpecifiers)
+        {
+            if (arrayDecl->Dimension == nullptr)
+            {
+                arrayDimensions.PushBack(ArrayDimensions::Unbounded);
+            }
+            else
+            {
+                ConstNumericVal arrayDimensionVal = FoldEvalStaticConstExprNumericValue(arrayDecl->Dimension);
+                int64_t nextDim = ExtractValueAsInt64(arrayDimensionVal, -1);
+                if (nextDim < 0)
+                {
+                    verboseCout << arrayDecl->start->getLine() << ": array rank specifier (" << nextDim << ") invalid or non foldable";
+                    arrayDimensions.PushBack(ArrayDimensions::Unknown);
+                }
+                else
+                {
+                    int asInt = static_cast<int>(nextDim);
+                    arrayDimensions.PushBack(asInt);
+                }
+            }
+        }
+
+        return true;
+    }
+
+    void SemanticOrchestrator::ValidateClass(azslParser::ClassDefinitionContext* ctx) noexcept(false)
+    {
+        using namespace std::string_literals;
+        auto curScopeName = m_scope->m_currentScopePath;
+        verboseCout << ctx->RightBrace()->getSymbol()->getLine() << ": exit class " << curScopeName << ". verifying compliance...\n";
+        // Get iterator into the symbol database from current scope name. (current scope should be the currently closing class)
+        // Access the KindInfo from iter->second, and "cast" the `anyInfo` variant to ClassInfo:
+        auto& classSubInfo = GetCurrentScopeSubInfoAs<ClassInfo>();
+        // Semantic validation. Iterate each base UID as registered in the ClassInfo:
+        for (auto b : classSubInfo.m_bases)
+        {
+            // this class original AST node:
+            auto declNode = get<AstClassDeclNode*>(classSubInfo.m_declNodeVt);
+            // get line location diagnostic message of its declaration keyword in source:
+            verboseCout << "  base: " << b.m_name << "\n";
+            // Fetch the base from name in the database
+            auto* infoBase = m_symbols->GetIdAndKindInfo(b.m_name);
+            assert(infoBase); // can't be undeclared since it already threw an error in RegisterBases.
+            // wannabe is "want-to-be-a-base"
+            auto& [baseUid, wannabeInfo] = *infoBase;
+            if (wannabeInfo.GetKind() != Kind::Interface)
+            {
+                ThrowAzslcOrchestratorException(ORCHESTRATOR_INVALID_INTERFACE, declNode->Class()->getSymbol(),
+                    ConcatString("base ", baseUid.m_name, " is not an interface (it is a "s,
+                        Kind::ToStr(wannabeInfo.GetKind()).data(), ")"));
+            }
+            auto& baseInterfaceInfo = wannabeInfo.GetSubRefAs<ClassInfo>();
+            for (auto basemember : baseInterfaceInfo.GetOrderedMembers())
+            {  // Check that any member present in base is present in this class
+                if (!classSubInfo.HasMember(basemember.GetNameLeaf()))
+                {
+                    ThrowAzslcOrchestratorException(ORCHESTRATOR_CLASS_REDEFINE, declNode->Class()->getSymbol(),
+                        ConcatString("class ", m_scope->m_currentScopeUID.m_name, " does not redefine ", basemember.m_name));
+                }
+            }
+        }
+    }
+
+    void SemanticOrchestrator::ValidateFunctionAndRegisterFamilySeenat(azslParser::LeadingTypeFunctionSignatureContext* ctx)
+    {
+        using namespace std::string_literals;
+
+        auto& [thisFuncId, thisFuncKind] = GetCurrentScopeIdAndKind();
+
+        // verify whether it overrides
+        auto* parentFunction = GetSymbolHiddenInBase(thisFuncId);
+        if (parentFunction)
+        {
+            // == register this function, to its base symbol's overrides seenat list ! ==
+            // let's construct access to that list, from the identifier we got out of GetSymbolHiddenInBase
+            auto& [baseFuncId, baseFuncKind] = *parentFunction;
+            // let's do a bit of sanity check on that symbol
+            Kind baseKind = baseFuncKind.GetKind();
+            if (baseKind != Kind::Function)
+            {   // today, it is impossible to reach that diagnostic, since the grammar doesn't allow it.
+                // but we are envisioning Properties as a future possible Kind in interfaces.
+                auto baseKindStr = Kind::ToStr(baseKind).data();
+                ThrowAzslcOrchestratorException(ORCHESTRATOR_HIDING_SYMBOL_BASE, ctx->Identifier()->getSymbol(),
+                    ConcatString("function ", thisFuncId.m_name, " is hiding a symbol of a base, that is not of Function kind, but is ", baseKindStr));
+            }
+            auto& baseFuncSubInfo = baseFuncKind.GetSubRefAs<FunctionInfo>();
+            if (std::find(baseFuncSubInfo.m_overrides.begin(),  baseFuncSubInfo.m_overrides.end(), thisFuncId) == baseFuncSubInfo.m_overrides.end())
+            {   // this collection is not a set, and ValidateFunctionAndRegisterFamilySeenat may be called multiple times on the same symbol
+                baseFuncSubInfo.m_overrides.emplace_back(thisFuncId);
+            }
+            // == and register the (one) base in our own data too. two-way 1toN bridging. ==
+            auto& thisFuncSubInfo = thisFuncKind.GetSubRefAs<FunctionInfo>();
+            thisFuncSubInfo.m_base = baseFuncId;
+        }
+
+        // semantic provision of override guarantee:
+        if (ctx->Override())
+        {
+            if (GetCurrentParentScopeIdAndKind().second.GetKind() != Kind::Class)
+            {   // free function case (or in interface !)
+                ThrowAzslcOrchestratorException(ORCHESTRATOR_INVALID_OVERRIDE_SPECIFIER_CLASS, ctx->Identifier()->getSymbol(),
+                    ConcatString("function ", thisFuncId.m_name, " has override specifier but is not part of a class"));
+            }
+            else
+            {   // in-class case
+                if (!parentFunction)
+                {
+                    ThrowAzslcOrchestratorException(ORCHESTRATOR_INVALID_OVERRIDE_SPECIFIER_BASE, ctx->Identifier()->getSymbol(),
+                        ConcatString("method ", thisFuncId.m_name, " has override specifier but is not found in any base"));
+                }
+            }
+        }
+
+        // verify parameters integrity; and reflow the default values into the first declaration site:
+        auto& funcInfo = thisFuncKind.GetSubRefAs<FunctionInfo>();
+        funcInfo.MergeDefaultParameters();
+
+        // forbid mixup of overloading & default param values (because it wreaks the resolution technique)
+        auto overloadSetId = IdentifierUID{QualifiedNameView{RemoveLastParenthesisGroup(thisFuncId.GetName())}};
+        auto* overloadSet = m_symbols->GetAsSub<OverloadSetInfo>(overloadSetId);
+        if (overloadSet->HasOverloads())  // (at least 2 concrete functions)
+        {
+            bool thisFuncIsCulprit = funcInfo.HasAnyDefaultParameterValue();
+            bool previousFuncIsCulprit = overloadSet->AnyOf([this](auto&& uid){return this->HasAnyDefaultParameterValue(uid);});
+            if (thisFuncIsCulprit || previousFuncIsCulprit)
+            {
+                ThrowAzslcOrchestratorException(ORCHESTRATOR_NO_DEFAULT_PARAM_WITH_OVERLOADS, ctx->Identifier()->getSymbol(),
+                                                 ConcatString("can't use default arguments in conjunction with function overloading. (function ", thisFuncId.m_name,
+                                                              thisFuncIsCulprit ? " has defaults arguments, but overloads exist)"
+                                                                                : " overloads a function that has default arguments)"));
+            }
+        }
+    }
+
+    void SemanticOrchestrator::ValidateSrg(azslParser::SrgDefinitionContext* ctx) noexcept(false)
+    {
+        auto& srgInfo = GetCurrentScopeSubInfoAs<SRGInfo>();
+
+        if (ctx->Semantic)
+        {
+            string semanticName = ctx->Semantic->getText();
+            if (srgInfo.m_semantic)
+            {
+                // We only care the specified semantic is the same as the currently defined semantic for the srg.
+                if (srgInfo.m_semantic->GetNameLeaf() != semanticName)
+                {
+                    const LineDirectiveInfo* originalSrglineInfo = m_preprocessorLineDirectiveFinder->GetNearestPreprocessorLineDirective(srgInfo.m_declNode->Semantic->getLine());
+                    string errorMsg = FormatString("'partial' extension of ShaderResourceGroup [%s] with semantic [%s] shall not bind a different semantic than [%s] found in line %u of %s",
+                        ctx->Name->getText().c_str(), semanticName.c_str(), srgInfo.m_semantic->GetNameLeaf().c_str(),
+                        originalSrglineInfo->m_forcedLineNumber, originalSrglineInfo->m_containingFilename.c_str());
+                    ThrowAzslcOrchestratorException(ORCHESTRATOR_SRG_EXTENSION_HAS_DIFFERENT_SEMANTIC, ctx->Semantic, errorMsg);
+                }
+                // All is good.
+                return;
+            }
+
+            // Make sure the SRG is referencing a registered srgSemantic (and of the correct kind)
+            auto uqName = UnqualifiedNameView{ semanticName };
+            auto semanticSymbol = LookupSymbol(uqName);
+            if (!semanticSymbol)
+            {
+                ThrowAzslcOrchestratorException(ORCHESTRATOR_INVALID_SEMANTIC_DECLARATION, ctx->ShaderResourceGroup()->getSymbol(),
+                                                 ConcatString("Declaration for semantic ", semanticName, " used in SRG ", ctx->Name->getText(), " was not found"));
+            }
+
+            auto& [semanticSymId, semanticSymKind] = *semanticSymbol;
+            Kind kind = semanticSymKind.GetKind();
+            if (kind != Kind::ShaderResourceGroupSemantic)
+            {
+                ThrowAzslcOrchestratorException(ORCHESTRATOR_INVALID_SEMANTIC_DECLARATION_TYPE, ctx->ShaderResourceGroup()->getSymbol(),
+                                                 ConcatString("Declaration for ", semanticName, " used in SRG ", ctx->Name->getText(),
+                                                              " is a ", Kind::ToStr(kind).data(),
+                                                              " but expected a ", Kind::ToStr(Kind::ShaderResourceGroupSemantic).data()));
+            }
+            const IdentifierUID& srgId = GetCurrentScopeIdAndKind().first;
+            auto* srgSemanticInfo = semanticSymKind.GetSubRefAs<ClassInfo>().Get<SRGSemanticInfo>();
+            auto userSrgIterator = m_frequencyToSrg.find(*srgSemanticInfo->m_frequencyId);
+            if (userSrgIterator == m_frequencyToSrg.end())
+            {
+                m_frequencyToSrg[*srgSemanticInfo->m_frequencyId] = srgId;
+            }
+            else if (userSrgIterator->second != srgId)
+            {
+                ThrowAzslcOrchestratorException(ORCHESTRATOR_SRG_REUSES_A_FREQUENCY, ctx->ShaderResourceGroup()->getSymbol(),
+                                                 ConcatString("SRG ", ctx->Name->getText(), " reuses frequencyId "
+                                                              , userSrgIterator->first, " already used by ",
+                                                              userSrgIterator->second));
+            }
+            // Good, the SRGSemantic is valid, remember a reference to its ID on the SRG:
+            srgInfo.m_semantic = semanticSymId;
+            if (srgSemanticInfo->m_variantFallback)
+            {
+                int variantFallbackValue = static_cast<int>(*(srgSemanticInfo->m_variantFallback));
+
+                // We pay the price in 16-byte (128-bit) chunks
+                int keyLength = ((variantFallbackValue + kShaderVariantKeyRegisterSize - 1) / kShaderVariantKeyRegisterSize) * kShaderVariantKeyRegisterSize;
+                if (keyLength > variantFallbackValue)
+                {
+                    PrintWarning(Warn::W1, ctx->ShaderResourceGroup()->getSymbol(),
+                                 "ShaderVariantFallback requires ", variantFallbackValue,
+                                 " bits, but will be bumped up to ", keyLength, " bits for padding.");
+                }
+
+                // Find a name that doesn't collide with previous declarations
+                string svkName = kShaderVariantKeyFallbackVarName;
+                auto existingSymbol = LookupSymbol(UnqualifiedNameView{ svkName });
+                while (existingSymbol)
+                {
+                    //There is a name collision, add a suffix to the name and try again.
+                    svkName = + "z";
+                    existingSymbol = LookupSymbol(UnqualifiedNameView{ svkName });
+                }
+
+                size_t line = 0;
+                auto& [uid, var] = AddIdentifier(UnqualifiedNameView{ svkName }, Kind::Variable, line);
+                auto& varInfo = var.GetSubAfterInitAs<Kind::Variable>();
+                varInfo.m_srgMember = true;
+
+                ArrayDimensions arrayDims;
+                arrayDims.PushBack(keyLength / kShaderVariantKeyRegisterSize);
+                varInfo.m_typeInfoExt = ExtendedTypeInfo{ CreateTypeRefInfo(UnqualifiedNameView{"uint4"}, OnNotFoundOrWrongKind::Diagnose),
+                                                         {}, arrayDims, {}, Packing::MatrixMajor::Default };
+
+                srgInfo.m_implicitStruct.PushMember(uid, Kind::Variable);
+                srgInfo.m_shaderVariantFallback = uid;
+            }
+        }
+
+        for (const IdentifierUID& viewId : srgInfo.m_srViews)
+        {
+            auto& [_, viewKindInfo] = *m_symbols->GetIdAndKindInfo(viewId.m_name);
+            auto& memberInfo = viewKindInfo.GetSubRefAs<VarInfo>();
+
+            // in case of core type: structured buffer
+            if (memberInfo.GetTypeClass() == TypeClass::StructuredBuffer)
+            {
+                // check that generic type is not a view or anything nonsensical
+                TypeClass genericClass = memberInfo.GetGenericParameterTypeClass();
+                bool genericTypeLooksGood = IsFundamental(genericClass) || IsUserDefined(genericClass);
+                if (!genericTypeLooksGood)
+                {
+                    ThrowAzslcOrchestratorException(ORCHESTRATOR_INVALID_EXTERNAL_BOUND_RESOURCE_VIEW, memberInfo.m_declNode->start,
+                        "externally bound resources can't be type-parameterized on view-types");
+                }
+            }
+        }
+
+        for (const IdentifierUID& cbId : srgInfo.m_CBs)
+        {
+            auto& [_, kindInfo] = *m_symbols->GetIdAndKindInfo(cbId.m_name);
+            auto& memberInfo = kindInfo.GetSubRefAs<VarInfo>();
+            assert(memberInfo.IsConstantBuffer());
+            const auto& genericName = memberInfo.GetGenericParameterTypeId().m_name;
+            auto genericClass = memberInfo.GetGenericParameterTypeClass();
+            if (!IsUserDefined(genericClass))
+            {
+                ThrowAzslcOrchestratorException(ORCHESTRATOR_INVALID_GENERIC_TYPE_CONSTANTBUFFER, memberInfo.m_declNode->start,
+                    ConcatString("ConstantBuffer<T>'s generic type ", genericName,
+                        " must be user defined, but seen as ", TypeClass::ToStr(genericClass).data()));
+            }
+            // further checks by actually fetching the symbol
+            IdAndKind* idkind = m_symbols->GetIdAndKindInfo(genericName);
+            if (!idkind)
+            {
+                ThrowAzslcOrchestratorException(ORCHESTRATOR_UNDECLARED_GENERIC_TYPE_CONSTANTBUFFER, memberInfo.m_declNode->start,
+                    ConcatString("ConstantBuffer<T>'s generic type ", genericName,
+                        " is not declared!"));
+            }
+            auto& [id, kind] = *idkind;
+            if (kind.GetKind() != Kind::Struct)
+            {
+                ThrowAzslcOrchestratorException(ORCHESTRATOR_INVALID_GENERIC_TYPE_CONSTANTBUFFER_STRUCT, memberInfo.m_declNode->start,
+                    ConcatString("ConstantBuffer<T>'s generic type ", genericName,
+                        " must be a struct, but seen as ", Kind::ToStr(kind.GetKind()).data()));
+            }
+        }
+    }
+
+    optional<int64_t> SemanticOrchestrator::TryFoldSRGSemantic(azslParser::SrgSemanticContext* ctx, size_t semanticTokenType, bool required)
+    {
+        // const ref used, to extend the returned object's temporary life
+        const string& intrinsicVarNameFromLexer = m_lexer->getVocabulary().getLiteralName(semanticTokenType);
+        string_view intrinsicVarName = Trim(intrinsicVarNameFromLexer, "\'");
+
+        auto semanticSymbol = LookupSymbol(UnqualifiedNameView{ intrinsicVarName });
+        if (!semanticSymbol)
+        {
+            if (!required)
+            {
+                return none;
+            }
+
+            ThrowAzslcOrchestratorException(ORCHESTRATOR_LITERAL_REQUIRED_SRG_SEMANTIC, ctx->start,
+                ConcatString(intrinsicVarName, " is required in SRG semantic"));
+        }
+        auto&[sId, sKind] = *semanticSymbol;
+        auto& srgSubInfo = GetCurrentScopeSubInfoAs<ClassInfo>();
+        if (!srgSubInfo.HasMember(sId))
+        {
+            assert(false); // impossible since already verified above. if we fail here, it's a broken program invariant. (incomplete registration)
+        }
+
+        auto& varInfo = sKind.GetSubRefAs<VarInfo>();
+
+        int64_t retValue = 0;
+        if (!TryGetConstExprValueAsInt64(varInfo.m_constVal, retValue))
+        {
+            throw AzslcOrchestratorException(ORCHESTRATOR_INVALID_INTEGER_CONSTANT,
+                ConcatString("Semantic pass error: couldn't get a meaningful integer constant for ", intrinsicVarName));
+        }
+
+        return retValue;
+    }
+
+    void SemanticOrchestrator::ValidateSrgSemantic(azslParser::SrgSemanticContext* ctx)
+    {
+        auto semanticInfo = GetCurrentScopeSubInfoAs<ClassInfo>().Get<SRGSemanticInfo>();
+
+        (*semanticInfo).m_frequencyId = TryFoldSRGSemantic(ctx, azslParser::FrequencyId, true);
+        if (*((*semanticInfo).m_frequencyId) > SRGSemanticInfo_MaxAllowedFrequency)
+        {
+            ThrowAzslcOrchestratorException(ORCHESTRATOR_INVALID_RANGE_FREQUENCY_ID, ctx->Identifier()->getSymbol(),
+                ConcatString("ShaderResourceGroupSemantic must define a FrequencyId with value between 0 and ", SRGSemanticInfo_MaxAllowedFrequency));
+        }
+
+        (*semanticInfo).m_variantFallback = TryFoldSRGSemantic(ctx, azslParser::ShaderVariantFallback);
+    }
+
+    ConstNumericVal SemanticOrchestrator::FoldEvalStaticConstExprNumericValue(tree::TerminalNode* numericLiteralToken, bool hintAsInt) const noexcept(false)
+    {
+        string text = numericLiteralToken->getText();
+        if (hintAsInt)
+        {
+            if (EndsWith(text, "u") || EndsWith(text, "U"))
+            {
+                return static_cast<uint32_t> (std::stoul(text, nullptr, 0/*auto base to support 0#,0x# prefixes*/));
+            }
+
+            return int32_t{std::stoi(text, nullptr, 0)}; // stoi(...) resolves floats as integers, use hintAsInt = false if you need a float
+        }
+        else
+        {
+            return float{ std::stof(text, nullptr) };
+        }
+    }
+
+    ConstNumericVal SemanticOrchestrator::FoldEvalStaticConstExprNumericValue(AstIdExpr* idExp) const
+    {
+        auto uqName = ExtractNameFromIdExpression(idExp);
+        auto maybeSymbol = LookupSymbol(uqName);
+        if (!maybeSymbol)
+        {
+            ThrowAzslcOrchestratorException(ORCHESTRATOR_DEPORTED_METHOD_DEFINITION, idExp->start,
+                ConcatString("in expected constant expression: identifier ", uqName, " not found"));
+        }
+        auto& [id, symbol] = *maybeSymbol;
+        auto what = symbol.GetKind();
+        if (what != Kind::Variable)
+        {
+            ThrowAzslcOrchestratorException(ORCHESTRATOR_DEPORTED_METHOD_DEFINITION, idExp->start,
+                ConcatString("in expected constant expression: identifier ", uqName, " did not refer to a variable, but a ", Kind::ToStr(what).data()));
+        }
+        auto const& var = symbol.GetSubRefAs<VarInfo>();
+        if (holds_alternative<monostate>(var.m_constVal))
+        {
+            ThrowAzslcOrchestratorException(ORCHESTRATOR_DEPORTED_METHOD_DEFINITION, idExp->start,
+                ConcatString("in expected constant expression: variable ", id.m_name, " couldn't be folded to a constant (tip: use --semantic --verbose to diagnose why)"));
+        }
+        return var.m_constVal;
+    }
+
+    namespace
+    {
+        DiagnosticStream FoldFailedCommonMessage(Token* tok, optional<string_view> identifier = none)
+        {
+            verboseCout << tok->getLine();
+            verboseCout << ": constant folding failed for " << (identifier ? *identifier : tok->getText());
+            return verboseCout;
+        };
+    }
+
+    ConstNumericVal SemanticOrchestrator::FoldEvalStaticConstExprNumericValue(VarInfo& varInfo) const
+    {
+        auto* declNode = varInfo.m_declNode;
+        // first thing: constraint storage-class and type modifiers:
+        bool isStaticConst = varInfo.CheckHasAllStorageFlags({StorageFlag::Static, StorageFlag::Const});
+        if (!isStaticConst)
+        {
+            FoldFailedCommonMessage(varInfo.m_declNode->start, string_view{varInfo.m_identifier}) << ": static & const storage flags not set\n";
+            return monostate{};
+        }
+
+        // second: check initializer syntax nodes
+        bool hasInitializer = HasStandardInitializer(declNode);
+        bool isStandardExpr = hasInitializer && declNode->variableInitializer()->standardVariableInitializer()->Expr;
+        if (isStandardExpr)
+        {
+            // we support only the expression sub-variant (not arrayElementInitializers and not samplerStateProperty).
+            AstExpr* expr = declNode->variableInitializer()->standardVariableInitializer()->Expr;
+            return FoldEvalStaticConstExprNumericValue(expr);
+        }
+        else
+        {
+            if (!hasInitializer && isStaticConst)
+            {
+                return int32_t{0}; // non-assigned statics are zero-initialized
+            }
+            else
+            {
+                FoldFailedCommonMessage(varInfo.m_declNode->start, string_view{varInfo.m_identifier}) << ": no standard variable initializer expression\n";
+            }
+        }
+        return monostate{};
+    }
+
+    ConstNumericVal SemanticOrchestrator::FoldEvalStaticConstExprNumericValue(AstExpr* expr) const
+    {
+        if (auto* litExpr = As<azslParser::LiteralExpressionContext*>(expr))
+        {
+            // we are on a literal so we need to restrict down to integers only
+            if (auto* intLit = litExpr->literal()->IntegerLiteral())
+            {   // easy case. we have the literal right there on a plate.
+                return FoldEvalStaticConstExprNumericValue(intLit);
+            }
+            else if (auto* floatLit = litExpr->literal()->FloatLiteral())
+            {   // easy case. we have the literal right there on a plate.
+                return FoldEvalStaticConstExprNumericValue(floatLit, false);
+            }
+            else
+            {
+                FoldFailedCommonMessage(expr->start) << ": initializer is not an integer literal\n";
+            }
+        }
+        else if (auto* idExpr = As<azslParser::IdentifierExpressionContext*>(expr))
+        {
+            // in case of initializers that refers to id, if they are variable they will be already parsed and folded.
+            auto uqName = ExtractNameFromIdExpression(idExpr->idExpression());
+            auto maybeSymbol = LookupSymbol(uqName);
+            if (!maybeSymbol)
+            {
+                FoldFailedCommonMessage(expr->start) << ": initializer referred to an undeclared symbol " << uqName << "\n";
+                return monostate{};
+            }
+            auto& [id, symbol] = *maybeSymbol;
+            auto what = symbol.GetKind();
+            if (what != Kind::Variable)
+            {
+                FoldFailedCommonMessage(expr->start) << ": initializer identifier " + uqName + " did not refer to a variable, but a " + Kind::ToStr(what).data();
+                return monostate{};
+            }
+            auto const& var = symbol.GetSubRefAs<VarInfo>();
+            return var.m_constVal;
+        }
+        else
+        {
+            FoldFailedCommonMessage(expr->start) << ": variable initializer is not a supported expression\n";
+        }
+        return monostate{};
+    }
+
+    QualifiedName SemanticOrchestrator::MakeFullyQualified(UnqualifiedNameView unqualifiedName) const
+    {
+        return ::AZ::ShaderCompiler::MakeFullyQualified(m_scope->m_currentScopePath, unqualifiedName);
+    }
+
+    TypeClass SemanticOrchestrator::GetTypeClass(IdentifierUID typeId) const
+    {
+        TypeClass toReturn = TypeClass::IsNotType;
+        auto* idKind = m_symbols->GetIdAndKindInfo(typeId.GetName());
+        if (idKind)  // found
+        {
+            auto& [_, kind] = *idKind;
+            switch (kind.GetKind())
+            {
+            case Kind::Struct: toReturn = TypeClass::Struct;
+                break;
+            case Kind::Class: toReturn = TypeClass::Class;
+                break;
+            case Kind::Enum: toReturn = TypeClass::Enum;
+                break;
+            case Kind::Interface: toReturn = TypeClass::Interface;
+                break;
+            case Kind::TypeAlias: toReturn = TypeClass::Alias;
+                break;
+            default:
+                if (auto* asType = kind.GetSubAs<TypeRefInfo>())
+                {
+                    toReturn = asType->m_typeClass;
+                }
+                break;
+            }
+        }
+        return toReturn;
+    }
+
+    IdentifierUID SemanticOrchestrator::LookupType(UnqualifiedNameView typeName, OnNotFoundOrWrongKind policy, optional<size_t> sourceline /*=none*/) const
+    {
+        auto getErrorIUID = [policy, typeName](){return policy == OnNotFoundOrWrongKind::Empty ? IdentifierUID{} : IdentifierUID{QualifiedName{typeName}};};
+        IdAndKind* type = LookupSymbol(UnqualifiedNameView{typeName});
+        if (!type)
+        {
+            if (policy == OnNotFoundOrWrongKind::Diagnose)
+            {
+                ThrowAzslcOrchestratorException(ORCHESTRATOR_DEPORTED_METHOD_DEFINITION,
+                    sourceline, none, ConcatString(" type ", string{ typeName }, " requested but not found."));
+            }
+            else
+            {
+                return getErrorIUID();
+            }
+        }
+        // found..
+        const auto& [uid, kind] = *type;
+        // ..is it of correct Kind ?
+        bool isType = IsKindOneOfTypeRelated(kind.GetKind());
+        if (!isType)
+        {
+            if (policy == OnNotFoundOrWrongKind::Diagnose)
+            {
+                ThrowAzslcOrchestratorException(ORCHESTRATOR_DEPORTED_METHOD_DEFINITION,
+                    sourceline, none, ConcatString(" type ", typeName.data(),
+                        " requested but found as ", Kind::ToStr(kind.GetKind()).data()));
+            }
+            else
+            {
+                return getErrorIUID();
+            }
+        }
+        return {type->first};
+    }
+
+    bool SemanticOrchestrator::TryFoldGenericArrayDimensions(ExtractedComposedType& extType, vector<tree::TerminalNode*>& genericDims) const
+    {
+        for (auto dim : genericDims)
+        {
+            auto cVal = FoldEvalStaticConstExprNumericValue(dim);
+            int64_t nextDim = ExtractValueAsInt64(cVal, -1);
+            if (nextDim < 0)
+            {
+                verboseCout << "SemanticOrchestrator::TryFoldGenericArrayDimensions could not fold the next dimension (" << nextDim << ")!";
+                return false;
+                // If we might want to allow such cases use this instead:
+                // extType.m_genericDimensions.PushBack(ArrayDimensions::unknown);
+            }
+            else
+            {
+                int asInt = static_cast<int>(nextDim);
+                extType.m_genericDimensions.PushBack(asInt);
+            }
+        }
+
+        return true;
+    }
+
+    ExtendedTypeInfo SemanticOrchestrator::CreateExtendedTypeInfo(AstType* ctx, ArrayDimensions dims, Packing::MatrixMajor mtxMajor) const
+    {
+        vector<tree::TerminalNode*> genericDims;
+        auto extType = ExtractComposedTypeNamesFromAstContext(ctx, &genericDims);
+        if (!TryFoldGenericArrayDimensions(extType, genericDims))
+        {
+            ThrowAzslcOrchestratorException(ORCHESTRATOR_DEPORTED_METHOD_DEFINITION, ctx->start,
+                ConcatString("SemanticOrchestrator::CreateExtendedTypeInfo failed for type (", ctx->getText(), ")"));
+        }
+        return CreateExtendedTypeInfo(extType, dims, mtxMajor);
+    }
+
+    ExtendedTypeInfo SemanticOrchestrator::CreateExtendedTypeInfo(AstFuncType* ctx, ArrayDimensions dims, Packing::MatrixMajor mtxMajor) const
+    {
+        return ctx->Void() ?
+            ExtendedTypeInfo{ CreateTypeRefInfo(UnqualifiedNameView{AZ::ShaderCompiler::Predefined::Void[0]}), {}, {}, {}, mtxMajor } :
+            CreateExtendedTypeInfo(ctx->type(), dims, mtxMajor);
+    }
+
+    ExtendedTypeInfo SemanticOrchestrator::CreateExtendedTypeInfo(const ExtractedComposedType& extractedComposed, ArrayDimensions dims, Packing::MatrixMajor mtxMajor) const
+    {
+        TypeRefInfo core = CreateTypeRefInfo(extractedComposed.m_core);
+        TypeRefInfo generic = CreateTypeRefInfo(extractedComposed.m_genericParam);
+        if (core.m_typeClass == TypeClass::Alias)
+        {
+            // if we're referring to a type alias, follow the trail and collapse the canonical type to its real target.
+            // it's unnecessary to loop on this, because the previously registered typealiases are necessarily collapsed.
+            // and the first registration is necessarily of an existing type (non alias). by recurrence there can only be a distance of 1 to resolve.
+            const TypeAliasInfo* targetAlias = m_symbols->GetAsSub<TypeAliasInfo>(core.m_typeId);
+            return targetAlias->m_canonicalType;
+        }
+        return ExtendedTypeInfo{core, generic, dims, extractedComposed.m_genericDimensions, mtxMajor};
+    }
+
+    IdAndKind* SemanticOrchestrator::GetSymbolHiddenInBase(IdentifierUID hidingCandidate)
+    {
+        IdAndKind* found = nullptr;
+        auto& [containingScopeId, containingScopeKind] = GetCurrentParentScopeIdAndKind();
+        if (containingScopeKind.GetKind() == Kind::Class)  // only class can have bases in AZSL
+        {
+            // get currently parsed class info:
+            auto& curClassInfo = GetCurrentParentScopeSubInfoAs<ClassInfo>();
+            // look for a match in any base
+            for (const IdentifierUID& base : curClassInfo.m_bases)
+            {
+                auto maybeBaseClass = m_symbols->GetIdAndKindInfo(base.m_name);
+                if (maybeBaseClass
+                    && maybeBaseClass->second.GetKind() == Kind::Interface)  // bases must be interfaces but we don't assume it's enforced prior to calls to this function
+                {
+                    const auto& baseClassInfo = maybeBaseClass->second.GetSubRefAs<ClassInfo>();
+                    bool baseHasSameNameMember = baseClassInfo.HasMember(hidingCandidate.GetNameLeaf());
+                    if (baseHasSameNameMember)
+                    {
+                        if (found)
+                        {
+                            throw AzslcOrchestratorException(ORCHESTRATOR_MULTIPLE_HIDDEN_SYMBOLS,
+                                ConcatString("Found multiple symbols hidden by ", hidingCandidate.m_name,
+                                    " in bases of ", containingScopeId.m_name,
+                                    ". First was ", found->first.m_name,
+                                    ", now also found in ", base.m_name, "."));
+                        }
+                        // reconstruct the UID found, and return that.
+                        string reconstructedPath = JoinPath(base.m_name, hidingCandidate.GetNameLeaf());
+                        found = m_symbols->GetIdAndKindInfo(QualifiedName{reconstructedPath});
+                    }
+                }
+            }
+        }
+        return found;
+    }
+
+    bool SemanticOrchestrator::IsScopeStructClassInterface() const
+    {
+        const KindInfo& scopeKind = m_symbols->GetIdAndKindInfo(m_scope->GetNameOfCurScope())->second;
+        return scopeKind.IsKindOneOf(Kind::Struct, Kind::Class, Kind::Interface);
+    }
+
+    void SemanticOrchestrator::MakeAndEnterAnonymousScope(string_view decorationPrefix, Token* scopeFirstToken)
+    {
+        UnqualifiedName unnamedBlockCode{ConcatString("$", decorationPrefix, m_anonymousCounter)};
+        AddIdentifier(unnamedBlockCode, Kind::Namespace, scopeFirstToken->getLine());
+        m_scope->EnterScope(unnamedBlockCode, scopeFirstToken->getTokenIndex());
+        ++m_anonymousCounter;
+    }
+}

+ 434 - 0
src/AzslcSemanticOrchestrator.h

@@ -0,0 +1,434 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include "AzslcScopeTracker.h"
+#include "PreprocessorLineDirectiveFinder.h"
+#include "AzslcUnboundedArraysValidator.h"
+
+namespace AZ::ShaderCompiler
+{
+    //! For argument of RegisterFunction
+    enum class AsFunc
+    {
+        Declaration,
+        Definition
+    };
+
+    //! Deals with jobs that requires access to both Scope and SymbolTable
+    struct SemanticOrchestrator
+    {
+        SemanticOrchestrator(SymbolAggregator* sema, ScopeTracker* scope, azslLexer* lexer,
+            PreprocessorLineDirectiveFinder* preprocessorLineDirectiveFinder = nullptr);
+
+        //! Helper shortcut: uses the current scope as a starting location to lookup a symbol.
+        //! Returns whatever SymbolAggretator's eponymous returns.
+        decltype(auto) LookupSymbol(UnqualifiedNameView name) const
+        {
+            return m_symbols->LookupSymbol(m_scope->m_currentScopePath, name);
+        }
+        //! non-const version
+        decltype(auto) LookupSymbol(UnqualifiedNameView name)
+        {
+            return m_symbols->LookupSymbol(m_scope->m_currentScopePath, name);
+        }
+
+        //! Does this symbol resolves to an existing ID from the current scope ?
+        //! Helper shortcut: will concatenate your symbol name to current scope before passing to SymbolAggregator
+        //! Returns whatever SymbolAggretator's eponymous returns.
+        decltype(auto) AddIdentifier(UnqualifiedNameView usym, Kind kind, optional<size_t> lineNumber = none)
+        {
+            return m_symbols->AddIdentifier(MakeFullyQualified(usym), kind, lineNumber);
+        }
+
+        auto GetCurrentScopeIdAndKind() -> IdAndKind&;
+
+        auto GetCurrentParentScopeIdAndKind() -> IdAndKind&;
+
+        //! Shorthand for get< AlternativeType > of the current-scope KindInfo's m_subInfo member.
+        template< typename AlternativeType >
+        auto GetCurrentScopeSubInfoAs() noexcept(false) -> AlternativeType&
+        {
+            static_assert(KindInfo::isSubAlternative_v< AlternativeType >,
+                          "please pass types that belongs to KindInfo::m_subInfo");
+            auto& curScope = GetCurrentScopeIdAndKind();
+            return curScope.second.GetSubRefAs<AlternativeType>();
+        }
+
+        //! Same as above but for the parent scope
+        template< typename AlternativeType >
+        auto GetCurrentParentScopeSubInfoAs() noexcept(false) -> AlternativeType&
+        {
+            static_assert(KindInfo::isSubAlternative_v< AlternativeType >,
+                          "please pass types that belongs to KindInfo::m_subInfo");
+            auto& parentScopeIdKind = GetCurrentParentScopeIdAndKind();
+            return parentScopeIdKind.second.GetSubRefAs<AlternativeType>();
+        }
+
+        //! Execute a class member function (method) definition
+        //! @param name         the function name
+        //! @param className    the C in C::Method(){}
+        auto RegisterDeportedMethod(UnqualifiedNameView name, azslParser::UserDefinedTypeContext* className, AstFuncSig* ctx) -> IdAndKind&;
+
+        //! Execute a function definition or declaration registration into the intermediate representation.
+        //! @param name is UNqualified in this version to allow to simplify the API for callers which don't care about symbols and scopes
+        //!             this is notably used by the normal function declaration visitor where only the name is parseable through a simpler Identifier rule.
+        auto RegisterFunction(UnqualifiedNameView name, AstFuncSig* ctx, AsFunc statementGenre) -> IdAndKind&;
+
+        //! Execute a function definition or declaration registration into the intermediate representation.
+        //! @param name is qualified in this version, to allow callers to do the scope stitching or lookup and resolution themselves.
+        //!             this is notably used by deported method definition registration, where classname C in C::M has to be resolved by the caller
+        auto RegisterFunction(QualifiedNameView name, AstFuncSig* ctx, AsFunc statementGenre) -> IdAndKind&;
+
+        auto RegisterFunctionDeclarationAndAddSeenat(UnqualifiedNameView name, AstFuncSig* signature) -> IdAndKind&;
+
+        template< typename ContextType >
+        auto RegisterStructuredType(ContextType* ctx, Kind kind) -> IdAndKind&
+        {
+            auto const& idText     = ctx->Name->getText();
+            size_t line            = ctx->Name->getLine();
+            verboseCout << line << ": " << Kind::ToStr(kind) << " decl: " << idText << "\n";
+            auto uqNameView        = UnqualifiedNameView{idText};
+            if (auto* param = ExtractSpecificParent<azslParser::FunctionParamContext>(ctx))
+            {
+                // because arguments participate in the signature of the function,
+                // and the scope of arguments is that very function, we have a catch-22.
+                throw AzslcOrchestratorException(ORCHESTRATOR_NO_INLINE_UDT_IN_PARAMETERS, param->start,
+                                                 ConcatString(" introducing new type (", idText,
+                                                              ") within function arguments is ill-formed"));
+            }
+            // register the new identifier:
+            IdAndKind& symbol      = AddIdentifier(uqNameView, kind, line);
+            auto& [uid, info]      = symbol;
+            // now fillup what we can about the kindinfo:
+            auto& classInfo        = info.GetSubRefAs<ClassInfo>();
+            classInfo.m_kind       = kind;
+            classInfo.m_declNodeVt = ctx;
+
+            if (kind == Kind::ShaderResourceGroupSemantic)
+            {
+                SRGSemanticInfo semanticInfo;
+                classInfo.m_subInfo = semanticInfo;
+            }
+
+            if (kind == Kind::Enum)
+            {
+                EnumerationInfo enumInfo;
+                auto* enumCtx   = dynamic_cast<azslParser::EnumDefinitionContext*>(ctx);
+                auto* scopedCtx = dynamic_cast<azslParser::ScopedEnumContext*>(enumCtx->enumKey());
+                enumInfo.m_isScoped = scopedCtx != nullptr;
+
+                // If we decide to support the enum struct|class name : type syntax, the underlying type will come from there
+                // The underlying type can be used in SemanticOrchestrator::GetTypeClass(IdentifierUID typeId) later if we need type casting
+                // this int is provisional, because in reality it depends on the collective values of enumerators.
+                ExtractedTypeExt enumType = { UnqualifiedNameView("int"), nullptr };
+                enumInfo.m_underlyingType = CreateTypeRefInfo(enumType);
+
+                classInfo.m_subInfo = enumInfo;
+            }
+
+            // If this is a structure inside an SRG, add it to the map:
+            const auto& [scopeId, scopeKind] = GetCurrentScopeIdAndKind();
+            if (kind == Kind::Struct && scopeKind.GetKind() == Kind::ShaderResourceGroup)
+            {
+                // access the class SRGInfo and add a member:
+                auto& srgInfo = GetCurrentScopeSubInfoAs<SRGInfo>();
+                srgInfo.m_structs.push_back(uid);
+            }
+            // If nested, register it as a member in its parent
+            if (scopeKind.IsKindOneOf(Kind::Struct, Kind::Class, Kind::Interface))
+            {
+                auto& parentInfo = GetCurrentScopeSubInfoAs<ClassInfo>();
+                parentInfo.PushMember(uid, kind);
+            }
+
+            return symbol;
+        }
+
+        auto RegisterTypeAlias(string_view newIdentifier, AstFuncType* existingTypeCtx, azslParser::TypeAliasingDefinitionStatementContext* ctx) -> IdAndKind&;
+
+        auto RegisterSRGSemantic(AstSRGSemanticDeclNode* ctx) -> IdAndKind&;
+
+        auto RegisterInterface(AstInterfaceDeclNode* ctx) -> IdAndKind&;
+
+        auto RegisterClass(AstClassDeclNode* ctx) -> IdAndKind&;
+
+        auto RegisterStruct(AstStructDeclNode* ctx) -> IdAndKind&;
+
+        auto RegisterEnum(AstEnumDeclNode* ctx)-> IdAndKind&;
+
+        auto RegisterVar(Token* nameIdentifier, AstUnnamedVarDecl* ctx) -> IdAndKind*;
+
+        void RegisterNamelessFunctionParameter(azslParser::FunctionParamContext* ctx);
+
+        void FillOutSrgField(AstNamedVarDecl* ctx, VarInfo &varInfo, IdentifierUID varUid, ArrayDimensions &arrayDims);
+
+        auto ExtractSamplerState(AstVarInitializer* ctx) -> SamplerStateDesc;
+
+        auto RegisterSRG(AstSRGDeclNode* ctx) -> IdAndKind&;
+
+        void RegisterSRGSemanticMember(AstSRGSemanticMemberDeclNode* ctx);
+
+        void RegisterEnumerator(azslParser::EnumeratorDeclaratorContext* ctx);
+
+        void RegisterBases(azslParser::BaseListContext* ctx);
+
+        void RegisterSeenat(IdAndKind& idPair, const TokensLocation& location);
+
+        //! Will register one seenat for each found symbol in the nested name expression,
+        //! `Base::Middle::Leaf` will register `Base`, `Base::Middle` and `Base::Middle::Leaf`
+        //! It assumes starting scope is current scope.
+        void RegisterSeenat(AstIdExpr* ctx);
+        //! Same but for a context withing a member-access-expression where the scope is relative to the scope of the type of LHS.
+        void RegisterSeenat(AstIdExpr* ctx, QualifiedNameView startupScope);
+
+        //! Check that the type of LHS expression (in a member access expression context) satisfies well-formed semantics
+        //! returns .first==true if is valid, and .first==false if the semantic fails to check.
+        //!         .second is the typeof(LHS)
+        auto VerifyLHSExprOfMAExprIsValid(azslParser::MemberAccessExpressionContext* ctx) const -> pair<bool, QualifiedName>;
+
+        //! Resolve the type from the expression, look it up, and verify that it is a kind that may hold sub-members.
+        auto VerifyTypeIsScopeComposable(azslParser::ExpressionContext* typeScope) const -> pair<bool, QualifiedName>;
+
+        //! look up the type, verify that it exists and is a kind that may hold sub-members.
+        //! takes supplementary parameters for better verbose or warning diagnostics.
+        auto VerifyTypeIsScopeComposable(QualifiedNameView lhsTypeName, optional<string> lhsExpressionText = none, optional<size_t> line = none) const -> pair<bool, QualifiedName>;
+
+        //! assemble a type (left) and an idexpr (right) to see if it forms a symbol that exists, and extracts its type.
+        auto ComposeMemberNameWithScopeAndGetType(QualifiedName scopingType, AstIdExpr* rhsMember) const -> QualifiedName;
+
+        // This family of functions will attempt to evaluate the type of an expression.
+        // returns: a typename. take care that unregistered types will probably not be rooted.
+        auto TypeofExpr(azslParser::ExpressionContext* ctx) const -> QualifiedName;
+        auto TypeofExpr(azslParser::ExpressionExtContext* ctx) const -> QualifiedName;
+        auto TypeofExpr(azslParser::OtherExpressionContext* ctx) const -> QualifiedName;
+        auto TypeofExpr(AstType* ctx) const -> QualifiedName;
+        auto TypeofExpr(AstFuncType* ctx) const -> QualifiedName;
+        auto TypeofExpr(AstIdExpr* ctx) const -> QualifiedName;
+        auto TypeofExpr(azslParser::IdentifierExpressionContext* ctx) const -> QualifiedName;
+        auto TypeofExpr(azslParser::MemberAccessExpressionContext* ctx) const -> QualifiedName;
+        auto TypeofExpr(azslParser::FunctionCallExpressionContext* ctx) const -> QualifiedName;
+        auto TypeofExpr(azslParser::ArrayAccessExpressionContext* ctx) const -> QualifiedName;
+        auto TypeofExpr(azslParser::ParenthesizedExpressionContext* ctx) const -> QualifiedName;
+        auto TypeofExpr(azslParser::CastExpressionContext* ctx) const -> QualifiedName;
+        auto TypeofExpr(azslParser::ConditionalExpressionContext* ctx) const -> QualifiedName;
+        auto TypeofExpr(azslParser::AssignmentExpressionContext* ctx) const -> QualifiedName;
+        auto TypeofExpr(azslParser::NumericConstructorExpressionContext* ctx) const -> QualifiedName;
+        auto TypeofExpr(azslParser::TypeofExpressionContext* ctx) const -> QualifiedName;
+        auto TypeofExpr(azslParser::LiteralExpressionContext* ctx) const -> QualifiedName;
+        auto TypeofExpr(azslParser::LiteralContext* ctx) const -> QualifiedName;
+        auto TypeofExpr(azslParser::CommaExpressionContext* ctx) const -> QualifiedName;
+
+        //! Parse the AST from a variable declaration and attempt to extract array dimensions integer constants [dim1][dim2]...
+        //! Return: <true> on success, <false> otherwise
+        bool TryFoldArrayDimensions(AstUnnamedVarDecl* ctx, ArrayDimensions& arrayDimensions);
+
+        void ValidateClass(azslParser::ClassDefinitionContext* ctx) noexcept(false);
+
+        void ValidateFunctionAndRegisterFamilySeenat(azslParser::LeadingTypeFunctionSignatureContext* ctx) noexcept(false);
+
+        // Verify if a symbol overrides a symbol in a base (parent) of the current scope.
+        // Currently Azsl can only have interface as parent of classes. And interfaces can only have function signatures.
+        // Therefore for now, in practice this function can only be used to check if a method overrides a function in a base interface.
+        // Returns: the symbol of the overridden function if found. nullopt if hidingCandidate doesn't hide.
+        // Will diagnose-throw if multiple bases have the candidate symbol's name.
+        auto GetSymbolHiddenInBase(IdentifierUID hidingCandidate) -> IdAndKind*;
+
+        void ValidateSrg(azslParser::SrgDefinitionContext* ctx) noexcept(false);
+
+        void ValidateSrgSemantic(azslParser::SrgSemanticContext* ctx) noexcept(false);
+
+        // That function is to get the interpreted numerical value of a literal token.
+        // Careful this should be used only on IntegerLiteral tokens (floats will partially parse or throw).
+        // Throws: out_of_range in case of overflow/underflow, or invalid_argument for unsupported letters.
+        // Hint: The function has no knowledge of upper contexts, so we can hint if we prefer the const resolved as integer or float
+        //   (by default it will try to resolve it as integer)
+        // Return: either a int32, a uint32 or a float. but not a monostate.
+        auto FoldEvalStaticConstExprNumericValue(tree::TerminalNode* numericLiteralToken, bool hintAsInt = true) const noexcept(false) -> ConstNumericVal;
+
+        // Extracted method from the AstIntLiteralOrId* overload for more flexibility.
+        auto FoldEvalStaticConstExprNumericValue(AstIdExpr* idExp) const -> ConstNumericVal;
+
+        // Primitive constant folding system. Evaluate integers in static const variables as much as possible.
+        // The returned value can hold any of 3 types:
+        //  - an empty monostate (no evaluable value detected, could be: unsupported expression, float, overflow, or non static-const..)
+        //  - signed int64
+        //  - unsigned int64
+        auto FoldEvalStaticConstExprNumericValue(VarInfo& varInfo) const -> ConstNumericVal;
+
+        auto FoldEvalStaticConstExprNumericValue(AstExpr* expr) const -> ConstNumericVal;
+
+        // Create an absolute path symbol for a new name, in the context of the current scope.
+        auto MakeFullyQualified(UnqualifiedNameView unqualifiedName) const -> QualifiedName;
+
+        // helper to query a type class from an ID
+        auto GetTypeClass(IdentifierUID typeId) const -> TypeClass;
+
+        enum class OnNotFoundOrWrongKind { Diagnose, Empty, CopeByCopy };
+        //! Shorthand for symbol lookup, but with supplementary checks, specifics to types.
+        //! throws if: the found symbol is not a type, or no found symbol and policy is Diagnose.
+        auto LookupType(UnqualifiedNameView typeName, OnNotFoundOrWrongKind policy = OnNotFoundOrWrongKind::CopeByCopy, optional<size_t> sourceline = none) const noexcept(false) -> IdentifierUID;
+
+        //! Find and return a registered type from an AST node. Will also resolve typeof expressions.
+        //! could work from any sort of context that has an ExtractTypeNameFromAstContext override
+        template <typename TypeCtx>
+        auto LookupType(TypeCtx* ctx, OnNotFoundOrWrongKind policy = OnNotFoundOrWrongKind::CopeByCopy) const -> IdentifierUID
+        {
+            IdentifierUID typeRef;
+            UnqualifiedName uqName;
+            AstTypeofNode* typeofCtx = ExtractTypeofAstNode(ctx);
+            if (typeofCtx)
+            {
+                uqName = UnqualifiedName{TypeofExpr(typeofCtx)};
+            }
+            else
+            {
+                auto core = ExtractComposedTypeNamesFromAstContext(ctx).m_core;
+                // the concept here, is to activate recursive lookup in case we have nested generics and/or typeof, in any combination.
+                // in practice for now, it will never recurse;
+                // because only generic parameters may be further AstNodes. And since we ignore them for now (collapsing behavior), we're set.
+                uqName = core.m_node ? UnqualifiedName{LookupType(core.m_node, policy).m_name} : core.m_name;
+            }
+            auto* idkind = LookupSymbol(uqName);
+            if (idkind)
+            {
+                if (idkind->second.GetKind() == Kind::TypeAlias)
+                {
+                    // recurse until not an alias !
+                    typeRef = LookupType(UnqualifiedNameView{GetTypeName(idkind)}, policy);
+                }
+                else
+                {
+                    // found core type (UDT or collapsed predefined)
+                    typeRef = idkind->first;
+                }
+            }
+            else
+            {   //! otherwise, we need to do damage control.
+                // TODO: if it is a matrix<float,3,4> it needs to be canonicalized to float3x4
+                if (policy == OnNotFoundOrWrongKind::Diagnose)
+                {
+                    throw std::runtime_error(DiagLine(ctx->start) + uqName + " is not a supported type expression ?");
+                }
+                PrintWarning(Warn::W3, ctx->start, "unidentified name ", uqName, " in type expression");
+                if (policy == OnNotFoundOrWrongKind::CopeByCopy)
+                {
+                    typeRef.m_name = QualifiedName{uqName};  // try to shove it, if we defend enough in code downstream this could end up working.
+                }
+            }
+            return typeRef;
+        }
+
+        // lookup the symbol database for a type of a given name (or discover the name through an Ast context) and compose a TypeRefInfo
+        // ArgumentType maybe UnqualifiedNameView or a TypeCtx (AstType or AstFuncType)
+        template< typename ContextOrNameT >
+        auto CreateTypeRefInfo(ContextOrNameT typeNameOrCtx, OnNotFoundOrWrongKind policy = OnNotFoundOrWrongKind::CopeByCopy) const -> TypeRefInfo
+        {
+            auto typeId      = LookupType(typeNameOrCtx, policy);
+            auto tClass      = GetTypeClass(typeId);
+            auto arithmetic  = IsNonGenericArithmetic(tClass) ? CreateArithmeticTypeInfo(typeId.GetName()) : ArithmeticTypeInfo{}; // TODO: canonicalize generics
+            return TypeRefInfo{typeId, arithmetic, tClass};
+        }
+
+        // shortcut helper, when we have a node ast, we use it in priority thanks to all the superior access it provides (for typeof)
+        auto CreateTypeRefInfo(const ExtractedTypeExt& extractedType) const -> TypeRefInfo
+        {
+            return extractedType.m_node ? CreateTypeRefInfo(extractedType.m_node) : CreateTypeRefInfo(extractedType.m_name);
+        }
+
+        // Just a helper function to compose the bigger version, that contains more data that can't be stored in the core type (TypeRefInfo).
+        // Array dimensions and mtxMajor are usually stored in VarInfo for example. If you have a custom way to discover them, use this helper to make your own ExtendedTypeInfo
+        auto CreateExtendedTypeInfo(AstType* ctx, ArrayDimensions dims, Packing::MatrixMajor mtxMajor) const -> ExtendedTypeInfo;
+        auto CreateExtendedTypeInfo(AstFuncType* ctx, ArrayDimensions dims, Packing::MatrixMajor mtxMajor) const -> ExtendedTypeInfo;
+        // Helper func which folds any possible generic dimensions into the extracted composed type
+        bool TryFoldGenericArrayDimensions(ExtractedComposedType& extType, vector<tree::TerminalNode*>& genericDims) const;
+
+        // another helper to streamline what to do directly with the result from ExtractTypeNameFromAstContext function families.
+        auto CreateExtendedTypeInfo(const ExtractedComposedType& extractedComposed, ArrayDimensions dims, Packing::MatrixMajor mtxMajor) const -> ExtendedTypeInfo;
+
+        //! check if current scope is a structured user defined type
+        bool IsScopeStructClassInterface() const;
+
+        //! Find a concrete function from an overload-set and an argument list.
+        //! (adjust the shoot of a lookup to something more precise in case that it's possible)
+        //! if maybeOverloadSet is not an overload-set the function returns identity
+        auto ResolveOverload(IdAndKind* maybeOverloadSet, azslParser::ArgumentListContext* argumentListCtx) const -> IdAndKind*;
+
+        //! Generate a unique name, create a corresponding namespace symbol, and enter its scope
+        void MakeAndEnterAnonymousScope(string_view decorationPrefix, Token* scopeFirstToken);
+
+    private:
+        //! for internal use when encountering unresolved symbols by lookup.
+        void DiagnoseUndeclaredSub(Token* atToken, QualifiedNameView startupScope, string partialName) const;
+
+        auto TryFoldSRGSemantic(azslParser::SrgSemanticContext* ctx, size_t semanticTokenType, bool required = false) -> optional<int64_t>;
+
+        auto CreateDecorationOfFunction(azslParser::FunctionParamsContext* parametersContext) const -> string;
+
+        auto CreateDecoratedIdentityOfFunction(QualifiedNameView name, azslParser::FunctionParamsContext* parametersContext) const -> QualifiedName;
+
+        //! e.g. provided "int a; X GetX(int);" then from expression "(a, GetX(2), true)" MangleArgumentList returns "(?int,/X,?bool)"
+        auto MangleArgumentList(azslParser::ArgumentListContext* ctx) const -> string;
+
+        //! queries whether a function has default parameters
+        bool HasAnyDefaultParameterValue(const IdentifierUID& functionUid) const;
+
+        //! A helper method. Configures the exception system to report the original
+        //! source file and line location given the line number in the file that is being compiled.
+        void OverrideAzslcExceptionFileAndLine(size_t azslLineNumber) const;
+
+        //! Same as bove, but gets the @azslLineNumber from the @errorToken.
+        void OverrideAzslcExceptionFileAndLine(Token* errorToken) const { OverrideAzslcExceptionFileAndLine(errorToken->getLine()); }
+
+        void ThrowAzslcOrchestratorException(uint32_t errorCode, optional<size_t> line, optional<size_t> column, const string& message) const
+        {
+            if (line)
+            {
+                OverrideAzslcExceptionFileAndLine(line.value());
+            }
+            throw AzslcOrchestratorException(errorCode, line, column, message);
+        }
+
+        void ThrowAzslcOrchestratorException(uint32_t errorCode, Token* token, const string& message) const
+        {
+            OverrideAzslcExceptionFileAndLine(token);
+            throw AzslcOrchestratorException(errorCode, token, message);
+        }
+
+        void ThrowRedeclarationAsDifferentKindInternal(string_view symbolName, Kind newKind, const KindInfo& kindInfo, size_t lineNumber) const
+        {
+            OverrideAzslcExceptionFileAndLine(lineNumber);
+            ThrowRedeclarationAsDifferentKind(symbolName, newKind, kindInfo, lineNumber);
+        }
+
+        void CheckQualifersAreOnlyInlineOrStatic(TypeQualifier qualifier, size_t line) const
+        {
+            auto okFlags = TypeQualifier{ StorageFlag::Inline } | StorageFlag::Static;
+            if (qualifier & ~okFlags)
+            {
+                ThrowAzslcOrchestratorException(ORCHESTRATOR_DISALLOWED_FUNCTION_MODIFIER, line,
+                    none, " Functions can only have either static or inline modifiers.");
+            }
+        }
+
+    public:
+        SymbolAggregator* m_symbols;
+        ScopeTracker*     m_scope;
+        azslLexer*        m_lexer;
+        PreprocessorLineDirectiveFinder* m_preprocessorLineDirectiveFinder;
+        UnboundedArraysValidator m_unboundedArraysValidator;
+
+        //! cached property informing of the presence of at least one input attachment use.
+        bool              m_subpassInputSeen = false;
+
+    private:
+        // remember frequencyId to srg association, for semantic validation
+        unordered_map<int64_t, IdentifierUID> m_frequencyToSrg;
+        int m_anonymousCounter;
+    };
+}

+ 337 - 0
src/AzslcSymbolAggregator.cpp

@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include "AzslcSymbolAggregator.h"
+
+namespace AZ::ShaderCompiler
+{
+    template <auto N>
+    void AddTypeBag(const AZ::ShaderCompiler::Predefined::Bag<N>& bag, SymbolTable& st)
+    {
+        for (const char* symbol : bag.m_bag)
+        {
+            QualifiedName azirName{"?"}; // AZIR prefix for predefined types
+            azirName += symbol;
+            auto& [uid, kindInfo] = st.AddIdentifier(azirName, Kind::Type);  // the kind is Type because all predefined are stored as such.
+            auto& typeInfo        = kindInfo.GetSubAfterInitAs<Kind::Type>();
+            auto typeClass        = TypeClass::FromStr(bag.m_name);
+            auto arithmetic       = IsNonGenericArithmetic(typeClass) ? CreateArithmeticTypeInfo(azirName) : ArithmeticTypeInfo{}; // TODO: canonicalize generics
+            typeInfo = TypeRefInfo{uid, arithmetic, typeClass};
+        }
+    }
+
+    static SymbolTable InitFixedTable()
+    {
+        SymbolTable st;
+        // Register the global namespace as a resident symbol in the database:
+        // This allows canonical treatment when working with `current scope`.
+        st.AddIdentifier(QualifiedNameView("/"), Kind::Namespace);
+
+        // temporarily disable the verbosity since it completely bloats the output
+        verboseCout << " registering of all predefined types in fixed symbol table (kept silent)...";
+        bool oldVerbosity = verboseCout.m_on;
+        verboseCout.m_on = false;
+
+        // another helpful canonicalization is for types.
+        // let's register all predefined so that TypeRef can be simplified to IdentifierUID
+        // the (pack op ...) is a C++17 unary-right-fold-expression using comma as op
+        std::apply([&st](auto&&... args) {(AddTypeBag(args, st), ...);}, AZ::ShaderCompiler::Predefined::All);
+
+        verboseCout.m_on = oldVerbosity;
+        verboseCout << " done\n";
+
+        return st;
+    }
+
+    SymbolAggregator::SymbolAggregator()
+        : m_fixed{ InitFixedTable() }
+    {
+    }
+
+    bool SymbolAggregator::HasIdentifier(QualifiedNameView symbol) const
+    {
+        return m_elastic.HasIdentifier(symbol) || m_fixed.HasIdentifier(symbol);
+    }
+
+    IdAndKind* SymbolAggregator::GetIdAndKindInfo(QualifiedNameView symbol)
+    {
+        auto toReturn = m_elastic.GetIdAndKindInfo(symbol);
+        if (!toReturn)
+        {   // I am going to go ahead and tolerate returning of non const data to the fixed table.
+            // Not ideal, but will cause much lost time and pondering, if we don't consider it through non-const Gets.
+            toReturn = const_cast<std::remove_const_t<decltype(m_fixed)>&>(m_fixed).GetIdAndKindInfo(symbol);
+        }
+        return toReturn;
+    }
+
+    const IdAndKind* SymbolAggregator::GetIdAndKindInfo(QualifiedNameView symbol) const
+    {
+        auto toReturn = m_elastic.GetIdAndKindInfo(symbol);
+        if (!toReturn)
+        {
+            toReturn = m_fixed.GetIdAndKindInfo(symbol);
+        }
+        return toReturn;
+    }
+
+    IdAndKind& SymbolAggregator::AddIdentifier(QualifiedNameView symbol, Kind kind, optional<size_t> lineNumber /*=none*/, AddIdentifierChecks checkPolicy /*= AddIdentifierChecks::ReservedNames*/)
+    {
+        // check against reserved names
+        static const std::unordered_set<string_view> ReservedNames =
+        {
+            "/Root_Constants",
+            "AZ_USE_SUBPASSINPUT",
+            RootConstantsViewName
+        };
+
+        if (checkPolicy == AddIdentifierChecks::ReservedNames && (
+                IsIn(symbol, ReservedNames) || IsIn(ExtractLeaf(symbol), ReservedNames) ))
+        {
+            throw AzslcException(ADVANCED_RESERVED_NAME_USED, "Symbol", lineNumber, none,
+                                 ConcatString(symbol, " is a reserved name"));
+        }
+        auto& symAndKind = m_elastic.AddIdentifier(symbol, kind, lineNumber);
+        AttachAccumulatedAttributes(symAndKind.first);
+        return symAndKind;
+    }
+
+    bool SymbolAggregator::DeleteIdentifier(IdentifierUID name)
+    {
+        m_idToAttributeMap.erase(name);
+        return m_elastic.DeleteIdentifier(name);
+    }
+
+    IdAndKind* SymbolAggregator::LookupSymbol(QualifiedNameView scope, UnqualifiedNameView name)
+    {
+        using namespace std::string_literals;
+        if (IsRooted(name))
+        {   // It is possible that fully-qualified symbols find their way inside unqualified-tainted names.
+            // For the reason mentioned in the comment decorating ExtractNameFromIdExpression() function. please refer.
+            return GetIdAndKindInfo(QualifiedNameView{name});
+        }
+        // try as floating symbol in priority (predefined are found at any scope)
+        IdAndKind* got = GetIdAndKindInfo(QualifiedName{"?"s + name.data()});
+        if (got)
+        {
+            return got;
+        }
+        assert(!IsLeafDecoratedByArguments(name)); // refer to ../Documentation/function-overloading/research.txt
+        // from now on scope matters
+        assert(IsRooted(scope));
+        // Iterative lookup of the closest reachable symbol
+        // by going further toward global.
+        string_view path = scope;
+        bool exit = false;
+        do
+        {
+            auto attempt = QualifiedName{JoinPath(path, name)};
+            got = GetIdAndKindInfo(attempt);
+            exit = path == "/";
+            path = LevelUp(path);
+        } while (!got && !exit);
+        return got;
+    }
+
+    UnqualifiedName SymbolAggregator::FindLeastQualifiedName(QualifiedNameView scope, IdentifierUID uid)
+    {
+        // start from the completely unqualified version:
+        IdAndKind*          got;
+        UnqualifiedName     name;
+        QualifiedName       target = RemoveLastParenthesisGroup(uid.GetName());
+        UnqualifiedName     nextname = ExtractLeaf(target);
+        vector<string_view> split = SplitPath(uid.m_name);
+        bool                found = false;
+        int                 numsplits = static_cast<int>(split.size());
+        for (int i = numsplits - 2;  // start from leaf-1 (e.g. from "/A/B/Leaf", name is already Leaf, we need to prepend "B", then "A")
+             i >= -1 && !found;  // loop until found, or for "all elements in the split, +1" (for the opportunity to try out the last nextname)
+             --i)                // progressively add qualifiers
+        {
+            name = nextname;
+            got = LookupSymbol(scope, name);
+            found = got && got->first.GetName() == target;
+            // prepare next iteration
+            if (i >= 0)
+            {
+                nextname = UnqualifiedName{JoinPath(split[i], name)};
+            }
+        }
+        return RemoveFloatingMark(name);
+    }
+
+    decltype(SymbolTable::m_order)& SymbolAggregator::GetOrderedSymbols()
+    {
+        return m_elastic.m_order;
+    }
+
+    const decltype(SymbolTable::m_order)& SymbolAggregator::GetOrderedSymbols() const
+    {
+        return m_elastic.m_order;
+    }
+
+    void SymbolAggregator::PushPendingAttribute(const AttributeInfo& attrInfo, AttributeScope scope)
+    {
+        m_orphanAttributesList[scope].push_back(attrInfo);
+    }
+
+    const vector<AttributeInfo>* SymbolAggregator::GetAttributeList(const IdentifierUID& uid) const
+    {
+        auto attrList = m_idToAttributeMap.find(uid);
+        return attrList == m_idToAttributeMap.end() ?
+              nullptr
+            : &attrList->second;
+    }
+
+    static optional<AttributeInfo> FindAttributeByNameInList(const vector<AttributeInfo>& attrList, const string& attributeName)
+    {
+        auto iter = std::find_if(attrList.cbegin(), attrList.cend(),
+            [=](const auto& attrInfo) { return attrInfo.m_attribute == attributeName; });
+
+        return iter == attrList.end() ?
+              none
+            : optional{ *iter };
+    }
+
+    optional<AttributeInfo> SymbolAggregator::GetAttribute(const IdentifierUID& uid, const string& attributeName) const
+    {
+        optional<AttributeInfo> result;
+        if (const auto attrList = GetAttributeList(uid))
+        {
+            result = FindAttributeByNameInList(*attrList, attributeName);
+        }
+        return result;
+    }
+
+    const vector<AttributeInfo>& SymbolAggregator::GetGlobalAttributeList() const
+    {
+        return m_orphanAttributesList[AttributeScope::Global];
+    }
+
+    void SymbolAggregator::AttachAccumulatedAttributes(const IdentifierUID& uid)
+    {
+        if (m_orphanAttributesList[AttributeScope::Attached].empty())
+        {
+            return; // Nothing to attach
+        }
+
+        m_idToAttributeMap.try_emplace(uid, std::move(m_orphanAttributesList[AttributeScope::Attached]));
+        m_orphanAttributesList[AttributeScope::Attached].clear();        
+    }
+
+    void SymbolAggregator::ReorderBySymbolDependency()
+    {
+        auto disambiguatorChar = '#';
+        // query for symbol kind; because that function is specific to this algorithm, it's ok locally only.
+        auto isFunctionOrVariable = [this, disambiguatorChar](string_view name)
+        {
+            name = Slice(name, 0, name.find_first_of(disambiguatorChar));
+            KindInfo& ki = GetIdAndKindInfo(QualifiedNameView{name})->second;
+            return std::make_pair(ki.IsKindOneOf(Kind::Function, Kind::OverloadSet), ki.IsKindOneOf(Kind::Variable));
+        };
+        // instanciate an empty solver and fill it up with the elastic symbols from the aggregator to reorder them
+        DependencySolver<IdentifierUID, 50_maxdep_pernode> solver;
+        // state variable that remembers the last iterated symbol that was of a specific nesting depth
+        stack<IdentifierUID> lastSymbolAtCurrentLevel;
+        size_t curDepth = 0;
+        for (const IdentifierUID& id : m_elastic.m_order)
+        {
+            // We need to uniquify names to preserve repetitions (it happens for function declarations versus definition).
+            // The solver is full of maps and sets so it will have the bad habit of deduplicating your nodes if not.
+            auto disambiguated = id;
+            while (solver.Has(disambiguated))
+            {
+                disambiguated.m_name += disambiguatorChar;
+            }
+            solver.AddNode(disambiguated);
+            // if you visualize the the AST with the root at the top and nestings growing downward. Brother symbols are horizontal.
+            //                 ●  '/'          ╔═════════════════╗               ●  '/'
+            //                                 ║we need to create║
+            //        ● 'g_fog'   ● 'class C'  ║  these links:   ║      ● 'g_fog' ← ● 'class C'
+            //                                 ╚═════════════════╝                       ↑
+            //                    ● 'struct C/S'                                    ● 'struct C/S'
+            size_t symbolDepth = GetSymbolDepth(disambiguated.GetName());
+            if (symbolDepth > curDepth)
+            {
+                lastSymbolAtCurrentLevel.push(disambiguated);
+            }
+            else
+            {
+                while (symbolDepth < lastSymbolAtCurrentLevel.size())
+                {
+                    lastSymbolAtCurrentLevel.pop();
+                }
+                // establish a horizontal link between symbols of the same level to preserve the apparition order
+                bool sameParentAsLast = GetParentName(disambiguated.GetName()) == GetParentName(lastSymbolAtCurrentLevel.top().GetName());
+                bool lastIsFunction = isFunctionOrVariable(lastSymbolAtCurrentLevel.top().GetName()).first;
+                // verifying !lastIsFunction, permits to break dependency cycles.
+                if (sameParentAsLast && !lastIsFunction)
+                {
+                    solver.AddDependency(disambiguated, lastSymbolAtCurrentLevel.top());
+                }
+                lastSymbolAtCurrentLevel.top() = disambiguated;
+            }
+            curDepth = symbolDepth;
+            if (!IsGlobal(disambiguated.GetName()))
+            {
+                // establish vertical links
+                string path;
+                IdentifierUID parent;
+                ForEachPathPart(disambiguated.GetName(), [this, &solver, &path, &parent, &isFunctionOrVariable](PathPart part)
+                                {
+                                    path = JoinPath(path, part.m_slice);
+                                    auto [isFunc, isVar] = isFunctionOrVariable(path);
+                                    IdentifierUID current{QualifiedNameView{path}};
+                                    bool parentIsEmptyOrRoot = parent.IsEmpty() || parent.GetName() == "/";
+                                    if (!parentIsEmptyOrRoot)
+                                    {
+                                        if (isFunc)
+                                        {
+                                            // functions depends on containing-scope's content
+                                            // e.g.
+                                            //    SRG A {
+                                            //       T var;
+                                            //       void Func() { var=x; }  // Func depends on var
+                                            //    }
+                                            // since the Emitter will pull Func out, we need it to appear after var.
+                                            // and var will be defined by the mutated form of A.
+                                            //   A problem this poses, is if any var depends on Func.
+                                            //   Which would be possible through initializers. example:
+                                            //      SRG A {
+                                            //         int getzero();
+                                            //         int var = getzero();
+                                            //      }
+                                            //   This situation is ill-formed
+                                            solver.AddDependency(current, parent);
+                                        }
+                                        else if (!isVar)
+                                        {
+                                            // types cannot depend on their containing symbol, it's the other way around.
+                                            //    class A {
+                                            //       class B{ A a; }; // error (A is incomplete)
+                                            //    };
+                                            // however, since symbol migrations in the Emitter may pull B out,
+                                            // we need B to appear before A. therefore A depends on B.
+                                            solver.AddDependency(parent, current);
+                                        }
+                                    }
+                                    if (!(isFunc || isVar) || parentIsEmptyOrRoot)
+                                    {
+                                        parent = current;
+                                    }
+                                });
+            }
+        }
+        assert(solver.m_order.size() == m_elastic.m_order.size());
+        solver.Solve();
+        // restore the original names
+        for (auto& id : solver.m_order)
+        {
+            id.m_name = Slice(id.m_name, 0, id.m_name.find_first_of(disambiguatorChar));
+        }
+        m_elastic.m_order = std::move(solver.m_order);
+    }
+}  // end of namespace AZ::ShaderCompiler

+ 163 - 0
src/AzslcSymbolAggregator.h

@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include "AzslcSymbolTable.h"
+#include "DependencySolver.tpl"
+
+namespace AZ::ShaderCompiler
+{
+    // mangled symbol path of the rootconstant constant buffer
+    static constexpr QualifiedNameView RootConstantsViewName{ "/rootconstantsCB" };
+
+    //! policy enum for AddIdentifier method parameter
+    enum class AddIdentifierChecks
+    {
+        None,
+        ReservedNames
+    };
+
+    /// Higher level table that holds 2 symbol databases: an intrinsic "fixed", and a user-source driven "elastic"
+    class SymbolAggregator
+    {
+    public:
+        SymbolAggregator();
+
+        auto HasIdentifier(QualifiedNameView symbol) const -> bool;
+
+        auto GetIdAndKindInfo(QualifiedNameView symbol) -> IdAndKind*;
+
+        auto GetIdAndKindInfo(QualifiedNameView symbol) const -> const IdAndKind*;
+
+        /// Register a fresh entry in the symbol map. No KindInfo filled up, the client must do it.
+        /// Will return a reference to the newly inserted data.
+        /// Will throw in case of ODR violation.
+        auto AddIdentifier(QualifiedNameView symbol, Kind kind, optional<size_t> lineNumber = none, AddIdentifierChecks = AddIdentifierChecks::ReservedNames) -> IdAndKind&;
+
+        bool DeleteIdentifier(IdentifierUID name);
+
+        /// Links a name to the closest reachable declaration.
+        /// Using current scope path. example: returns "/gfx/device" if you pass e.g. "/gfx/detail/" and "device"
+        /// This is an unfortunately complex feature, refer to https://en.cppreference.com/w/cpp/language/unqualified_lookup
+        /// If you have an already fully-qualified name, this function is not for you (just use GetIdentifier).
+        auto LookupSymbol(QualifiedNameView scope, UnqualifiedNameView name) -> IdAndKind*;
+
+        /// Lookup in context, the most iteratively less-qualified version, that still points to the same symbol
+        auto FindLeastQualifiedName(QualifiedNameView scope, IdentifierUID uid) -> UnqualifiedName;
+
+        /// The order collection form the elastic table. (we'll just assume the fixed-table order is totally un-interesting)
+        auto GetOrderedSymbols() -> decltype(SymbolTable::m_order)&;
+
+        /// const version
+        auto GetOrderedSymbols() const -> const decltype(SymbolTable::m_order)&;
+
+        /// Immediately access the contained SubInfo from a symbol lookup
+        /// Convenient shortcut when you know what you are working with.
+        /// Will return nullptr if you request a wrong subtype T, or if the symbol is absent.
+        template<typename T>
+        auto GetAsSub(IdentifierUID symbol) const -> const T*
+        {
+            auto idKindPtr = GetIdAndKindInfo(symbol.GetName());
+            if (!idKindPtr)
+            {
+                return nullptr;
+            }
+            return idKindPtr->second.GetSubAs<T>();
+        }
+
+        /// mutable version (Scott Meyers's way of factorizing)
+        template<typename T>
+        auto GetAsSub(IdentifierUID symbol) -> T*
+        {
+            return const_cast<T*>(const_cast<const SymbolAggregator*>(this)->GetAsSub<T>(symbol));
+        }
+
+        template <typename Sub>
+        using Id_Sub_Kind = tuple<IdentifierUID, Sub*, KindInfo*>;
+
+        /// list of pre-gotten subinfo of type Sub, filtered from GetOrderedSymbols()
+        template<typename Sub>
+        vector<Sub*> GetOrderedSubInfosOfSubType()
+        {
+            vector<Sub*> filteredList;
+            for (const auto& srg : GetOrderedSymbols())
+            {
+                auto* sub = GetAsSub<Sub>(srg);
+                if (sub)
+                {
+                    filteredList.push_back(sub);
+                }
+            }
+            return filteredList;
+        }
+
+        /// Ordered symbol list, filtered by subinfo of type Sub
+        /// each element is a pair (id, Sub*)
+        template<typename Sub>
+        vector<pair<IdentifierUID, Sub*>> GetOrderedSymbolsOfSubType_2()
+        {
+            vector<pair<IdentifierUID, Sub*>> filteredList;
+            for (const auto& srg : GetOrderedSymbols())
+            {
+                auto& [uid, info] = *m_elastic.GetIdAndKindInfo(srg.m_name);
+                auto* sub = info.GetSubAs<Sub>();
+                if (sub)
+                {
+                    filteredList.emplace_back( uid, sub );
+                }
+            }
+            return filteredList;
+        }
+
+        /// Ordered symbol list, filtered by subinfo of type Sub
+        /// each element is a tuple (id, Sub*, KindInfo*)
+        template<typename Sub>
+        vector<Id_Sub_Kind<Sub>> GetOrderedSymbolsOfSubType_3()
+        {
+            vector<Id_Sub_Kind<Sub>> filteredList;
+            for (const auto& srg : GetOrderedSymbols())
+            {
+                auto& [uid, info] = *m_elastic.GetIdAndKindInfo(srg.m_name);
+                auto* sub = info.GetSubAs<Sub>();
+                if (sub)
+                {
+                    filteredList.emplace_back( uid, sub, &info );
+                }
+            }
+            return filteredList;
+        }
+
+        //! Adds a new attribute either to the global or attached attribute list
+        void PushPendingAttribute(const AttributeInfo& attrInfo, AttributeScope scope);
+
+        //! Gets the list of attributes attached to the identifier, if any. nullptr if none
+        const vector<AttributeInfo>* GetAttributeList(const IdentifierUID& uid) const;
+
+        //! Gets the attribute with the matching name attached to the identifier, if any
+        optional<AttributeInfo> GetAttribute(const IdentifierUID& uid, const string& attributeName) const;
+
+        //! Gets the list of global (unattached) attributes
+        const vector<AttributeInfo>& GetGlobalAttributeList() const;
+
+        //! Change (in-place) the elastic.m_order vector to shuffle the symbol IDs so that they
+        //! get ordered to respect dependencies on each other.
+        void ReorderBySymbolDependency();
+
+        const SymbolTable m_fixed;    // populated once with startup symbols (global namespace and predefined types)
+        SymbolTable       m_elastic;  // from user's source
+
+    protected:
+        /// Attaches the accumulated non-global attributes to the uid and flushes the list
+        void AttachAccumulatedAttributes(const IdentifierUID& uid);
+
+        //! List of encountered, but pending, attributes
+        array< vector<AttributeInfo>, AttributeScope::EndEnumeratorSentinel_ > m_orphanAttributesList;
+        //! List of attached attributes (during parsing, encountered attributes accumulates, then flow to their definitive place)
+        map< IdentifierUID, vector<AttributeInfo> > m_idToAttributeMap;
+    };
+}

+ 79 - 0
src/AzslcSymbolTable.cpp

@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include "AzslcSymbolTable.h"
+
+namespace AZ::ShaderCompiler
+{
+    bool SymbolTable::HasIdentifier(QualifiedNameView symbol) const
+    {
+        return m_symbols.find({symbol}) != m_symbols.cend();
+    }
+
+    IdAndKind* SymbolTable::GetIdAndKindInfo(QualifiedNameView symbol)
+    {
+        auto iter = m_symbols.find({symbol});
+        return iter == m_symbols.cend() ? nullptr : &(*iter);
+    }
+
+    const IdAndKind* SymbolTable::GetIdAndKindInfo(QualifiedNameView symbol) const
+    {
+        auto iter = m_symbols.find({symbol});
+        return iter == m_symbols.cend() ? nullptr : &(*iter);
+    }
+
+    bool SymbolTable::DeleteIdentifier(IdentifierUID name)
+    {
+        auto iter = m_symbols.find(name);
+        bool found = iter != m_symbols.end();
+        if (found)
+        {
+            m_symbols.erase(iter);
+            // remove from order list too
+            m_order.erase(std::remove(m_order.begin(), m_order.end(), name), m_order.end());
+        }
+        return found;
+    }
+
+    IdAndKind& SymbolTable::AddIdentifier(QualifiedNameView symbol, Kind kind, optional<size_t> lineNumber /*= none*/)
+    {
+        IdentifierUID idUID{symbol};
+        auto fetchedIdIt = m_symbols.find(idUID);
+        if (fetchedIdIt != m_symbols.end())
+        {   // found
+            if (kind != fetchedIdIt->second.GetKind())
+            {
+                ThrowRedeclarationAsDifferentKind(symbol, kind, fetchedIdIt->second, lineNumber);
+            }
+            else
+            {
+                throw AzslcException(ORCHESTRATOR_ODR_VIOLATION, "Semantic", lineNumber, none,
+                                     ConcatString("ODR (One Definition Rule) violation. Redeclaration of ",
+                                                  Kind::ToStr(kind), " ", ExtractLeaf(symbol), " in ", LevelUp(symbol), "  ",
+                                                  GetFirstSeenLineMessage(fetchedIdIt->second), "\n"));
+
+                // Since a redeclaration becomes the new identity, we must erase its old appearance in the order list:
+                //  (motivated by the requirement that if we emit a function body, it can't be at the site of its first declaration.
+                //   since the definition is the only guaranteed place where the body will not refer to yet-undeclared symbols)
+                //  As a bonus, it also cleans-up the --dumpsym option which had duplicated output.
+                //  (reinstates the invariant of unicity of apparition)
+                m_order.erase(std::remove(m_order.begin(), m_order.end(), fetchedIdIt->first), m_order.end());
+            }
+        }
+        else
+        {   // not found
+            fetchedIdIt = m_symbols.insert(IdToKindMap::value_type{idUID, {}}).first;
+            verboseCout << "new identifier added to database. name " << idUID.m_name << " kind " << Kind::ToStr(kind) << "\n";
+        }
+        // InitAs completely reassigns the subinfo with a freshly constructed type.
+        fetchedIdIt->second.InitAs(kind);
+        // remember the apparition order:
+        m_order.push_back(fetchedIdIt->first);
+        return *fetchedIdIt;
+    }
+}  // end of namespace AZ::ShaderCompiler

+ 39 - 0
src/AzslcSymbolTable.h

@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include "GenericUtils.h"
+#include "AzslcKindInfo.h"
+#include "DiagnosticStream.h"
+
+namespace AZ::ShaderCompiler
+{
+    // A symbol database
+    class SymbolTable
+    {
+    public:
+        auto HasIdentifier(QualifiedNameView symbol) const -> bool;
+
+        auto GetIdAndKindInfo(QualifiedNameView symbol) -> IdAndKind*;
+
+        auto GetIdAndKindInfo(QualifiedNameView symbol) const -> const IdAndKind*;
+
+        /// Register a fresh entry in the symbol map. No KindInfo filled up, the client must do it.
+        /// Will return a reference to the newly inserted data.
+        /// Will throw in case of ODR violation.
+        auto AddIdentifier(QualifiedNameView symbol, Kind kind, optional<size_t> lineNumber = none) -> IdAndKind&;
+
+        /// Return true if found and deleted
+        bool DeleteIdentifier(IdentifierUID name);
+
+        // [GFX TODO]2: use densemap/oahm to avoid fragmentation (depends on [Task 5])
+        IdToKindMap           m_symbols;   // declarations of any kind and attached information (unordered bag, but O(1+) lookup)
+        vector<IdentifierUID> m_order;     // remember order of apparition in the original source (useful for iteration during dump or emission)
+                                           // the order gets altered during Mid-end treatment, by the SymbolAggregator to be arranged in the head-visit AST order instead.
+    };
+}

+ 220 - 0
src/AzslcSymbolTranslation.cpp

@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include "AzslcSymbolTranslation.h"
+#include "AzslcHomonymVisitor.h"
+
+namespace AZ::ShaderCompiler
+{
+    void SymbolTranslation::SetAccessSymbolQueryFunctor(std::function<KindInfo*(QualifiedNameView)> accessSymbol)
+    {
+        m_accessSymbol = accessSymbol;
+    }
+
+    void SymbolTranslation::SetGetSeenatFunctor(std::function< vector<Seenat>& (QualifiedNameView) > getSeenats)
+    {
+        m_getSeenats = getSeenats;
+    }
+
+    void SymbolTranslation::RegisterLandingScope(const IdentifierUID& originalSymbol, QualifiedNameView landingScope)
+    {
+        using RE = RelationshipExtent;
+        using std::make_pair;
+        assert(GetParentName(originalSymbol.GetName()) != landingScope);
+        auto findIterator = m_landingScope.find(originalSymbol.GetName());
+        if (findIterator != m_landingScope.end())
+        {   // if we are re-registering the same symbol, nothing to do. but check that the landingScope is the same as originally registered:
+            assert(findIterator->second.m_landingScope == landingScope);
+            return;
+        }
+        m_landingScope[originalSymbol.GetName()].m_landingScope = landingScope;
+        // let's pre-cache the locations of all occurrences of this symbol in the program, for quick lookup later when we only have tokenID.
+        HomonymVisitor hv(m_accessSymbol);
+        hv(originalSymbol,
+           [&, this](const Seenat& at, RE relation)
+           {
+               if (relation == RE::Self)
+               {   // that's the definition point itself...
+                   auto span = at.m_where.m_expressionSpan;
+                   m_definitionCtxStartTokenOfMigratedSymbol[span.a] = make_pair(at.m_referredDefinition.GetName(), span.b);
+               }
+               else if (relation == RE::Reference)
+               {
+                   PreCacheTokenOfReference_(at);  // register tokenId of the seenat, to lookup maps that will give us the IDExpressionDesc& quickly later
+               }
+               else if (relation == RE::OverloadSet)
+               {
+                   // when we are over the definition of an overload, we don't need to register it, since it will have its own RegisterLandingScope call.
+                   // however! there will be no such call from the "naked" overload-set, even though it may store references to call sites that hasn't been resolved.
+                   // so we will do a shallow recursion here to get its references, to be sure to get them to mutate too.
+                   if (!IsLeafDecoratedByArguments(at.m_referredDefinition.GetName()))
+                   {
+                       RegisterLandingScope(at.m_referredDefinition, landingScope);
+                   }
+               }
+           },
+           RelationshipExtentFlag{RE::Self} | RE::Reference | RE::OverloadSet);
+    }
+
+    void SymbolTranslation::AddCustomBehavior(QualifiedNameView originalSymbol, BehaviorEventFlag on, TranslationBehaviorDelegate action)
+    {
+        m_landingScope[originalSymbol].m_customBehavior = CustomBehavior{action, on};
+    }
+
+    void SymbolTranslation::MigrateDisambiguateAndCache(QualifiedNameView originalSymbol) const
+    {
+        // can't execute that action more than once
+        assert(m_renames.find(originalSymbol) == m_renames.end());
+        const auto& landingScope = m_landingScope.find(originalSymbol)->second;
+        bool kindIsFunction = m_accessSymbol(originalSymbol)->IsKindOneOf(Kind::Function, Kind::OverloadSet);
+        // the reason we treat functions differently is that some call sites are un-resolved, it's up to the target compiler to do the final overload resolution.
+        // as long as we tolerate this loose semantics, we cannot uniquify function names, we need to preserve their "homonymous-ness".
+        // however, there other kinds of symbols, like variables and types within function scopes, need to be unique to avoid collisions.
+        auto flatteningStrategy = kindIsFunction ? CollapseArguments : PreserveArgumentsUnicity;
+        // flatten will preserve the original qualification for unicity purposes. SRG::Inner::m_color becomes SRG_Inner_m_color
+        string flattened = Flatten(originalSymbol.data(), flatteningStrategy);
+        // check if the new symbol name is free in the desired scope
+        QualifiedName collisionCandidate{JoinPath(landingScope.m_landingScope, flattened)};
+        int counter = 1;
+        auto mappedRenameIter = m_mappedRenames.find(collisionCandidate);
+        while (m_accessSymbol(collisionCandidate) || mappedRenameIter != m_mappedRenames.end())
+        {
+            if (mappedRenameIter != m_mappedRenames.end() && kindIsFunction)
+            {   // overloads HAVE to end up with the same name (if we disambiguate we're losing overloading).
+                // verify that the original symbol of that collision, refers to the same overload-set:
+                if (BelongsToOverloadSet_(mappedRenameIter->second, originalSymbol))
+                {
+                    break;
+                }
+            }
+            // mutate it a bit until it's free.
+            auto attempt = flattened + std::to_string(counter);
+            collisionCandidate = QualifiedName{JoinPath(landingScope.m_landingScope, attempt)};
+            ++counter;
+            mappedRenameIter = m_mappedRenames.find(collisionCandidate);
+        }
+        m_renames[originalSymbol] = collisionCandidate;  // cache for rename lookups
+        m_mappedRenames[collisionCandidate] = originalSymbol;  // cache for reverse lookups
+    }
+
+    QualifiedNameView SymbolTranslation::GetLandingScope(QualifiedNameView originalSymbol) const
+    {
+        auto it = m_landingScope.find(originalSymbol);
+        return it == m_landingScope.end() ? QualifiedNameView{GetParentName(originalSymbol)} : QualifiedNameView{it->second.m_landingScope};
+    }
+
+    void SymbolTranslation::FindTranslation_(FindTranslation_Parameters& params) const
+    {
+        auto landingScopeIter = m_landingScope.find(params.m_originalPath);
+        params.m_foundMutation = (landingScopeIter == m_landingScope.end()) ? nullptr : &landingScopeIter->second;
+        const auto cacheIter = m_renames.find(params.m_originalPath);
+        if (cacheIter == m_renames.end())  // no translation-cache
+        {
+            if (!params.m_foundMutation)
+            {   // no migration registered
+                // check parents.
+                auto parent = GetParentName(params.m_originalPath);
+                if (parent.empty() || parent == "/")
+                {   // no parent, just copy the input.
+                    params.m_result = params.m_originalPath;
+                }
+                else
+                {   // go down the stack (leftward in the nestings) A/B/C -> A/B
+                    FindTranslation_Parameters recursiveParams{parent};
+                    FindTranslation_(recursiveParams);
+                    // as we climb the callstack, reconstruct the result by restitching left and right parts G/B -> G/B/C
+                    params.m_result = QualifiedName{JoinPath(recursiveParams.m_result, ExtractLeaf(params.m_originalPath))};
+                }
+            }
+            else
+            {   // in case of a registered migration request
+                MigrateDisambiguateAndCache(params.m_originalPath);  // execute the evolved logic and cache it.
+                params.m_result = m_renames.find(params.m_originalPath)->second;  // get from cache
+            }
+        }
+        else
+        {   // we have a cache, use that.
+            params.m_result = cacheIter->second;
+        }
+    }
+
+    string SymbolTranslation::GetTranslatedName(QualifiedNameView originalSymbol, UsageContext qualificationStrategy, ssize_t tokenId) const
+    {
+        FindTranslation_Parameters translationParams{originalSymbol};
+        FindTranslation_(translationParams);
+        const QualifiedName& renamed = translationParams.m_result;  // QualifiedName are mangled
+        // DeclarationSite is for grammar contexts that usually don't accept more than identifiers. (no nested name specifiers)
+        // the ReferenceSite strategy is to emit the fully qualified HLSL all the time.
+        //   we could try to reduce verbosity by using FindLeastQualifiedName but that'll be for later.
+        // Unmangling by convention will un-decorate function names to strip them to their core name. So be consistent for both strategy.
+        auto translation = qualificationStrategy == UsageContext::DeclarationSite ? string{RemoveLastParenthesisGroup(ExtractLeaf(renamed))}
+                                                                                  : UnMangle(string{renamed});
+        // find a potential callback
+        if (translationParams.m_foundMutation
+            && translationParams.m_foundMutation->m_customBehavior
+            && (translationParams.m_foundMutation->m_customBehavior->m_when & static_cast<BehaviorEvent::EnumType>(qualificationStrategy)))
+        {
+            auto callback = &(*translationParams.m_foundMutation->m_customBehavior);
+            translation = callback->m_action(originalSymbol, qualificationStrategy, translation, tokenId);
+        }
+        return translation;
+    }
+
+    pair<QualifiedNameView, ssize_t> SymbolTranslation::OverOriginalDefinitionOf(ssize_t tokenId) const
+    {
+        auto iterator = m_definitionCtxStartTokenOfMigratedSymbol.find(tokenId);
+        return iterator != m_definitionCtxStartTokenOfMigratedSymbol.end() ? std::make_pair(QualifiedNameView{iterator->second.first}, iterator->second.second)
+                                                                           : std::make_pair(QualifiedNameView{}, -1_ssz);
+    }
+
+    SymbolTranslation::IDExpressionDesc SymbolTranslation::GetIdExpression(ssize_t tokenId) const
+    {
+        // double step lookup:
+        // tok1::tok3::tok5
+        // ↑         ↓
+        // └────maps─┘
+        //
+        // …then tok1 → idexpr
+        auto& t2t    = m_anyTokenIdToFirstTokenIdOfAnIdExpr;
+        auto& ft2ide = m_firstTokenIdToIdExpression;
+
+        auto ftIt = t2t.find(tokenId);
+        ssize_t tokenIdToSearch = ftIt == t2t.end() ? tokenId : ftIt->second;
+
+        auto idIt = ft2ide.find(tokenIdToSearch);
+        return idIt == ft2ide.end() ? IDExpressionDesc{} : idIt->second;
+    }
+
+    void SymbolTranslation::PreCacheTokenOfReference_(const Seenat &at)
+    {
+        IDExpressionDesc& idExprDesc = m_firstTokenIdToIdExpression[at.m_where.m_expressionSpan.a];
+        idExprDesc.m_span = at.m_where.m_expressionSpan;
+        // fillup the spans map
+        for (ssize_t i = at.m_where.m_expressionSpan.a; i <= at.m_where.m_expressionSpan.b; ++i)
+        {
+            m_anyTokenIdToFirstTokenIdOfAnIdExpr[i] = at.m_where.m_expressionSpan.a;
+        }
+        // verify that we are the rightmost and update.  (for def of 'rightmost' refer to IDExpressionDesc's comment)
+        ssize_t index = at.m_where.m_focusedTokenId;
+        if (index >= idExprDesc.m_mutatedRightMost.m_index)
+        {
+            idExprDesc.m_mutatedRightMost.m_index = index;
+            idExprDesc.m_mutatedRightMost.m_symbol = at.m_referredDefinition;
+        }
+    }
+
+    // checks whether 2 functions are overloads
+    bool SymbolTranslation::BelongsToOverloadSet_(QualifiedNameView originalName1, QualifiedNameView originalName2) const
+    {
+        string_view core = RemoveLastParenthesisGroup(originalName1);
+        auto* coreSymbol = m_accessSymbol(QualifiedNameView{core});
+        return coreSymbol &&   // not nullptr (the set exists), and...
+            (core == originalName2    // either the overload-set itself is directly the same symbol, or...
+             || coreSymbol->GetSubRefAs<OverloadSetInfo>().Has({originalName2}));   // the symbol we're looking for, belongs to that set.
+    }
+}

+ 163 - 0
src/AzslcSymbolTranslation.h

@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include "AzslcListener.h"
+
+namespace AZ::ShaderCompiler
+{
+    MAKE_REFLECTABLE_ENUM_POWER(BehaviorEvent, OnDeclaration, OnReference);
+    using BehaviorEventFlag = Flag<BehaviorEvent>;
+
+    //! DeclarationSite is for usages like XX in "struct XX {};" or "int XX = 2;"
+    //! ReferenceSite   is for usages in expressions
+    enum class UsageContext
+    {
+        DeclarationSite = BehaviorEvent::OnDeclaration,
+        ReferenceSite   = BehaviorEvent::OnReference,
+    };
+
+    //! store translated names (map of names to new names)
+    //! used to transform /SRG/cb0/pistils to SRG0_cb0_pistils in a disambiguated form
+    class SymbolTranslation
+    {
+    public:
+        //! accessSymbol    is a function to query a symbol in the symbol table. (it is recommend to bind it to SymbolAggregator::GetIdAndKindInfo())
+        void SetAccessSymbolQueryFunctor(std::function<KindInfo*(QualifiedNameView)> accessSymbol);
+
+        void SetGetSeenatFunctor(std::function< vector<Seenat>& (QualifiedNameView) > getSeenats);
+
+        //! landingScope is the destination scope for the (translated) declaration of the symbol.
+        void RegisterLandingScope(const IdentifierUID& originalSymbol, QualifiedNameView landingScope);
+
+        //! respect this signature for your event handlers.
+        //!   originalSymbol        is a reminder of the symbol you are mutating
+        //!   qualificationStrategy is in case you chose a flag of both events, you can distinguish the context.
+        //!   naturalRename         is the "otherwise produced" translation result, if you return it as is, that'd be an "identity" custom behavior.
+        //!   tokenId               often unavailable (=NotOverToken ==-1) but when available it indicates which token index we're iterating over in the original stream
+        using TranslationBehaviorDelegate =
+            std::function<
+                string (QualifiedNameView originalSymbol, UsageContext qualificationStrategy, string naturalRename, ssize_t tokenId) >;
+
+        //! register specific behaviors to post-mutate names differently than the default algorithm.
+        //! a behavior is for one original symbol. it can trigger on declaration or reference or both
+        void AddCustomBehavior(QualifiedNameView originalSymbol, BehaviorEventFlag on, TranslationBehaviorDelegate action);
+
+        //! Create a declaration in the IR for a symbol "originalSymbol" at its destination scope registered previously.
+        void MigrateDisambiguateAndCache(QualifiedNameView originalSymbol) const;
+
+        //! Query the registrar
+        QualifiedNameView GetLandingScope(QualifiedNameView originalSymbol) const;
+
+        //! Get the translated name for a symbol in AZSL
+        string GetTranslatedName(QualifiedNameView originalSymbol, UsageContext qualificationStrategy, ssize_t tokenId) const;
+
+        //! In the idExpression:
+        //! nested1::nested2::mutatedRightMost::expressionRightMost
+        //! Even if nested2 has been mutated, it doesn't matter because it's expressionRightMost that we are qualifying.
+        //! So it's the destination of expressionRightMost that matters, since we will rewrite all its left-hand nesting elements
+        //! to fix this expression to refer to the new path for expressionRightMost.
+        //! If expressionRightMost itself doesn't have a mutation registered, that doesn't mean one of its parent doesn't.
+        //! This is the one we really care about: "the deepest parent with a mutation". here: mutatedRightMost
+        //! Thus, the part of the expression that will mutate is this:
+        //! [nested1::nested2::mutatedRightMost]::expressionRightMost
+        //! nested1 and nested2 will change to the new path (possibly empty), and mutatedRightMost will change to the new name.
+        struct IDExpressionDesc
+        {
+            bool IsEmpty() const
+            {
+                return m_mutatedRightMost.m_symbol.m_name.empty();
+            }
+            struct MutatedRightMostInfo
+            {
+                IdentifierUID m_symbol;    // original name (un-mutated)
+                ssize_t       m_index = 0; // in the token stream (same space as m_span.a/b)
+            } m_mutatedRightMost;
+            misc::Interval m_span;  // nested::mut::most
+            //                         ^               ^
+        };
+
+        //! For when we roam over tokens, try to query for potential migrations
+        pair<QualifiedNameView, ssize_t> OverOriginalDefinitionOf(ssize_t tokenId) const;
+
+        //! Query whether this token belongs to an idExpression, and retrieve a descriptor to it
+        IDExpressionDesc GetIdExpression(ssize_t tokenId) const;
+
+        IDExpressionDesc GetIdExpression(Token* token) const
+        {
+            return GetIdExpression(static_cast<ssize_t>(token->getTokenIndex()));
+        }
+
+        //! execute the logic explained in the comment above struct IDExpressionDesc
+        //! that is: mutate intermediate symbols by rewriting the path.
+        //! emit a fully qualified valid HLSL expression after mutations.
+        template< typename NextTokenFunctor >
+        string TranslateIdExpression(const IDExpressionDesc& idExprDesc, ssize_t tokenId, const NextTokenFunctor& getNext) const
+        {
+            string part1 = GetTranslatedName(idExprDesc.m_mutatedRightMost.m_symbol.m_name, UsageContext::ReferenceSite, tokenId);
+            string part2;
+            for (ssize_t t = idExprDesc.m_mutatedRightMost.m_index + 1; t <= idExprDesc.m_span.b; ++t)
+            {
+                part2 += getNext(t);
+            }
+            return part1 + part2;
+        }
+
+    private:
+        struct CustomBehavior
+        {
+            TranslationBehaviorDelegate m_action;
+            BehaviorEventFlag m_when;
+        };
+        struct SymbolMutation
+        {
+            QualifiedName m_landingScope;
+            optional<CustomBehavior> m_customBehavior;
+        };
+
+        struct FindTranslation_Parameters
+        {
+            QualifiedNameView m_originalPath;
+            QualifiedName m_result;
+            const SymbolMutation* m_foundMutation = nullptr;
+        };
+        // recursive entry to be able to treat a parent migration at any level
+        //  A/B/C/D will check for D first, but we need to check for the rightmost change.
+        // if A has a migration and B has a migration too, only B counts.
+        // so we check descending, D, C, B -> stop. and recompose when going up.-> B'/C/D
+        void FindTranslation_(FindTranslation_Parameters& params) const;
+
+        // (private) factorization to simplify the "register translation" loop
+        void PreCacheTokenOfReference_(const Seenat& at);
+
+        // extracted for readability
+        bool BelongsToOverloadSet_(QualifiedNameView originalName1, QualifiedNameView originalName2) const;
+
+        // cache for renames (stateful rename that can store disambiguation attempts):
+        mutable unordered_map< QualifiedName, QualifiedName >        m_renames;
+        // database of registered mutations:
+        unordered_map< QualifiedName, SymbolMutation >               m_landingScope;
+        // quick query for "on top of migrated symbol original definition's first token"
+        // also store the end token for easy jump-over
+        unordered_map< ssize_t, pair<QualifiedName, ssize_t> >       m_definitionCtxStartTokenOfMigratedSymbol;
+        // example: tok1::tok3::tok5 (with tok2 and 4 being ::) -> 5 tokens 1 idexpr.
+        // since IDExpression objects are not immutable (mutatedRightMostSymbol iteratively updated)
+        // we can't tolerate data duplication. so we chose to store only the first token id (tok1) as key for an idexpression
+        unordered_map< ssize_t, IDExpressionDesc >                   m_firstTokenIdToIdExpression;
+        // to fill the gap we can map left out tokens 'tok2 tok3 tok4 tok5' to tok1
+        unordered_map< ssize_t, ssize_t >                            m_anyTokenIdToFirstTokenIdOfAnIdExpr;
+
+        // internal fast-lookup cache to help symbol disambiguation against renames
+        // alternatively, boost::bimap could be used, or an equivalent.
+        mutable unordered_map< QualifiedName, QualifiedName >        m_mappedRenames;  // disambiguated rename to original-symbol
+
+        // external IR access functors
+        std::function< KindInfo* (QualifiedNameView) >               m_accessSymbol;
+        std::function< vector<Seenat>& (QualifiedNameView) >         m_getSeenats;
+    };
+}

+ 38 - 0
src/AzslcTokenToAst.h

@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include "antlr4-runtime.h"
+
+namespace AZ::ShaderCompiler
+{
+    /// Observation: A CST's leaves are "token object" pointers.
+    ///  But there is no way to find the reverse association: from token to AST.
+    /// This class fills that gap.
+    class TokenToAst
+    {
+    public:
+        using AstNode = antlr4::ParserRuleContext;
+
+        AstNode* GetNode(ssize_t tokenId)
+        {
+            auto iterator = m_tokenToAst.find(tokenId);
+            return iterator == m_tokenToAst.end() ? nullptr : iterator->second;
+        }
+
+        void SetAssociation(ssize_t tokenId, AstNode* node)
+        {
+            m_tokenToAst[tokenId] = node;
+        }
+
+    protected:
+
+        // generic tokenid to ast pointer map
+        unordered_map< ssize_t, AstNode* > m_tokenToAst;
+    };
+}

+ 456 - 0
src/AzslcTypes.h

@@ -0,0 +1,456 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include "AzslcUtils.h"
+
+namespace AZ::ShaderCompiler
+{
+    // type classes before canonicalization.
+    // post canonicalization, you can't have generic arithmetic nor typeof.
+    MAKE_REFLECTABLE_ENUM (TypeClass,
+        // Error case
+        IsNotType,
+        // The void type
+        Void,
+        // Predefined
+        Scalar,
+        Vector,
+        GenericVector,  // vector<t,d>
+        Matrix,
+        GenericMatrix,  // matrix<t,d1,d2>
+        Texture,
+        GenericTexture,
+        MultisampledTexture,
+        Sampler,
+        StructuredBuffer,
+        Buffer,
+        ByteAddressBuffer,
+        ConstantBuffer,
+        StreamOutput,
+        LibrarySubobject,
+        OtherViewBufferType,
+        OtherPredefined,  // IO patch
+        // Not predefined
+        Struct,
+        Class,
+        Interface,
+        Enum,
+        TypeofExpression,
+        Alias
+    );
+
+    // == category queries ==
+
+    inline bool IsUserDefined(TypeClass typeClass)
+    {
+        return typeClass.IsOneOf(TypeClass::Struct, TypeClass::Class, TypeClass::Interface, TypeClass::Enum);
+    }
+
+    //! a product type is a (non-union) 'structured' type. (Cartesian product in type theory)
+    inline bool IsProductType(TypeClass typeClass)
+    {
+        return typeClass.IsOneOf(TypeClass::Struct, TypeClass::Class, TypeClass::Interface);
+    }
+
+    // predefined types are all HLSL base types (void included)
+    inline bool IsPredefinedType(TypeClass typeClass)
+    {
+        return !typeClass.IsOneOf(TypeClass::IsNotType, TypeClass::TypeofExpression, TypeClass::Alias)
+            && !IsUserDefined(typeClass);
+    }
+
+    // an arithmetic type, but not non-generic
+    inline bool IsNonGenericArithmetic(TypeClass typeClass)
+    {
+        return typeClass.IsOneOf(TypeClass::Scalar, TypeClass::Vector, TypeClass::Matrix);
+    }
+
+    // has a generic to canonicalize
+    inline bool IsGenericArithmetic(TypeClass typeClass)
+    {
+        return typeClass.IsOneOf(TypeClass::GenericVector, TypeClass::GenericMatrix);
+    }
+
+    inline bool IsArithmetic(TypeClass typeClass)
+    {
+        return IsGenericArithmetic(typeClass) || IsNonGenericArithmetic(typeClass);
+    }
+
+    // a fundamental is void or arithmetic
+    inline bool IsFundamental(TypeClass typeClass)
+    {
+        return typeClass == TypeClass::Void || IsArithmetic(typeClass);
+    }
+
+    // type that behaves like its generic parameter
+    inline bool IsChameleon(TypeClass typeClass)
+    {  // IO patch are not strict chameleon because they behave like an array. if we consider array collapsing then maybe they are.
+        return typeClass.IsOneOf(TypeClass::StructuredBuffer, TypeClass::Buffer, TypeClass::StreamOutput, TypeClass::ConstantBuffer);
+    }
+
+    inline bool HasGenericParameter(TypeClass typeClass)
+    { // TODO: to add InputPath/OutputPatch because it has a generic parameter. (when you do it, update ExtractGenericTypeParameterNameFromAstContext)
+        return IsChameleon(typeClass) || IsGenericArithmetic(typeClass) || typeClass.IsOneOf(TypeClass::GenericTexture, TypeClass::MultisampledTexture);
+    }
+
+    inline bool IsViewTypeBuffer(TypeClass typeClass)
+    {
+        return typeClass.IsOneOf(TypeClass::ConstantBuffer, TypeClass::StructuredBuffer, TypeClass::Buffer, TypeClass::ByteAddressBuffer, TypeClass::OtherViewBufferType);
+    }
+
+    inline bool IsViewType(TypeClass typeClass)
+    {   // note that a constant buffer is not a view type
+        return IsViewTypeBuffer(typeClass)
+            || typeClass.IsOneOf(TypeClass::Texture, TypeClass::GenericTexture, TypeClass::MultisampledTexture, TypeClass::Sampler);
+    }
+
+    //Example Texture2D m_myTex[]; is supported.
+    inline bool CanBeDeclaredAsUnboundedArray(TypeClass typeClass)
+    {
+        return IsViewType(typeClass);
+    }
+
+    // Get TypeClass for type inside a predefined context
+    inline TypeClass AnalyzeTypeClass(azslParser::PredefinedTypeContext* predefinedNode)
+    {
+        TypeClass toReturn = TypeClass::OtherPredefined;  // default value if nothing of the under was non null.
+        using PredefinedNodeT = std::remove_pointer_t<decltype(predefinedNode)>;  // DRY
+        // map TypeClasses to the same indexes than the context functions list
+        array<TypeClass, 16> contextClasses = { TypeClass::Scalar,
+                                                TypeClass::Vector,
+                                                TypeClass::GenericVector,
+                                                TypeClass::Matrix,
+                                                TypeClass::GenericMatrix,
+                                                TypeClass::Texture,
+                                                TypeClass::MultisampledTexture,
+                                                TypeClass::GenericTexture,
+                                                TypeClass::Sampler,
+                                                TypeClass::StructuredBuffer,
+                                                TypeClass::Buffer,
+                                                TypeClass::ByteAddressBuffer,
+                                                TypeClass::ConstantBuffer,
+                                                TypeClass::StreamOutput,
+                                                TypeClass::OtherViewBufferType,
+                                                TypeClass::LibrarySubobject};
+        // create a constexpr valuelist of member function pointers
+        using FunctionList = ValueTplList< &PredefinedNodeT::scalarType,
+                                           &PredefinedNodeT::vectorType,
+                                           &PredefinedNodeT::genericVectorType,
+                                           &PredefinedNodeT::matrixType,
+                                           &PredefinedNodeT::genericMatrixPredefinedType,
+                                           &PredefinedNodeT::texturePredefinedType,
+                                           &PredefinedNodeT::msTexturePredefinedType,
+                                           &PredefinedNodeT::genericTexturePredefinedType,
+                                           &PredefinedNodeT::samplerStatePredefinedType,
+                                           &PredefinedNodeT::structuredBufferPredefinedType,
+                                           &PredefinedNodeT::bufferPredefinedType,
+                                           &PredefinedNodeT::byteAddressBufferTypes,
+                                           &PredefinedNodeT::constantBufferTemplated,
+                                           &PredefinedNodeT::streamOutputPredefinedType,
+                                           &PredefinedNodeT::otherViewResourceType,
+                                           &PredefinedNodeT::subobjectType >;
+        static_assert( countTemplateParameters_v<FunctionList> == contextClasses.size() );
+        // This meta-loop will be unfolded at build time, therefore the generic lambda will be instantiated individually (by each type)
+        ForEachValue<FunctionList>([&toReturn, &predefinedNode, &contextClasses](auto ctxMemFn, auto index)
+                                    {
+                                        if ((predefinedNode->*ctxMemFn)())  // pointer-to-member-function call.
+                                        {
+                                            toReturn = contextClasses[index];
+                                        }
+                                    });
+        return toReturn;
+    }
+
+    // Get TypeClass for type inside a type context
+    inline TypeClass AnalyzeTypeClass(AstType* typeNode)
+    {
+        TypeClass toReturn = TypeClass::IsNotType;
+        if (typeNode != nullptr)
+        {
+            if (typeNode->predefinedType())
+            {
+                toReturn = AnalyzeTypeClass(typeNode->predefinedType());
+            }
+            else if (typeNode->userDefinedType() && typeNode->userDefinedType()->anyStructuredTypeDefinition())
+            {
+                if (typeNode->userDefinedType()->anyStructuredTypeDefinition()->classDefinition())
+                {
+                    toReturn = TypeClass::Class;
+                }
+                else if (typeNode->userDefinedType()->anyStructuredTypeDefinition()->interfaceDefinition())
+                {
+                    toReturn = TypeClass::Interface;
+                }
+                else if (typeNode->userDefinedType()->anyStructuredTypeDefinition()->structDefinition())
+                {
+                    toReturn = TypeClass::Struct;
+                }
+                else if (typeNode->userDefinedType()->anyStructuredTypeDefinition()->enumDefinition())
+                {
+                    toReturn = TypeClass::Enum;
+                }
+            }
+            else if (typeNode->typeofExpression())
+            {
+                toReturn = TypeClass::TypeofExpression;
+            }
+        }
+        return toReturn;
+    }
+
+    // Get TypeClass for type inside a functype context
+    inline TypeClass AnalyzeTypeClass(AstFuncType* funcTypeNode)
+    {
+        TypeClass toReturn = TypeClass::IsNotType;
+        if (funcTypeNode != nullptr)
+        {
+            if (funcTypeNode->Void())
+            {
+                toReturn = TypeClass::Void;
+            }
+            else
+            {
+                AstType* typeNode = funcTypeNode->type();
+                toReturn = AnalyzeTypeClass(typeNode);
+            }
+        }
+        return toReturn;
+    }
+
+    // Analyze a type (by parsing it) and return what class it belongs to.
+    // Don't let too wild uncontrolled input sneak in there, there may be vectors of attack.
+    inline TypeClass AnalyzeTypeClass(string_view typeName)
+    {
+        assert(typeName.find("/") == string::npos);  // forgot to unmangle ? use the TentativeName overload in these cases
+        // Construct a mini program of the form "type a();" and check the AST.
+        // Deduce the type class from the node.
+        string miniprogram { typeName };
+        miniprogram += " a();";
+        ANTLRInputStream input(miniprogram);
+        azslLexer lexer(&input);
+        lexer.removeErrorListeners();
+        CommonTokenStream tokens(&lexer);
+        azslParser parser(&tokens);
+        parser.removeErrorListeners();
+        azslParser::CompilationUnitContext* unit = parser.compilationUnit();
+        bool failCondition = parser.getNumberOfSyntaxErrors() > 0
+                          || unit->Declarations.empty()
+                          || unit->Declarations[0]->attributedFunctionDeclaration() == nullptr
+                          || unit->Declarations[0]->attributedFunctionDeclaration()->functionDeclaration() == nullptr
+                          || unit->Declarations[0]->attributedFunctionDeclaration()->functionDeclaration()->hlslFunctionDeclaration() == nullptr;
+        AstFuncType* funcTypeNode = nullptr;
+        if (!failCondition)
+        {
+            funcTypeNode = unit->Declarations[0]->
+                               attributedFunctionDeclaration()->
+                                   functionDeclaration()->
+                                       hlslFunctionDeclaration()->
+                                           leadingTypeFunctionSignature()->
+                                               functionType();
+        }
+        return AnalyzeTypeClass(funcTypeNode);
+    }
+
+    struct TentativeName
+    {
+        string mangled;
+    };
+    // try to analyze a type with a few iterations to remove mangling while the result is NotAType
+    inline TypeClass AnalyzeTypeClass(TentativeName typeName)
+    {
+        string try1 = UnMangle(typeName.mangled);
+        TypeClass result = AnalyzeTypeClass(try1);
+        if (result == TypeClass::IsNotType)
+        {
+            // this version does not preserve the global one, this can help for fundamentals
+            string try2 = ReplaceSeparators(typeName.mangled, "::");
+            result = AnalyzeTypeClass(try2);
+        }
+        return result;
+    }
+
+    /// Rows and Cols (this is specific to shader languages to identify vector and matrix types)
+    struct ArithmeticTypeInfo
+    {
+        void ResolveSize()
+        {
+            m_size = Packing::PackedSizeof(m_underlyingScalar);
+        }
+
+        /// Get the size of a single base element
+        const uint32_t GetBaseSize() const
+        {
+            return m_size;
+        }
+
+                /// Get the size of a the element with regard to dimensions as well
+        const uint32_t GetTotalSize() const
+        {
+            return m_size * (m_cols > 0 ? m_cols : 1) * (m_rows > 0 ? m_rows : 1);
+        }
+
+        /// True if the type is a vector type. If it's a vector type it cannot be a matrix as well.
+        const bool IsVector() const
+        {
+            // This treats special cases like 2x1, 3x1 and 4x1 as vectors
+            // The behavior is consistent with dxc packing rules
+            return (m_cols == 1 && m_rows > 1) || (m_cols > 1 && m_rows == 0);
+        }
+
+        /// True if the type is a matrix type. If it's a matrix type it cannot be a vector as well.
+        const bool IsMatrix() const
+        {
+            // This fails special cases like 2x1, 3x1 and 4x1,
+            //   but allows cases like 1x2, 1x3 and 1x4.
+            // The behavior is consistent with dxc packing rules
+            return m_rows > 0 && m_cols > 1;
+        }
+
+        /// If initialized as a fundamental -> not empty.
+        const bool IsEmpty() const
+        {
+            return m_underlyingScalar == -1;
+        }
+
+        // for pretty print
+        string UnderlyingScalarToStr() const
+        {
+            return m_underlyingScalar >= 0 && m_underlyingScalar < AZ::ShaderCompiler::Predefined::Scalar.size() ?
+                AZ::ShaderCompiler::Predefined::Scalar[m_underlyingScalar] : "<NA>";
+        }
+
+        uint32_t m_size = 0;                     // In bytes. Size of 0 indicates TypeRefInfo which hasn't been resolved or is a struct
+        uint32_t m_rows = 0;                     // 0 means it's not a matrix (effective Rows = 1). 1 or more means a Matrix
+        uint32_t m_cols = 0;                     // 0 means it's not a vector (effective Cols = 1). 1 or more means a Vector or Matrix
+        int      m_underlyingScalar = -1;        // index into AZ::ShaderCompiler::Predefined::Scalar, all fundamentals end up in a scalar at its leaf.
+    };
+
+    //! TypeRefInfo holds resolved immutable information of a core type (the `matrix2x2` in `column_major matrix2x2 a[3];`)
+    //! Its own id (containing mangled core name)
+    //! A type class (scalar, matrix, buffer, UDT...)
+    //! Information around the fundamental if applicable (not UDT).
+    struct TypeRefInfo
+    {
+        TypeRefInfo() = default;
+        TypeRefInfo(IdentifierUID typeId, const ArithmeticTypeInfo& fundamentalInfo, TypeClass typeClass)
+            : m_arithmeticInfo{fundamentalInfo},
+              m_typeClass{typeClass},
+              m_typeId{typeId}
+        {}
+
+        //! is plain data: sum type or fundamental. but not enum; because we don't know what underlying they have.
+        const bool IsPackable() const
+        {
+            return m_typeClass.IsOneOf(TypeClass::Struct, TypeClass::Class)
+                || !m_arithmeticInfo.IsEmpty();
+        }
+
+        //! non assigned TypeRefInfo
+        bool IsEmpty() const
+        {
+            return m_typeId.m_name.empty();
+        }
+
+        //! if the type is a SubpassInput, it's an input attachment
+        bool IsInputAttachment(const azslLexer* lexer) const
+        {
+            return IsViewType(m_typeClass)
+                 && (  m_typeId.GetNameLeaf() == Trim(lexer->getVocabulary().getLiteralName(azslLexer::SubpassInput), "\'")
+                    || m_typeId.GetNameLeaf() == Trim(lexer->getVocabulary().getLiteralName(azslLexer::SubpassInputMS), "\'"));
+        }
+
+        friend bool operator == (const TypeRefInfo& lhs, const TypeRefInfo& rhs)
+        {
+            return lhs.m_typeId == rhs.m_typeId;
+        }
+        friend bool operator != (const TypeRefInfo& lhs, const TypeRefInfo& rhs)
+        {
+            return !operator==(lhs,rhs);
+        }
+
+        IdentifierUID       m_typeId;
+        TypeClass           m_typeClass;
+        ArithmeticTypeInfo  m_arithmeticInfo;
+    };
+
+    //! Run a syntactic analysis of an arithmetic type name and extract info on its composition
+    inline ArithmeticTypeInfo CreateArithmeticTypeInfo(QualifiedName a_typeName)
+    {
+        assert(IsArithmetic( /*slow*/AnalyzeTypeClass(TentativeName{a_typeName}) ));  // no need to call this function if you don't have a fundamental (non void)
+        assert(!IsGenericArithmetic( /*slow*/AnalyzeTypeClass(TentativeName{a_typeName}) ));
+        // ↑ fatal aspect. The input needs to be canonicalized earlier to minimize this function's complexity.
+
+        ArithmeticTypeInfo toReturn;
+
+        string typeName = UnMangle(a_typeName);
+        size_t baseTypeLen = typeName.length();
+
+        // In shading languages we have vector and matrix types which use number of columns and possibly rows (for matrices)
+        // The digits always appear at the end of the type, so we can count back to resolve them
+        auto& colsChar = typeName.at(baseTypeLen - 1);
+        if (isdigit(colsChar))
+        {   // Fundamental types ending with a digit are either vectors (1, 2, 3, 4) or matrices (1x1, 2x2, 3x3, 4x4, etc)
+
+            // Vectors' sizes are defined in components, we opt to put those in Columns for consistency with the Matrix type below.
+            // A float3x4 matrix in DXC is represented as class.matrix.float.3.4 = type { [3 x <4 x float>] }
+            toReturn.m_cols = colsChar - '0';
+            baseTypeLen--;
+            assert(toReturn.m_cols >= 1 && toReturn.m_cols <= 4); // We should not be hitting asserts with any shader input, this is a bug in the tool
+
+            if (baseTypeLen >= 2)
+            {   // It's possible we are in a matrix type so let's check for rows too!
+
+                // Documentation: https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx-graphics-hlsl-per-component-math
+                // A matrix is a data structure that contains rows and columns of data.The data can be any of the scalar data types,
+                //   however, every element of a matrix is the same data type.The number of rows and columns is specified with the
+                //   row-by-column string that is appended to the data type.
+
+                auto& rowsChar = typeName.at(baseTypeLen - 2);
+                if (isdigit(rowsChar))
+                {
+                    assert(typeName.at(baseTypeLen - 1) == 'x'); // We should not be hitting asserts with any shader input, this is a bug in the tool
+
+                    toReturn.m_rows = rowsChar - '0';
+                    baseTypeLen -= 2;
+                    assert(toReturn.m_rows >= 1 && toReturn.m_rows <= 4); // We should not be hitting asserts with any shader input, this is a bug in the tool
+                }
+            }
+        }
+
+        // In any case baseTypeLen gives us our base type without vector or matrix information
+        string baseType = typeName.substr(0, baseTypeLen);
+
+        // 2 special cases: generic vector and matrix with no generic parameter -> they default to float, 4, 4.
+        // https://github.com/Microsoft/DirectXShaderCompiler/issues/2034
+        // temporarily support them, but when we canonicalize generics it will be supported earlier.
+        if (baseType == "vector" || baseType == "matrix")
+        {
+            baseType = "float";
+        }
+
+        auto it = ::std::find(AZ::ShaderCompiler::Predefined::Scalar.begin(), AZ::ShaderCompiler::Predefined::Scalar.end(), baseType);
+        assert(it != AZ::ShaderCompiler::Predefined::Scalar.end()); // baseType must exist in the Scalar bag by program invariant.
+        toReturn.m_underlyingScalar = static_cast<int>( std::distance(AZ::ShaderCompiler::Predefined::Scalar.begin(), it) );
+        toReturn.ResolveSize();
+        return toReturn;
+    }
+
+    MAKE_REFLECTABLE_ENUM(RootParamType,
+        SRV,               // t
+        UAV,               // u
+        Sampler,           // s
+        CBV,               // b, bound under an SrgTable as a View. Used for external buffers
+        SrgConstantCB,     // b, bound through a root descriptor. Used for SRG Constants.
+        RootConstantCB     // b, bound through a CB under root signature. Used for root constants.
+    );
+    MAKE_REFLECTABLE_ENUM(BindingType, T, U, S, B);  //!< as HLSL register type. (please keep in the same order as RootParamType for simple mapping)
+
+    //! map SRV->T | UAV->U | Sampler->S | CBV,SrgConsant,RootConstant->B
+    BindingType RootParamTypeToBindingType(RootParamType paramType);
+}

+ 223 - 0
src/AzslcUnboundedArraysValidator.cpp

@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include "AzslcUnboundedArraysValidator.h"
+
+namespace AZ::ShaderCompiler
+{
+    void UnboundedArraysValidator::SetOptions(const Options& options)
+    {
+        if (!options.m_maxSpaces)
+        {
+            throw std::runtime_error{ "--max-spaces must be greater than 0" };
+        }
+        m_options = options;
+    }
+
+    bool UnboundedArraysValidator::CheckUnboundedArrayFieldCanBeAddedToSrg(const IdentifierUID& srgUid, SRGInfo& srgInfo, const IdentifierUID& varUid, const VarInfo& varInfo, TypeClass typeClass,
+        string* errorMessage)
+    {
+        if (!CanBeDeclaredAsUnboundedArray(typeClass))
+        {
+            if (errorMessage)
+            {
+                *errorMessage = ConcatString("Data type of ( ", varUid.GetName(), " ) can not be packed as unbounded array");
+            }
+            return false;
+        }
+
+        srgInfo.m_unboundedArrays.push_back(varUid);
+
+        if (!CheckResourceCanBeAddedToSrgWhenUniqueIndicesIsEnabled(srgUid, varUid, errorMessage))
+        {
+            return false;
+        }
+
+        // Only types that can be declared as unbounded array consume register space.
+        BindingType bindingType = GetBindingType(varInfo.m_typeInfoExt);
+        auto spaceIndex = GetSpaceIndexForSrg(srgUid);
+        ArrayOfUnboundedUids& arrayOfUnboundedUids = m_unboundedUidsPerSpace[spaceIndex];
+        const IdentifierUID& unboundedUid = arrayOfUnboundedUids[bindingType];
+        if (!unboundedUid.IsEmpty())
+        {
+            // An unbounded array was already declared for this resource type. We can not add
+            // another declaration of this type in this register space.
+            if (errorMessage)
+            {
+                *errorMessage = ConcatString("More than one unbounded resource (", unboundedUid.GetName(), " and ", varUid.GetName(), ") in register space");
+            }
+            return false;
+        }
+
+        // Register the unbounded array.
+        arrayOfUnboundedUids[bindingType] = varUid;
+        return true;
+    }
+
+    bool UnboundedArraysValidator::CheckFieldCanBeAddedToSrg(bool isUnboundedArray, const IdentifierUID& srgUid, SRGInfo& srgInfo, const IdentifierUID& varUid, const VarInfo& varInfo, TypeClass typeClass,
+        string* errorMessage)
+    {
+        if (isUnboundedArray)
+        {
+            return CheckUnboundedArrayFieldCanBeAddedToSrg(srgUid, srgInfo, varUid, varInfo, typeClass, errorMessage);
+        }
+        auto spaceIndex = GetSpaceIndexForSrg(srgUid);
+
+        if (m_unboundedUidsPerSpace.empty())
+        {
+            // All good. No unbounded array of any kind has been registered so far.
+            return true;
+        }
+
+        if (!CanBeDeclaredAsUnboundedArray(typeClass))
+        {
+            // The only possibility of error is if --use-spaces is false,
+            // and there's already an unbounded array of type 'b' declared in another SRG.
+            if (!m_options.m_useSpacesEnabled)
+            {
+                // We use register 0 because if --use-spaces was not specified in the command line
+                // then there's only one space for all registers and its index is 0, aka "space0".
+                const ArrayOfUnboundedUids& arrayOfUnboundedUids = m_unboundedUidsPerSpace[0];
+                const IdentifierUID& unboundedUid = arrayOfUnboundedUids[BindingType::B];
+                if (!unboundedUid.IsEmpty())
+                {
+                    if (errorMessage)
+                    {
+                        *errorMessage = ConcatString("The unbounded resource [", unboundedUid.GetName(), "], doesn't allow [", varUid.GetName(), "] to be added to the register space");
+                    }
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        if (!CheckResourceCanBeAddedToSrgWhenUniqueIndicesIsEnabled(srgUid, varUid, errorMessage))
+        {
+            return false;
+        }
+
+        // Only types that can be declared as unbounded array consume register space.
+        const ArrayOfUnboundedUids& arrayOfUnboundedUids = m_unboundedUidsPerSpace[spaceIndex];
+        BindingType bindingType = GetBindingType(varInfo.m_typeInfoExt);
+        const IdentifierUID& unboundedUid = arrayOfUnboundedUids[bindingType];
+        if (!unboundedUid.IsEmpty())
+        {
+            // An unbounded array was already declared for this resource type. We can not add
+            // another declaration of this type in this register space.
+            if (errorMessage)
+            {
+                *errorMessage = ConcatString("The unbounded resource [", unboundedUid.GetName(), "], doesn't allow [", varUid.GetName(), "] to be added to the register space");
+            }
+            return false;
+        }
+
+        return true;
+    }
+
+    UnboundedArraysValidator::SpaceIndex UnboundedArraysValidator::GetSpaceIndexForSrg(const IdentifierUID& srgUid)
+    {
+        SpaceIndex spaceIndex = 0;
+        auto findIt = m_srgToSpaceIndex.find(srgUid);
+        if (findIt == m_srgToSpaceIndex.end())
+        {
+            if (m_options.m_useSpacesEnabled)
+            {
+                if (m_options.m_maxSpaces - 1 > m_maxSpaceIndex)
+                {
+                    spaceIndex = static_cast<SpaceIndex>(m_srgToSpaceIndex.size());
+                    m_maxSpaceIndex = std::max(spaceIndex, m_maxSpaceIndex);
+                }
+                else
+                {
+                    spaceIndex = m_maxSpaceIndex;
+                }
+            }
+            // We are using resize() instead of push_back() because if the size doesn't change resize() is no-op.
+            m_unboundedUidsPerSpace.resize(static_cast<size_t>(m_maxSpaceIndex) + 1);
+            m_srgToSpaceIndex.emplace(srgUid, spaceIndex);
+        }
+        else
+        {
+            spaceIndex = findIt->second;
+        }
+        return spaceIndex;
+    }
+
+    bool UnboundedArraysValidator::CheckResourceCanBeAddedToSrgWhenUniqueIndicesIsEnabled(const IdentifierUID& srgUid, const IdentifierUID& varUid, string* errorMessage) const
+    {
+        if (m_options.m_useUniqueIndicesEnabled)
+        {
+            if (m_options.m_useSpacesEnabled)
+            {
+                // if --unique-idx is true, and --use-spaces is true, we allow only one
+                // unbounded array per SRG. But if, for a given SRG and unbounded array was already
+                // registered then it is an error to add another variable that consumes register resources.
+                IdentifierUID unboundedArrayUid = GetFirstUnboundedArrayFromSrg(srgUid);
+                if (!unboundedArrayUid.IsEmpty())
+                {
+                    if (errorMessage)
+                    {
+                        *errorMessage = ConcatString("The unbounded resource [", unboundedArrayUid.GetName(), "], doesn't allow [", varUid.GetName(), "] to be added to the register space");
+                    }
+                    return false;
+                }
+            }
+            else
+            {
+                // if --unique-idx is true, and --use-spaces is false, we allow only one
+                // unbounded array across all SRGs. If an unbounded array was already
+                // registered in any SRG then it is an error to add another variable that consumes register resources.
+                IdentifierUID unboundedArrayUid = GetFirstUnboundedArrayAcrossAllSrgs();
+                if (!unboundedArrayUid.IsEmpty())
+                {
+                    if (errorMessage)
+                    {
+                        *errorMessage = ConcatString("The unbounded resource [", unboundedArrayUid.GetName(), "], doesn't allow [", varUid.GetName(), "] to be added to the register space");
+                    }
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    IdentifierUID UnboundedArraysValidator::GetFirstUnboundedArrayFromSrg(const IdentifierUID& srgUid) const
+    {
+        const auto& itor = m_srgToSpaceIndex.find(srgUid);
+        if (itor == m_srgToSpaceIndex.end())
+        {
+            return {};
+        }
+        SpaceIndex spaceIndex = itor->second;
+        const ArrayOfUnboundedUids& arrayOfUnboundedUids = m_unboundedUidsPerSpace[spaceIndex];
+        for (const auto& uid : arrayOfUnboundedUids)
+        {
+            if (!uid.IsEmpty())
+            {
+                return uid;
+            }
+        }
+        return {};
+    }
+
+    IdentifierUID UnboundedArraysValidator::GetFirstUnboundedArrayAcrossAllSrgs() const
+    {
+        for (const auto& arrayOfUnboundedUids : m_unboundedUidsPerSpace)
+        {
+            for (const auto& uid : arrayOfUnboundedUids)
+            {
+                if (!uid.IsEmpty())
+                {
+                    return uid;
+                }
+            }
+        }
+        return {};
+    }
+
+} // namespace AZ::ShaderCompiler

+ 98 - 0
src/AzslcUnboundedArraysValidator.h

@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include "GenericUtils.h"
+#include "AzslcKindInfo.h"
+
+namespace AZ::ShaderCompiler
+{
+    //! Helper class for validation of all possible use cases for unbounded arrays.
+    struct UnboundedArraysValidator final
+    {
+        UnboundedArraysValidator() = default;
+        ~UnboundedArraysValidator() = default;
+
+        struct Options
+        {
+            //! Takes the value of --use-spaces command line option.
+            //! When false, only one unbounded array of each type (b, t, u, s) can be daclared
+            //! across all SRGs.
+            //! When true, each SRG can have a its own unique set of unbounded arrays for b, t, u, s.
+            bool m_useSpacesEnabled = false;
+            //! Takes the value of --unique-idx command line option.
+            //! When false each register resource type b, t, u, s can have its own unbounded array per register space.
+            //! When true only one of b, t, u, s can have the unbounded array.
+            bool m_useUniqueIndicesEnabled = false;
+            //! Takes the value of --max-spaces
+            //! Max number of register spaces space0, space1, space2, ... space<m_optionMaxSpaces - 1>
+            uint32_t m_maxSpaces = std::numeric_limits<uint32_t>::max();
+        };
+
+        //! asserts if at least one option has an invalid value.
+        void SetOptions(const Options& options);
+
+        //! Validates, semantically speaking, if a variable/field can be added to a SRG.
+        //! The key concept is that unbounded arrays are only allowed for unpackable shader resources like SRV, UAV, Samplers and CBV.
+        //! And the other key concept is that an unbounded array takes "ownership" of the whole register range starting from the register index
+        //! assigned to them.
+        //! For example:
+        //! ShaderResourceGroup MySrg {
+        //!     Texture2D m_a;    // Owns t0 registers. OK
+        //!     Texture2D m_b[];  // Unbounded Array. Owns t1+ registers. OK.
+        //!     Texture2D m_c;    // BAD because the whole t1+ register range is being assigned to "m_b[]"
+        //! }
+        //! If it returns false, *errorMessage will have the details.
+        //! param isUnboundedArray If true the field is being declared as "m_var[]" instead if "m_var".
+        bool CheckFieldCanBeAddedToSrg(bool isUnboundedArray, const IdentifierUID& srgUid, SRGInfo& srgInfo, const IdentifierUID& varUid, const VarInfo& varInfo, TypeClass typeClass,
+            string* errorMessage = nullptr);
+
+    private:
+        //! Helper for CheckFieldCanBeAddedToSrg. Only called if @varUid was declared as an unbounded array.
+        //! If it returns false, *errorMessage will have the details.
+        bool CheckUnboundedArrayFieldCanBeAddedToSrg(const IdentifierUID& srgUid, SRGInfo& srgInfo, const IdentifierUID& varUid, const VarInfo& varInfo, TypeClass typeClass,
+            string* errorMessage = nullptr);
+
+        //! When this function is called, it is guaranteed that varUid refers to a variable
+        //! that can be bound to a resource register.
+        //! For example:
+        //! A variable like "float m_var;" can NOT be bound to a resource register.
+        //! A variable like "Texture2D m_var;" is a resource that can be bound to a "tX" register. 
+        bool CheckResourceCanBeAddedToSrgWhenUniqueIndicesIsEnabled(const IdentifierUID& srgUid, const IdentifierUID& varUid, string* errorMessage = nullptr) const;
+
+        IdentifierUID GetFirstUnboundedArrayFromSrg(const IdentifierUID& srgUid) const;
+
+        IdentifierUID GetFirstUnboundedArrayAcrossAllSrgs() const;
+
+        using SpaceIndex = uint32_t; // represents register space0, space1, ...
+
+        //! Returns the space index that corresponds to the given SRG.
+        //! Calculating the correct space index depends on the commmand
+        //! line options --use-spaces (Options.m_useSpacesEnabled), and
+        //! --max-spaces (Options.m_maxSpaces).
+        //! The calculated space index is stored in m_srgToSpaceIndex the first time
+        //! this function is called for any given SRG.
+        SpaceIndex GetSpaceIndexForSrg(const IdentifierUID& srgUid);
+
+        //! Some of the command line options pertinent to validation of unbounded arrays.
+        Options m_options;
+
+        //! Key is an SRG Uid, value is its space index.
+        unordered_map<IdentifierUID, SpaceIndex> m_srgToSpaceIndex;
+        //! Keeps track of max SpaceIndex value in m_srgToSpaceIndex.
+        SpaceIndex m_maxSpaceIndex = 0; 
+        using ArrayOfUnboundedUids = array<IdentifierUID, BindingType::EndEnumeratorSentinel_>;
+        //! The index is the SpaceIndex, the content represents a size-fixed array, where each subscript,
+        //! if not empty, means that there's already an unbounded array declared for such resource
+        //! type, therefore we can detect if the user is trying to declare another resource after an unbounded
+        //! array, which is forbidden.
+        //! The size of this array is managed in GetSpaceIndexForSrg().
+        vector<ArrayOfUnboundedUids> m_unboundedUidsPerSpace;
+
+    };
+} // namespace AZ::ShaderCompiler

+ 1322 - 0
src/AzslcUtils.h

@@ -0,0 +1,1322 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+
+#include "AzslcMangling.h"
+#include "AzslcCommon.h"
+#include "AzslcException.h"
+#include "DiagnosticStream.h"
+
+#include "antlr4-runtime.h"
+#include "generated/azslLexer.h"
+#include "generated/azslParserBaseListener.h"
+#include "generated/azslParser.h"
+
+#include "AzslcPredefinedTypes.h"
+
+#include "ReflectableEnums.h"
+#include "ReflectableEnumsUtils.h"
+
+#if defined(_WIN32) || defined(_WIN64)
+# define AzslcStrnicmp _strnicmp
+#elif defined(__APPLE__)
+# define AzslcStrnicmp strncasecmp
+#elif defined (__linux__) || defined(__unix__)
+# include <strings.h>
+# define AzslcStrnicmp strncasecmp
+#endif
+
+namespace AZ::ShaderCompiler
+{
+    extern DiagnosticStream verboseCout;
+    extern DiagnosticStream warningCout;
+    extern Endl             azEndl;
+
+    using AstType                       = azslParser::TypeContext;                  // all usertypes and predefined, but cannot be Void
+    using AstFuncType                   = azslParser::FunctionTypeContext;          // all (can be Void)
+    using AstTypeofNode                 = azslParser::TypeofExpressionContext;
+    using AstPredefinedTypeNode         = azslParser::PredefinedTypeContext;
+    using AstClassDeclNode              = azslParser::ClassDefinitionContext;
+    using AstStructDeclNode             = azslParser::StructDefinitionContext;
+    using AstEnumDeclNode               = azslParser::EnumDefinitionContext;
+    using AstInterfaceDeclNode          = azslParser::InterfaceDefinitionContext;
+    using AstSRGSemanticDeclNode        = azslParser::SrgSemanticContext;
+    using AstSRGSemanticMemberDeclNode  = azslParser::SrgSemanticMemberDeclarationContext;
+    using AstSRGDeclNode                = azslParser::SrgDefinitionContext;
+    using AstSRGMemberNode              = azslParser::SrgMemberDeclarationContext;
+    using AstNamedVarDecl               = azslParser::NamedVariableDeclaratorContext;
+    using AstUnnamedVarDecl             = azslParser::UnnamedVariableDeclaratorContext;
+    using AstFuncSig                    = azslParser::LeadingTypeFunctionSignatureContext;
+    using AstIdExpr                     = azslParser::IdExpressionContext;
+    using AstExpr                       = azslParser::ExpressionContext;
+    using AstMemberAccess               = azslParser::MemberAccessExpressionContext;
+    using AstVarInitializer             = azslParser::VariableInitializerContext;
+
+    inline constexpr ssize_t operator ""_ssz(unsigned long long n)
+    {
+        return n;
+    }
+
+    inline bool EqualNoCase(string_view str1, string_view str2)
+    {
+        if (str1.length() != str2.length())
+        {
+            return false;
+        }
+
+        return AzslcStrnicmp(str1.data(), str2.data(), str2.length()) == 0;
+    }
+
+    inline bool StartsWithNoCase(string_view str1, string_view str2)
+    {
+        if (str1.length() < str2.length())
+        {
+            return false;
+        }
+
+        return AzslcStrnicmp(str1.data(), str2.data(), str2.length()) == 0;
+    }
+
+    // this format is the Microsoft standard for error list parsing "file(line,column): message"
+    inline string DiagLine(size_t line)
+    {
+        using namespace std::string_literals;
+        return AzslcException::s_currentSourceFileName + "("s + std::to_string(line) + "):";
+    }
+
+    inline string DiagLine(optional<int> line)
+    {
+        return line ? DiagLine(static_cast<size_t>(*line)) : string{};
+    }
+
+    inline string DiagLine(optional<size_t> line)
+    {
+        return line ? DiagLine(*line) : string{};
+    }
+
+    inline string DiagLine(antlr4::Token* token)
+    {
+        return DiagLine(token->getLine());
+    }
+
+    inline string DiagLine(tree::TerminalNode* astNode)
+    {
+        return DiagLine(astNode->getSymbol());
+    }
+
+    //! low level version with everything parameterizable
+    template<typename... Types>
+    inline void PrintWarning(DiagnosticStream& stream, Warn::EnumType level, optional<size_t> lineNumber, optional<size_t> column, Types&&... messageBits)
+    {
+        stream << PushLevel{} << level
+               << AzslcException::MakeErrorMessage(lineNumber ? ToString(*lineNumber) : "", column ? ToString(*column) : "",
+                                                   "", false, "", ConcatString(messageBits..., "\n"))
+               << PopLevel{};
+    }
+
+    //! version for clients with only, maybe, a line number
+    template<typename... Types>
+    inline void PrintWarning(Warn::EnumType level, optional<size_t> line, Types&&... messageBits)
+    {
+        PrintWarning(warningCout, level, line, none, messageBits...);
+    }
+
+    //! version for clients with a token (richest, preferred way)
+    template<typename... Types>
+    inline void PrintWarning(Warn::EnumType level, antlr4::Token* token, Types&&... messageBits)
+    {
+        PrintWarning(warningCout, level, token->getLine(), token->getCharPositionInLine() + 1, messageBits...);
+    }
+
+    inline bool WasParsedAsPredefinedType(AstType* ctx)
+    {
+        return ctx->predefinedType();
+    }
+
+    inline bool WasParsedAsPredefinedType(AstFuncType* ctx)
+    {
+        return ctx->Void() || WasParsedAsPredefinedType(ctx->type());
+    }
+
+    inline AstTypeofNode* ExtractTypeofAstNode(AstType* ctx)
+    {
+        return ctx->typeofExpression();
+    }
+
+    inline AstTypeofNode* ExtractTypeofAstNode(AstFuncType* ctx)
+    {
+        return ctx->type() ? ExtractTypeofAstNode(ctx->type()) : nullptr;
+    }
+
+    template<typename AnyOther>
+    inline AstTypeofNode* ExtractTypeofAstNode(AnyOther*)
+    {
+        return nullptr;
+    }
+
+    using ConstNumericVal = variant<monostate, int32_t, uint32_t, float>;
+
+
+    //! Extracts <float> from <ConstNumericVal>. When not possible it will throw if <defval> is not set or fall back to <defval> if set.
+    inline float ExtractValueAsFloat(const ConstNumericVal& var, optional<float> defval = none)
+    {
+        if (holds_alternative<monostate>(var))
+        {
+            if (defval == none)
+            {
+                throw std::logic_error{ "Constant value did not hold anything. Set defval if you want a fallback option." };
+            }
+            else
+            {
+                return *defval;
+            }
+        }
+        else if (holds_alternative<int32_t>(var))
+        {
+            auto intVal = get<int32_t>(var);
+            // Casting to float has precision loss: https://onlinegdb.com/By0AJTUVE
+            if (intVal > 16777216 || intVal < -16777216)
+            {
+                PrintWarning(Warn::W3, none, "warning: Casting integer ", intVal, " to float, will result in ", static_cast<float>(intVal));
+            }
+            return static_cast<float>(intVal);
+        }
+        else if (holds_alternative<uint32_t>(var))
+        {
+            auto uintVal = get<uint32_t>(var);
+            // Same comment
+            if (uintVal > 16777216)
+            {
+                PrintWarning(Warn::W3, none, "warning: Casting integer ", uintVal, " to float, will result in ", static_cast<float>(uintVal));
+            }
+            return static_cast<float>(uintVal);
+        }
+
+        assert(holds_alternative<float>(var));
+        return get<float>(var);
+    }
+
+    inline int64_t ExtractValueAsInt64(const ConstNumericVal& var, optional<int64_t> defval = none)
+    {
+        if (holds_alternative<monostate>(var))
+        {
+            if (defval == none)
+            {
+                throw std::logic_error{ "Constant value did not hold anything. Set defval if you want a fallback option." };
+            }
+            else
+            {
+                return *defval;
+            }
+        }
+        else if (holds_alternative<float>(var))
+        {
+            // Casting from float has precision loss: https://onlinegdb.com/By0AJTUVE
+            auto floatVal = get<float>(var);
+            auto intVal = static_cast<int64_t>(floatVal);
+            PrintWarning(Warn::W3, none, "warning: Casting float ", floatVal, " to integer, will result in ", intVal);
+            return intVal;
+        }
+        else if (holds_alternative<int32_t>(var))
+        {
+            return get<int32_t>(var);
+        }
+        return static_cast<int64_t>(get<uint32_t>(var));
+    }
+
+    //! Safe way to call ExtractValueAsInt64 which returns false instead of throwing
+    inline bool TryGetConstExprValueAsInt64(const ConstNumericVal& foldedIdentifier, int64_t& returnValue) noexcept(true)
+    {
+        if (holds_alternative<monostate>(foldedIdentifier))
+        {
+            return false;
+        }
+
+        returnValue = ExtractValueAsInt64(foldedIdentifier); // Won't throw because we have already checked holds_alternative<monostate>
+        return true;
+    }
+
+    MAKE_REFLECTABLE_ENUM_POWER (StorageFlag,
+        Static, Const, Extern, Shared, Groupshared, Precise, Uniform, Volatile, RowMajor, ColumnMajor, In, Out, InOut, Inline, Option, Enumerator, Rootconstant, Unknown
+    );
+    using TypeQualifier = Flag<StorageFlag>;
+
+    struct ArrayDimensions
+    {
+        const bool IsArray() const
+        {
+            return !m_dimensions.empty();
+        }
+
+        const bool IsUnbounded() const
+        {
+            // For now m_tex[][4][5] won't be supported.
+            // But m_tex[] is supported.
+            return (m_dimensions.size() == 1) && (m_dimensions[0] == Unbounded);
+        }
+
+        //! Have all dimensions been statically resolved ?
+        const bool AreAllDimsFullyConstantFolded() const
+        {
+            return std::all_of(m_dimensions.begin(), m_dimensions.end(), [](int d) { return (d >= 0); });
+        }
+
+        int GetDimensionAt_OrDefault(int dimensionIndex, int defaultValueIfNoSuchDim) const
+        {
+            return m_dimensions.size() > dimensionIndex ? m_dimensions[dimensionIndex] : defaultValueIfNoSuchDim;
+        }
+
+        //! pretty print a representation of an array variable declarator suffix in C-like form
+        //! {} is ""
+        //! {unbounded} is "[]"
+        //! {unknown} is "[?]"
+        //! {1,2} is "[1][2]"
+        string ToString(const string& prefix = "[", const string& separator = "][", const string& suffix = "]") const
+        {
+            vector<string> asStrs;
+            std::transform(m_dimensions.begin(), m_dimensions.end(), std::back_inserter(asStrs),
+                           [](int d) -> string { return d == Unknown ? "?" : d == Unbounded ? "" : std::to_string(d); });
+            return asStrs.empty() ? "" : prefix + Join(asStrs.begin(), asStrs.end(), separator) + suffix;
+        }
+
+        void PushBack(int newDimension)
+        {
+            m_dimensions.push_back(newDimension);
+        }
+
+        void Clear()
+        {
+            m_dimensions.clear();
+        }
+
+        bool Empty() const
+        {
+            return m_dimensions.empty();
+        }
+
+        auto begin() const
+        {
+            return m_dimensions.begin();
+        }
+
+        auto end() const
+        {
+            return m_dimensions.end();
+        }
+
+        friend bool operator == (const ArrayDimensions& lhs, const ArrayDimensions& rhs)
+        {
+            return lhs.m_dimensions == rhs.m_dimensions;
+        }
+        friend bool operator != (const ArrayDimensions& lhs, const ArrayDimensions& rhs)
+        {
+            return !operator==(lhs,rhs);
+        }
+
+        //! value taken by dimensions that couldn't be constant-folded (variable initializer)
+        static constexpr int Unknown = -1;
+        //! dimension with an empty bracket []
+        static constexpr int Unbounded = -2;
+
+        //! multi array dimensions. e.g. a[2][3] will be a vector of {2,3}
+        //! a[][3] (or a[var][3]) will be a vector of {unknown,3}
+        //!  (note: if `var` is a statically foldable const expression, then it has a chance to resolved to its initial value)
+        //! empty {} means "not an array"
+        vector<unsigned int> m_dimensions;
+    };
+
+    //! OutputFormat
+    //!  Specifies the pixel output format hint for the render target
+    //! 
+    //! None - hints that the target is unused
+    //! R32 - one channel (R), can be Float32, Uint32 or Sint32
+    //! R32G32 - two channels (RG), can be Float32, Uint32 or Sint32
+    //! R32A32- two channels (RA), can be Float32, Uint32 or Sint32
+    //! R16G16B16A16_FLOAT - Default. Four or less channels (RGBA), Float16
+    //! R16G16B16A16_UNORM - four or less channels (RGBA), Unorm16
+    //! R16G16B16A16_SNORM - four or less channels (RGBA), Snorm16
+    //! R16G16B16A16_UINT - four or less channels (RGBA), Uint16
+    //! R16G16B16A16_SINT - four or less channels (RGBA), Sint16
+    //! R32G32B32A32 - four channels (RGBA), can be Float32, Uint32 or Sint32
+    
+    MAKE_REFLECTABLE_ENUM(OutputFormat,
+        None, R32, R32G32, R32A32, R16G16B16A16_FLOAT, R16G16B16A16_UNORM, R16G16B16A16_SNORM, R16G16B16A16_UINT, R16G16B16A16_SINT, R32G32B32A32
+    );
+
+    namespace Packing
+    {
+        constexpr uint32_t s_bytesPerRegister = 16;
+        constexpr uint32_t s_bytesPerComponent = 4;
+
+        enum class Layout : uint32_t
+        {
+            CStylePacking,           //!< Dense packing with no padding. Also known as Scalar. Default for structured buffers in DirectX.
+            DirectXPacking,          //!< DirectX style packing, default for cbuffer layouts in DirectX
+            RelaxedDirectXPacking,   //!< As DirectX standard, but assumes dense array packing and invariant to row-column major (best fit)
+            RelaxedStd140Packing,    //!< Vector-relaxed OpenGL std140, default for Uniform buffer packing for Vulkan (base alignment)
+            RelaxedStd430Packing,    //!< Vector-relaxed OpenGL std430, default for Storage buffer packing for Vulkan (scalar alignment)
+            StrictStd140Packing,     //!< Strict OpenGL std140, default for Uniform buffer packing for OpenGL
+            StrictStd430Packing,     //!< Strict OpenGL std430, default for Storage buffer packing for OpenGL
+            DirectXStoragePacking = CStylePacking,   //!< DirectX style packing for Storage buffers. That's actually Scalar
+        };
+
+        //! Used together with Layout. Defines what alignment rules the next chunk should follow
+        enum class Alignment : uint32_t
+        {
+            asVectorStart,
+            asVectorEnd,
+            asMatrixStart,
+            asMatrixEnd,
+            asStructStart,
+            asStructEnd,
+            asArrayStart,
+            asArrayEnd,
+        };
+
+        // Some standards (std140/std430) have extended alignment rules for structures, which dictate that
+        //  the alignment inside a struct behaves like base rather than scalar.
+        // For all other cases the alignment doesn't change so we can return the input.
+        static Layout GetExtendedLayout(const Layout& scalarLayout)
+        {
+            if (scalarLayout == Layout::RelaxedStd430Packing)
+            {
+                return Layout::RelaxedStd140Packing;                
+            }
+            else if (scalarLayout == Layout::StrictStd430Packing)
+            {
+                return Layout::StrictStd140Packing;                                
+            }
+
+            return scalarLayout;
+        }
+
+        static uint32_t AlignUp(const uint32_t value, const uint32_t alignment)
+        {
+            if (alignment <= 1)
+            {
+                return value;
+            }
+
+            uint32_t mask = alignment - 1;
+            return (value + mask) & ~mask;
+        }
+
+        static uint32_t AlignStructToLargestMember(Layout layout, uint32_t currentSize, uint32_t memberSize)
+        {
+            if (memberSize == 0)
+            {
+                return currentSize;
+            }
+
+            if (layout == Layout::RelaxedStd140Packing ||
+                layout == Layout::RelaxedStd430Packing ||
+                layout == Layout::StrictStd140Packing ||
+                layout == Layout::StrictStd430Packing)
+            {
+                // Alignment starts as s_bytesPerComponent, can only double (power of two) and cannot exceed s_bytesPerRegister
+                auto alignment = s_bytesPerComponent;
+                while (alignment < memberSize && alignment < s_bytesPerRegister)
+                {
+                    alignment *= 2;
+                }
+                return AlignUp(currentSize, alignment);
+            }
+
+            return currentSize;
+        }
+
+        // Checks if the parameters correspond to either a Vector or a Matrix collapsed to a Vector.
+        static bool IsVectorAligned(const Alignment& alignment, uint32_t rows, uint32_t cols)
+        {
+            if (alignment == Alignment::asVectorStart ||
+                alignment == Alignment::asVectorEnd)
+            {
+                return true;
+            }
+
+            if (alignment == Alignment::asMatrixStart ||
+                alignment == Alignment::asMatrixEnd)
+            {
+                if (rows <= 1 || cols <= 1)
+                {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        //! Aligns the offset for the next structure
+        //! Vulkan : https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#interfaces-resources-layout
+        //!          https://github.com/microsoft/DirectXShaderCompiler/blob/master/docs/SPIR-V.rst#memory-layout-rules
+        //! OpenGL : https://www.khronos.org/registry/OpenGL/specs/gl/glspec45.core.pdf#page=159
+        //!          https://github.com/Microsoft/DirectXShaderCompiler/blob/master/docs/SPIR-V.rst
+        static uint32_t AlignOffset(Layout layout, uint32_t currentSize, const Alignment& alignment, uint32_t rows, uint32_t cols)
+        {
+            switch (layout)
+            {
+            case Layout::CStylePacking:
+                return currentSize;
+
+            case Layout::DirectXPacking:
+            case Layout::RelaxedDirectXPacking:
+                if (alignment == Alignment::asMatrixStart ||
+                    alignment == Alignment::asArrayStart ||
+                    alignment == Alignment::asStructStart ||
+                    alignment == Alignment::asStructEnd)
+                {
+                    return AlignUp(currentSize, s_bytesPerRegister);
+                }
+
+                // Vectors, matrix endings or array endings don't require special alignment
+                return currentSize;
+
+            case Layout::RelaxedStd140Packing:
+                if (alignment == Alignment::asArrayStart ||
+                    alignment == Alignment::asStructStart ||
+                    alignment == Alignment::asArrayEnd ||
+                    alignment == Alignment::asStructEnd )
+                {
+                    return AlignUp(currentSize, s_bytesPerRegister);
+                }
+
+                if (IsVectorAligned(alignment, rows, cols))
+                {
+                    // Matrices with only one row or only one column collapse to vectors and don't require alignment if the size fits the remaining register
+                    // const auto elements = std::max(rows, cols);
+                    // According to the base alignment packing rules vectors with 3 elements align as 4-element vectors with strict std140. 
+                    // dxc doesn't seem to follow that standard with relaxed std140.
+                    return currentSize;
+                }
+
+                if (alignment == Alignment::asMatrixStart ||
+                    alignment == Alignment::asMatrixEnd)
+                {
+                    return AlignUp(currentSize, s_bytesPerRegister);
+                }
+
+                return currentSize;
+
+            case Layout::RelaxedStd430Packing:
+            case Layout::StrictStd140Packing:
+            case Layout::StrictStd430Packing:
+                // The relaxed std430 (= scalar packing) for Vulkan doesn't dictate any special rules for matrix, array or struct
+                // start or end decorations, but individual case are handled uniquely
+                // dxc is also not without exceptions: https://github.com/microsoft/DirectXShaderCompiler/issues/2639
+                // so fully handling complex structs might take more effort
+                return currentSize;
+
+            default:
+                throw std::runtime_error{ "PackNextChunk: Unknown format should be handled properly!" };
+                break;
+            }
+
+            return 0; // Prevents warning C4715
+        }
+
+        //! Packs the next chunk of data to the current offset and returns the new offset
+        //! Alignment - dictates the alignment rules to follow for packing nextChunkSize
+        static uint32_t PackNextChunk(Layout layout, uint32_t nextChunkSize, uint32_t& offset)
+        {
+            switch (layout)
+            {
+            case Layout::CStylePacking:
+                return offset + nextChunkSize;
+
+            case Layout::DirectXPacking:
+            case Layout::RelaxedDirectXPacking:
+            case Layout::RelaxedStd140Packing:
+            case Layout::StrictStd140Packing:
+            {
+                // Rule - sizes within 16 bytes should not cross the 16-byte boundary
+                const auto remaining = offset % s_bytesPerRegister;
+                if (remaining > 0 && remaining + nextChunkSize > s_bytesPerRegister)
+                {
+                    offset = AlignUp(offset, s_bytesPerRegister);
+                }
+                return offset + nextChunkSize;
+            }
+
+            case Layout::RelaxedStd430Packing:
+            case Layout::StrictStd430Packing:
+                return offset + nextChunkSize;
+
+            default:
+                throw std::runtime_error{ "PackNextChunk: Unknown format should be handled properly!" };
+                break;
+            }
+
+            return 0; // Prevents warning C4715
+        }
+
+        //! Packs the base size into an array of certain dimensions
+        //! Dimensions can be 0, in which case it returns the baseSize, because there is no array rules to follow
+        //! out Alignment - The minimum alignment required to pack this structure
+        static uint32_t PackIntoArray(Layout layout, uint32_t baseSize, const ArrayDimensions& dimensions)
+        {
+            if (dimensions.Empty())
+            {
+                return baseSize;
+            }
+
+            // Sanity check. We can't do this earlier as some array dimensions are not resolvable at compile time,
+            //  but still valid at runtime.
+            if (!dimensions.AreAllDimsFullyConstantFolded())
+            {
+                throw std::runtime_error{"Layout packing failed for unresolved array dimensions. Please check previous warnings!"};
+            }
+
+            switch (layout)
+            {
+            case Layout::CStylePacking:
+            case Layout::RelaxedDirectXPacking:
+            case Layout::RelaxedStd430Packing:
+            case Layout::StrictStd430Packing:
+            case Layout::StrictStd140Packing:
+            {
+                uint32_t totalElements = 1;
+                for (auto dimension : dimensions)
+                {
+                    totalElements *= dimension;
+                }
+
+                return baseSize * totalElements;
+            }
+
+            case Layout::DirectXPacking:
+            {
+                // DirectX elements always start on a new register
+                const auto unalignedSize = baseSize;
+                baseSize = AlignUp(baseSize, s_bytesPerRegister);
+
+                uint32_t totalElements = 1;
+                for (auto dimension : dimensions)
+                {
+                    totalElements *= dimension;
+                }
+                return baseSize * (totalElements - 1) + unalignedSize; // The last element doesn't occupy its full register
+            }
+
+            case Layout::RelaxedStd140Packing:
+            {
+                baseSize = AlignUp(baseSize, s_bytesPerRegister);
+
+                uint32_t totalElements = 1;
+                for (auto dimension : dimensions)
+                {
+                    totalElements *= dimension;
+                }
+                return baseSize * totalElements;
+            }
+
+            default:
+                throw std::runtime_error{ "PackNextChunk: Unknown format should be handled properly!" };
+                break;
+            }
+
+            return 0; // Prevents warning C4715
+        }
+
+        //! Packs the base size as a vector or a matrix
+        //! Rows and/or cols can be 0. However, if Rows is greater than 0, Cols cannot be 0.
+        //! Note! Regardless of major, matrices are defined as rows-by-columns! Rows == 0 means it's not a matrix
+        static uint32_t PackAsVectorMatrix(Layout layout, uint32_t baseSize, uint32_t rows, uint32_t cols, bool rowMajor)
+        {
+            if (cols <= 1 && rows <= 1)
+            {   // This is neither - return
+                return baseSize;
+            }
+
+            // Sanity check - if it's a matrix then rows > 0 and cols must be in the [1..4] range
+            if (rows > 0)
+            {
+                // https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx-graphics-hlsl-per-component-math
+                assert(rows <= 4);
+                assert(cols >= 1 && cols <= 4);
+            }
+
+            switch (layout)
+            {
+            case Layout::CStylePacking:
+                return baseSize
+                    * (cols == 0 ? 1 : cols)
+                    * (rows == 0 ? 1 : rows);
+
+            case Layout::DirectXPacking:
+                if (rows > 0)
+                {   // It's a matrix
+
+                    if (rowMajor && rows > 1 && cols == 1)
+                    {
+                        // This is a very special case which we can't replicate the same way dxc packs the data
+                        // Additionally, this results in a lot of wasted space, so it's explicitly forbidden
+                        throw std::runtime_error{ "PackAsVectorMatrix: row_major packing for 2x1, 3x1 and 4x1 matrix types is not allowed!" };
+                    }
+
+                    const auto fullSize = ((rowMajor) ? rows : cols) * s_bytesPerRegister;           // Use Rows or Cols to calculate the size
+
+                    // Note! A matrix does not occupy the entire last register if its number of elements is less than 4
+                    const auto adjustSize = (4 - ((rowMajor) ? cols : rows)) * s_bytesPerComponent;  // Use the opposite (Cols or Rows)
+                    return fullSize - adjustSize;
+                }
+
+                // It's a vector
+                return cols * s_bytesPerComponent;
+
+            case Layout::RelaxedDirectXPacking:
+                if (rows > 0)
+                {   // It's a matrix
+                    auto major = (rows < cols) ? rows : cols; // Take the smaller fit
+                    return major * s_bytesPerRegister;
+                }
+
+                // It's a vector
+                return cols * s_bytesPerComponent;
+
+            case Layout::RelaxedStd140Packing:
+            case Layout::RelaxedStd430Packing:
+            case Layout::StrictStd140Packing:
+            case Layout::StrictStd430Packing:
+                if (rows > 0)
+                {   // It's a matrix
+
+                    if (rows == 1)
+                    {
+                        // It collapses it as a vector
+                        return cols * s_bytesPerComponent;
+                    }
+
+                    if (cols == 1)
+                    {
+                        // It collapses it as a vector
+                        return rows * s_bytesPerComponent;
+                    }
+
+                    auto matrixStride = (layout == Layout::RelaxedStd430Packing && rows == 2) ? 2 * s_bytesPerComponent : s_bytesPerRegister;
+                    auto matrixElements = rowMajor ? rows : cols;
+                    if (rowMajor)
+                    {
+                        std::swap(matrixStride, matrixElements);
+                    }
+
+                    return matrixStride * matrixElements;
+                }
+
+                // It's a vector
+                return cols * s_bytesPerComponent;
+            }
+
+            return 0; // Prevents warning C4715
+        }
+
+        inline uint32_t PackedSizeof(int indexInAzslPredefined_Scalar)
+        {
+            // the array is generated but it's expected to look like: {"bool", "double", "dword", "float", "half", "int", "uint", "unsigned int"}
+            // just update that code if it changes one day, the assert will pop.
+            if (indexInAzslPredefined_Scalar == 1)
+            {
+                assert(string_view{"double"} == AZ::ShaderCompiler::Predefined::Scalar[1]);
+                // Shader packing reference:
+                // https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx-graphics-hlsl-packing-rules
+                return 8;
+            }
+            else if (indexInAzslPredefined_Scalar < 0)
+            {
+                return 0;  // non-predefined case, surely meaning UDT.
+            }
+            assert(indexInAzslPredefined_Scalar < AZ::ShaderCompiler::Predefined::Scalar.size()); // #craefulgang.
+            return 4;
+        }
+    };
+
+    struct TokensLocation
+    {
+        misc::Interval m_expressionSpan;  // eg for `void F()` -> type id-expr brace brace -> m_expressionSpan concerns id-expr. for `nested::leaf` -> all 3 tokens are in the span
+        ssize_t        m_focusedTokenId;  // eg for `void F()` -> `F` is the focused token. for id-expr, each nested identifier has a seenat. in `nested::leaf` -> 2 TokensLocation. one with 'nested' as focus, one with 'leaf' as focus; both with the same expression span.
+        size_t         m_line;
+        size_t         m_charPos;
+    };
+
+    // Most contexts (all?) will duck-type into this function
+    template <typename ContextT>
+    TokensLocation MakeTokensLocation(ContextT* ctx, const Token* focusedToken)
+    {
+        TokensLocation tl;
+        tl.m_expressionSpan = const_cast<std::remove_const_t<ContextT>*>(ctx)->getSourceInterval();
+        tl.m_focusedTokenId = static_cast<ssize_t>(focusedToken->getTokenIndex());
+        tl.m_line           = focusedToken->getLine();
+        tl.m_charPos        = focusedToken->getCharPositionInLine();
+        return tl;
+    };
+
+    // version for single token
+    inline TokensLocation MakeTokensLocation(const Token* tok)
+    {
+        TokensLocation tl;
+        tl.m_expressionSpan = misc::Interval{tok->getTokenIndex(), tok->getTokenIndex()};
+        tl.m_focusedTokenId = static_cast<ssize_t>(tok->getTokenIndex());
+        tl.m_line           = tok->getLine();
+        tl.m_charPos        = tok->getCharPositionInLine();
+        return tl;
+    };
+
+    struct Seenat
+    {
+        IdentifierUID  m_referredDefinition;
+        TokensLocation m_where;      // location in original stream, of a token group that can refer to a symbol.
+                                     // (it's an interval because of nested-name-specifiers)
+    };
+
+// before being able to clean that up with C++20 concepts, let's factorize the SFINAE expression to filter the accepted iterator types by an expected trait
+#define SFINAE_IS_SAME(DependentTypeToCompare, ExpectedType)\
+    enable_if_t< is_same_v<typename DependentTypeToCompare, ExpectedType> >* = nullptr
+
+    // Create a yaml element list for 'any collection' of Seenat (passed as a pseudo range)
+    // - {line: x, col: y}
+    template <typename SeenatRangeIter, SFINAE_IS_SAME(SeenatRangeIter::value_type, Seenat) >
+    string ToYaml(SeenatRangeIter begin, SeenatRangeIter end, string_view indent)
+    {
+        string s;
+        for (SeenatRangeIter it = begin; it != end; ++it)
+        {
+            s += indent;
+            s += "- {line: " + std::to_string(it->m_where.m_line);
+            s += ", col: " + std::to_string(it->m_where.m_charPos + 1);  // +1 because charPos starts at 0. most editors start at 1
+            s += "}\n";
+        }
+        return s;
+    }
+
+    // version for pseudo-range of IdentifierUID elements
+    template <typename UIDRangeIter, SFINAE_IS_SAME(UIDRangeIter::value_type, IdentifierUID) >
+    string ToYaml(UIDRangeIter begin, UIDRangeIter end, string_view indent)
+    {
+        string s;
+        for (UIDRangeIter it = begin; it != end; ++it)
+        {
+            s += indent;
+            s += "- {name: '" + it->m_name + "'}\n";
+        }
+        return s;
+    }
+#undef SFINAE_IS_SAME
+
+    // like PathPart but more specialized for grammatical elements specificity of an idExpression
+    struct IdExpressionPart
+    {
+        string GetText() const
+        {
+            return m_token->getText();
+        }
+
+        bool IsScopeToken() const
+        {
+            return m_type == GlobalScopeOperator || m_type == ScopeResolutionOperator;
+        }
+
+        // Azslc Intermediate Representation uses slash separators for scopes
+        // This function can help constructing internal "mangled" forms of qualified symbol names
+        string GetAZIRMangledText() const
+        {
+            return IsScopeToken() ? "/" : GetText();
+        }
+
+        enum Type
+        {
+            NestedNameSpecifier,      //  nns::leaf    (nns)
+            ScopeResolutionOperator,  //  nns::leaf    (::)
+            GlobalScopeOperator,      //  ::leaf       (leading ::)
+            LoneUnqualifiedId,        //  leaf         (leaf)
+            QualifiedLeaf             //  nns::leaf    (leaf)
+        };
+        Token* m_token;
+        Type   m_type;
+    };
+
+    template <typename Functor>
+    void ForEachIdExpressionPart(AstIdExpr* ctx, Functor action)
+    {
+        static_assert( is_invocable<Functor, IdExpressionPart>::value, "please pass function objects that takes IdExpressionPart as argument" );
+        if (ctx->unqualifiedId())
+        {
+            Token* loneLeaf = ctx->unqualifiedId()->Identifier()->getSymbol();
+            action(IdExpressionPart{loneLeaf, IdExpressionPart::LoneUnqualifiedId});
+        }
+        else
+        {
+            Token* globalSROToken = ctx->qualifiedId()->nestedNameSpecifier()->GlobalSROToken;
+            if (globalSROToken)
+            {
+                action(IdExpressionPart{globalSROToken, IdExpressionPart::GlobalScopeOperator});
+            }
+            for (auto* identifier : ctx->qualifiedId()->nestedNameSpecifier()->Identifier())
+            {
+                action(IdExpressionPart{identifier->getSymbol(), IdExpressionPart::NestedNameSpecifier});
+                auto* nextToken = identifier->getSymbol()->getTokenSource()->nextToken().get();
+                action(IdExpressionPart{nextToken, IdExpressionPart::ScopeResolutionOperator});
+            }
+            Token* last = ctx->qualifiedId()->unqualifiedId()->Identifier()->getSymbol();
+            action(IdExpressionPart{last, IdExpressionPart::QualifiedLeaf});
+        }
+    }
+
+    // This is a helper to make client code look smoother (free of internal detail about branching in non-null tree paths...)
+    // It will reconstruct the mangled string for the identifier by looping over nested name elements.
+    // The return type is Unqualified, even if a global SRO token is present.
+    // When a global SRO token is present (leading :: in an idExression) then the identifier
+    // is necessarily fully qualified (FQ).
+    // We assume all identifiers are relative (non FQ), but in this one case,
+    // we know that it's FQ.
+    // This is too little a case to go out of our way and construct a QualifiedName.
+    // Even if that sounds cool and clean, it breaks the principle of canonicality.
+    // The QualifiedName and UnqualifiedName types are made to make compile time assignment
+    // incompatible.
+    // But the distinction here would have to be made at runtime.
+    // Effectively changing the return type to a variant<UnqualifiedName, QualifiedName>
+    // .. losing the compile time advantage of the whole original design choice.
+    // Not only that, but propagating this information upward will pollute the clients, who
+    // will feel compelled to preserve this information like it was a precious bit of entropy.
+    // Except.. it is not precious. We can treat a FQ symbol as unqualified,
+    // because all names will have to go through the MakeFullyQualified cruncher anyway.
+    // (and that cruncher is canonical enough to return identity when it sees a FQ symbol)
+    // Making this choice, we streamline treatment of names and simplify code.
+    inline UnqualifiedName ExtractNameFromIdExpression(azslParser::IdExpressionContext* ctx)
+    {
+        std::stringstream ss;  // accumulator
+        ForEachIdExpressionPart(ctx, [&ss](const IdExpressionPart& part) { ss << part.GetAZIRMangledText(); });
+        return UnqualifiedName{ss.str()};
+    }
+
+    template <typename TemplateContext>
+    UnqualifiedName ExtractNameFromAnyContextWithName(TemplateContext* ctx)
+    {
+        return UnqualifiedName{ctx->Name->getText()};
+    }
+
+    template <typename ParentType>
+    bool Is3ParentRuleOfType(antlr4::ParserRuleContext* ctx)
+    {
+        if (ctx == nullptr || ctx->parent == nullptr || ctx->parent->parent == nullptr)  // input canonicalization
+        {
+            return false;
+        }
+        auto threeUp = ctx->parent->parent->parent;
+        return dynamic_cast<ParentType>(threeUp);
+    }
+
+    // is def
+    inline bool IsParentRuleAFunctionDefinition(azslParser::FunctionParamContext* ctx)
+    {
+        return Is3ParentRuleOfType<azslParser::HlslFunctionDefinitionContext*>(ctx);
+    }
+
+    // is decl
+    inline bool IsParentRuleAFunctionDeclaration(azslParser::FunctionParamContext* ctx)
+    {
+        return Is3ParentRuleOfType<azslParser::HlslFunctionDeclarationContext*>(ctx);
+    }
+
+    inline bool IsRHSOfMemberAccess(tree::ParseTree* ctx)
+    {
+        auto* asMemberAccess = As<AstMemberAccess*>(ctx->parent);
+        return asMemberAccess && asMemberAccess->Member == ctx;
+    }
+
+    //! Checks if your current rule is at distance 0 of a rule you want to target; (according to pointer arithmetic distance)
+    //!   example with target = ArrayAccessExpresison
+    //!     $ ((T)a::b.c)[x]   from c: unqualifiedId -> idExpression -> (right of) MemberAccessExpression -> CastExpression -> ParenthesizedExpression -> ArrayAccessExpression
+    //!                                all rules involved are 0-away (transparent) for pointer arithmetic, thus-> return true
+    //!                        from b: unqualifiedId -> qualifiedId -> idExpression -> (left of) MemberAccessExpression
+    //!                                stop here. because LHS of 'dot'. the "pointer-like" operation is offseted by RHS. thus -> return false
+    //!     $ (c + d)[x]       from any of c or d: the link is broken here because '+' introduced a distance, the subscript applies on the temporary that results. thus -> return false
+    //! returns:    the found target, or nullptr if not found or not 0-away
+    template< typename TargetRule >
+    inline TargetRule* FindRuleInAstThatIs0AwayWrtPointerDistance(antlr4::ParserRuleContext* ctx)
+    {
+        // possible intermediates (that don't introduce a distance):
+        using Nested    = azslParser::NestedNameSpecifierContext;
+        using Qualif    = azslParser::QualifiedIdContext;
+        using UnQualif  = azslParser::UnqualifiedIdContext;
+        using IdExpr    = azslParser::IdentifierExpressionContext;
+        using BraceExpr = azslParser::ParenthesizedExpressionContext;
+        using Cast      = azslParser::CastExpressionContext;
+        //and AstIdExpr, AstMemberAccess too
+
+        if (ctx == nullptr)
+        {
+            return nullptr;
+        }
+        if (TargetRule* callCtx = Is<TargetRule*>(ctx) ? As<TargetRule*>(ctx) : As<TargetRule*>(ctx->parent)) // goal
+        {
+            return callCtx;
+        }
+        bool zeroDist = DynamicTypeIsAnyOf<Nested, Qualif, UnQualif, IdExpr, BraceExpr, Cast, AstIdExpr>(ctx)
+                        || IsRHSOfMemberAccess(ctx);
+        auto* parentAsParserRuleCtx = polymorphic_downcast<ParserRuleContext*>(ctx->parent);
+        return zeroDist ? FindRuleInAstThatIs0AwayWrtPointerDistance<TargetRule>(parentAsParserRuleCtx) // if still valid, recurse
+                        : nullptr;
+    }
+
+    //! checks if your current rule is at distance 0 of a subscript []; according to pointer arithmetic distance.
+    //! refer to comment of function FindRuleInAstThatIs0AwayWrtPointerDistance for examples
+    inline bool IsNextToArrayAccessExpression(antlr4::ParserRuleContext* ctx)
+    {
+        using AAExpr = azslParser::ArrayAccessExpressionContext; // end condition
+        return FindRuleInAstThatIs0AwayWrtPointerDistance<AAExpr>(ctx);
+    }
+
+    //! checks if your current rule is at distance 0 of a function call syntax f(); according to pointer arithmetic distance
+    //! example: $ ((T)a::b.c)(x)
+    //! returns: the argument list of the found call expression, if found. nullptr otherwise
+    inline azslParser::ArgumentListContext* GetArgumentListIfBelongsToFunctionCall(antlr4::ParserRuleContext* ctx)
+    {
+        using Call = azslParser::FunctionCallExpressionContext; // end condition
+        Call* found = FindRuleInAstThatIs0AwayWrtPointerDistance<Call>(ctx);
+        return found ? found->argumentList() : nullptr;
+    }
+
+    //! try to find a specific context type that this context would be a child of.
+    template <typename LookedUp>
+    inline LookedUp* ExtractSpecificParent(antlr4::ParserRuleContext* ctx)
+    {
+        if (ctx == nullptr)
+        {
+            return nullptr;
+        }
+        auto* asCtx = As<LookedUp*>(ctx);
+        if (asCtx) // goal
+        {
+            return asCtx;
+        }
+        return ctx->parent == nullptr ? nullptr : ExtractSpecificParent<LookedUp>(polymorphic_downcast<ParserRuleContext*>(ctx->parent));
+    }
+
+    //! try to find a variable initializer that this context would be a child of.
+    inline AstVarInitializer* ExtractInitializerParent(antlr4::ParserRuleContext* ctx)
+    {
+        return ExtractSpecificParent<AstVarInitializer>(ctx);
+    }
+
+    inline azslParser::FunctionParamContext* ParamContextOverVariableDeclarator(AstUnnamedVarDecl* ctx)
+    {
+        return As<azslParser::FunctionParamContext*>(ctx->parent);
+    }
+
+    inline azslParser::VariableDeclarationContext* VarDeclContextOverVariableDeclarator(AstNamedVarDecl* ctx)
+    {
+        return As<azslParser::VariableDeclarationContext*>(ctx->parent->parent);
+    }
+
+    inline azslParser::TypeContext* ExtractTypeFromVariableDeclarator(AstUnnamedVarDecl* ctx, azslParser::FunctionParamContext** funcParamContextOut = nullptr)
+    {
+        auto* paramCtx = ParamContextOverVariableDeclarator(ctx);
+        if (paramCtx != nullptr)
+        {
+            if (funcParamContextOut)
+            {
+                *funcParamContextOut = paramCtx;
+            }
+            return paramCtx->type();
+        }
+        auto* varDeclCtx = VarDeclContextOverVariableDeclarator(As<AstNamedVarDecl*>(ctx->parent));
+        if (varDeclCtx != nullptr)
+        {
+            return varDeclCtx->type();
+        }
+        return nullptr;
+    }
+
+    /// move up the AST into parent rules to ry to get a name. in case of function parameters, name can be omitted so this function may return null
+    inline Token* ExtractVariableNameIdentifier(AstUnnamedVarDecl* ctx)
+    {
+        return Is<AstNamedVarDecl>(ctx->parent) ? polymorphic_downcast<AstNamedVarDecl*>(ctx->parent)->Name
+                                                : polymorphic_downcast<azslParser::FunctionParamContext*>(ctx->parent)->Name;
+    }
+
+    inline ParserRuleContext* GetParentIfIsNamedVarDecl_OtherwiseIdentity(AstUnnamedVarDecl* ctx)
+    {
+        if (!ctx || !Is<AstNamedVarDecl>(ctx->parent))
+        {
+            return ctx;
+        }
+        return polymorphic_downcast<AstNamedVarDecl*>(ctx->parent);
+    }
+
+    inline string ExtractVariableNameSamplerBodyDeclaration(azslParser::SamplerBodyDeclarationContext* ctx)
+    {
+        // parent1 is variableInitializer ; parent2 is unnamedVariableDeclarator
+        return ExtractVariableNameIdentifier(polymorphic_downcast<AstUnnamedVarDecl*>(ctx->parent->parent))->getText();
+    }
+
+    inline bool TypeIsSamplerComparisonState(AstUnnamedVarDecl* ctx)
+    {
+        auto* typeCtx = ExtractTypeFromVariableDeclarator(ctx);
+        return typeCtx->predefinedType() &&
+               typeCtx->predefinedType()->samplerStatePredefinedType() &&
+               typeCtx->predefinedType()->samplerStatePredefinedType()->SamplerComparisonState();
+    }
+
+
+    inline azslParser::StorageFlagsContext* ExtractStorageFlagsFromVariableDeclarator(AstUnnamedVarDecl* ctx)
+    {
+        auto* paramCtx = ParamContextOverVariableDeclarator(ctx);
+        if (paramCtx != nullptr)
+        {
+            return paramCtx->storageFlags();
+        }
+        auto* varDeclCtx = VarDeclContextOverVariableDeclarator(As<AstNamedVarDecl*>(ctx->parent));
+        if (varDeclCtx != nullptr)
+        {
+            return varDeclCtx->storageFlags();
+        }
+        return nullptr;
+    }
+
+    inline bool IsFlag(azslParser::StorageFlagContext* ctx, StorageFlag flag)
+    {
+        switch (flag)
+        {
+        case StorageFlag::Const: return ctx->Const();
+        case StorageFlag::Extern: return ctx->Extern();
+        case StorageFlag::Groupshared: return ctx->Groupshared();
+        case StorageFlag::Precise: return ctx->Precise();
+        case StorageFlag::Shared: return ctx->Shared();
+        case StorageFlag::Static: return ctx->Static();
+        case StorageFlag::Uniform: return ctx->Uniform();
+        case StorageFlag::Volatile: return ctx->Volatile();
+        case StorageFlag::RowMajor: return ctx->RowMajor();
+        case StorageFlag::ColumnMajor: return ctx->ColumnMajor();
+        case StorageFlag::In: return ctx->In();
+        case StorageFlag::Out: return ctx->Out();
+        case StorageFlag::InOut: return ctx->Inout();
+        case StorageFlag::Inline: return ctx->Inline();
+        case StorageFlag::Rootconstant: return ctx->Rootconstant();
+        case StorageFlag::Option: return ctx->Option();
+        case StorageFlag::Enumerator: return false; // Not a data-driven flag
+        case StorageFlag::Unknown: return false; // Not a data-driven flag
+        default: break;
+        }
+        return false;
+    }
+
+    inline StorageFlag AsFlag(azslParser::StorageFlagContext* ctx)
+    {
+        return ctx->Const()        ? StorageFlag::Const
+             : ctx->Extern()       ? StorageFlag::Extern
+             : ctx->Groupshared()  ? StorageFlag::Groupshared
+             : ctx->Precise()      ? StorageFlag::Precise
+             : ctx->Shared()       ? StorageFlag::Shared
+             : ctx->Static()       ? StorageFlag::Static
+             : ctx->Uniform()      ? StorageFlag::Uniform
+             : ctx->Volatile()     ? StorageFlag::Volatile
+             : ctx->RowMajor()     ? StorageFlag::RowMajor
+             : ctx->ColumnMajor()  ? StorageFlag::ColumnMajor
+             : ctx->In()           ? StorageFlag::In
+             : ctx->Out()          ? StorageFlag::Out
+             : ctx->Inout()        ? StorageFlag::InOut
+             : ctx->Inline()       ? StorageFlag::Inline
+             : ctx->Option()       ? StorageFlag::Option
+             : ctx->Rootconstant() ? StorageFlag::Rootconstant
+
+            // Everything unknown can still be stored, but won't be checked in any special way
+             : ctx->Linear()          ? StorageFlag::Unknown
+             : ctx->Centroid()        ? StorageFlag::Unknown
+             : ctx->Nointerpolation() ? StorageFlag::Unknown
+             : ctx->Noperspective()   ? StorageFlag::Unknown
+             : ctx->Sample()          ? StorageFlag::Unknown
+             : ctx->Point()           ? StorageFlag::Unknown
+             : ctx->Line_()           ? StorageFlag::Unknown
+             : ctx->Triangle()        ? StorageFlag::Unknown
+             : ctx->LineAdj()         ? StorageFlag::Unknown
+             : ctx->TriangleAdj()     ? StorageFlag::Unknown
+             :                          StorageFlag::Unknown;
+    }
+
+    // Either just a string, or string + its original source node.
+    // keeping the source node allows for typeof(expression) resolution, and reversely keeping the name allows for clear strings during debugging.
+    //  Ext for extended.
+    struct ExtractedTypeExt
+    {
+        UnqualifiedName m_name;
+        AstType*        m_node = nullptr;
+    };
+    // composed type parts. 'Buffer<float4>' will have 'Buffer' as core and 'float4' as genericParam
+    struct ExtractedComposedType
+    {
+        ExtractedTypeExt m_core;
+        ExtractedTypeExt m_genericParam; // note: could be made recursive one day. use unique_ptr here
+        ArrayDimensions  m_genericDimensions; // can expand generic dimensions too later. for MSTexture, IO patch, matrix<t,N,M> etc...
+    };
+    // Compose middleEnd configurations, wraps minimal user options from command line.
+    struct MiddleEndConfiguration
+    {
+        int m_rootConstantsMaxSize;
+        AZ::ShaderCompiler::Packing::Layout m_packConstantBuffers;
+        AZ::ShaderCompiler::Packing::Layout m_packDataBuffers;
+        bool m_isRowMajor;
+        bool m_padRootConstantCB;
+    };
+
+    ExtractedComposedType ExtractComposedTypeNamesFromAstContext(AstType* ctx, vector<tree::TerminalNode*>* genericDims = nullptr);
+
+    // Oftentimes a type rule looks like: `genericTexture: TextureKeyword '<' type '>'` , this function extracts `type`.
+    // Types parent of that ctx should have an AnalyzeClass that returns HasGenericParameter
+    // for more power, this function can return an AstPointer to a type rule directly in case of availability.
+    inline ExtractedComposedType ExtractComposedTypeNamesFromAstContext(AstPredefinedTypeNode* ctx, vector<tree::TerminalNode*>* genericDims = nullptr)
+    {
+        if (ctx->bufferPredefinedType())
+        {   // use aggregate initialization syntax to construct the return type (tops 4 elements, but here we fillup 2 only)
+            return {ExtractedTypeExt{UnqualifiedName{ctx->bufferPredefinedType()->bufferType()->getText()}},                   // m_core
+                    ExtractedTypeExt{UnqualifiedName{ctx->bufferPredefinedType()->scalarOrVectorOrMatrixType()->getText()}}};  // m_genericParam
+        }
+        else if (ctx->genericMatrixPredefinedType())
+        {
+            auto intLit1 = ctx->genericMatrixPredefinedType()->IntegerLiteral(0);
+            auto intLit2 = ctx->genericMatrixPredefinedType()->IntegerLiteral(1);
+            if (genericDims && intLit1 && intLit2)
+            {
+                genericDims->push_back(intLit1);
+                genericDims->push_back(intLit2);
+            }
+            return {ExtractedTypeExt{UnqualifiedName{ctx->genericMatrixPredefinedType()->Matrix()->getText()}},       // m_core
+                    ExtractedTypeExt{UnqualifiedName{ctx->genericMatrixPredefinedType()->scalarType()->getText()}}};  // m_genericParam
+        }
+        else if (ctx->streamOutputPredefinedType())
+        {
+            auto* core = ctx->streamOutputPredefinedType()->streamOutputObjectType();
+            AstType* genericCtx = ctx->streamOutputPredefinedType()->type();
+            return {ExtractedTypeExt{UnqualifiedName{core->getText()}},                     // m_core
+                    ExtractedTypeExt{UnqualifiedName{genericCtx->getText()}, genericCtx}};  // for the generic, since this is a type context, we will return it as a node, and let the client recurse if needed.
+        }
+        else if (ctx->structuredBufferPredefinedType())
+        {
+            auto* core = ctx->structuredBufferPredefinedType()->structuredBufferName();
+            AstType* genericCtx = ctx->structuredBufferPredefinedType()->type();
+            return {ExtractedTypeExt{UnqualifiedName{core->getText()}},                     // m_core
+                    ExtractedTypeExt{UnqualifiedName{genericCtx->getText()}, genericCtx}};  // same as above
+        }
+        else if (ctx->genericTexturePredefinedType())
+        {
+            return {ExtractedTypeExt{UnqualifiedName{ctx->genericTexturePredefinedType()->textureType()->getText()}},         // m_core
+                    ExtractedTypeExt{UnqualifiedName{ctx->genericTexturePredefinedType()->scalarOrVectorType()->getText()}}}; // m_genericParam
+        }
+        else if (ctx->msTexturePredefinedType())
+        {
+            auto intLit = ctx->msTexturePredefinedType()->IntegerLiteral();
+            if (genericDims && intLit)
+            {
+                genericDims->push_back(intLit);
+            }
+            return {ExtractedTypeExt{UnqualifiedName{ctx->msTexturePredefinedType()->textureTypeMS()->getText()}},        // m_core
+                    ExtractedTypeExt{UnqualifiedName{ctx->msTexturePredefinedType()->scalarOrVectorType()->getText()}}};  // m_genericParam
+        }
+        else if (ctx->genericVectorType())
+        {
+            auto intLit = ctx->genericVectorType()->IntegerLiteral();
+            if (genericDims && intLit)
+            {
+                genericDims->push_back(intLit);
+            }
+            return {ExtractedTypeExt{UnqualifiedName{ctx->genericVectorType()->Vector()->getText()}},        // m_core
+                    ExtractedTypeExt{UnqualifiedName{ctx->genericVectorType()->scalarType()->getText()}}};   // m_genericParam
+        }
+        else if (ctx->constantBufferTemplated())
+        {
+            auto coreName = ctx->constantBufferTemplated()->CBCoreType->getText();
+            AstType* genericCtx = ctx->constantBufferTemplated()->GenericTypeName;
+            return {ExtractedTypeExt{UnqualifiedName{coreName}},                           // m_core
+                    ExtractedTypeExt{UnqualifiedName{genericCtx->getText()}, genericCtx}};  // same as previous 2 comments above
+        }
+        // all other rules are getable without artefacts for sure from a simple getText
+        return {ExtractedTypeExt{UnqualifiedName{ctx->getText()}}};     // only core, no generic part.
+    }
+
+    //! from a special rule: scalarOrVectorOrMatrixType
+    inline ExtractedComposedType ExtractComposedTypeNamesFromAstContext(azslParser::ScalarOrVectorOrMatrixTypeContext* ctx, vector<tree::TerminalNode*>* genericDims = nullptr)
+    {
+        return {ExtractedTypeExt{UnqualifiedName{ctx->getText()}}};
+                // for now there is no generic part, but mind it when you fix the grammar to support generic vector.
+    }
+
+    //! from userDefinedType context
+    inline ExtractedComposedType ExtractComposedTypeNamesFromAstContext(azslParser::UserDefinedTypeContext* ctx, vector<tree::TerminalNode*>* genericDims = nullptr)
+    {
+        if (ctx->idExpression())
+        {
+            return {ExtractedTypeExt{ExtractNameFromIdExpression(ctx->idExpression())}};
+        }
+        assert(ctx->anyStructuredTypeDefinition());
+        auto* anyStructLikeCtx = ctx->anyStructuredTypeDefinition();
+        auto structName = VisitFirstNonNull([](auto* ctx) { return ExtractNameFromAnyContextWithName(ctx); },
+                                            anyStructLikeCtx->classDefinition(),
+                                            anyStructLikeCtx->interfaceDefinition(),
+                                            anyStructLikeCtx->enumDefinition(),
+                                            anyStructLikeCtx->structDefinition());
+        return {ExtractedTypeExt{UnqualifiedName{structName}}};
+    }
+
+    //! from type context (next to highest level)
+    inline ExtractedComposedType ExtractComposedTypeNamesFromAstContext(AstType* ctx, vector<tree::TerminalNode*>* genericDims /*= nullptr*/)
+    {
+        if (ctx->userDefinedType())
+        {
+            return ExtractComposedTypeNamesFromAstContext(ctx->userDefinedType(), genericDims);
+        }
+        else if (WasParsedAsPredefinedType(ctx))
+        {
+            return ExtractComposedTypeNamesFromAstContext(ctx->predefinedType(), genericDims);
+        }
+        // this could be a typeof, let's return the node for further resolve !
+        return {ExtractedTypeExt{UnqualifiedName{ctx->getText()}, ctx}};
+    }
+
+    //! from function type context (highest type level)
+    inline ExtractedComposedType ExtractComposedTypeNamesFromAstContext(AstFuncType* ctx)
+    {
+        if (ctx->type())
+        {
+            return ExtractComposedTypeNamesFromAstContext(ctx->type());
+        }
+        else if (ctx->Void())
+        {
+            assert(string_view{AZ::ShaderCompiler::Predefined::Void[0]} == ctx->getText());
+            return {UnqualifiedName{ctx->getText()}}; // "void"
+        }
+        throw std::logic_error((DiagLine(ctx->start) + " internal error: can't extract name on unsupported expression"));
+    }
+
+    //! Parse an HLSL semantic from a context into (semantic name, semantic index, is system value)
+    inline tuple<string, int, bool> ExtractHlslSemantic(azslParser::HlslSemanticContext* hlslSemantic)
+    {
+        // Semantic name and index
+        auto semanticName = hlslSemantic->getText();
+        size_t index      = semanticName.find_last_not_of("0123456789") + 1;
+        int semanticIndex = (index == semanticName.length()) ?
+                            0 : std::stoi(semanticName.substr(index));
+
+        auto colon = semanticName.find_first_of(":");
+        auto firstChar = (colon == string::npos) ? 0 : colon + 1;
+        firstChar = semanticName.find_first_not_of(" ", firstChar);
+        semanticName = semanticName.substr(firstChar, index - firstChar);
+
+        bool isSystemValue = (hlslSemantic->Name->HLSLSemanticSystem() != nullptr);
+
+        return { semanticName, semanticIndex, isSystemValue };
+    }
+
+    inline bool HasStandardInitializer(AstVarInitializer* ctx)
+    {
+        return ctx && ctx->standardVariableInitializer();
+    }
+
+    inline bool HasStandardInitializer(AstUnnamedVarDecl* ctx)
+    {
+        return ctx && HasStandardInitializer(ctx->variableInitializer());
+    }
+}

+ 156 - 0
src/CMakeLists.txt

@@ -0,0 +1,156 @@
+cmake_minimum_required(VERSION 3.7)
+
+project (Azslc)
+
+SET (CMAKE_CXX_STANDARD 17)
+
+if (MSVC)
+  add_compile_options("/std:c++17")
+  add_compile_options("/permissive-")
+  add_compile_options("/Zc:__cplusplus")
+  add_compile_options("/utf-8")
+  add_compile_options("/MP")
+else()
+  add_compile_options("--std=c++17")
+endif()
+
+  set(CompilerFlags
+        CMAKE_CXX_FLAGS
+        CMAKE_CXX_FLAGS_DEBUG
+        CMAKE_CXX_FLAGS_RELEASE
+        CMAKE_C_FLAGS
+        CMAKE_C_FLAGS_DEBUG
+        CMAKE_C_FLAGS_RELEASE
+        )
+
+if (MSVC)
+  foreach(CompilerFlag ${CompilerFlags})
+    string(REPLACE "/MT" "/MD" ${CompilerFlag} "${${CompilerFlag}}")
+  endforeach()
+else()
+  set (CMAKE_CXX_FLAGS "-Wno-logical-op-parentheses")
+endif()
+
+if (MSVC)
+  add_definitions(-D_CRT_SECURE_NO_WARNINGS)
+  add_definitions(-D_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING)
+endif()
+
+# get all chars that are not separators before the end
+string(REGEX MATCH "[^/\\]*$" buildPlatform ${CMAKE_BINARY_DIR})
+
+file(GLOB_RECURSE  azslc_PlatformSRC
+  "${PROJECT_SOURCE_DIR}/../Platform/*.cpp"
+  "${PROJECT_SOURCE_DIR}/../Platform/*.h"
+)
+
+file(GLOB azslc_LocalSRC
+  "${PROJECT_SOURCE_DIR}/*.cpp"
+  "${PROJECT_SOURCE_DIR}/*.tpl"
+  "${PROJECT_SOURCE_DIR}/*.h"
+)
+
+file(GLOB azslc_ExternalSRC
+  "${PROJECT_SOURCE_DIR}/external/docopt/*.cpp"
+  "${PROJECT_SOURCE_DIR}/external/jsoncpp/dist/json/*.h"
+  "${PROJECT_SOURCE_DIR}/external/jsoncpp/dist/*.cpp"
+  "${PROJECT_SOURCE_DIR}/external/mpark-variant/master/*.hpp"
+  "${PROJECT_SOURCE_DIR}/external/tiny/*.h"
+)
+
+file(GLOB azslc_GeneratedSrc
+  "${PROJECT_SOURCE_DIR}/generated/*.cpp"
+)
+
+if (MSVC)
+  include_external_msproject(antlr4_static ${CMAKE_BINARY_DIR}/external/antlr4/runtime/Cpp/runtime/antlr4_static.vcxproj)
+endif()
+
+set( ANTLR4CPP_INCLUDE_DIRS
+  ${PROJECT_SOURCE_DIR}/external/antlr4/runtime/Cpp/runtime/src
+  ${PROJECT_SOURCE_DIR}/external/antlr4/runtime/Cpp/runtime/src/dfa
+  ${PROJECT_SOURCE_DIR}/external/antlr4/runtime/Cpp/runtime/src/misc
+  ${PROJECT_SOURCE_DIR}/external/antlr4/runtime/Cpp/runtime/src/tree
+)
+# add antrl4cpp artifacts to project environment
+
+set( MPARK_VARIANT_INCLUDE_DIRS
+  ${PROJECT_SOURCE_DIR}/external/mpark-variant/master
+)
+
+set( TINY_OPTIONAL_INCLUDE_DIRS
+  ${PROJECT_SOURCE_DIR}/external/tiny
+)
+
+include_directories(
+    ${PROJECT_SOURCE_DIR}
+    ${PROJECT_SOURCE_DIR}/external
+    ${ANTLR4CPP_INCLUDE_DIRS}
+    ${MPARK_VARIANT_INCLUDE_DIRS}
+    ${TINY_OPTIONAL_INCLUDE_DIRS}
+)
+
+if (MSVC)
+  link_directories(
+    ${CMAKE_BINARY_DIR}/external/antlr4/runtime/Cpp/dist/${CMAKE_BUILD_TYPE}
+  )
+elseif (UNIX)
+  link_directories(
+    ${CMAKE_BINARY_DIR}/external/antlr4/runtime/Cpp/dist/
+  )
+endif()
+
+if (MSVC)
+    # For Windows builds we need to replace the std::regex with boost::regex for as long as we use docopt
+    set( BOOST_PATH              ${PROJECT_SOURCE_DIR}/external/boost)
+    set( BOOST_INCLUDE_DIRS      ${BOOST_PATH} )
+    set( BOOST_LINK_DIRS         ${BOOST_PATH}/stage/lib )
+  
+  add_definitions(-DDOCTOPT_USE_BOOST_REGEX)
+
+  include_directories( ${BOOST_INCLUDE_DIRS} )
+
+  link_directories( ${BOOST_LINK_DIRS} )
+endif()
+
+add_executable(azslc ${azslc_LocalSRC} ${azslc_PlatformSRC} ${azslc_GeneratedSrc} ${azslc_ExternalSRC})
+
+source_group("azslc" FILES ${azslc_LocalSRC})
+source_group("generated" FILES ${azslc_GeneratedSrc})
+source_group("external" FILES ${azslc_ExternalSRC})
+source_group("platforms" FILES ${azslc_PlatformSRC})
+
+if (MSVC)
+  target_link_libraries(azslc antlr4-runtime-static)
+elseif (UNIX AND NOT APPLE)
+  target_link_libraries(azslc libantlr4-runtime.a stdc++fs)
+elseif (UNIX)
+  target_link_libraries(azslc libantlr4-runtime.a)
+endif()
+
+if (MSVC)
+  SET(boostRegexReleaseLib boost_regex-vc142-mt-x64-1_70)
+  SET(boostRegexDebugLib boost_regex-vc142-mt-gd-x64-1_70)
+  SET(LINK_REGEX_LIBRARY optimized ${boostRegexReleaseLib} debug ${boostRegexDebugLib})
+  target_link_libraries(azslc ${LINK_REGEX_LIBRARY})
+  add_dependencies(azslc antlr4_static)
+
+  add_custom_command(TARGET azslc POST_BUILD
+      COMMAND if $(Configuration) == "Debug" (COMMAND ${CMAKE_COMMAND} -E copy ${BOOST_LINK_DIRS}/${boostRegexDebugLib}.dll ${CMAKE_BINARY_DIR}/$(Configuration)/)
+      COMMAND if NOT $(Configuration) == "Debug" (COMMAND ${CMAKE_COMMAND} -E copy ${BOOST_LINK_DIRS}/${boostRegexReleaseLib}.dll ${CMAKE_BINARY_DIR}/$(Configuration)/)
+      COMMAND ${CMAKE_COMMAND} -E make_directory "${PROJECT_SOURCE_DIR}/../bin/${buildPlatform}/$(Configuration)"
+      COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/$(Configuration)/azslc.exe" "${PROJECT_SOURCE_DIR}/../bin/${buildPlatform}/$(Configuration)"
+      COMMAND if $(Configuration) == "Debug" (COMMAND ${CMAKE_COMMAND} -E copy ${BOOST_LINK_DIRS}/${boostRegexDebugLib}.dll "${PROJECT_SOURCE_DIR}/../bin/${buildPlatform}/$(Configuration)")
+      COMMAND if NOT $(Configuration) == "Debug" (COMMAND ${CMAKE_COMMAND} -E copy ${BOOST_LINK_DIRS}/${boostRegexReleaseLib}.dll "${PROJECT_SOURCE_DIR}/../bin/${buildPlatform}/$(Configuration)")
+)
+endif()
+
+set_property(GLOBAL PROPERTY USE_FOLDERS ON)
+
+set_target_properties( azslc
+    PROPERTIES
+    EXECUTABLE_OUTPUT_PATH "${CMAKE_BINARY_DIR}/"
+    LIBRARY_OUTPUT_PATH "${CMAKE_BINARY_DIR}/"
+)
+
+set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT azslc)

+ 99 - 0
src/DependencySolver.h

@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <iterator>
+
+namespace AZ::ShaderCompiler
+{
+    enum EasyToReadMaxDepCountInteger : size_t
+    {};
+
+    constexpr EasyToReadMaxDepCountInteger operator ""_maxdep_pernode(unsigned long long n)
+    {
+        return EasyToReadMaxDepCountInteger(n);
+    }
+
+    namespace detail
+    {
+        template< typename T, size_t N >
+        struct ElasticArray : array<T, N>
+        {
+            using Base = array<T, N>;
+            using Base::array;
+            using Base::operator=;
+            using Base::operator[];
+            using Base::begin;
+
+            auto end() -> decltype(Base::end());
+            auto end() const -> decltype(Base::end());
+            auto at(size_t n) -> decltype(Base::at(n));
+            auto at(size_t n) const -> decltype(Base::at(n));
+            T& push_back(const T& t);
+
+            size_t m_end = 0;
+        };
+
+        template< typename ID, EasyToReadMaxDepCountInteger MaxDep >
+        struct DependencySolverNode
+        {
+            void PushDep(ID d);
+            bool DependsOn(ID d) const;
+
+            // memory-inlined array to diminish fragmentatioon
+            static constexpr size_t MaxDependencies = MaxDep;
+            ElasticArray<ID, MaxDependencies> m_dependencies;
+        };
+    }
+
+    template< typename NodeIdT, EasyToReadMaxDepCountInteger MaxDepPerNode >
+    class DependencySolver
+    {
+    public:
+        using ID = NodeIdT;
+        using Node = detail::DependencySolverNode<ID, MaxDepPerNode>;
+
+        void AddDependency(ID node, ID toDependency);
+
+        void AddNode(ID node);
+
+        bool CheckAllDepSatisfied();
+
+        void Solve();
+
+        bool Has(ID node) const;
+
+        unordered_map< ID, Node > m_nodes;
+        vector< ID > m_order;
+
+    private:
+        // Tarjan Topological-Sort 1976
+        void Visit(ID curNode);
+
+        void ClearAllTemps();
+        void PermMark(ID n);
+        void TempMark(ID n);
+        bool HasPermMark(ID n) const;
+        bool HasTempMark(ID n) const;
+        void RemoveTempMark(ID n);
+        bool ExistsNodesWithoutPermanentMark() const;
+        // get any node that is not marked
+        ID SelectUnmarked();
+
+        unordered_set<ID> m_permMarks;
+        unordered_set<ID> m_tempMarks;
+        vector<ID> m_result; // L
+    };
+}
+
+#ifndef NDEBUG
+namespace AZ::Tests
+{
+    void DoAsserts6();
+}
+#endif

+ 253 - 0
src/DependencySolver.tpl

@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include "DependencySolver.h"
+
+namespace AZ::ShaderCompiler
+{
+    namespace detail
+    {
+        template<typename T, size_t N>
+        auto ElasticArray<T,N>::end() -> decltype(Base::end())
+        {
+            auto realEnd = begin();
+            std::advance(realEnd, m_end);
+            return realEnd;
+        }
+
+        template<typename T, size_t N>
+        auto ElasticArray<T,N>::end() const -> decltype(Base::end())
+        {
+            auto realEnd = begin();
+            std::advance(realEnd, m_end);
+            return realEnd;
+        }
+
+        template<typename T, size_t N>
+        auto ElasticArray<T,N>::at(size_t n) -> decltype(Base::at(n))
+        {
+            if (n >= m_end)
+            {
+                throw std::runtime_error{"ElasticArray::at out of bounds"};
+            }
+            return Base::at(n);
+        }
+
+        template<typename T, size_t N>
+        auto ElasticArray<T,N>::at(size_t n) const -> decltype(Base::at(n))
+        {
+            if (n >= m_end)
+            {
+                throw std::runtime_error{"ElasticArray::at out of bounds"};
+            }
+            return Base::at(n);
+        }
+
+        template<typename T, size_t N>
+        T& ElasticArray<T,N>::push_back(const T& element)
+        {
+            ++m_end;
+            at(m_end - 1) = element;
+            return this->operator[](m_end - 1);
+        }
+
+        template< typename ID, EasyToReadMaxDepCountInteger MaxDep >
+        void DependencySolverNode<ID, MaxDep>::PushDep(ID d)
+        {
+            if (!DependsOn(d))  // guarantee no duplicates for strong invariants in other algorithms
+            {
+                m_dependencies.push_back(d);
+            }
+        }
+
+        template< typename ID, EasyToReadMaxDepCountInteger MaxDep >
+        bool DependencySolverNode<ID, MaxDep>::DependsOn(ID d) const
+        {
+            return std::find(m_dependencies.begin(), m_dependencies.end(), d) != m_dependencies.end();
+        }
+    }
+
+    template< typename NodeIdT, EasyToReadMaxDepCountInteger MaxDepPerNode >
+    void DependencySolver<NodeIdT, MaxDepPerNode>::AddDependency(ID node, ID toDependency)
+    {
+	    m_nodes[node].PushDep(toDependency);
+    }
+
+    template< typename NodeIdT, EasyToReadMaxDepCountInteger MaxDepPerNode >
+    void DependencySolver<NodeIdT, MaxDepPerNode>::AddNode(ID node)
+    {
+        m_order.push_back(node);
+	    m_nodes[node];
+    }
+
+    template< typename NodeIdT, EasyToReadMaxDepCountInteger MaxDepPerNode >
+    bool DependencySolver<NodeIdT, MaxDepPerNode>::CheckAllDepSatisfied()
+    {
+	    for (size_t i = 0; i < m_order.size(); ++i)
+	    {
+		    ID iterNode = m_order[i];
+		    Node& n = m_nodes[iterNode];
+		    for (size_t j = i + 1; j < m_order.size(); ++j)
+		    {
+			    if (n.DependsOn(m_order[j]))
+                {
+				    return false;
+                }
+		    }
+	    }
+	    return true;
+    }
+
+    template< typename NodeIdT, EasyToReadMaxDepCountInteger MaxDepPerNode >
+    void DependencySolver<NodeIdT, MaxDepPerNode>::Solve()
+    {
+	    ClearAllTemps();
+	    while (ExistsNodesWithoutPermanentMark())
+	    {
+		    ID n = SelectUnmarked();
+		    Visit(n);
+	    }
+	    assert(m_order.size() == m_result.size());
+	    std::copy(m_result.begin(), m_result.end(), m_order.begin());
+    }
+
+    template< typename NodeIdT, EasyToReadMaxDepCountInteger MaxDepPerNode >
+    bool DependencySolver<NodeIdT, MaxDepPerNode>::Has(ID node) const
+    {
+        return m_nodes.find(node) != m_nodes.end();
+    }
+
+    template< typename NodeIdT, EasyToReadMaxDepCountInteger MaxDepPerNode >
+    void DependencySolver<NodeIdT, MaxDepPerNode>::Visit(ID curNode)
+    {
+	    if (HasPermMark(curNode))
+	    {
+		    return;
+	    }
+	    if (HasTempMark(curNode)) // not a DAG
+	    {
+		    throw std::runtime_error{"cycles in the dependencies"};
+	    }
+	    TempMark(curNode);  // mark curNode with a temporary mark
+	    for (ID dependee : m_nodes[curNode].m_dependencies)  // for each node 'dependee' with an edge from 'curNode' to 'dependee'
+	    {
+		    Visit(dependee);
+	    }
+	    RemoveTempMark(curNode);
+	    PermMark(curNode); // mark curNode with a permanent mark
+	    m_result.push_back(curNode);  // add curNode to tail of L
+    }
+
+    template< typename NodeIdT, EasyToReadMaxDepCountInteger MaxDepPerNode >
+    void DependencySolver<NodeIdT, MaxDepPerNode>::ClearAllTemps()
+    {
+	    m_permMarks.clear();
+	    m_tempMarks.clear();
+	    m_result.clear();
+    }
+
+    template< typename NodeIdT, EasyToReadMaxDepCountInteger MaxDepPerNode >
+    void DependencySolver<NodeIdT, MaxDepPerNode>::PermMark(ID n)
+    {
+	    m_permMarks.insert(n);
+    }
+
+    template< typename NodeIdT, EasyToReadMaxDepCountInteger MaxDepPerNode >
+    void DependencySolver<NodeIdT, MaxDepPerNode>::TempMark(ID n)
+    {
+	    m_tempMarks.insert(n);
+    }
+
+    template< typename NodeIdT, EasyToReadMaxDepCountInteger MaxDepPerNode >
+    bool DependencySolver<NodeIdT, MaxDepPerNode>::HasPermMark(ID n) const
+    {
+	    return m_permMarks.find(n) != m_permMarks.end();
+    }
+
+    template< typename NodeIdT, EasyToReadMaxDepCountInteger MaxDepPerNode >
+    bool DependencySolver<NodeIdT, MaxDepPerNode>::HasTempMark(ID n) const
+    {
+	    return m_tempMarks.find(n) != m_tempMarks.end();
+    }
+
+    template< typename NodeIdT, EasyToReadMaxDepCountInteger MaxDepPerNode >
+    void DependencySolver<NodeIdT, MaxDepPerNode>::RemoveTempMark(ID n)
+    {
+	    m_tempMarks.erase(n);
+    }
+
+    template< typename NodeIdT, EasyToReadMaxDepCountInteger MaxDepPerNode >
+    bool DependencySolver<NodeIdT, MaxDepPerNode>::ExistsNodesWithoutPermanentMark() const
+    {
+	    return m_permMarks.size() < m_nodes.size();
+    }
+
+    template< typename NodeIdT, EasyToReadMaxDepCountInteger MaxDepPerNode >
+    typename DependencySolver<NodeIdT, MaxDepPerNode>::ID DependencySolver<NodeIdT, MaxDepPerNode>::SelectUnmarked()
+    {
+        // assumes not empty
+	    auto iter = m_order.begin();
+	    ID nodeId;
+	    do
+	    {
+            nodeId = *iter;
+		    ++iter;
+	    } while (iter != m_order.end() && HasPermMark(nodeId));
+	    return nodeId;
+    }
+}
+
+#ifndef NDEBUG
+namespace
+{
+    using ID = enum I : int {};
+    ID operator "" _id (unsigned long long i) { return ID(i); }
+}
+
+namespace AZ::Tests
+{
+    inline void DoAsserts6()
+    {
+		using namespace AZ::ShaderCompiler;
+        DependencySolver<ID, 3_maxdep_pernode> ds;
+
+        ds.AddNode(0_id);
+        ds.AddDependency(0_id, 4_id);
+        ds.AddDependency(0_id, 3_id);
+        ds.AddNode(3_id);
+        ds.AddNode(2_id);
+        ds.AddNode(1_id);
+        ds.AddNode(4_id);
+        ds.AddNode(5_id);
+        ds.AddDependency(5_id, 0_id);
+        ds.AddNode(6_id);
+        ds.AddDependency(6_id, 5_id);
+        ds.AddNode(7_id);
+        ds.AddDependency(7_id, 6_id);
+        ds.AddDependency(7_id, 5_id);
+        ds.AddNode(8_id);
+        ds.AddDependency(8_id, 6_id);
+        ds.AddNode(9_id);
+        ds.AddDependency(9_id, 6_id);
+        ds.AddNode(10_id);
+        ds.AddDependency(10_id, 8_id);
+        ds.AddDependency(10_id, 9_id);
+        ds.AddDependency(10_id, 3_id);
+
+        try
+        {
+            ds.Solve();
+            assert(ds.CheckAllDepSatisfied());
+        }
+        catch (exception&)
+        {
+            assert(false);
+        }
+    }
+}
+#endif

+ 167 - 0
src/DiagnosticStream.h

@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include "ReflectableEnums.h"
+
+#include <iostream>
+
+namespace AZ
+{
+    //! stream manipulator: new line and flush
+    struct Endl {};
+
+    //! stream manipulator: warning severity level
+    MAKE_REFLECTABLE_ENUM(Warn,
+        W0,     // Suppress all warnings
+        W1,     // Display level 1 (severe) warnings [default]
+        W2,     // Display level 1 & 2 warnings
+        W3,     // Display level 1 & 2 & 3 warnings
+        Wx,     // Treats any currently ativated warning, as error
+        Wx1,    // Warnings up to level 1 are errors
+        Wx2,    // Warnings up to level 2 are errors
+        Wx3     // Warnings up to level 3 are errors
+    );
+
+    //! stream manipulator: stack up a severity level on a DiagnosticStream object (streams used for warnings have stateful warning levels, they can be backup-ed and restored thanks to this)
+    //! you can use it by making an instance: e.g.: warningCout << PushLevel{} << Warn::W3 << "my warning level 3" << PopLevel{};
+    struct PushLevel {};
+
+    //! stream manipulator: pop a severity level on a DiagnosticStream object (restore the previous level before the push)
+    struct PopLevel {};
+
+    //! recover the level (as integer) value of an enumerator
+    inline int ExtractLevel(Warn level)
+    {
+        return level == Warn::Wx ? -1
+            : (level >= Warn::Wx1 ? level - Warn::Wx1 + 1
+                : level);
+    }
+
+    struct DiagnosticStream
+    {
+        DiagnosticStream() : m_wrappedStream(std::cout)
+        {}
+        DiagnosticStream(decltype(std::cout)& streamToWrap) : m_wrappedStream(streamToWrap)
+        {}
+
+        typedef DiagnosticStream Self;
+
+        template< typename Any >
+        Self& operator<< (Any&& thing)  // universal reference
+        {
+            if (m_on && PassLevelFilter())
+            {
+                m_wrappedStream << thing;
+            }
+
+            if (AsError() && m_onErrorCallback)
+            {
+                m_onErrorCallback(ConcatString("Warnings of level ", ExtractLevel(m_activeManipulator.top()), " are errors under current settings"));
+            }
+            return *this;
+        }
+
+        Self& operator<< (Warn::EnumType& level)
+        {
+            return operator<<(Warn::EnumType{ level });  // create an xvalue
+        }
+
+        Self& operator<< (Warn::EnumType&& level)
+        {
+            assert(level > Warn::W0); // silent warning messages make no sense.
+            assert(level < Warn::Wx); // don't stream manipulators other than warning levels (error manipulators don't make sense)
+            m_activeManipulator.top() = level;
+            return *this;
+        }
+
+        Self& operator<< (PushLevel& level)
+        {
+            return operator<<(PushLevel{ level });
+        }
+
+        Self& operator<< (PushLevel&& level)
+        {
+            m_activeManipulator.push(m_activeManipulator.top());
+            return *this;
+        }
+
+        Self& operator<< (PopLevel& level)
+        {
+            return operator<<(PopLevel{ level });
+        }
+
+        Self& operator<< (PopLevel&& level)
+        {
+            if (m_activeManipulator.size() > 1)
+            {
+                m_activeManipulator.pop();
+            }
+            return *this;
+        }
+
+        // non-templates functions have priority over templates, when the type matches.
+        Self& operator<< (Endl&)
+        {
+            return operator<<(Endl{});  // pass an unnamed temporary, therefore an xvalue, therefore matching the function thereunder.
+        }
+
+        // Endl types here are not compatible with any << operator overload defined in basic_ostream. so we need to 'branch' out, using overloading.
+        Self& operator<< (Endl&&)
+        {
+            if (m_on)
+            {
+                m_wrappedStream << std::endl;
+            }
+            return *this;
+        }
+
+        void SetRevealedWarningLevel(Warn level)
+        {
+            assert(level < Warn::Wx);
+            m_warningLevel = level;
+        }
+
+        void SetAsErrorLevel(Warn level)
+        {
+            assert(level >= Warn::Wx);
+            m_warningAsErrorLevel = level;
+        }
+
+    private:
+        int CurrentErrorLevel() const
+        {
+            int asInt = ExtractLevel(m_warningAsErrorLevel);
+            asInt = asInt == -1 ? ExtractLevel(m_warningLevel) : asInt;
+            return m_warningAsErrorLevel == Warn::EndEnumeratorSentinel_ ? -1 : asInt;
+        }
+
+        // active level is an error according to settings
+        bool AsError() const
+        {
+            return ExtractLevel(m_activeManipulator.top()) <= CurrentErrorLevel();
+        }
+
+        // current level should be visible
+        bool PassLevelFilter() const
+        {
+            return AsError()  // errors must be displayed, even if the stream setting is stricter
+                || ExtractLevel(m_activeManipulator.top()) <= ExtractLevel(m_warningLevel);
+        }
+
+    public:
+        bool m_on = true;
+        decltype(std::cout)& m_wrappedStream;
+        std::function< void(string_view) > m_onErrorCallback;  //!< receive a message in case of a streamed element of a warning level enough to trigger an error
+
+    private:
+        Warn m_warningAsErrorLevel = Warn::EndEnumeratorSentinel_;  //!< no warning is an error by default
+        Warn m_warningLevel = Warn::W1;                             //!< current activated level setting. default warning is W1
+        stack<Warn> m_activeManipulator{ {Warn::W1} };          //!< store manipulators. start with an initial value corresponding to the default filter.
+    };
+}

+ 612 - 0
src/GenericUtils.h

@@ -0,0 +1,612 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include "StdUtils.h"
+#include "MetaUtils.h"
+
+#include <sstream>
+
+#define AZ_STRINGIFY(x) #x
+#define AZ_TOSTRING(x) AZ_STRINGIFY(x)
+#define AZ_FILELINE __FILE__ ":" AZ_TOSTRING(__LINE__)
+
+namespace AZ
+{
+    // Create substring views of views. Works like python slicing operator [n:m] with limited modulo semantics.
+    // what I ultimately desire is the range v.3 feature eg `letters[{2,end-2}]`
+    // http://ericniebler.com/2014/12/07/a-slice-of-python-in-c/
+    inline constexpr string_view Slice(const string_view& in, int64_t st, int64_t end)
+    {
+        auto inSSize = (int64_t)in.size();
+        if (inSSize == 0)
+        {
+            return in;
+        }
+
+        while (end < 0)
+        {
+            end = end + inSSize + 1;
+        }
+
+        while (st < 0)
+        {
+            st = st + inSSize + 1;
+        }
+        if (end > inSSize)
+        {
+            end = end % inSSize;
+        }
+
+        if (st > inSSize)
+        {
+            st = st % inSSize;
+        }
+        return in.substr(st, end - st);
+    }
+
+    //static_assert(Slice("0123", 1, 2) == "1");  // visual studio 2017 can't run this. gcc 7 works. and VCpre2018 works https://godbolt.org/z/_Ycv6G
+    static_assert(Slice("0123", 1, 1) == "");
+    static_assert(Slice("0123", 0, 1) == "0");
+    static_assert(Slice("0123", 0, 4) == "0123");
+    static_assert(Slice("0123", 0, -1) == "0123");
+    static_assert(Slice("0123", 0, 0) == "");
+    //static_assert(Slice("0123", -3, -2) == "2");  // VS is incapable of getting this right either. (proof that it should, as dynamic assert below at //*)
+    //static_assert(Slice("0123", -2, -1) == "3");
+    static_assert(Slice("0123", -1, -0) == "");
+
+    // waiting for C++20
+    inline bool StartsWith(string_view haystack, string_view needle)
+    {
+        return Slice(haystack, 0, needle.size()).find(needle, 0) != string::npos;
+        // visual studio bug prevents us from using constexpr here
+    //https://developercommunity.visualstudio.com/content/problem/275141/c2131-expression-did-not-evaluate-to-a-constant-fo.html
+    }
+
+    inline bool EndsWith(string_view haystack, string_view needle)
+    {
+        return Slice(haystack, haystack.size() - needle.size(), haystack.size()).find(needle, 0) != string::npos;
+        // visual studio bug prevents us from using constexpr here
+    //https://developercommunity.visualstudio.com/content/problem/275141/c2131-expression-did-not-evaluate-to-a-constant-fo.html
+    }
+
+    // ability to create size_t literals
+    // waiting for Working Group to get their stuff together https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/tGoPjUeHlKo
+    inline constexpr std::size_t operator ""_sz(unsigned long long n)
+    {
+        return n;
+    }
+
+    template <class Container>
+    Container Split(const string& str, string_view delims = " ")
+    {
+        Container cont;
+
+        std::size_t current, previous = 0;
+        current = str.find_first_of(delims);
+        while (current != string::npos)
+        {
+            if (current > previous)
+            {
+                cont.push_back(str.substr(previous, current - previous));
+            }
+            previous = current + 1;
+            current = str.find_first_of(delims, previous);
+        }
+        cont.push_back(str.substr(previous, current - previous));
+        return cont;
+    }
+
+    inline string_view GetFileNameWithoutExtension(string_view fileName)
+    {
+        size_t lastIndex = fileName.find_last_of(".");
+        if (lastIndex == -1)
+        {
+            lastIndex = fileName.size();
+        }
+
+        return fileName.substr(0, lastIndex);
+    }
+
+    // debug-build asserted dyn_cast, otherwise, release-build static_cast (idea from boost library)
+    template <typename To, typename From>
+    To polymorphic_downcast(From instance)
+    {
+        static_assert(std::is_reference_v<To> || std::is_pointer_v<To>, "can't use non-ref/ptr types fot polymorphic casts");
+        static_assert(std::is_reference_v<From> || std::is_pointer_v<From>, "can't use non-ref/ptr types fot polymorphic casts");
+#if !defined(NDEBUG)
+        assert(dynamic_cast<To>(instance));
+#endif
+        return static_cast<To>(instance);
+    }
+
+    /// surround a string with a prefix and a suffix
+    inline string Decorate(string_view prefix, string_view body, string_view suffix)
+    {
+        std::stringstream ss;
+        ss << prefix.data();
+        ss << body.data();
+        ss << suffix.data();
+        return ss.str();
+    }
+
+    /// 2 arguments version in case both sides are the same
+    inline string Decorate(string_view prefixAndSuffix, string_view body)
+    {
+        return Decorate(prefixAndSuffix, body, prefixAndSuffix);
+    }
+
+    /// reverse the effect of a symmetrical decoration
+    inline string_view Undecorate(string_view decoration, string_view body)
+    {
+        auto indexStart = StartsWith(body, decoration) ? decoration.length() : 0;
+        auto indexEnd = EndsWith(body, decoration) ? body.length() - decoration.length() : body.length();
+        return Slice(body, indexStart, indexEnd);
+    }
+
+    // Erase-Remove algorithm which removes all whitespaces from a string.
+    inline string RemoveWhitespaces(string haystack)
+    {
+        haystack.erase(std::remove_if(haystack.begin(), haystack.end(), [](unsigned char c) {return std::isspace(c); }), haystack.end());
+        return haystack;
+    }
+
+    /// tells whether a position in a string is surrounded by round braces
+    /// e.g. true  for arguments {"a(b)", 2}
+    /// e.g. true  for arguments {"a()", 1}  by convention
+    /// e.g. false for arguments {"a()", 2}  by convention
+    /// e.g. false for arguments {"a(b)", 0}
+    /// e.g. false for arguments {"a(b)c", 4}
+    /// e.g. false for arguments {"a(b)c(d)", 4}
+    /// e.g. true  for arguments {"a((b)c(d))", 5}
+    inline bool WithinMatchedParentheses(string_view haystack, size_t charPosition)
+    {
+        const auto hayLen = haystack.length();
+        // we don't even need to be looking at the left side, we suppose balanced construction.
+        // let's evaluate the level of nesting at the asked pos.
+        int nesting = 0;
+        for (size_t pos = 0; pos <= charPosition && pos < hayLen; ++pos)
+        {
+            nesting += haystack[pos] == '(' ? 1
+                : (haystack[pos] == ')' ? -1 : 0);
+        }
+        return nesting > 0;
+    }
+
+    /// replace all occurrences of substring `sub` with substring `to` within haystack.
+    ///  e.g: Replace("aaa#aaa", "#", "_") gives-> "aaa_aaa"
+    inline string Replace(string haystack, string_view sub, string_view to)
+    {
+        decltype(sub.length()) pos = 0;
+        auto lFrom = sub.length();
+        auto lTo = to.length();
+        while (((pos = haystack.find(sub, pos)) != string::npos))
+        {
+            haystack.replace(pos, lFrom, to);
+            pos += lTo;
+        }
+        return haystack;
+    }
+
+    // this one is inspired by the docopt utilities. trims whitespace by default, but can be used to trim quotes.
+    constexpr inline string_view Trim(string_view haystack, string_view toTrim = " \t\n")
+    {
+        const auto strEnd = haystack.find_last_not_of(toTrim);
+        if (strEnd == string::npos)
+        {
+            return {};
+        }
+        haystack.remove_suffix(haystack.size() - strEnd - 1);
+
+        const auto strBegin = haystack.find_first_not_of(toTrim);
+        haystack.remove_prefix(strBegin);
+
+        return haystack;
+    }
+
+    template <typename Iter>
+    string Join(Iter begin, Iter end, string_view separator = "")
+    {
+        if (begin == end)
+            return "";
+
+        std::stringstream ss;
+        ss << *begin;
+
+        auto aggregate = [&ss, &separator](auto s) { ss << separator.data() << s; };
+        std::for_each(std::next(begin), end, aggregate);
+
+        return ss.str();
+    }
+
+    template< typename Iterator, typename Predicate >
+    bool Contains(Iterator begin, Iterator end, Predicate p)
+    {
+        return std::find_if(begin, end, p) != end;
+    }
+
+    //! closest possible form of python's `in` keyword
+    template< typename Element, typename Container >
+    bool IsIn(const Element& element, const Container& container)
+    {
+        return std::find(container.begin(), container.end(), element) != container.end();
+    }
+
+    inline string Unescape(string_view escapedText)
+    {
+        std::stringstream out;
+        auto it = escapedText.begin();
+        while (it != escapedText.end())
+        {
+            char c = *it;
+            if (c == '\\' && (it + 1) != escapedText.end())
+            {
+                ++it;
+                char next = *it;
+                switch (next)
+                {
+                case '\\': c = '\\'; break;
+                case 'a': c = '\a';  break;
+                case 'b': c = '\b';  break;
+                case 'f': c = '\f';  break;
+                case 'n': c = '\n';  break;
+                case 'r': c = '\r';  break;
+                case 't': c = '\t';  break;
+                case 'v': c = '\v';  break;
+                case '"': c = '\"';  break;
+                default:
+                    out << '\\';
+                    c = next;
+                    break;
+                }
+            }
+            out << c;
+            ++it;
+        }
+        return out.str();
+    }
+
+    template< typename T >
+    inline string ToString(T anything)
+    {
+        std::ostringstream oss;
+        oss << anything;
+        return oss.str();
+    }
+
+    inline string&& ToString(string&& s)
+    {
+        return std::forward<string&&>(s);
+    }
+
+    inline const string& ToString(const string& s)
+    {
+        return s;
+    }
+
+    inline string ToString(const char* const  s)
+    {
+        return s;
+    }
+
+    inline string ToString(string_view sv)
+    {
+        return string{ sv };
+    }
+
+    template<typename... Types>
+    string ConcatString(Types&&... args)
+    {
+        string ret;
+        (ret += ... += ToString(args));
+        return ret;
+    }
+
+    template<typename... Args>
+    string FormatString(const char* format, Args... args)
+    {
+        int size = snprintf(nullptr, 0, format, args...) + 1; // Extra space for '\0'
+        assert(size > 0);
+        std::unique_ptr<char[]> buf(new char[size]);
+        snprintf(buf.get(), size, format, args...);
+        return string(buf.get());
+    }
+
+    // Is-One-Of will check if a variable is equal to any of the values listed on the other parameters.
+    // Example: IsOneOf(variableKind, Function, Enumeration) is short for: variableKind == Function || variableKind == Enumeration.
+    // 2 arguments count: recursion terminal overload.
+    template <typename T>
+    bool IsOneOf(T value, T tocheck)
+    {
+        return value == tocheck;
+    }
+    // Any argument count version
+    template <typename T, typename... Args>
+    bool IsOneOf(T value, T khead, Args... tail)
+    {
+        return value == khead || IsOneOf(value, tail...);
+    }
+
+    // shortcut for dynamic cast to open the opportunity to migrate to adhoc rtti later
+    template <typename DestT, typename SrcT>
+    DestT As(SrcT source)
+    {
+        static_assert((std::is_pointer_v<SrcT> && std::is_pointer_v<DestT>) || (std::is_reference_v<SrcT> && std::is_reference_v<DestT>));
+        return dynamic_cast<DestT>(source);
+    }
+
+    // shortcut of As<> != nullptr
+    template <typename DestT, typename SrcT>
+    bool Is(SrcT source)
+    {
+        using PtrDestT = std::conditional_t< std::is_pointer_v<DestT>, DestT, DestT* >;
+        using ConsistentDestT = std::conditional_t< std::is_pointer_v<SrcT>, PtrDestT, DestT >;
+        return As<ConsistentDestT>(source) != nullptr;
+    }
+
+    // tail case
+    template <typename Deduced>
+    bool DynamicTypeIsAnyOf(Deduced* base)
+    {
+        return false;
+    }
+    // checks if a passed pointer to an instance, is-base-or-derived-of any one of the given types in the template argument list.
+    // verify that As< at-least-one-of-args >(base) returns non-null
+    template <typename Head, typename... Tail, typename Deduced>
+    bool DynamicTypeIsAnyOf(Deduced* base)
+    {
+        return Is<Head>(base) || DynamicTypeIsAnyOf<Tail...>(base);
+    }
+
+    template <typename EnumTypeTemplateParam>
+    struct Flag
+    {
+        template <class T>
+        using SubEnumType = typename T::EnumType;
+
+        // transform the input parameter into the underlying enum. in the case that the client instantiated the Flag from a MAKE_REFLECTABLE_ENUM_POWER macro.
+        // or just keep the original client type.
+        using EnumType = DetectedOr_t<EnumTypeTemplateParam, SubEnumType, EnumTypeTemplateParam>;
+
+        static_assert(std::is_enum_v<EnumType>);
+
+        Flag()
+        {
+            if constexpr (typename DetectedOr<EnumTypeTemplateParam, SubEnumType, EnumTypeTemplateParam>::value_t())
+            {
+                using It = typename EnumTypeTemplateParam::Iterator;
+                assert(*(++It{ EnumType(4) }) == 8);  // verify that the reflectable enum is a power enum (flag-able).
+            }
+        }
+        Flag(EnumType e) : m_value{ static_cast<UnderlyingT>(e) }
+        {}
+
+        friend Flag operator & (Flag f, EnumType e)
+        {
+            return EnumType(f.m_value & static_cast<UnderlyingT>(e));
+        }
+
+        friend Flag operator & (Flag lhs, Flag rhs)
+        {
+            return Flag(EnumType(lhs.m_value & rhs.m_value));
+        }
+
+        friend Flag operator | (Flag f, EnumType e)
+        {
+            return EnumType(f.m_value | static_cast<UnderlyingT>(e));
+        }
+
+        friend Flag& operator &= (Flag& f, EnumType e)
+        {
+            f.m_value &= static_cast<UnderlyingT>(e);
+            return f;
+        }
+
+        friend Flag& operator |= (Flag& f, EnumType e)
+        {
+            f.m_value |= static_cast<UnderlyingT>(e);
+            return f;
+        }
+
+        friend Flag& operator |= (Flag& f, const Flag& f2)
+        {
+            f.m_value |= f2.m_value;
+            return f;
+        }
+
+        friend Flag operator ~ (const Flag& a_f)
+        {
+            return Flag(EnumType(~a_f.m_value));
+        }
+
+        explicit operator bool() const
+        {
+            return m_value != 0;
+        }
+
+        bool AsBool() const
+        {
+            return operator bool();
+        }
+
+        bool IsEmpty() const
+        {
+            return m_value == 0;
+        }
+
+        using UnderlyingT = std::underlying_type_t<EnumType>;
+        UnderlyingT m_value = 0;
+    };
+
+    inline string_view GetCurrentOsName()
+    {
+#if defined(_WIN64)
+        return "Win64";
+#elif defined (_Win32)
+        return "Win32";
+#elif defined (__APPLE__)
+        return "MacOS";
+#elif defined (__linux__) || defined(__unix__) || defined(_POSIX_VERSION)
+        return "Unix";
+#endif
+    }
+
+    /// Log(N) find immediate lower element query in map-keys
+    template< typename T, typename U >
+    auto Infimum(map<T, U> const& ctr, T query)
+    {
+        auto it = ctr.upper_bound(query);
+        return it == ctr.begin() ? ctr.cend() : --it;
+    }
+
+    /// Log(N) disjointed segments belong query
+    /// You can represent segments as you wish, as long as:
+    ///   you provide the predicate to determine belonging.
+    ///   map-keys are segment start points.
+    ///   segments don't overlap.
+    /// returns: iterator to found interval key, or cend()
+    template< typename T, typename U, typename IntervalCheckPredicate >
+    auto FindInterval(const map<T, U>& ctr, const T& query, IntervalCheckPredicate&& isInIntervalPredicate)
+    {
+        auto inf = Infimum(ctr, query);
+        return inf == ctr.end() ? ctr.cend() : (isInIntervalPredicate(query, inf->second) ? inf : ctr.cend());
+    }
+
+    /// Log(N) disjointed segments belong query
+    /// segments are represented by their start points in the key, and last point in values. segments can't overlap.
+    /// returns: iterator to found interval key, or cend()
+    template< typename T, typename U>
+    auto FindInterval(const map<T, U>& ctr, const T& query)
+    {
+        return FindInterval(ctr, query, [](T q, U last) {return q <= last; });
+    }
+
+    template< typename Deduced >
+    decltype(auto) CastToRValueReference(Deduced&& value)
+    {
+        return static_cast<std::remove_reference_t<Deduced>&&>(value);
+    }
+
+    /// add a missing operator for convenience and shortness of code
+    inline bool operator == (string_view lhs, char rhs)
+    {
+        return lhs.length() == 1 && lhs[0] == rhs;
+    }
+
+    inline string ToLower(string_view original)
+    {
+        string result;
+        auto len = original.length();
+        result.resize(len);
+        for (int i = 0; i < len; ++i)
+        {
+            result[i] = static_cast<char>(tolower(original[i]));
+        }
+        return result;
+    }
+}
+
+#ifndef NDEBUG
+#include <set>
+
+namespace AZ::Tests
+{
+    inline void DoAsserts2()
+    {
+        DoAsserts3();
+
+        using namespace std::literals::string_view_literals;
+
+        assert(Slice("0123", 1, -1) == "123");
+        assert(Slice("0123", 1, 2) == "1");
+        assert(Slice("0123", -3, -2) == "2"); //*
+        assert(Slice("0123", -2, -1) == "3");
+
+        assert(Unescape(R"(my beautiful string\non two lines.)") == "my beautiful string\non two lines.");
+        assert(Unescape(R"(just\ a backslash)") == R"(just\ a backslash)");
+        assert(Unescape(R"(real \\backslash)") == "real \\backslash");
+        assert(Unescape(R"(unrecog \j escape)") == R"(unrecog \j escape)");
+        assert(Unescape(R"(at end\)") == R"(at end\)");
+        assert(Unescape(R"(with \"quotes\")") == R"(with "quotes")");
+
+        assert(StartsWith("bleu", "bl"));
+        assert(!StartsWith("0bleu", "bl"));
+        assert(StartsWith("bleu", ""));
+        assert(!StartsWith("", "0"));
+
+        assert(!EndsWith("", "0"));
+        assert(!EndsWith("stuff", "0"));
+        assert(EndsWith("", ""));
+        assert(EndsWith("0", "0"));
+        assert(EndsWith("00", "0"));
+        assert(EndsWith("nice", ""));
+        assert(EndsWith("nice", "e"));
+        assert(EndsWith("nice", "ce"));
+        assert(EndsWith("nice", "nice"));
+
+        // initializer list
+        auto list = { "nice", "things", "come", "to", "an", "end" };
+        assert(Decorate("", Join(list.begin(), list.end(), ", "), ".") == "nice, things, come, to, an, end.");
+
+        assert(Undecorate("///", Decorate("///", "/my nice string/")) == "/my nice string/"sv);
+        assert(Undecorate("\"", "non decorated") == "non decorated"sv);
+        assert(Undecorate("\"", "\"decorated\"") == "decorated"sv);
+
+        // or collection
+        set<string> myset = { "zz", "mm", "aa", "bb" };
+        assert(Join(myset.begin(), myset.end()) == "aabbmmzz");
+
+        assert(Trim("\"stuff\"", "\"") == "stuff"sv);
+        assert(Trim(" stuff ") == "stuff"sv);
+        assert(Trim(" stuff ", ".") == " stuff "sv);
+        assert(Trim("  ", " ") == ""sv);
+
+        auto unevenSplit = Split<vector<string>>("A, BB, CCC, DDDD", ", ");
+        assert(Split<vector<string>>("A, BB, CCC, DDDD", ", ")[0] == "A");
+        assert(Split<vector<string>>("A, BB, CCC, DDDD", ", ")[1] == "BB");
+        assert(Split<vector<string>>("A, BB, CCC, DDDD", ", ")[2] == "CCC");
+        assert(Split<vector<string>>("A, BB, CCC, DDDD", ", ")[3] == "DDDD");
+        auto halfSplit = Split<vector<string>>("A, BB, CCC, DDDD", "C");
+        assert(halfSplit[0] == "A, BB, ");
+        assert(halfSplit[1] == ", DDDD");
+
+        {
+            using Map = map<int, int>;
+            Map intervals{ {2,5}, {8,9} };
+
+            auto red = Infimum(intervals, 4);
+            assert(red->first == 2);
+            auto green = Infimum(intervals, 6);
+            assert(green->first == 2);
+            auto pink = Infimum(intervals, 8);
+            assert(pink->first == 8);
+            auto yellow = Infimum(intervals, 1);
+            assert(yellow == intervals.cend());
+            auto larger_than_all = Infimum(intervals, 15);
+            assert(larger_than_all->first == 8);
+            assert(FindInterval(intervals, 4) != intervals.cend());
+            assert(FindInterval(intervals, 6) == intervals.cend());
+            assert(FindInterval(intervals, 8) != intervals.cend());
+            assert(FindInterval(intervals, 1) == intervals.cend());
+        }
+
+        assert(Replace("Srg/A", "/", "::") == "Srg::A");
+
+        assert(WithinMatchedParentheses("a(b)", 2));
+        assert(WithinMatchedParentheses("a()", 1));
+        assert(!WithinMatchedParentheses("a()", 2));
+        assert(!WithinMatchedParentheses("a(b)", 0));
+        assert(!WithinMatchedParentheses("a(b)c", 4));
+        assert(!WithinMatchedParentheses("a(b)c(d)", 4));
+        assert(WithinMatchedParentheses("a((b)c(d))", 5));
+
+        assert(IsIn("hibou", std::initializer_list<const char*>{ "chouette", "hibou", "jay" }));
+        assert(!IsIn("hibou", std::initializer_list<const char*>{ "chouette", "jay" }));
+    }
+}
+#endif

+ 426 - 0
src/MetaUtils.h

@@ -0,0 +1,426 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include "StdUtils.h"
+
+#include <type_traits>
+#include <utility>
+
+namespace AZ
+{
+    // meta-variable for syntax simplification to test multiple types for equivalence with one:
+    template <typename IsT, typename ...AnyOfThese>
+    constexpr bool isAnyOf_v = std::disjunction< std::is_same<IsT, AnyOfThese>... >::value;
+
+    //examples:
+    static_assert(isAnyOf_v< int, float, double, void, int, int* >);
+    static_assert(!isAnyOf_v< size_t&, float, double, void, int, int* >);
+
+    template <typename... Types>
+    struct TypeList;
+
+    // auto: http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0127r1.html
+    template <auto... Values>
+    struct ValueTplList;
+
+    // typelist to tuple type converter
+    template<typename T>
+    struct TypeListAsTuple;
+    template<template <typename...> class TL, typename... Ts>
+    struct TypeListAsTuple<TL<Ts...>>
+    {
+        using type = tuple<Ts...>;
+    };
+    template<typename TL>
+    using TypeListAsTuple_t = typename TypeListAsTuple<TL>::type;
+
+    // value list to tuple constexpr variable converter
+    template<typename T>
+    struct ValueListAsTuple;
+    template<template <auto...> class L, auto... Vs>
+    struct ValueListAsTuple<L<Vs...>>
+    {
+        static constexpr auto value = std::make_tuple(Vs...);
+        using type = std::remove_const_t<decltype(value)>; // need to remove const because constexpr implies const, which is not useful in the typedef
+    };
+    template<typename T>
+    constexpr auto valueListAsTuple_v = ValueListAsTuple<T>::value;
+    template<typename T>
+    using ValueListAsTuple_t = typename ValueListAsTuple<T>::type;
+
+    // meta-construction capable to extract the arity of a template parameter pack, or list. (of any templated type, be it TypeList or tuple or variant.)
+    template <class TypeListT>
+    struct CountTemplateParameters;
+    // version for type lists
+    template <template<class...> class Tmpl, class... Ts>
+    struct CountTemplateParameters<Tmpl<Ts...> >
+    {
+        static constexpr int value = sizeof...(Ts);
+    };
+    // version for value lists
+    template <template<auto...> class Tmpl, auto... Ts>
+    struct CountTemplateParameters<Tmpl<Ts...> >
+    {
+        static constexpr int value = sizeof...(Ts);
+    };
+    // alias template variable
+    template <class TypeListT>
+    constexpr int countTemplateParameters_v = CountTemplateParameters<TypeListT>::value;
+
+    // example usage:
+    static_assert(countTemplateParameters_v< TypeList<int, bool, float> > == 3);
+    static_assert(countTemplateParameters_v< ValueTplList<1, 2, 4, 8> > == 4);
+
+    struct NotFoundT { using type = NotFoundT; };
+
+    // lazy meta evaluation enabler
+    template<class T> struct IdentityT { using type = T; };
+
+    // indexed direct accessor at<n>
+    template<size_t at, typename TL>
+    struct AtT
+    {
+        static constexpr bool out_of_bounds = at >= countTemplateParameters_v<TL>;
+        using type = typename std::conditional< out_of_bounds,
+            IdentityT<NotFoundT>,  // we need Identity to enable SFINAE
+            IdentityT<
+            std::tuple_element<at, TypeListAsTuple_t<TL> >
+            > >::type::type::type;
+    };
+    // shortcut
+    template <size_t at, typename TL>
+    using At_t = typename AtT<at, TL>::type;
+
+    // tests
+    static_assert(std::is_same_v< At_t<1, TypeList<int, bool, float>>, bool >);
+    // verify that we can access out of bound without a complete build stop
+    static_assert(std::is_same_v< At_t<10, TypeList<int, bool, float>>, NotFoundT >);
+
+    template <auto V>
+    struct ConstVal : std::integral_constant<decltype(V), V>
+    {};
+
+    // is-integral-type-or-enum-type trait
+    template <typename T>
+    struct IsIntegralConstant
+    {
+        static constexpr bool value = std::is_integral_v<T> || std::is_enum<T>::value;
+    };
+    template <typename T, auto N>
+    struct IsIntegralConstant<std::integral_constant<T, N>>
+    {
+        static constexpr bool value = IsIntegralConstant<T>::value;
+    };
+    template <auto N>
+    struct IsIntegralConstant<ConstVal<N>>
+    {
+        static constexpr bool value = IsIntegralConstant<decltype(N)>::value;
+    };
+    template <typename T> constexpr auto isIntegralConstant_v = IsIntegralConstant<T>::value;
+
+    // tests:
+    static_assert(!isIntegralConstant_v<int*>);
+    static_assert(isIntegralConstant_v<std::integral_constant<int, 3>>);
+    static_assert(isIntegralConstant_v<ConstVal<2>>);
+    static_assert(std::is_same_v< At_t<1, TypeList< int, ConstVal<2>>>, ConstVal<2> >);
+    static_assert(isIntegralConstant_v< At_t<1, TypeList< int, ConstVal<2>>>>);
+
+    // type list head extractor
+    template <class TypeListT>
+    struct HeadType;
+    template <template<class...> class Tmpl, class T, class... Ts>
+    struct HeadType<Tmpl<T, Ts...> >
+    {
+        using type = T;
+    };
+    // same thing for values
+    template <class TypeListT>
+    struct HeadValue;
+    template <template<auto...> class Tmpl, auto T, auto... Ts>
+    struct HeadValue<Tmpl<T, Ts...> >
+    {
+        static constexpr auto value = T;
+    };
+    // alias template
+    template <class TypeListT>
+    using HeadType_t = typename HeadType<TypeListT>::type;
+    // same for value
+    template <class ValueListT>
+    constexpr auto HeadValue_v = HeadValue<ValueListT>::value;
+
+    // test
+    static_assert(std::is_same_v< HeadType_t< TypeList<int, double> >, int >);
+    static_assert(HeadValue_v< ValueTplList<1, 2, 3> > == 1);
+
+    // type list tail extractor
+    template <class TypeListT>
+    struct TailTypeList;
+    template <template<class...> class Tmpl, class T, class... Ts>
+    struct TailTypeList<Tmpl<T, Ts...> >
+    {
+        using type = TypeList<Ts...>;
+    };
+    // same thing for values
+    template <class TypeListT>
+    struct TailValueList;
+    template <template<auto...> class Tmpl, auto T, auto... Ts>
+    struct TailValueList<Tmpl<T, Ts...> >
+    {
+        using type = ValueTplList<Ts...>;
+    };
+    // alias template
+    template <typename TypeListT>
+    using TailTypeList_t = typename TailTypeList<TypeListT>::type;
+    // ends up being the same for values, because the tail of a valuelist is still a type.
+    template <typename TypeListT>
+    using TailValueList_t = typename TailValueList<TypeListT>::type;
+
+    // since it's a runtime (unrolled) loop, you will get default-constructed instances of each type as parameter to your functor
+    template <typename TypeListT, typename GenericLambda, int index = 0>
+    void ForEachType(GenericLambda functor)
+    {
+        auto head = HeadType_t<TypeListT>{};
+        functor(head, ConstVal<index>{});
+        if constexpr (1 < countTemplateParameters_v<TypeListT>)
+        {
+            using Rest = TailTypeList_t<TypeListT>;
+            ForEachType<Rest, GenericLambda, index + 1>(functor);
+        }
+    };
+
+    template <typename ValueTplListT, typename GenericLambda, int index = 0>
+    void ForEachValue(GenericLambda functor)
+    {
+        auto head = HeadValue_v<ValueTplListT>;
+        functor(head, ConstVal<index>{});
+        if constexpr (1 < countTemplateParameters_v<ValueTplListT>)
+        {
+            using Rest = TailValueList_t<ValueTplListT>;
+            ForEachValue<Rest, GenericLambda, index + 1>(functor);
+        }
+    };
+
+    // Constructs a default-initialized value with the type_atN in the variant<type_at0, type_at1...> at index `index`.
+    // with `index` being a runtime parameter. And re-assign the variant to that new object
+    // Requires that all types in the variant are default constructible.
+    template <typename VariantT>
+    VariantT&& IndexedFactory(VariantT&& variant, int index)
+    {
+        ForEachType< std::remove_reference_t<VariantT> >([&variant, index](auto var, auto ii_c)
+            {
+                if (ii_c == index)
+                {
+                    static_assert(std::is_default_constructible_v< decltype(var) >);
+                    variant = decltype(var){};
+                }
+            });
+        return variant;
+    }
+
+    // meta technique to find index of a type in a typelist
+    template <typename>
+    constexpr int MetaFind(int)
+    {   // not found case
+        return -1;
+    }
+    template <typename NeedleT, typename T, typename... Ts>
+    constexpr int MetaFind(int ind = 0)
+    {
+        if (std::is_same_v<NeedleT, T>)
+        {
+            return ind;
+        }
+        else
+        {
+            return MetaFind<NeedleT, Ts...>(ind + 1);
+        }
+    }
+    // flat 2 template parameters versions
+    template <typename T, typename T2>
+    struct MetaIndexOf : std::integral_constant<int, -1>
+    {};
+    template <typename T>
+    struct MetaIndexOf<T, T> : std::integral_constant<int, 0>
+    {};
+    // destructurer version (access the contents of a typelist)
+    template <typename T, template<typename...> class Tmpl, typename... Ts>
+    struct MetaIndexOf<T, Tmpl<Ts...> >
+        : std::integral_constant< int, MetaFind<T, Ts...>() >
+    {};
+    // template variable version of it:
+    template <typename Needle, typename HaystackList>
+    constexpr int metaFind_v = MetaIndexOf<Needle, HaystackList>::value;
+
+    // test
+    static_assert(metaFind_v< bool, bool > == 0);
+    static_assert(metaFind_v< bool, int > == -1);
+    static_assert(metaFind_v< int, TypeList<bool, float, int, double> > == 2);
+    static_assert(metaFind_v< double, TypeList<bool, float, int, double> > == 3);
+    static_assert(metaFind_v< bool, TypeList<bool, float, int, double> > == 0);
+    static_assert(metaFind_v< long, TypeList<bool, float, int, double> > == -1);
+
+    // make a metaFind for values:
+
+    // let's try with a value wrapped in a type because going direct auto route didn't work in MSVC (compiler not good enough)
+
+    template <typename>
+    struct BoxAll;
+    // convenience helper to directly make a typelist of boxed values from values
+    template< template<auto...> class VL, auto... Vs >
+    struct BoxAll< VL<Vs...> >
+    {
+        using type = TypeList< ConstVal<Vs>... >;
+    };
+    template<typename T>
+    using BoxAll_t = typename BoxAll<T>::type;
+
+    static_assert(std::is_same_v< BoxAll_t<ValueTplList<0>>, TypeList<ConstVal<0>> >);
+
+    // At for values
+    template <size_t at, typename VL>
+    constexpr int at_v = AtT<at, BoxAll_t<VL>>::type::value;
+
+    static_assert(at_v< 2, ValueTplList<2, 4, 8, 16, 32> > == 8);
+
+    // finder
+    template <auto V, typename VL>
+    constexpr int metaFindV_v = metaFind_v< ConstVal<V>, BoxAll_t<VL> >;
+
+    // tests
+    static_assert(metaFindV_v< 0, ValueTplList<0> > == 0);
+    static_assert(metaFindV_v< 5, ValueTplList<0> > == -1);
+    static_assert(metaFindV_v< 0, ValueTplList<nullptr, 0> > == 1);
+    // this test doesn't pass on visual studio because of the bad quality of the compiler.
+    // however funnily intellisense correctly passes it.
+    //static_assert(metaFindV_v< 2, ValueTplList<1, (long)2, 2, 3, 4> > == 2);
+    static_assert(metaFindV_v< -1, ValueTplList<-2, -1, 42> > == 1);
+
+    // define isContainedIn in terms of find
+    template <typename T, typename TemplInst>
+    constexpr bool isContainedIn_v = metaFind_v<T, TemplInst> != -1;
+
+    // example use:
+    static_assert(isContainedIn_v< bool, pair<bool, int> >);
+    static_assert(!isContainedIn_v< float, pair<bool, int> >);
+    // it works with variant too... anything with a template type list.
+    static_assert(isContainedIn_v< int, tuple<bool, int, float> >);
+    static_assert(isContainedIn_v< int, TypeList<bool, int, float> >);
+    static_assert(!isContainedIn_v< long, TypeList<bool, int, float> >);
+
+
+    template<typename L, typename T>
+    struct PushFrontImpl;
+    template<template<class...> class L, typename... U, typename T>
+    struct PushFrontImpl<L<U...>, T>
+    {
+        using type = L<T, U...>;
+    };
+    // the API is you can push a bunch of T at once, but usually used with one T. PushFront_t< List, float >
+    template<typename L, typename... T>
+    using PushFront_t = typename PushFrontImpl<L, T...>::type;
+
+    // find a key, in a typelist that is arranged like a map: TypeList< key, value, key, value ... >
+    // and return the associated value
+    // KeyT: the key to look for (needle)
+    // KVPTypeList: a key-value paired typelist
+    // may return NotFound type
+    template <typename KeyT, typename KVPTypeList>
+    struct MetaFindValueNextToKey
+    {
+        static constexpr auto index = metaFind_v< KeyT, KVPTypeList >;
+        // the sub type of interest is just coming at the next index in the list:
+        using type = std::conditional_t< (index < 0), NotFoundT,
+            At_t<index + 1, KVPTypeList> >;
+    };
+    // Simplified API
+    template <typename KeyT, typename KVPTypeList>
+    using MetaFindValueNextToKey_t = typename MetaFindValueNextToKey<KeyT, KVPTypeList>::type;
+
+
+    // detected_or from upcoming standards
+    namespace detail
+    {
+        template <class Default, class AlwaysVoid, template<class...> class Op, class... Args>
+        struct Detector
+        {
+            using value_t = std::false_type;
+            using type = Default;
+        };
+
+        template <class Default, template<class...> class Op, class... Args>
+        struct Detector<Default, std::void_t<Op<Args...>>, Op, Args...>
+        {
+            using value_t = std::true_type;
+            using type = Op<Args...>;
+        };
+    }
+
+    template <class Default, template<class...> class Op, class... Args>
+    using DetectedOr = detail::Detector<Default, void, Op, Args...>;
+
+    template <class Default, template<class...> class Op, class... Args>
+    using DetectedOr_t = typename DetectedOr<Default, Op, Args...>::type;
+}
+
+#ifndef NDEBUG
+#include <cassert>
+#include "variant.hpp"
+#include <string>
+
+namespace AZ::Tests
+{
+    inline void DoAsserts3()
+    {
+        ForEachValue< ValueTplList<1, 2, (size_t)3> >([](auto t, auto) { static_assert(std::is_same_v<decltype(t), int> || std::is_same_v<decltype(t), size_t>); });
+
+        ForEachValue< ValueTplList<(long)1, (long)2> >([](auto t, auto) { static_assert(std::is_same_v<decltype(t), long>); });
+
+        ForEachValue< ValueTplList<'a', 'b', 'c'> >([](auto t, auto i)
+            {
+                assert(i == 0 && t == 'a'
+                    || i == 1 && t == 'b'
+                    || i == 2 && t == 'c');
+            });
+
+        using TL = TypeList< ConstVal<10>, int >;
+        ForEachType< TL >([](auto inst, auto ii_c)
+            {
+                using KeyAtii = At_t<decltype(ii_c)::value, TL>;
+                static_assert(ii_c == 0 && isIntegralConstant_v< KeyAtii > && KeyAtii{} == 10
+                    || ii_c == 1 && std::is_same_v< KeyAtii, int >);
+            });
+
+        {  // check that the variant factory works
+            using mpark::variant;
+            using mpark::monostate;
+            using mpark::get;
+            using mpark::holds_alternative;
+
+            struct S { bool b = true; };
+            using V = variant<monostate, int, string, S>;
+            V v;
+
+            assert(holds_alternative<monostate>(v));
+
+            IndexedFactory(v, 1);
+            assert(holds_alternative<int>(v));
+
+            IndexedFactory(v, 2);
+            assert(holds_alternative<string>(v));
+
+            IndexedFactory(v, 0);
+            assert(holds_alternative<monostate>(v));
+
+            IndexedFactory(v, 3);
+            assert(holds_alternative<S>(v));
+            assert(get<3>(v).b == true);
+        }
+    }
+}
+#endif

+ 44 - 0
src/PreprocessorLineDirectiveFinder.h

@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include "StdUtils.h"
+
+namespace AZ::ShaderCompiler
+{
+    //! Describes a preprocessor line directive.
+    //! this is not part of the AST, that is why this is not in AzslcUtils.h
+    struct LineDirectiveInfo
+    {
+        size_t m_physicalTokenLine;  //!< line where the preprocessor token appears
+        size_t m_forcedLineNumber;   //!< the line number as specified (parsed from syntax input)
+        string m_containingFilename;
+    };
+
+    //! An interface capable of finding the original line in the source file
+    //! for the given flat file that is being compiled.
+    //! A preprocessed azsl file is the file generated by MCPP after all #include and macro definitions
+    //! have been resolved in a single flat file.
+    //! If an error is observed at any given line in the flat file, you can use this interface to find the original file
+    //! and line number where the error is coming from.
+    class PreprocessorLineDirectiveFinder
+    {
+    public:
+        virtual ~PreprocessorLineDirectiveFinder() {};
+
+        virtual const LineDirectiveInfo* GetNearestPreprocessorLineDirective(size_t azslLineNumber) const = 0;
+
+        virtual size_t GetLineNumberInOriginalSourceFile(const LineDirectiveInfo& lineInfo, size_t azslLineNumber) const
+        {
+            const size_t lineNumberOfDirectiveinAzslSource = lineInfo.m_physicalTokenLine;
+            const size_t relativeLineNumber = azslLineNumber - lineNumberOfDirectiveinAzslSource;
+            const size_t absoluteLineNumberInIncludedFile = lineInfo.m_forcedLineNumber + relativeLineNumber - 1;
+            return absoluteLineNumberInIncludedFile;
+        }
+    };
+}

+ 194 - 0
src/ReflectableEnums.h

@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <string_view>
+
+// preprocessor sadness. would not have to do that if we had BOOST.PP
+#define MSVCFIX_EXPAND(x) x
+#define AZ_FOR_EACH_1(pppredicate,  val, x)      pppredicate(x, val)
+#define AZ_FOR_EACH_2(pppredicate,  val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_1 (pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_3(pppredicate,  val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_2 (pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_4(pppredicate,  val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_3 (pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_5(pppredicate,  val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_4 (pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_6(pppredicate,  val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_5 (pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_7(pppredicate,  val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_6 (pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_8(pppredicate,  val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_7 (pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_9(pppredicate,  val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_8 (pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_10(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_9 (pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_11(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_10(pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_12(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_11(pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_13(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_12(pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_14(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_13(pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_15(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_14(pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_16(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_15(pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_17(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_16(pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_18(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_17(pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_19(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_18(pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_20(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_19(pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_21(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_20(pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_22(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_21(pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_23(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_22(pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_24(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_23(pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_25(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_24(pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_26(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_25(pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_27(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_26(pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FOR_EACH_28(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_27(pppredicate, val + 1, __VA_ARGS__))
+#define AZ_FIRST_ARG_(N, ...) N
+#define AZ_FIRST_ARG(args) AZ_FIRST_ARG_ args  // hack needed for MSVC bug
+// Laurent Deniau's method
+#define AZ_FOR_EACH_NARG(...) AZ_FOR_EACH_NARG_(__VA_ARGS__, AZ_FOR_EACH_RSEQ_N())
+#define AZ_FOR_EACH_NARG_(...) MSVCFIX_EXPAND(AZ_FOR_EACH_ARG_N(__VA_ARGS__))
+#define AZ_FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, N, ...) N
+#define AZ_FOR_EACH_RSEQ_N() 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
+#define AZ_CONCATENATE(x,y) x##y
+#define AZ_FOR_EACH_(N, pppredicate, val, ...) MSVCFIX_EXPAND(AZ_CONCATENATE(AZ_FOR_EACH_, N)(pppredicate, val, __VA_ARGS__))
+// Final API:
+// use as such:  AZ_FOR_EACH( LAMBDAMACRO_1ARG, LIST_OF_PARAMETERS* )
+#define AZ_FOR_EACH(pppredicate, ...)\
+    AZ_FOR_EACH_(AZ_FOR_EACH_NARG(__VA_ARGS__), pppredicate, 0, __VA_ARGS__)
+
+
+#define GEN_ONE_ENUMERATOR_LINE(X, val)          X,
+#define GEN_ONE_ENUMERATOR_LINE_INIT(X, val)     X = val,
+#define GEN_ONE_ENUMERATOR_LINE_INIT_PWR(X, val) X = 0x00000001 << (val),
+
+#define GEN_ONE_CASE(X, val) case X: return #X;
+
+#define GEN_TO_STRING_FUNCTION(...)\
+static constexpr string_view ToStr(EnumType enumValue)\
+{\
+  switch (enumValue)\
+  {\
+    AZ_FOR_EACH( GEN_ONE_CASE, __VA_ARGS__ )\
+    default: break;\
+  }\
+  return "<error>";\
+}
+
+#define GEN_ONE_IF_STR_EQ(X, val) if (str == #X) return X;
+
+#define GEN_FROM_STRING_FUNCTION(...)\
+static constexpr EnumType FromStr(string_view str)\
+{\
+  AZ_FOR_EACH( GEN_ONE_IF_STR_EQ, __VA_ARGS__ )\
+  return EndEnumeratorSentinel_;\
+}
+
+// public API here
+#define MAKE_REFLECTABLE_ENUM_(EnumTypeName, EnumeratorInit, EnumeratorNextOp, ...)\
+struct EnumTypeName\
+{\
+    /** the real internal enum type */\
+    enum EnumType\
+    {\
+        AZ_FOR_EACH( EnumeratorInit, __VA_ARGS__ )\
+        EnumeratorInit(EndEnumeratorSentinel_, AZ_FOR_EACH_NARG(__VA_ARGS__))\
+    };\
+    \
+    template <auto... Values> struct MetaVals{};\
+    /** meta programming access of enumerators in the form of a template value list */\
+    using MetaValueList = MetaVals<\
+        AZ_FOR_EACH( GEN_ONE_ENUMERATOR_LINE, __VA_ARGS__ )\
+        EndEnumeratorSentinel_\
+    >;\
+    /** construction by default and by implicit conversion from the internal enum type */\
+    EnumTypeName() = default;\
+    EnumTypeName(EnumType val_) : m_value{val_} {}\
+    /** stringifiers/serializer */\
+    GEN_TO_STRING_FUNCTION(__VA_ARGS__)\
+    GEN_FROM_STRING_FUNCTION(__VA_ARGS__)\
+    /** currently held enumerator value. defaults constructs to end */\
+    EnumType m_value = EndEnumeratorSentinel_;\
+    /** conversion operator to avoid to have to leak .m_value everywhere */\
+    operator EnumType() const { return m_value; }\
+    struct Iterator\
+    {\
+        EnumType m_i;\
+        constexpr EnumType operator*() { return m_i; }\
+        constexpr Iterator operator++() { m_i = static_cast<EnumType>(EnumeratorNextOp); return {m_i}; }\
+        constexpr bool operator!=(const Iterator& rhs) const { return m_i != rhs.m_i; }\
+    };\
+    /** sub-type to make it clear on client sites */ \
+    struct Enumerate\
+    {\
+        /** ranges API to list enumerators */ \
+        constexpr Iterator begin() const { return Iterator{EnumType( AZ_FIRST_ARG((__VA_ARGS__)) )}; }\
+        constexpr Iterator end()   const { return Iterator{EndEnumeratorSentinel_}; }\
+    };\
+    /** query: "is current value any of these ... ?" */\
+    bool IsOneOf(EnumType rhs) const { return m_value == rhs; }\
+    /** query: "is current value any of these ... ?" */\
+    template <typename... Args> bool IsOneOf(EnumType rhsHead, Args... packTail) const\
+    {\
+        return m_value == rhsHead || IsOneOf(packTail...);\
+    }\
+    /** direct value checks: avoid to need to access .m_value (to look/behave like a native enum) */\
+    friend bool operator==(EnumTypeName lhs,           EnumTypeName::EnumType rhs) { return lhs.m_value == rhs;         }\
+    friend bool operator==(EnumTypeName::EnumType lhs, EnumTypeName rhs          ) { return lhs         == rhs.m_value; }\
+    friend bool operator==(EnumTypeName lhs,           EnumTypeName rhs          ) { return lhs.m_value == rhs.m_value; }\
+    friend bool operator!=(EnumTypeName lhs,           EnumTypeName::EnumType rhs) { return lhs.m_value != rhs;         }\
+    friend bool operator!=(EnumTypeName::EnumType lhs, EnumTypeName rhs          ) { return lhs         != rhs.m_value; }\
+    friend bool operator!=(EnumTypeName lhs,           EnumTypeName rhs          ) { return lhs.m_value != rhs.m_value; }\
+}
+
+#define MAKE_REFLECTABLE_ENUM(EnumTypeName, ...)          MAKE_REFLECTABLE_ENUM_(EnumTypeName, GEN_ONE_ENUMERATOR_LINE_INIT, m_i + 1, __VA_ARGS__)
+#define MAKE_REFLECTABLE_ENUM_POWER(EnumTypeName, ...)    MAKE_REFLECTABLE_ENUM_(EnumTypeName, GEN_ONE_ENUMERATOR_LINE_INIT_PWR, (m_i == 0 ? 1 : m_i << 1), __VA_ARGS__)
+
+// can't use an "enum class" because scopes are not flexible (can't `using`).
+//  free functions becomes un-implementable because of the need for a "closure" predicate in az_foreach
+
+// test:
+#ifndef NDEBUG
+#include <cassert>
+
+namespace AZ::Tests
+{
+    MAKE_REFLECTABLE_ENUM(MyEnum,
+        Enumerand1, Enumerand2, Enumerand3);
+
+    static_assert(MyEnum::Enumerand3 == 2);
+    static_assert(MyEnum::ToStr(MyEnum::Enumerand3) == "Enumerand3");
+    static_assert(MyEnum::FromStr("Enumerand3") == MyEnum::Enumerand3);
+
+    // POWER version
+    MAKE_REFLECTABLE_ENUM_POWER(MyEnumFlaggable,
+        Flag1, Flag2, Flag3);
+
+    // test executed:
+    inline void DoAsserts4()
+    {
+        int i = 0;
+        for (auto e : MyEnum::Enumerate{})
+        {
+            if (i == 0) assert(e == MyEnum::Enumerand1);
+            if (i == 1) assert(e == MyEnum::Enumerand2);
+            if (i == 2) assert(e == MyEnum::Enumerand3);
+            ++i;
+        }
+
+        MyEnum e;
+        e = MyEnum::Enumerand2;
+
+        assert(e.IsOneOf(MyEnum::Enumerand1, MyEnum::Enumerand2));
+        assert(!e.IsOneOf(MyEnum::Enumerand3));
+        assert(!e.IsOneOf(MyEnum::Enumerand1, MyEnum::Enumerand3));
+
+        i = 0;
+        for (auto e : MyEnumFlaggable::Enumerate{})
+        {
+            assert(e == 1 << i);
+            ++i;
+        }
+
+        static_assert(MyEnumFlaggable::ToStr(MyEnumFlaggable::Flag1) == "Flag1");
+        static_assert(MyEnumFlaggable::FromStr("Flag2") == MyEnumFlaggable::Flag2);
+
+    } // builds cleanly with gcc 8 --pedantic, clang 7 --pedantic, msvc 2017 /permissive- /Wall
+}
+#endif

+ 42 - 0
src/ReflectableEnumsUtils.h

@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include "MetaUtils.h"
+#include "ReflectableEnums.h"
+
+namespace AZ
+{
+    // GenratedEnum are enums made with the MAKE_REFLECTABLE_ENUM macro
+
+    // get a std::make_tuple(Enumerator1, Enumerator2...);
+    template<typename GeneratedEnum>
+    constexpr auto enumeratorsAsTuple_v = valueListAsTuple_v< typename GeneratedEnum::MetaValueList >;
+
+    // get a tuple< T::EnumType, T::EnumType...>
+    template<typename GeneratedEnum>
+    using EnumeratorsAsTuple_t = ValueListAsTuple_t< typename GeneratedEnum::MetaValueList >;
+
+    template<typename GeneratedEnum>
+    constexpr auto enumeratorsCount_v = countTemplateParameters_v< typename GeneratedEnum::MetaValueList >;
+}
+
+#ifndef NDEBUG
+#include <cassert>
+
+namespace AZ::Tests
+{
+    static_assert(at_v<1, MyEnum::MetaValueList> == MyEnum::Enumerand2);
+    static_assert(at_v<2, MyEnum::MetaValueList> == MyEnum::Enumerand3);
+    static_assert(at_v<3, MyEnum::MetaValueList> == MyEnum::EndEnumeratorSentinel_);
+
+    static_assert(at_v<0, MyEnumFlaggable::MetaValueList> == MyEnumFlaggable::Flag1);
+    static_assert(at_v<1, MyEnumFlaggable::MetaValueList> == MyEnumFlaggable::Flag2);
+    static_assert(at_v<3, MyEnumFlaggable::MetaValueList> == MyEnumFlaggable::EndEnumeratorSentinel_);
+}
+#endif

+ 162 - 0
src/StdUtils.h

@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+// setup of low level library includes in this file.
+// if not defined -> fallback to std
+
+#ifdef _WIN32
+// I don't recommend custom lib utilities on windows because they don't have natvis and are hard to debug.
+#else
+#define USE_TINY_OPTIONAL
+//#define USE_BOOST_OPTIONAL
+#define USE_MPARK_VARIANT
+#endif
+
+#include <cassert>
+#include <cctype>
+#include <cfloat>
+
+#include <algorithm>
+#include <array>
+#include <functional>
+#include <iostream>
+#include <map>
+#include <regex>
+#include <set>
+#include <stack>
+#include <string_view>
+#include <tuple>
+#include <unordered_map>
+#include <unordered_set>
+
+#ifdef USE_MPARK_VARIANT
+#include "variant.hpp"
+namespace StdUtils = mpark;
+#else
+#include <variant>
+namespace StdUtils = std;
+#endif
+
+#if defined(USE_MPARK_VARIANT) && !defined(_WIN32)
+// one shortcut for is_invocable, because Xcode9 couldn't get it right:
+template <typename C, typename... T>
+using is_invocable = mpark::lib::is_invocable<C, T...>;
+// same for invoke_result
+template <typename C, typename... T>
+using invoke_result_t = mpark::lib::invoke_result_t<C, T...>;
+#else
+template <typename C, typename... T>
+using is_invocable = std::is_invocable<C, T...>;
+
+template <typename C, typename... T>
+using invoke_result_t = std::invoke_result_t<C, T...>;
+#endif
+
+#if defined(USE_TINY_OPTIONAL)
+#include <tiny/optional.h>
+#elif defined(USE_BOOST_OPTIONAL)
+#include <boost/optional/optional.hpp>
+#else // std
+#include <optional>
+inline constexpr auto none = std::nullopt;
+#endif
+
+namespace AZ
+{
+    // C++17 `std::variant` for C++11/14/17
+    using StdUtils::variant;
+    using StdUtils::monostate;
+    using StdUtils::get;
+    using StdUtils::holds_alternative;
+
+    // Alternatives for the C++17 std::optional
+#if defined(USE_TINY_OPTIONAL)
+    using tiny::optional;
+    using tiny::none;
+#elif defined(USE_BOOST_OPTIONAL)
+    using boost::optional;
+    using boost::none;
+#else
+    using std::optional;
+#endif
+
+    // Configure basic symbols so we can use them unqualified -> easy to change to AzStd without big refactorings.
+
+    using std::enable_if_t;
+    using std::is_same_v;
+
+    using std::count;
+    using std::exception;
+    using std::pair;
+    using std::tuple;
+
+    using std::string;
+    using std::string_view;
+
+    using std::array;
+    using std::map;
+    using std::set;
+    using std::stack;
+    using std::unordered_map;
+    using std::unordered_set;
+    using std::vector;
+
+    // exception type of VisitFirstNonNull
+    struct AllNull : std::runtime_error
+    {
+        using runtime_error::runtime_error;
+    };
+
+    // Type-heterogeneity-preserving multi pointer object single visitor.
+    // Returns whatever the passed functor would.
+    // Throws if all passed objects are null.
+    template <typename Lambda, typename T>
+    invoke_result_t<Lambda, T*> VisitFirstNonNull(Lambda functor, T* object) noexcept(false)
+    {
+        if (object)
+        {
+            return functor(object);
+        }
+        throw AllNull{ "no non-null object passed" };
+    }
+
+    template <typename Lambda, typename T, typename... TOther>
+    invoke_result_t<Lambda, T*> VisitFirstNonNull(Lambda functor, T* object, TOther*... rest) noexcept(false)
+    {
+        if (object)
+        {
+            return functor(object);
+        }
+        else
+        {
+            return VisitFirstNonNull(functor, rest...);
+        }
+    }
+
+    template< typename SetType >
+    void SetMerge(SetType& dest, SetType& src)
+    {
+#ifdef _WIN32
+        dest.merge(src);  // when you have correct libraries.
+#else
+        for (auto it = src.begin(); it != src.end(); )
+        {
+            if (dest.find(*it) == dest.end())
+            {
+                dest.insert(std::move(*it));
+                it = src.erase(it);
+            }
+            else
+            {
+                ++it;
+            }
+        }
+#endif
+    }
+}

+ 447 - 0
src/Texture2DMSto2DCodeMutator.cpp

@@ -0,0 +1,447 @@
+/*
+* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
+* its licensors.
+*
+* For complete copyright and license terms please see the LICENSE at the root of this
+* distribution (the "License"). All use of this software is governed by the License,
+* or, if provided, by the license below or the license accompanying this file. Do not
+* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*
+*/
+
+#include "Texture2DMSto2DCodeMutator.h"
+
+
+namespace AZ::ShaderCompiler
+{
+    static constexpr char FunctionNameLoad[] = "Load";
+    static constexpr char FunctionNameGetSamplePosition[] = "GetSamplePosition";
+    static constexpr char FunctionNameGetDimensions[] = "GetDimensions";
+
+    ///////////////////////////////////////////////////////////////////////
+    // azslParserBaseListener Overrides ...
+    void Texture2DMSto2DCodeMutator::enterFunctionCallExpression(azslParser::FunctionCallExpressionContext* ctx)
+    {
+        const auto expressionCtx = ctx->expression();
+        const std::string functionName = expressionCtx->stop->getText();
+
+        if (functionName == FunctionNameLoad)
+        {
+            OnEnterLoad(ctx);
+        }
+        else if (functionName == FunctionNameGetSamplePosition)
+        {
+            OnEnterGetSamplePosition(ctx);
+        }
+        else if (functionName == FunctionNameGetDimensions)
+        {
+            OnEnterGetDimensions(ctx);
+        }
+    }
+    ///////////////////////////////////////////////////////////////////////
+
+
+    ///////////////////////////////////////////////////////////////////////
+    // ICodeEmissionMutator Overrides ...
+    const CodeMutation* Texture2DMSto2DCodeMutator::GetMutation(ssize_t tokenId) const
+    {
+        auto itor = m_mutations.find(tokenId);
+        if (itor == m_mutations.end())
+        {
+            return nullptr;
+        }
+        return &itor->second;
+    }
+    ///////////////////////////////////////////////////////////////////////
+
+    void Texture2DMSto2DCodeMutator::RunMiddleEndMutations()
+    {
+        if (MutateTypeOfMultiSampleVariables())
+        {
+            MutateMultiSampleSystemSemantics();
+        }
+    }
+
+    //! A helper function that returns the symbol name contained in @expressionCtx.
+    static UnqualifiedName GetSymbolName(azslParser::ExpressionContext* expressionCtx)
+    {
+        const auto& children = expressionCtx->children;
+        // We only care for cases with three children:
+        // "<Symbol>", ".", "<funcName>"
+        if (children.size() == 3)
+        {
+            string symbolName = Replace(children[0]->getText(), "::", "/");
+            return UnqualifiedName{ symbolName };
+        }
+        return UnqualifiedName();
+    }
+
+    Texture2DMSto2DCodeMutator::TextureMSType Texture2DMSto2DCodeMutator::GetMultiSampledTextureClass(const UnqualifiedName& uqSymbolName)
+    {
+        if (uqSymbolName.empty())
+        {
+            return TextureMSType::None;
+        }
+        // We only care if the symbol that is calling Load(...) is of type Texture2DMS or Texture2DMSArray
+        IdAndKind* idkind = m_ir->m_sema.LookupSymbol(uqSymbolName);
+        if (!idkind)
+        {
+            return TextureMSType::None;
+        }
+        auto& [uid, kind] = *idkind;
+        if (kind.GetKind() != Kind::Variable)
+        {
+            return TextureMSType::None;
+        }
+        auto varInfo = kind.GetSubAs<VarInfo>();
+        if (varInfo->GetTypeClass() != TypeClass::MultisampledTexture)
+        {
+            return TextureMSType::None;
+        }
+        if (EndsWith(varInfo->m_typeInfoExt.m_coreType.m_typeId.GetName(), "Array"))
+        {
+            return TextureMSType::Texture2DMSArray;
+        }
+        return TextureMSType::Texture2DMS;
+    }
+
+    void Texture2DMSto2DCodeMutator::OnEnterLoad(azslParser::FunctionCallExpressionContext* ctx)
+    {
+        // First we must capture the complete name of the symbol that called <Symbol>.Load(...)
+        const auto expressionCtx = ctx->expression();
+        const UnqualifiedName uqSymbolName = GetSymbolName(expressionCtx);
+        const TextureMSType textureMSType = GetMultiSampledTextureClass(uqSymbolName);
+        if (textureMSType == TextureMSType::None)
+        {
+            return;
+        }
+
+        // Define the mutations.
+        const auto argumentListCtx = ctx->argumentList();
+        const auto argumentsCtx = argumentListCtx->arguments();
+        auto vectorOfArguments = argumentsCtx->expression();
+
+
+        // For Texture2DMS Load has two variants:
+        // 1- Two arguments: int2 location, int sampleIndex
+        //    When mutating this variant to Texture2D the first argument will be prepended
+        //    with "int3("<location> and the second argument "sampleIndex" will be replaced with "0)".
+        // 2- Three arguments: int2 location, int sampleIndex, int2 offset
+        //    When mutating this variant to Texture2D the first argument will be prepended
+        //    with "int3("<location> and the second argument "sampleIndex" will be replaced with "0)",
+        //    the third argument will remain as is.
+        // For Texture2DMSArray it's the same as above, except that the first argument is of type int3.
+        //    And it will be wrapped with an int4.
+        const string wrapperType = textureMSType == TextureMSType::Texture2DMSArray ? "int4(" : "int3(";
+        if (vectorOfArguments.size() >= 2)
+        {
+            {
+                auto firstArgContext = vectorOfArguments[0];
+                ssize_t tokenIndex = firstArgContext->start->getTokenIndex();
+                CodeMutation firstArgMutation;
+                firstArgMutation.m_prepend.emplace(wrapperType);
+                m_mutations.emplace(tokenIndex, firstArgMutation);
+            }
+            {
+                // There's already a "," token that will be emitted after the first argument
+                // So all we have to do is simply replace the second argument with "0)"
+                // and will get in the end: int3( @firstArgContext, 0) or int4( @firstArgContext, 0) for MSArray.
+                // Also keep in mind that we are working with ParseRuleContexts, and they are a range of
+                // tokens, for the second argument all tokens will be dropped with "" empty strings,
+                // and the last token will be dropped with "0)".
+                auto secondArgContext = vectorOfArguments[1];
+                const ssize_t startingTokenIndex = secondArgContext->start->getTokenIndex();
+                const ssize_t stoppingTokenIndex = secondArgContext->stop->getTokenIndex();
+                for (ssize_t tokenIndex = startingTokenIndex; tokenIndex < stoppingTokenIndex; ++tokenIndex)
+                {
+                    CodeMutation codeMutation;
+                    codeMutation.m_replace.emplace("");
+                    m_mutations.emplace(tokenIndex, codeMutation);
+                }
+                CodeMutation codeMutation;
+                codeMutation.m_replace.emplace("0)");
+                m_mutations.emplace(stoppingTokenIndex, codeMutation);
+            }
+        }
+    }
+
+    void Texture2DMSto2DCodeMutator::OnEnterGetSamplePosition(azslParser::FunctionCallExpressionContext* ctx)
+    {
+        // First we must capture the complete name of the symbol that called <Symbol>.GetSamplePosition(...)
+        const auto expressionCtx = ctx->expression();
+        const UnqualifiedName uqSymbolName = GetSymbolName(expressionCtx);
+        const TextureMSType textureMSType = GetMultiSampledTextureClass(uqSymbolName);
+        if (textureMSType == TextureMSType::None)
+        {
+            return;
+        }
+
+        // Because GetSamplePosition() doesn't exist for Texture2D/Texture2DArray, we will replace
+        // the whole expresion with float2(0, 0).
+        const ssize_t startingTokenIndex = ctx->start->getTokenIndex();
+        const ssize_t stoppingTokenIndex = ctx->stop->getTokenIndex();
+        for (ssize_t tokenIndex = startingTokenIndex; tokenIndex < stoppingTokenIndex; ++tokenIndex)
+        {
+            CodeMutation codeMutation;
+            codeMutation.m_replace.emplace("");
+            m_mutations.emplace(tokenIndex, codeMutation);
+        }
+        CodeMutation codeMutation;
+        codeMutation.m_replace.emplace("float2(0, 0)");
+        m_mutations.emplace(stoppingTokenIndex, codeMutation);
+    }
+
+    void Texture2DMSto2DCodeMutator::OnEnterGetDimensions(azslParser::FunctionCallExpressionContext* ctx)
+    {
+        const auto expressionCtx = ctx->expression();
+        const UnqualifiedName uqSymbolName = GetSymbolName(expressionCtx);
+        const TextureMSType textureMSType = GetMultiSampledTextureClass(uqSymbolName);
+        if (textureMSType == TextureMSType::None)
+        {
+            return;
+        }
+
+        // For Texture2DMS GetDimensions(...) only has one variant:
+        //      GetDimensions (width, height, samples)
+        // All we have to do for Texture2D is to drop ", samples" and add "; samples = 1" after the closing parenthesis. We'll get:
+        //      GetDimensions (width, height); samples = 1
+        // For Texture2DMSArray GetDimensions(...) only has one variant:
+        //      GetDimensions (width, height, elems, samples)
+        // All we have to do for Texture2DArray is to drop ", samples"  and add "; samples = 1" after the closing parenthesis. We'll get:
+        //      GetDimensions (width, height, elems); samples = 1
+        // Remark: The last ";" is already present in the original code, this is why we append "; samples = 1" instead
+        //         of "; samples = 1;"
+        const auto argumentListCtx = ctx->argumentList();
+        const auto argumentsCtx = argumentListCtx->arguments();
+
+        // From argumentsCtx we can detect the last "," token and we'll
+        // add it to the mutation as a replacement with "".
+        auto vectorOfCommas = argumentsCtx->Comma();
+        auto lastCommaIndex = vectorOfCommas.size() - 1;
+        auto lastCommaToken = vectorOfCommas[lastCommaIndex];
+        {
+            ssize_t tokenIndex = lastCommaToken->getSymbol()->getTokenIndex();
+            CodeMutation codeMutation;
+            codeMutation.m_replace.emplace("");
+            m_mutations.emplace(tokenIndex, codeMutation);
+        }
+
+        // Drop the last argument.
+        auto vectorOfArguments = argumentsCtx->expression();
+        auto lastArgumentIndex = vectorOfArguments.size() - 1;
+        auto lastArgumentCtx = vectorOfArguments[lastArgumentIndex];
+        // Capture the name of the variable that gets the number of samples because
+        // it will be assigned the value 1.
+        string lastArgumentName = lastArgumentCtx->getText();
+        {
+            const ssize_t startingTokenIndex = lastArgumentCtx->start->getTokenIndex();
+            const ssize_t stoppingTokenIndex = lastArgumentCtx->stop->getTokenIndex();
+            for (ssize_t tokenIndex = startingTokenIndex; tokenIndex <= stoppingTokenIndex; ++tokenIndex)
+            {
+                CodeMutation codeMutation;
+                codeMutation.m_replace.emplace("");
+                m_mutations.emplace(tokenIndex, codeMutation);
+            }
+        }
+
+        // Get the rule context for the closing right parenthesis ")".
+        // "; samples = 1" will be added after it.
+        const auto rightParenthesisNode = ctx->argumentList()->RightParen();
+        {
+            const ssize_t parenthesisTokenIndex = rightParenthesisNode->getSymbol()->getTokenIndex();
+            CodeMutation codeMutation;
+            string samplesExpression = AZ::FormatString("; %s = 1 ", lastArgumentName.c_str());
+            codeMutation.m_append.emplace(samplesExpression);
+            m_mutations.emplace(parenthesisTokenIndex, codeMutation);
+        }
+    }
+
+    size_t Texture2DMSto2DCodeMutator::MutateTypeOfMultiSampleVariables()
+    {
+        size_t mutationCount = 0;
+
+        // Get all variables that are members of something of type Texture2DMS
+        // We use this function pointer to find SRGs that have no references.
+        auto texture2DMSFilterFunc = +[](KindInfo* kindInfo) {
+            const auto* varInfo = kindInfo->GetSubAs<VarInfo>();
+            return varInfo->m_typeInfoExt.m_coreType.m_typeClass == TypeClass::MultisampledTexture;
+        };
+
+        vector<IdentifierUID> texture2DMSVariables = m_ir->GetFilteredSymbolsOfSubType<VarInfo>(texture2DMSFilterFunc);
+        for (const auto& uid : texture2DMSVariables)
+        {
+            auto varInfo = m_ir->GetSymbolSubAs<VarInfo>(uid.GetName());
+            auto& typeId = varInfo->m_typeInfoExt.m_coreType.m_typeId;
+            auto typeName = typeId.GetName();
+            if (typeName == "?Texture2DMS")
+            {
+                typeId.m_name = QualifiedName{ "?Texture2D" };
+            }
+            else
+            {
+                typeId.m_name = QualifiedName{ "?Texture2DArray" };
+            }
+            ++mutationCount;
+        }
+
+        return mutationCount;
+    }
+
+    void Texture2DMSto2DCodeMutator::MutateMultiSampleSystemSemantics()
+    {
+        //Let's find all variables that have a system semantic.
+        auto variablesWithSystemSemanticFilterFunc = +[](KindInfo* kindInfo) {
+            const auto* varInfo = kindInfo->GetSubAs<VarInfo>();
+            if (!varInfo->m_declNode)
+            {
+                return false;
+            }
+            auto* semanticOption = varInfo->m_declNode->SemanticOpt;
+            if (!semanticOption)
+            {
+                return false;
+            }
+            return semanticOption->hlslSemanticName()->HLSLSemanticSystem() != nullptr;
+        };
+
+        vector<IdentifierUID> systemSemanticVariables = m_ir->GetFilteredSymbolsOfSubType<VarInfo>(variablesWithSystemSemanticFilterFunc);
+        for (const auto& uid : systemSemanticVariables)
+        {
+            auto varInfo = m_ir->GetSymbolSubAs<VarInfo>(uid.GetName());
+
+            // Get the semantic name.
+            auto systemSemanticName = varInfo->m_declNode->SemanticOpt->hlslSemanticName()->HLSLSemanticSystem()->getText();
+
+            static const std::array<string_view, 2> SystemSemanticsNames =
+            {
+                "SV_SampleIndex",
+                "SV_Coverage",
+            };
+
+            if (!IsIn(systemSemanticName, SystemSemanticsNames))
+            {
+                continue;
+            }
+
+            //Semantics can be part of a struct, or function parameters.
+            if (ParamContextOverVariableDeclarator(varInfo->m_declNode))
+            {
+                // This is a function parameter.
+                IdentifierUID functionUid = IdentifierUID{ GetParentName(uid.GetName()) };
+                DropMultiSamplingSystemSemanticFromFunction(uid, varInfo, systemSemanticName, functionUid);
+            }
+            else
+            {
+                // This is a variable within a struct
+                IdentifierUID structUid = IdentifierUID{ GetParentName(uid.GetName()) };
+                MutateMultiSamplingSystemSemanticInStruct(uid, varInfo, systemSemanticName, structUid);
+            }
+        }
+    }
+
+    //! A helper method that figures out how a function argument should look like
+    //! when mutated into a local variable.
+    static string GetLocalVariableStringFromFunctionArgument(const UnqualifiedName& uqName, AstUnnamedVarDecl* ctx, const char * initializationValue)
+    {
+        azslParser::FunctionParamContext* paramCtx = nullptr;
+        auto typeCtx = ExtractTypeFromVariableDeclarator(ctx, &paramCtx);
+        auto variableTypeStr = typeCtx->getText();
+        if (initializationValue)
+        {
+            return FormatString("%s %s = (%s)%s;\n", variableTypeStr.c_str(), uqName.c_str(), variableTypeStr.c_str(), initializationValue);
+        }
+        return FormatString("%s %s;\n", variableTypeStr.c_str(), uqName.c_str());
+    }
+
+    void Texture2DMSto2DCodeMutator::DropMultiSamplingSystemSemanticFromFunction(const IdentifierUID& varUid, const VarInfo* varInfo, const string& systemSemanticName, const IdentifierUID& functionUid)
+    {
+        // Let's get the FunctionInfo object and report this variable, which will be dropped from the
+        // input arguments and will be re-emitted as a local variable to avoid compiler errors from other
+        // pieces of code that may reference the semantic.
+        // Example:
+        // PSOutput mainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex)
+        // {
+        //     ...
+        //     int2 sampleIndexVector = int2(sampleIndex, sampleIndex);
+        //     ...
+        // }
+        // Will look like this when emitted (Which will avoid compilation errors)
+        // PSOutput mainPS(VSOutput IN)
+        // {
+        //     uint sampleIndex = 0;
+        //     ...
+        //     int2 sampleIndexVector = int2(sampleIndex, sampleIndex);
+        //     ...
+        // }
+        FunctionInfo* functionInfo = m_ir->GetSymbolSubAs<FunctionInfo>(functionUid.GetName());
+        functionInfo->DeleteParameter(varUid);
+
+        string initializationValue = "0";
+        if (systemSemanticName == "SV_Coverage")
+        {
+            // SV_Coverage is a mask where each bit represents active sample indices.
+            // In this case we initialize to -1, because bitwise it will look like as if
+            // all samples are active.
+            // Usually code that that uses SV_Coverage loops over this mask (limited by the number of samples,
+            // which will be 1 for no-MS) for each sampleIndex.
+            // By settings to -1 it will mimic full coverage and the rendering logic will
+            // work seamlessly. It could be set to "1", but "-1" would cover all cases.
+            initializationValue = "-1";
+        }
+
+        auto newCode = GetLocalVariableStringFromFunctionArgument(varUid.GetNameLeaf(), varInfo->m_declNode, initializationValue.c_str());
+
+        // The idea is to find the TokenIndex of the opening bracket "{",
+        // Once we know that TokenIndex we can add code mutation as an appended
+        // string.
+        auto hlslFunctionDefinitionContext = ExtractSpecificParent<azslParser::HlslFunctionDefinitionContext>(functionInfo->m_defNode);
+        auto blockContext = hlslFunctionDefinitionContext->block();
+        auto leftBraceTokenIndex = blockContext->LeftBrace()->getSymbol()->getTokenIndex();
+        auto itor = m_mutations.find(leftBraceTokenIndex);
+        if (itor == m_mutations.end())
+        {
+            CodeMutation mutation;
+            mutation.m_append.emplace(newCode);
+            m_mutations.emplace(leftBraceTokenIndex, mutation);
+        }
+        else
+        {
+            CodeMutation& mutation = itor->second;
+            string prevCode = mutation.m_append.value();
+            mutation.m_append.emplace(prevCode + newCode);
+        }
+    }
+
+    void Texture2DMSto2DCodeMutator::MutateMultiSamplingSystemSemanticInStruct(const IdentifierUID& varUid, const VarInfo* varInfo, const string& systemSemanticName, const IdentifierUID& structUid)
+    {
+        // This is the case of member variable of a struct, but it is a system semantic.
+        // Example:
+        // struct VSOutput
+        // {
+        //     float4 m_position : SV_Position;
+        //     float2 m_texCoord : TEXCOORD0;
+        //     uint   m_sampleIndex : SV_SampleIndex;  <--- This is the variable in question.
+        // };
+        // Will look like this when emitted (Which will avoid compilation errors)
+        // PSOutput mainPS(VSOutput IN)
+        // {
+        //     float4 m_position : SV_Position;
+        //     float2 m_texCoord : TEXCOORD0;
+        //     static const uint   m_sampleIndex = 0; <-- Became a static const, and of course, the semantic is removed.
+        //     ...
+        // }
+        string initializationValue = "0";
+        if (systemSemanticName == "SV_Coverage")
+        {
+            initializationValue = "-1";
+        }
+        auto newCode = GetLocalVariableStringFromFunctionArgument(varUid.GetNameLeaf(), varInfo->m_declNode, initializationValue.c_str());
+        auto tokenIndex = varInfo->m_declNode->start->getTokenIndex();
+        CodeMutation mutation;
+        mutation.m_prepend.emplace("static const ");
+        mutation.m_replace.emplace(newCode);
+        m_mutations.emplace(tokenIndex, mutation);
+    }
+
+} //namespace AZ::ShaderCompiler

+ 99 - 0
src/Texture2DMSto2DCodeMutator.h

@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include "GenericUtils.h"
+#include "AzslcUtils.h"
+#include "AzslcCodeEmissionMutator.h"
+#include "AzslcIntermediateRepresentation.h"
+
+namespace AZ::ShaderCompiler
+{
+    //! This is the main class that handles coversion of MultiSampling related
+    //! variables, function calls and system semantics to their non-MultiSampling
+    //! versions.
+    //! 
+    //! This class is only active if --no-ms compiler flag is specified. Only if the --no-ms
+    //! compiler flag is active then the following events are true:
+    //!     - As a first step, it implements azslParserBaseListener::enterFunctionCallExpression() so
+    //!       the SemaCheckListener can invoke it.
+    //!     - After AST parsing is done, RunMiddleEndMutations() is called at the end of IntermediateRepresentation::MiddleEnd() step to
+    //!      calculate further code mutations.
+    //!     - During code emission, the CodeEmitter invokes the ICodeEmissionMutator interface to see if token
+    //!       variables or lines of codes have mutations available.
+    struct Texture2DMSto2DCodeMutator 
+        : public azslParserBaseListener
+        , public ICodeEmissionMutator
+    {
+        Texture2DMSto2DCodeMutator() = delete;
+        explicit Texture2DMSto2DCodeMutator(IntermediateRepresentation* ir) : m_ir(ir) {}
+        virtual ~Texture2DMSto2DCodeMutator() = default;
+
+        ///////////////////////////////////////////////////////////////////////
+        // azslParserBaseListener Overrides ...
+        void enterFunctionCallExpression(azslParser::FunctionCallExpressionContext* ctx) override;
+        ///////////////////////////////////////////////////////////////////////
+
+        ///////////////////////////////////////////////////////////////////////
+        // ICodeEmissionMutator Overrides ...
+        const CodeMutation* GetMutation(ssize_t tokenId) const override;
+        ///////////////////////////////////////////////////////////////////////
+
+        //! Should be called at the end of the IntermediateRepresentation::MiddleEnd() transformations.
+        void RunMiddleEndMutations();
+
+    private:
+        //! Called when parsing a function call expression of type
+        //! Texture2DMS.Load(...)
+        void OnEnterLoad(azslParser::FunctionCallExpressionContext* ctx);
+        
+        //! Called when parsing a function call expression of type
+        //! Texture2DMS.GetSamplePosition(...)
+        void OnEnterGetSamplePosition(azslParser::FunctionCallExpressionContext* ctx);
+
+        //! Called when parsing a function call expression of type
+        //! Texture2DMS.GetDimensions(...)
+        void OnEnterGetDimensions(azslParser::FunctionCallExpressionContext* ctx);
+
+        //! Changes the variable types:
+        //!     Texture2DMS to Texture2D.
+        //!     Texture2DMSArray to Texture2DArray. 
+        //! Returns the number of variables whose type was mutated
+        size_t MutateTypeOfMultiSampleVariables();
+
+        //! Mutates or Drops system semantics from struct definitions or
+        //! input arguments in entry point shader functions.
+        //! See Texture2DMSto2DCodeMutator.cpp for mutation examples.
+        void MutateMultiSampleSystemSemantics();
+
+        //! Called when a function argument, in a function definition, is a variable qualified
+        //! with the system semantics SV_SampleIndex or SV_Coverage. When converting to no-MultiSampling
+        //! the variable must be removed from the input arguments of the function.
+        //! See Texture2DMSto2DCodeMutator.cpp for mutation examples.
+        void DropMultiSamplingSystemSemanticFromFunction(const IdentifierUID& varUid, const VarInfo* varInfo, const string& systemSemanticName, const IdentifierUID& functionUid);
+
+        //! Called when a member variable, in a struct definition, is a variable qualified
+        //! with the system semantics SV_SampleIndex or SV_Coverage. When converting to no-MultiSampling
+        //! the variable must be mutated into an initialized "static const" of the same type as
+        //! the semantic.
+        //! See Texture2DMSto2DCodeMutator.cpp for mutation examples. 
+        void MutateMultiSamplingSystemSemanticInStruct(const IdentifierUID& varUid, const VarInfo* varInfo, const string& systemSemanticName, const IdentifierUID& structUid);
+
+        //! Given an unqualified symbol name, checks within the current parsing scope
+        //! if the symbol is a MultiSampling type of variable. 
+        enum class TextureMSType { None, Texture2DMS, Texture2DMSArray };
+        TextureMSType GetMultiSampledTextureClass(const UnqualifiedName& uqSymbolName);
+
+        //! Cached when RunMiddleEndMutations is called.
+        IntermediateRepresentation* m_ir = nullptr;
+
+        //! A map of TokenIndex to Mutation. If a TokenIndex is present,
+        //! it means it should produce mutated text during emission.
+        unordered_map< ssize_t, CodeMutation > m_mutations;
+    };
+} // namespace AZ::ShaderCompiler

+ 558 - 0
src/azslLexer.g4

@@ -0,0 +1,558 @@
+/*
+Work from Tim Jones
+MIT license
+-
+modifications by Amazon. C 2018
+ */
+lexer grammar azslLexer;
+
+channels { PREPROCESSOR }
+
+AppendStructuredBuffer : 'AppendStructuredBuffer';
+Bool : 'bool';
+Bool1 : 'bool1';
+Bool2 : 'bool2';
+Bool3 : 'bool3';
+Bool4 : 'bool4';
+Bool1x1 : 'bool1x1';
+Bool1x2 : 'bool1x2';
+Bool1x3 : 'bool1x3';
+Bool1x4 : 'bool1x4';
+Bool2x1 : 'bool2x1';
+Bool2x2 : 'bool2x2';
+Bool2x3 : 'bool2x3';
+Bool2x4 : 'bool2x4';
+Bool3x1 : 'bool3x1';
+Bool3x2 : 'bool3x2';
+Bool3x3 : 'bool3x3';
+Bool3x4 : 'bool3x4';
+Bool4x1 : 'bool4x1';
+Bool4x2 : 'bool4x2';
+Bool4x3 : 'bool4x3';
+Bool4x4 : 'bool4x4';
+Buffer : 'Buffer';
+BuiltInTriangleIntersectionAttributes : 'BuiltInTriangleIntersectionAttributes';
+ByteAddressBuffer : 'ByteAddressBuffer';
+Break : 'break';
+Case : 'case';
+CBuffer : 'cbuffer';
+ConstantBuffer : 'constantbuffer';
+ConstantBufferCamel : 'ConstantBuffer';
+Centroid : 'centroid';
+Class : 'class';
+ColumnMajor : 'column_major';
+Const : 'const';
+ConsumeStructuredBuffer : 'ConsumeStructuredBuffer';
+Continue : 'continue';
+Default : 'default';
+Discard : 'discard';
+Do : 'do';
+Double : 'double';
+Double1 : 'double1';
+Double2 : 'double2';
+Double3 : 'double3';
+Double4 : 'double4';
+Double1x1 : 'double1x1';
+Double1x2 : 'double1x2';
+Double1x3 : 'double1x3';
+Double1x4 : 'double1x4';
+Double2x1 : 'double2x1';
+Double2x2 : 'double2x2';
+Double2x3 : 'double2x3';
+Double2x4 : 'double2x4';
+Double3x1 : 'double3x1';
+Double3x2 : 'double3x2';
+Double3x3 : 'double3x3';
+Double3x4 : 'double3x4';
+Double4x1 : 'double4x1';
+Double4x2 : 'double4x2';
+Double4x3 : 'double4x3';
+Double4x4 : 'double4x4';
+Else : 'else';
+Enum : 'enum';
+Extern : 'extern';
+FeedbackTexture2D : 'FeedbackTexture2D';
+FeedbackTexture2DArray : 'FeedbackTexture2DArray';
+Float : 'float';
+Float1 : 'float1';
+Float2 : 'float2';
+Float3 : 'float3';
+Float4 : 'float4';
+Float1x1 : 'float1x1';
+Float1x2 : 'float1x2';
+Float1x3 : 'float1x3';
+Float1x4 : 'float1x4';
+Float2x1 : 'float2x1';
+Float2x2 : 'float2x2';
+Float2x3 : 'float2x3';
+Float2x4 : 'float2x4';
+Float3x1 : 'float3x1';
+Float3x2 : 'float3x2';
+Float3x3 : 'float3x3';
+Float3x4 : 'float3x4';
+Float4x1 : 'float4x1';
+Float4x2 : 'float4x2';
+Float4x3 : 'float4x3';
+Float4x4 : 'float4x4';
+For : 'for';
+Groupshared : 'groupshared';
+Global: 'global';
+Half : 'half';
+Half1 : 'half1';
+Half2 : 'half2';
+Half3 : 'half3';
+Half4 : 'half4';
+Half1x1 : 'half1x1';
+Half1x2 : 'half1x2';
+Half1x3 : 'half1x3';
+Half1x4 : 'half1x4';
+Half2x1 : 'half2x1';
+Half2x2 : 'half2x2';
+Half2x3 : 'half2x3';
+Half2x4 : 'half2x4';
+Half3x1 : 'half3x1';
+Half3x2 : 'half3x2';
+Half3x3 : 'half3x3';
+Half3x4 : 'half3x4';
+Half4x1 : 'half4x1';
+Half4x2 : 'half4x2';
+Half4x3 : 'half4x3';
+Half4x4 : 'half4x4';
+If : 'if';
+In : 'in';
+Inline : 'inline';
+Rootconstant : 'rootconstant';   // Amazon extension
+Inout : 'inout' | 'in out';
+InputPatch : 'InputPatch';
+Int : 'int';
+Int1 : 'int1';
+Int2 : 'int2';
+Int3 : 'int3';
+Int4 : 'int4';
+Int1x1 : 'int1x1';
+Int1x2 : 'int1x2';
+Int1x3 : 'int1x3';
+Int1x4 : 'int1x4';
+Int2x1 : 'int2x1';
+Int2x2 : 'int2x2';
+Int2x3 : 'int2x3';
+Int2x4 : 'int2x4';
+Int3x1 : 'int3x1';
+Int3x2 : 'int3x2';
+Int3x3 : 'int3x3';
+Int3x4 : 'int3x4';
+Int4x1 : 'int4x1';
+Int4x2 : 'int4x2';
+Int4x3 : 'int4x3';
+Int4x4 : 'int4x4';
+Interface : 'interface';
+Line_ : 'line';
+LineAdj : 'lineadj';
+Linear : 'linear';
+LineStream : 'LineStream';
+Long : 'long';
+Matrix : 'matrix';
+Nointerpolation : 'nointerpolation';
+Noperspective : 'noperspective';
+Option : 'option';
+Out : 'out';
+OutputPatch : 'OutputPatch';
+Override : 'override';   // Amazon extension
+Partial : 'partial';   // Amazon extension
+Packoffset : 'packoffset';
+Point : 'point';
+PointStream : 'PointStream';
+Precise : 'precise';
+RasterizerOrderedBuffer : 'RasterizerOrderedBuffer';
+RasterizerOrderedByteAddressBuffer : 'RasterizerOrderedByteAddressBuffer';
+RasterizerOrderedStructuredBuffer : 'RasterizerOrderedStructuredBuffer';
+RasterizerOrderedTexture1D : 'RasterizerOrderedTexture1D';
+RasterizerOrderedTexture1DArray : 'RasterizerOrderedTexture1DArray';
+RasterizerOrderedTexture2D : 'RasterizerOrderedTexture2D';
+RasterizerOrderedTexture2DArray : 'RasterizerOrderedTexture2DArray';
+RasterizerOrderedTexture3D : 'RasterizerOrderedTexture3D';
+RayDesc : 'RayDesc';
+RaytracingAccelerationStructure : 'RaytracingAccelerationStructure';
+Register : 'register';
+Return : 'return';
+RowMajor : 'row_major';
+RWBuffer : 'RWBuffer';
+RWByteAddressBuffer : 'RWByteAddressBuffer';
+RWStructuredBuffer : 'RWStructuredBuffer';
+RWTexture1D : 'RWTexture1D';
+RWTexture1DArray : 'RWTexture1DArray';
+RWTexture2D : 'RWTexture2D';
+RWTexture2DArray : 'RWTexture2DArray';
+RWTexture3D : 'RWTexture3D';
+Sample : 'sample';
+Sampler : 'sampler';
+SamplerCapitalS : 'Sampler';
+SamplerComparisonState : 'SamplerComparisonState';
+SamplerState : 'SamplerState';
+Shared : 'shared';
+Static : 'static';
+Struct : 'struct';
+StructuredBuffer : 'StructuredBuffer';
+SubpassInput : 'SubpassInput';
+SubpassInputMS : 'SubpassInputMS';
+Switch : 'switch';
+Texture1D : 'Texture1D';
+Texture1DArray : 'Texture1DArray';
+Texture2D : 'Texture2D';
+Texture2DArray : 'Texture2DArray';
+Texture2DMS : 'Texture2DMS';
+Texture2DMSArray : 'Texture2DMSArray';
+Texture3D : 'Texture3D';
+TextureCube : 'TextureCube';
+TextureCubeArray : 'TextureCubeArray';
+Triangle : 'triangle';
+TriangleAdj : 'triangleadj';
+TriangleStream : 'TriangleStream';
+Uniform : 'uniform';
+// For the --listpredefined option to work, we absolutely need all types to exist in (simple) rules
+// that can return strings from Vocabulary::getLiteralName()
+// so that rule: Uint : 'uint' | 'unsigned int' | 'dword' # can't be used because it's non-trivialness makes it a non-citizen.
+// I tried to recompose the rule with fragments, but fragment also are not listed in the vocabulary. (_literalNames)
+Uint : 'uint';
+UnsignedInt : 'unsigned int';
+Uint1 : 'uint1';
+Uint2 : 'uint2';
+Uint3 : 'uint3';
+Uint4 : 'uint4';
+Uint1x1 : 'uint1x1';
+Uint1x2 : 'uint1x2';
+Uint1x3 : 'uint1x3';
+Uint1x4 : 'uint1x4';
+Uint2x1 : 'uint2x1';
+Uint2x2 : 'uint2x2';
+Uint2x3 : 'uint2x3';
+Uint2x4 : 'uint2x4';
+Uint3x1 : 'uint3x1';
+Uint3x2 : 'uint3x2';
+Uint3x3 : 'uint3x3';
+Uint3x4 : 'uint3x4';
+Uint4x1 : 'uint4x1';
+Uint4x2 : 'uint4x2';
+Uint4x3 : 'uint4x3';
+Uint4x4 : 'uint4x4';
+Dword : 'dword';
+// Amazon addition: DXC didn't advertise it, but the vector/matrix forms of dword are now accepted
+Dword1 : 'dword1';
+Dword2 : 'dword2';
+Dword3 : 'dword3';
+Dword4 : 'dword4';
+Dword1x1 : 'dword1x1';
+Dword1x2 : 'dword1x2';
+Dword1x3 : 'dword1x3';
+Dword1x4 : 'dword1x4';
+Dword2x1 : 'dword2x1';
+Dword2x2 : 'dword2x2';
+Dword2x3 : 'dword2x3';
+Dword2x4 : 'dword2x4';
+Dword3x1 : 'dword3x1';
+Dword3x2 : 'dword3x2';
+Dword3x3 : 'dword3x3';
+Dword3x4 : 'dword3x4';
+Dword4x1 : 'dword4x1';
+Dword4x2 : 'dword4x2';
+Dword4x3 : 'dword4x3';
+Dword4x4 : 'dword4x4';
+Vector : 'vector';
+Volatile : 'volatile';
+Void : 'void';
+While : 'while';
+
+// libray subobject types from modern HLSL:
+StateObjectConfig : 'StateObjectConfig';
+LocalRootSignature : 'LocalRootSignature';
+GlobalRootSignature : 'GlobalRootSignature';
+SubobjectToExportsAssociation : 'SubobjectToExportsAssociation';
+RaytracingShaderConfig : 'RaytracingShaderConfig';
+RaytracingPipelineConfig : 'RaytracingPipelineConfig';
+RaytracingPipelineConfig1 : 'RaytracingPipelineConfig1';
+TriangleHitGroup : 'TriangleHitGroup';
+ProceduralPrimitiveHitGroup : 'ProceduralPrimitiveHitGroup';
+
+// AZSL: Allow Sampler states to be defined in the AZSL code as part of the SRG definition
+ADDRESS_U : 'AddressU';
+ADDRESS_V : 'AddressV';
+ADDRESS_W : 'AddressW';
+BORDER_COLOR : 'BorderColor';
+MIN_FILTER : 'MinFilter';
+MAG_FILTER : 'MagFilter';
+MIP_FILTER : 'MipFilter';
+MAX_ANISOTROPY : 'MaxAnisotropy';
+MAX_LOD : 'MaxLOD';
+MIN_LOD : 'MinLOD';
+MIP_LOD_BIAS : 'MipLODBias';
+COMPARISON_FUNC : 'ComparisonFunc';
+REDUCTION_TYPE : 'ReductionType';
+
+FILTER_MODE_POINT : 'Point';
+FILTER_MODE_LINEAR : 'Linear';
+
+REDUCTION_TYPE_FILTER : 'Filter';
+REDUCTION_TYPE_COMPARISON : 'Comparison';
+REDUCTION_TYPE_MINIMUM : 'Minimum';
+REDUCTION_TYPE_MAXIMUM : 'Maximum';
+
+ADDRESS_MODE_WRAP : 'Wrap';
+ADDRESS_MODE_MIRROR : 'Mirror';
+ADDRESS_MODE_CLAMP : 'Clamp';
+ADDRESS_MODE_BORDER : 'Border';
+ADDRESS_MODE_MIRROR_ONCE : 'MirrorOnce';
+
+COMPARISON_FUNCTION_NEVER : 'Never';
+COMPARISON_FUNCTION_LESS : 'Less';
+COMPARISON_FUNCTION_EQUAL : 'Equal';
+COMPARISON_FUNCTION_LESS_EQUAL : 'LessEqual';
+COMPARISON_FUNCTION_GREATER : 'Greater';
+COMPARISON_FUNCTION_NOT_EQUAL : 'NotEqual';
+COMPARISON_FUNCTION_GREATER_EQUAL : 'GreaterEqual';
+COMPARISON_FUNCTION_ALWAYS : 'Always';
+
+BORDER_COLOR_OPAQUE_BLACK : 'OpaqueBlack';
+BORDER_COLOR_TRANSPARENT_BLACK : 'TransparentBlack';
+BORDER_COLOR_OPAQUE_WHITE : 'OpaqueWhite';
+
+LeftParen : '(';
+RightParen : ')';
+LeftBracket : '[';
+RightBracket : ']';
+LeftBrace : '{';
+RightBrace : '}';
+LeftDoubleBracket : '[[';
+
+Less : '<';
+LessEqual : '<=';
+Greater : '>';
+GreaterEqual : '>=';
+LeftShift : '<<';
+RightShift : '>>';
+
+Plus : '+';
+PlusPlus : '++';
+Minus : '-';
+MinusMinus : '--';
+Star : '*';
+Div : '/';
+Mod : '%';
+
+And : '&';
+Or : '|';
+AndAnd : '&&';
+OrOr : '||';
+Caret : '^';
+Not : '!';
+Tilde : '~';
+
+Question : '?';
+Colon : ':';
+ColonColon : '::';
+Semi : ';';
+Comma : ',';
+Assign : '=';
+// '*=' | '/=' | '%=' | '+=' | '-=' | '<<=' | '>>=' | '&=' | '^=' | '|='
+StarAssign : '*=';
+DivAssign : '/=';
+ModAssign : '%=';
+PlusAssign : '+=';
+MinusAssign : '-=';
+LeftShiftAssign : '<<=';
+RightShiftAssign : '>>=';
+AndAssign : '&=';
+XorAssign : '^=';
+OrAssign : '|=';
+
+Equal : '==';
+NotEqual : '!=';
+
+Dot : '.';
+
+True : 'true';
+False : 'false';
+
+// AMAZON
+KW_AssociatedType : 'associatedtype' ;
+KW_TypeAlias : 'typealias' ;
+KW_Typedef : 'typedef' ;
+KW_Fundamental : 'fundamental' ; // [GFX TODO]
+KW_Typeof : 'typeof' ;  // simple decltype-like operator
+
+// AZSLc extensions
+KW_ext_print_message : '__azslc_print_message' ;
+KW_ext_print_symbol : '__azslc_print_symbol' ;
+KW_ext_prtsym_fully_qualified : '__azslc_prtsym_fully_qualified' ;
+KW_ext_prtsym_least_qualified : '__azslc_prtsym_least_qualified' ;
+KW_ext_prtsym_constint_value : '__azslc_prtsym_constint_value' ;
+
+// AZSL SRG
+FrequencyId : 'FrequencyId';
+ShaderVariantFallback : 'ShaderVariantFallback';
+ShaderResourceGroupSemantic : 'ShaderResourceGroupSemantic';
+ShaderResourceGroup : 'ShaderResourceGroup';
+
+HLSLSemanticStream:
+    'BINORMAL'      Digit*
+  | 'BLENDINDICES'  Digit*
+  | 'BLENDWEIGHT'   Digit*
+  | 'COLOR'         Digit*
+  | 'NORMAL'        Digit*
+  | 'POSITION'      Digit*
+  | 'POSITIONT'
+  | 'PSIZE'         Digit*
+  | 'TANGENT'       Digit*
+  | 'TEXCOORD'      Digit*
+  | 'FOG'
+  | 'TESSFACTOR'    Digit*
+  | 'TEXCOORD'      Digit*
+  | 'VFACE'
+  | 'VPOS'          Digit*
+  | 'DEPTH'         Digit*
+;
+
+HLSLSemanticSystem:
+    'SV_'       (Nondigit | Digit)*
+  | 'Sv_'       (Nondigit | Digit)*
+  | 'sV_'       (Nondigit | Digit)*
+  | 'sv_'       (Nondigit | Digit)*
+;
+
+Identifier
+    :   Nondigit (Nondigit | Digit)*
+    ;
+
+fragment
+Nondigit
+    :   [a-zA-Z_]
+    ;
+
+fragment
+Digit
+    :   [0-9]
+    ;
+
+fragment
+DecimalOrOctalIntegerLiteral
+	:   Digit+
+    ;
+
+fragment
+HexadecimalIntegerLiteral
+    :   ('0x' | '0X') HexadecimalDigit+
+    ;
+
+fragment
+HexadecimalDigit
+    :   [0-9a-fA-F]
+    ;
+
+fragment
+FractionalConstant
+    :   DigitSequence? '.' DigitSequence
+    |   DigitSequence '.'
+    ;
+
+fragment
+ExponentPart
+    :   'e' Sign? DigitSequence
+    |   'E' Sign? DigitSequence
+    ;
+
+fragment
+Sign
+    :   '+' | '-'
+    ;
+
+fragment
+DigitSequence
+    :   Digit+
+    ;
+
+fragment
+HexadecimalDigitSequence
+    :   HexadecimalDigit+
+    ;
+
+fragment
+IntegerSuffix
+    :   [uU]?[lL]?
+    |   [lL]?[uU]?
+    ;
+
+IntegerLiteral
+    :   DecimalOrOctalIntegerLiteral IntegerSuffix?
+    |   HexadecimalIntegerLiteral IntegerSuffix?
+    ;
+
+// https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx-graphics-hlsl-appendix-grammar#floating-point-numbers
+fragment
+FloatingSuffix
+    :   [hHflFL]
+    ;
+
+FloatLiteral
+    :   FractionalConstant ExponentPart? FloatingSuffix?
+    |   DigitSequence ExponentPart FloatingSuffix?
+    ;
+
+fragment
+EscapeSequence
+    :   SimpleEscapeSequence
+    ;
+
+fragment
+SimpleEscapeSequence
+    :   '\\' ['"?abfnrtv\\]
+    ;
+
+StringLiteral
+    :   '"' SCharSequence? '"'
+    ;
+
+fragment
+SCharSequence
+    :   SChar+
+    ;
+
+fragment
+SChar
+    :   ~["\\\r\n]
+    |   EscapeSequence
+    ;
+
+PragmaDirective
+    :   '#' Whitespace? 'pragma' Whitespace ~[\r\n]*
+        -> channel(PREPROCESSOR)
+    ;
+
+// valid line directives examples:
+// #1
+// # 1
+// # 1 "file"
+// #line 1
+// #line 1 "file"
+// # line 1 "file" 2  // comment
+LineDirective
+    :   '#' Whitespace? ('line' Whitespace)? IntegerLiteral Whitespace? StringLiteral?
+        -> channel(PREPROCESSOR)
+    ;
+
+Whitespace
+    :   [ \t]+ -> skip
+    ;
+
+Newline
+    :   (   '\r' '\n'?
+        |   '\n'
+        )  -> skip
+    ;
+
+// Amazon: The original mode switches from Tim Jones were not working.
+BlockComment
+    :   '/*' .*? '*/' -> channel(HIDDEN)
+    ;
+
+LineComment
+    :   '//' ~[\r\n]* -> channel(HIDDEN)
+    ;

+ 945 - 0
src/azslParser.g4

@@ -0,0 +1,945 @@
+/*
+Original grammar by Tim Jones
+MIT license
+-
+Edits by Amazon, C 2018
+*/
+
+parser grammar azslParser;
+
+options
+   { tokenVocab = azslLexer; }
+
+// entry rule
+compilationUnit:
+    Declarations+=topLevelDeclaration* EOF
+;
+
+topLevelDeclaration:
+        anyStructuredTypeDefinitionStatement
+    |   variableDeclarationStatement
+    |   attributedFunctionDefinition
+    |   attributedFunctionDeclaration
+    |   attributeSpecifierSequence
+    |   compilerExtensionStatement      // AZSLc specific
+    |   typeAliasingDefinitionStatement // AZSLc specific
+    |   attributedSrgDefinition         // AZSLc specific
+    |   attributedSrgSemantic           // AZSLc specific
+    |   Semi
+;
+
+// Amazon: AZSL has scopes, and identifiers can be qualified
+idExpression:
+        unqualifiedId   // stricly unqualified (no nested specifiers at all)
+    |   qualifiedId     // could be relatively qualified OR fully qualified.
+;
+
+unqualifiedId:
+    Identifier
+;
+
+qualifiedId:
+    nestedNameSpecifier unqualifiedId
+;
+
+nestedNameSpecifier:
+    GlobalSROToken='::'? (Identifier '::')*
+;
+
+classDefinitionStatement:
+    classDefinition Semi
+;
+
+classDefinition:
+    Class Name=Identifier BaseListOpt=baseList?
+    LeftBrace classMemberDeclaration* RightBrace
+;
+
+baseList:
+    Colon idExpression (Comma idExpression)*
+;
+
+classMemberDeclaration:
+        variableDeclarationStatement
+    |   attributedFunctionDefinition
+    |   attributedFunctionDeclaration
+    |   typeAliasingDefinitionStatement  // AZSL specificity
+    |   anyStructuredTypeDefinitionStatement   // Amazon extension (DXC supports it)
+;
+
+structDefinitionStatement:
+    structDefinition Semi
+;
+
+structDefinition:
+    Struct Name=Identifier
+    LeftBrace structMemberDeclaration* RightBrace
+;
+
+structMemberDeclaration:
+        variableDeclarationStatement
+    |   anyStructuredTypeDefinitionStatement  // AZSL+
+    |   typeAliasingDefinitionStatement // AZSL+
+;
+
+anyStructuredTypeDefinitionStatement:
+    attributeSpecifierAny* anyStructuredTypeDefinition Semi
+;
+
+enumDefinitionStatement:
+    enumDefinition Semi
+;
+
+enumDefinition:
+    enumKey Name=Identifier
+    LeftBrace List=enumeratorListDefinition? RightBrace
+;
+
+enumKey:
+        Enum                                # UnscopedEnum
+    |   Enum (Class|Struct)                 # ScopedEnum
+;
+
+enumeratorListDefinition:
+    Enumerators+=enumeratorDeclarator (',' Enumerators+=enumeratorDeclarator)* ','?
+;
+
+enumeratorDeclarator:
+    Name=Identifier ('=' Value=expression)?
+;
+
+// AZSL+
+anyStructuredTypeDefinition:
+        classDefinition
+    |   interfaceDefinition
+    |   structDefinition
+    |   enumDefinition
+;
+
+interfaceDefinitionStatement:
+    interfaceDefinition Semi;
+
+interfaceDefinition:
+    Interface Name=Identifier
+    LeftBrace interfaceMemberDeclaration* RightBrace
+;
+
+interfaceMemberDeclaration:
+        attributedFunctionDeclaration
+    |   associatedTypeDeclaration            // AZSL+
+    |   anyStructuredTypeDefinitionStatement  // AZSL+
+;
+
+constantBufferTemplated:
+    CBCoreType=(ConstantBuffer | ConstantBufferCamel) '<' GenericTypeName=type '>'
+;
+
+variableDeclarationStatement:
+    variableDeclaration Semi
+;
+
+functionParams:
+    Void | functionParam (Comma functionParam)*
+;
+
+functionParam:
+    attributeSpecifierAny* storageFlags type Name=Identifier? unnamedVariableDeclarator
+;
+
+hlslSemantic:
+    Colon Name=hlslSemanticName
+;
+
+hlslSemanticName:
+    HLSLSemanticStream         // Semantics related to shader stages streams
+  | HLSLSemanticSystem         // Semantics related to system provided attributes
+  | Identifier                 // Anything we might have missed, including new or platform-specific semantics
+;
+
+// --------------------------------------
+// ATTRIBUTES
+// --------------------------------------
+
+attributeArguments:
+    literal (Comma literal)*
+;
+
+attributeArgumentList:
+    LeftParen attributeArguments RightParen
+;
+
+attribute:
+    Global ColonColon (Namespace=Identifier ColonColon)? Name=Identifier attributeArgumentList?     # GlobalAttribute
+  | (Namespace=Identifier ColonColon)? Name=Identifier attributeArgumentList?                       # AttachedAttribute
+;
+
+attributeSpecifier:
+    LeftBracket attribute RightBracket
+;
+
+attributeSpecifierSequence:
+    LeftDoubleBracket  Attributes+=attribute (Comma Attributes+=attribute)* RightBracket RightBracket
+;
+
+attributeSpecifierAny:
+    attributeSpecifier
+  | attributeSpecifierSequence
+;
+
+// --------------------------------------
+// STATEMENTS
+// --------------------------------------
+
+block:
+    LeftBrace Stmts+=statement* RightBrace
+;
+
+statement:
+        variableDeclarationStatement
+    |   embeddedStatement
+    |   anyStructuredTypeDefinitionStatement
+;
+
+forInitializer:
+        variableDeclaration
+    |   expressionExt
+;
+
+switchLabel:
+        Case Expr=expression Colon # CaseSwitchLabel
+    |   Default Colon              # DefaultSwitchLabel
+;
+
+switchSection:
+    switchLabel+ statement+
+;
+
+switchBlock:
+    LeftBrace switchSection* RightBrace
+;
+
+embeddedStatement:
+        Semi # EmptyStatement
+    |   block # BlockStatement
+    |   Expr=expressionExt Semi # ExpressionStatement
+
+    // Selection statement
+    |   attributeSpecifier* If LeftParen Condition=expressionExt RightParen Stmt=embeddedStatement elseClause? # IfStatement
+    |   attributeSpecifier* Switch LeftParen Expr=expressionExt RightParen switchBlock # SwitchStatement
+
+    // Iteration statement
+    |   attributeSpecifier* While LeftParen condition=expressionExt RightParen embeddedStatement # WhileStatement
+    |   attributeSpecifier* Do embeddedStatement While LeftParen Condition=expressionExt RightParen Semi # DoStatement
+    |   attributeSpecifier* For LeftParen forInitializer? Semi Condition=expressionExt? Semi iterator=expressionExt? RightParen embeddedStatement # ForStatement
+
+    // Jump statement
+    |   Break Semi # BreakStatement
+    |   Continue Semi # ContinueStatement
+    |   Discard Semi # DiscardStatement
+    |   Return Expr=expressionExt? Semi # ReturnStatement
+
+    // AZSLc specific
+    |   compilerExtensionStatement # ExtenstionStatement
+    |   typeAliasingDefinitionStatement # TypeAliasingDefinitionStatementLabel
+;
+
+elseClause:
+    Else Stmt=embeddedStatement
+;
+
+// --------------------------------------
+// EXPRESSIONS
+// --------------------------------------
+expression:
+        literal                                                                                 # LiteralExpression
+    |   idExpression                                                                            # IdentifierExpression
+    |   LeftParen Expr=expressionExt RightParen                                                 # ParenthesizedExpression
+    |   LHSExpr=expression DotToken=Dot Member=idExpression                                     # MemberAccessExpression
+    |   Expr=expression LeftBracket Index=expression RightBracket                               # ArrayAccessExpression
+    |   Expr=expression argumentList                                                            # FunctionCallExpression
+    |   scalarOrVectorOrMatrixType argumentList                                                 # NumericConstructorExpression
+    |<assoc=right> LeftParen type (ArrayRankSpecifiers+=arrayRankSpecifier)* RightParen Expr=expression  # CastExpression
+    |   Expr=expression Operator=postfixUnaryOperator                                           # PostfixUnaryExpression
+    |<assoc=right> Operator=prefixUnaryOperator Expr=expression                                 # PrefixUnaryExpression
+    |   Left=expression Operator=binaryOperator Right=expression                                # BinaryExpression
+    |<assoc=right> Condition=expression Question TrueExpr=expressionExt Colon FalseExpr=expressionExt # ConditionalExpression  // ternary
+    |<assoc=right> Left=expression Operator=assignmentOperator Right=expressionExt              # AssignmentExpression
+;
+
+// comma-expr are problematic because the parser is greedy,
+// and it catches comma-expr situations in undesired places. like function arguments or enum declarations.
+// therefore we need to cut in two categories, to restrict these situations.
+// all expressions + comma-expressions
+expressionExt:
+        Expr=expression                                       # OtherExpression
+    |   Left=expressionExt Operator=Comma Right=expression    # CommaExpression
+;
+
+postfixUnaryOperator:
+        PlusPlus
+    |   MinusMinus
+;
+
+prefixUnaryOperator:
+        Plus
+    |   Minus
+    |   Not
+    |   Tilde
+    |   PlusPlus
+    |   MinusMinus
+;
+
+binaryOperator:
+        Star
+    |   Div
+    |   Mod
+    |   Plus
+    |   Minus
+    |   LeftShift
+    |   RightShift
+    |   Less
+    |   Greater
+    |   LessEqual
+    |   GreaterEqual
+    |   Equal
+    |   NotEqual
+    |   And
+    |   Caret
+    |   Or
+    |   AndAnd
+    |   OrOr
+;
+
+assignmentOperator:
+        Assign
+    |   StarAssign
+    |   DivAssign
+    |   ModAssign
+    |   PlusAssign
+    |   MinusAssign
+    |   LeftShiftAssign
+    |   RightShiftAssign
+    |   AndAssign
+    |   XorAssign
+    |   OrAssign
+;
+
+argumentList:
+    LeftParen arguments? RightParen
+;
+
+arguments:
+    expression (Comma expression)*
+;
+
+// --------------------------------------
+// TYPES
+// --------------------------------------
+
+// note that in FXC "int static" (that is `storageFlags type storageFlags`) wasn't valid.
+// but in DXC it's flexible like in C/C++, so we have a restriction compared to DXC here.
+variableDeclaration:
+    attributeSpecifierAny* storageFlags type variableDeclarators
+;
+
+variableDeclarators:
+    VarDecls+=namedVariableDeclarator (Comma VarDecls+=namedVariableDeclarator)*
+;
+
+unnamedVariableDeclarator:
+    (ArrayRankSpecifiers+=arrayRankSpecifier)*
+    SemanticOpt=hlslSemantic?
+    packOffsetNode?
+    RegisterAllocation=registerAllocation?
+    variableInitializer?
+;
+
+namedVariableDeclarator:
+    Name=Identifier
+    unnamedVariableDeclarator
+;
+
+variableInitializer:
+        '=' standardVariableInitializer
+    |   samplerBodyDeclaration
+;
+
+standardVariableInitializer:
+        '{' arrayElementInitializers '}'
+    |   Expr=expression
+;
+
+arrayElementInitializers:
+    standardVariableInitializer (Comma standardVariableInitializer)* Comma?
+;
+
+arrayRankSpecifier:
+    LeftBracket Dimension=expression? RightBracket
+;
+
+packOffsetNode:
+    Colon PackoffsetKeyword=Packoffset LeftParen
+    PackOffsetRegister=Identifier (Dot PackOffsetComponent=Identifier)?
+    RightParen
+;
+
+storageFlags:
+    storageFlag*
+;
+
+storageFlag:
+    // Type modifiers
+        Const
+    |   RowMajor
+    |   ColumnMajor
+    // Storage classes
+    |   Extern
+    |   Inline
+    |   Rootconstant
+    |   Option
+    |   Precise
+    |   Shared
+    |   Groupshared
+    |   Static
+    |   Uniform
+    |   Volatile
+    // Interpolation modifiers
+    |   Linear
+    |   Centroid
+    |   Nointerpolation
+    |   Noperspective
+    |   Sample
+    // Parameter modifiers (only valid on function params)
+    |   In
+    |   Out
+    |   Inout
+    // Geometry shader primitive type
+    |   Point
+    |   Line_
+    |   Triangle
+    |   LineAdj
+    |   TriangleAdj
+;
+
+type:
+        predefinedType
+    |   userDefinedType
+    |   typeofExpression
+;
+
+predefinedType:
+        bufferPredefinedType
+    |   byteAddressBufferTypes
+    |   patchPredefinedType
+    |   matrixType
+    |   genericMatrixPredefinedType
+    |   samplerStatePredefinedType
+    |   scalarType
+    |   streamOutputPredefinedType
+    |   structuredBufferPredefinedType
+    |   texturePredefinedType
+    |   genericTexturePredefinedType
+    |   msTexturePredefinedType
+    |   vectorType
+    |   genericVectorType
+    |   constantBufferTemplated   // needed here to get recognized as a predefined type
+    |   otherViewResourceType
+    |   subobjectType
+    |   rtxBuiltInTypes
+;
+
+subobjectType:
+        StateObjectConfig
+    |   LocalRootSignature
+    |   GlobalRootSignature
+    |   SubobjectToExportsAssociation
+    |   RaytracingShaderConfig
+    |   RaytracingPipelineConfig
+    |   RaytracingPipelineConfig1
+    |   TriangleHitGroup
+    |   ProceduralPrimitiveHitGroup
+;
+
+// Acceleration structures
+otherViewResourceType:
+    RaytracingAccelerationStructure
+;
+
+rtxBuiltInTypes:
+        BuiltInTriangleIntersectionAttributes
+    |   RayDesc
+;
+
+bufferPredefinedType:
+    bufferType Less scalarOrVectorOrMatrixType Greater
+;
+
+bufferType:
+        Buffer
+    |   RWBuffer
+    |   RasterizerOrderedBuffer
+;
+
+byteAddressBufferTypes:
+        ByteAddressBuffer
+    |   RWByteAddressBuffer
+    |   RasterizerOrderedByteAddressBuffer
+;
+
+patchPredefinedType:
+    patchType Less
+    Name=userDefinedType Comma ControlPoints=IntegerLiteral
+    Greater
+;
+
+patchType:
+        InputPatch
+    |   OutputPatch
+;
+
+samplerStatePredefinedType:
+        Sampler
+    |   SamplerCapitalS
+    |   SamplerState
+    |   SamplerComparisonState
+;
+
+scalarType:
+        Bool
+    |   Int
+    |   Uint
+    |   UnsignedInt
+    |   Dword
+    |   Half
+    |   Float
+    |   Double
+;
+
+streamOutputPredefinedType:
+    streamOutputObjectType Less type Greater
+;
+
+streamOutputObjectType:
+        PointStream
+    |   LineStream
+    |   TriangleStream
+;
+
+structuredBufferPredefinedType:
+    structuredBufferName Less type Greater
+;
+
+structuredBufferName:
+        AppendStructuredBuffer
+    |   ConsumeStructuredBuffer
+    |   RWStructuredBuffer
+    |   StructuredBuffer
+    |   RasterizerOrderedStructuredBuffer
+;
+
+textureType:
+        Texture1D
+    |   Texture1DArray
+    |   RasterizerOrderedTexture1D
+    |   RasterizerOrderedTexture1DArray
+    |   Texture2D
+    |   Texture2DArray
+    |   RasterizerOrderedTexture2D
+    |   RasterizerOrderedTexture2DArray
+    |   Texture3D
+    |   RasterizerOrderedTexture3D
+    |   TextureCube
+    |   TextureCubeArray
+    |   RWTexture1D
+    |   RWTexture1DArray
+    |   RWTexture2D
+    |   RWTexture2DArray
+    |   RWTexture3D
+    |   SubpassInput
+    |   SubpassInputMS
+;
+
+texturePredefinedType:
+    textureType
+;
+
+genericTexturePredefinedType:
+    textureType Less scalarOrVectorType Greater
+;
+
+textureTypeMS:
+        Texture2DMS
+    |   Texture2DMSArray
+;
+
+msTexturePredefinedType:
+    textureTypeMS Less scalarOrVectorType (Comma Samples=IntegerLiteral)? Greater
+;
+
+vectorType:
+        Vector
+    |   Bool1
+    |   Bool2
+    |   Bool3
+    |   Bool4
+    |   Int1
+    |   Int2
+    |   Int3
+    |   Int4
+    |   Uint1
+    |   Uint2
+    |   Uint3
+    |   Uint4
+    |   Dword1
+    |   Dword2
+    |   Dword3
+    |   Dword4
+    |   Half1
+    |   Half2
+    |   Half3
+    |   Half4
+    |   Float1
+    |   Float2
+    |   Float3
+    |   Float4
+    |   Double1
+    |   Double2
+    |   Double3
+    |   Double4
+;
+
+genericVectorType:
+    Vector Less scalarType Comma Size_=IntegerLiteral Greater
+;
+
+scalarOrVectorType:
+        scalarType
+    |   vectorType
+;
+
+scalarOrVectorOrMatrixType:
+        scalarType
+    |   vectorType
+    |   matrixType
+;
+
+matrixType:
+        Matrix
+    |   Bool1x1
+    |   Bool1x2
+    |   Bool1x3
+    |   Bool1x4
+    |   Bool2x1
+    |   Bool2x2
+    |   Bool2x3
+    |   Bool2x4
+    |   Bool3x1
+    |   Bool3x2
+    |   Bool3x3
+    |   Bool3x4
+    |   Bool4x1
+    |   Bool4x2
+    |   Bool4x3
+    |   Bool4x4
+    |   Int1x1
+    |   Int1x2
+    |   Int1x3
+    |   Int1x4
+    |   Int2x1
+    |   Int2x2
+    |   Int2x3
+    |   Int2x4
+    |   Int3x1
+    |   Int3x2
+    |   Int3x3
+    |   Int3x4
+    |   Int4x1
+    |   Int4x2
+    |   Int4x3
+    |   Int4x4
+    |   Uint1x1
+    |   Uint1x2
+    |   Uint1x3
+    |   Uint1x4
+    |   Uint2x1
+    |   Uint2x2
+    |   Uint2x3
+    |   Uint2x4
+    |   Uint3x1
+    |   Uint3x2
+    |   Uint3x3
+    |   Uint3x4
+    |   Uint4x1
+    |   Uint4x2
+    |   Uint4x3
+    |   Uint4x4
+    |   Dword1x1
+    |   Dword1x2
+    |   Dword1x3
+    |   Dword1x4
+    |   Dword2x1
+    |   Dword2x2
+    |   Dword2x3
+    |   Dword2x4
+    |   Dword3x1
+    |   Dword3x2
+    |   Dword3x3
+    |   Dword3x4
+    |   Dword4x1
+    |   Dword4x2
+    |   Dword4x3
+    |   Dword4x4
+    |   Half1x1
+    |   Half1x2
+    |   Half1x3
+    |   Half1x4
+    |   Half2x1
+    |   Half2x2
+    |   Half2x3
+    |   Half2x4
+    |   Half3x1
+    |   Half3x2
+    |   Half3x3
+    |   Half3x4
+    |   Half4x1
+    |   Half4x2
+    |   Half4x3
+    |   Half4x4
+    |   Float1x1
+    |   Float1x2
+    |   Float1x3
+    |   Float1x4
+    |   Float2x1
+    |   Float2x2
+    |   Float2x3
+    |   Float2x4
+    |   Float3x1
+    |   Float3x2
+    |   Float3x3
+    |   Float3x4
+    |   Float4x1
+    |   Float4x2
+    |   Float4x3
+    |   Float4x4
+    |   Double1x1
+    |   Double1x2
+    |   Double1x3
+    |   Double1x4
+    |   Double2x1
+    |   Double2x2
+    |   Double2x3
+    |   Double2x4
+    |   Double3x1
+    |   Double3x2
+    |   Double3x3
+    |   Double3x4
+    |   Double4x1
+    |   Double4x2
+    |   Double4x3
+    |   Double4x4
+;
+
+genericMatrixPredefinedType:
+    Matrix Less scalarType Comma
+    Rows_=IntegerLiteral Comma Cols_=IntegerLiteral
+    Greater
+;
+
+registerAllocation:
+    Colon Register LeftParen Address=Identifier RightParen
+;
+
+samplerStateProperty:
+    Name=Identifier EqualsToken=Assign Expr=expression Semi
+;
+
+literal:
+        True
+    |   False
+    |   FloatLiteral
+    |   IntegerLiteral
+    |   StringLiteral+
+;
+
+// leading type means a function where the return type is stated first (contrary to trailing type)
+leadingTypeFunctionSignature:
+    storageFlags functionType (ClassName=userDefinedType ColonColon)? Name=Identifier
+    genericParameterList?
+    LeftParen functionParams? RightParen
+    Override? hlslSemantic?  // AZSL+
+;
+
+hlslFunctionDefinition:
+    leadingTypeFunctionSignature
+    block
+;
+
+hlslFunctionDeclaration:
+    leadingTypeFunctionSignature
+    Semi
+;
+
+functionType:
+        type
+    |   Void
+;
+
+userDefinedType:
+        idExpression
+    |   anyStructuredTypeDefinition   // to allow "struct tag_ {} var;"
+;
+
+// ====================
+// == AZSL specifics ==
+
+// swift/slang concept of protocol's "virtual type"
+associatedTypeDeclaration:
+    KW_AssociatedType Name=Identifier genericConstraint? Semi
+;
+
+// typedef support (extension of fxc accepted language, but normal for dxc)
+typedefStatement:
+    KW_Typedef ExistingType=functionType NewTypeName=Identifier Semi
+;
+
+// swift/slang-like manner of writing typedef. also close to C++11 using
+typealiasStatement:
+    KW_TypeAlias NewTypeName=Identifier '=' ExistingType=functionType Semi
+;
+
+typeAliasingDefinitionStatement:
+    typealiasStatement | typedefStatement
+;
+
+typeofExpression:
+    KW_Typeof '(' (Expr=expressionExt|functionType) ')' ('::' SubQualification=idExpression)?
+;
+
+genericParameterList:
+    '<' genericTypeDefinition (',' genericTypeDefinition)* '>'
+;
+
+genericTypeDefinition:
+    GenericTypeName=Identifier genericConstraint?
+;
+
+// defined as its own rule, the language becomes automatically extensible if we want to extend
+// it later, to an expression, for protocol set operations: (ILight & IColor)
+genericConstraint:
+    ':' userDefinedType
+// |   ':' languageDefinedConstraint // [GFX TODO]
+;
+
+languageDefinedConstraint:
+    KW_Fundamental
+;
+
+functionDeclaration:
+    hlslFunctionDeclaration
+;
+
+attributedFunctionDeclaration:
+    attributeSpecifierAny* functionDeclaration
+;
+
+functionDefinition:
+    hlslFunctionDefinition
+;
+
+attributedFunctionDefinition:
+    attributeSpecifierAny* functionDefinition
+;
+
+// special debugging intrinsics of the compiler
+compilerExtensionStatement:
+        KW_ext_print_message '(' Message=StringLiteral ')' Semi
+    |   KW_ext_print_symbol '(' (idExpression|typeofExpression) ',' (KW_ext_prtsym_fully_qualified
+                                                                   | KW_ext_prtsym_least_qualified
+                                                                   | KW_ext_prtsym_constint_value) ')' Semi
+;
+
+// AZSL SRG
+
+srgDefinition:
+    Partial? ShaderResourceGroup Name=Identifier (':' Semantic=Identifier)?
+    LeftBrace srgMemberDeclaration* RightBrace
+;
+
+attributedSrgDefinition:
+    attributeSpecifierAny* srgDefinition
+;
+
+srgMemberDeclaration:
+        structDefinitionStatement
+    |   attributedFunctionDeclaration
+    |   attributedFunctionDefinition
+    |   variableDeclarationStatement
+    |   enumDefinitionStatement
+    |   typeAliasingDefinitionStatement
+;
+
+srgSemantic:
+    ShaderResourceGroupSemantic Name=Identifier srgSemanticBodyDeclaration
+;
+
+attributedSrgSemantic:
+    attributeSpecifierAny* srgSemantic
+;
+
+srgSemanticBodyDeclaration:
+    LeftBrace srgSemanticMemberDeclaration* RightBrace
+;
+
+srgSemanticMemberDeclaration:
+        Frequency=FrequencyId '=' FrequencyValue=literal Semi
+    |   VariantFallback=ShaderVariantFallback '=' VariantFallbackValue=literal Semi
+;
+
+samplerBodyDeclaration:
+    LeftBrace samplerMemberDeclaration* RightBrace
+;
+
+samplerMemberDeclaration:
+        maxAnisotropyOption
+    |   minFilterOption
+    |   magFilterOption
+    |   mipFilterOption
+    |   reductionTypeOption
+    |   comparisonFunctionOption
+    |   addressUOption
+    |   addressVOption
+    |   addressWOption
+    |   minLodOption
+    |   maxLodOption
+    |   mipLodBiasOption
+    |   borderColorOption
+;
+
+// sampler specific options
+maxAnisotropyOption: MAX_ANISOTROPY '=' IntegerLiteral ';';
+minFilterOption: MIN_FILTER '=' filterModeEnum ';';
+magFilterOption: MAG_FILTER '=' filterModeEnum ';';
+mipFilterOption: MIP_FILTER '=' filterModeEnum ';';
+reductionTypeOption: REDUCTION_TYPE '=' reductionTypeEnum ';';
+comparisonFunctionOption: COMPARISON_FUNC '=' comparisonFunctionEnum ';';
+addressUOption: ADDRESS_U '=' addressModeEnum ';';
+addressVOption: ADDRESS_V '=' addressModeEnum ';';
+addressWOption: ADDRESS_W '=' addressModeEnum ';';
+minLodOption: MIN_LOD '=' FloatLiteral ';';
+maxLodOption: MAX_LOD '=' FloatLiteral ';';
+mipLodBiasOption: MIP_LOD_BIAS '=' FloatLiteral ';';
+borderColorOption: BORDER_COLOR '=' borderColorEnum ';';
+
+filterModeEnum: FILTER_MODE_POINT | FILTER_MODE_LINEAR;
+reductionTypeEnum: REDUCTION_TYPE_FILTER | REDUCTION_TYPE_COMPARISON | REDUCTION_TYPE_MINIMUM | REDUCTION_TYPE_MAXIMUM;
+addressModeEnum: ADDRESS_MODE_WRAP | ADDRESS_MODE_MIRROR | ADDRESS_MODE_CLAMP | ADDRESS_MODE_BORDER | ADDRESS_MODE_MIRROR_ONCE;
+comparisonFunctionEnum:
+  COMPARISON_FUNCTION_NEVER |
+  COMPARISON_FUNCTION_LESS |
+  COMPARISON_FUNCTION_EQUAL |
+  COMPARISON_FUNCTION_LESS_EQUAL |
+  COMPARISON_FUNCTION_GREATER |
+  COMPARISON_FUNCTION_NOT_EQUAL |
+  COMPARISON_FUNCTION_GREATER_EQUAL |
+  COMPARISON_FUNCTION_ALWAYS;
+borderColorEnum: BORDER_COLOR_OPAQUE_BLACK | BORDER_COLOR_TRANSPARENT_BLACK | BORDER_COLOR_OPAQUE_WHITE;

+ 78 - 0
src/exportKeywords.py

@@ -0,0 +1,78 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+"""
+All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
+its licensors.
+
+For complete copyright and license terms please see the LICENSE at the root of this
+distribution (the "License"). All use of this software is governed by the License,
+or, if provided, by the license below or the license accompanying this file. Do not
+remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+"""
+import sys
+import os
+import importlib
+from argparse import ArgumentParser
+
+if __name__ == "__main__":
+    os.system('') # activate VT100 mode for windows console
+    parser = ArgumentParser()
+    parser.add_argument(
+        '--compiler', dest='compiler',
+        type=str,
+        help='the path to the compiler exe',
+        default="../bin/win_x64/Release/azslc.exe"
+    )
+    args = parser.parse_args()
+    exe = args.compiler
+
+    if not os.path.exists(exe):
+        print("error: no exe found at", exe, " please pass a path on the command line")
+        exit(1)
+
+    sys.path.append("../tests")
+    testFuncs = importlib.import_module("testfuncs")
+
+    keywords = testFuncs.dumpKeywords(exe)
+    if not keywords[1]:
+        sys.exit(1)
+
+    with open("AzslcPredefinedTypes.h", 'w') as outfile:
+        copyright = '''/*
+* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
+* its licensors.
+*
+* For complete copyright and license terms please see the LICENSE at the root of this
+* distribution (the "License"). All use of this software is governed by the License,
+* or, if provided, by the license below or the license accompanying this file. Do not
+* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*
+*/
+#pragma once
+
+// THIS FILE IS AUTO-GENERATED BY exportKeywords.py
+
+#include <array>
+#include <tuple>
+
+namespace AZ::ShaderCompiler::Predefined
+{
+'''
+        outfile.write(copyright)
+        for categ, words in keywords[0].items():
+            outfile.write("static constexpr std::array<const char*, " + str(len(words)) + "> ");
+            outfile.write(categ)
+            outfile.write(" = {\n")
+            outfile.write(',\n'.join(['"' + w + '"' for w in words]))
+            outfile.write("};\n\n")
+
+        outfile.write("template<size_t N> struct Bag { const char* m_name = nullptr; const std::array<const char*, N> m_bag; };\n\n");
+        tupdecl = "static constexpr auto All = std::make_tuple("
+        outfile.write(tupdecl);
+        whitespaces = " " * len(tupdecl)
+        outfile.write((",\n" + whitespaces).join(['Bag<' + str(len(val)) + '>{"' + key + '", ' + key + '}' for key, val in keywords[0].items()]))
+        outfile.write(");\n\n");
+
+        outfile.write("};\n")  # end namespace

BIN
src/external/antlr-4.7.1-complete.jar


+ 23 - 0
src/external/docopt/LICENSE-MIT

@@ -0,0 +1,23 @@
+Copyright (c) 2012 Vladimir Keleshev, <[email protected]>
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the Software
+without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to
+whom the Software is furnished to do so, subject to the
+following conditions:
+
+The above copyright notice and this permission notice shall
+be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 689 - 0
src/external/docopt/docopt.cpp

@@ -0,0 +1,689 @@
+//
+//  docopt.cpp
+//  docopt
+//
+//  Created by Jared Grubb on 2013-11-03.
+//  Copyright (c) 2013 Jared Grubb. All rights reserved.
+//
+
+//#define _ITERATOR_DEBUG_LEVEL 0 // fix  https://github.com/docopt/docopt.cpp/issues/49
+
+#include "docopt.h"
+#include "docopt_util.h"
+#include "docopt_private.h"
+
+#include "docopt_value.h"
+
+#include <vector>
+#include <unordered_set>
+#include <unordered_map>
+#include <map>
+#include <string>
+#include <iostream>
+#include <cassert>
+#include <cstddef>
+
+using namespace docopt;
+
+DOCOPT_INLINE
+std::ostream& docopt::operator<<(std::ostream& os, value const& val)
+{
+	if (val.isBool()) {
+		bool b = val.asBool();
+		os << (b ? "true" : "false");
+	} else if (val.isLong()) {
+		long v = val.asLong();
+		os << v;
+	} else if (val.isString()) {
+		std::string const& str = val.asString();
+		os << '"' << str << '"';
+	} else if (val.isStringList()) {
+		auto const& list = val.asStringList();
+		os << "[";
+		bool first = true;
+		for(auto const& el : list) {
+			if (first) {
+				first = false;
+			} else {
+				os << ", ";
+			}
+			os << '"' << el << '"';
+		}
+		os << "]";
+	} else {
+		os << "null";
+	}
+	return os;
+}
+
+#if 0
+#pragma mark -
+#pragma mark Parsing stuff
+#endif
+
+class Tokens {
+public:
+	Tokens(std::vector<std::string> tokens, bool isParsingArgv = true)
+	: fTokens(std::move(tokens)),
+	  fIsParsingArgv(isParsingArgv)
+	{}
+
+	explicit operator bool() const {
+		return fIndex < fTokens.size();
+	}
+
+	static Tokens from_pattern(std::string const& source) {
+		static const std::regex re_separators {
+			"(?:\\s*)" // any spaces (non-matching subgroup)
+			"("
+			"[\\[\\]\\(\\)\\|]" // one character of brackets or parens or pipe character
+			"|"
+			"\\.\\.\\."  // elipsis
+			")" };
+
+		static const std::regex re_strings {
+			"(?:\\s*)" // any spaces (non-matching subgroup)
+			"("
+			"\\S*<.*?>"  // strings, but make sure to keep "< >" strings together
+			"|"
+			"[^<>\\s]+"     // string without <>
+			")" };
+
+		// We do two stages of regex matching. The '[]()' and '...' are strong delimeters
+		// and need to be split out anywhere they occur (even at the end of a token). We
+		// first split on those, and then parse the stuff between them to find the string
+		// tokens. This is a little harder than the python version, since they have regex.split
+		// and we dont have anything like that.
+
+		std::vector<std::string> tokens;
+		std::for_each(std::sregex_iterator{ source.begin(), source.end(), re_separators },
+			      std::sregex_iterator{},
+			      [&](std::smatch const& match)
+			      {
+				      // handle anything before the separator (this is the "stuff" between the delimeters)
+				      if (match.prefix().matched) {
+					      std::for_each(std::sregex_iterator{match.prefix().first, match.prefix().second, re_strings},
+							    std::sregex_iterator{},
+							    [&](std::smatch const& m)
+							    {
+								    tokens.push_back(m[1].str());
+							    });
+				      }
+
+				      // handle the delimter token itself
+				      if (match[1].matched) {
+					      tokens.push_back(match[1].str());
+				      }
+			      });
+
+		return Tokens(tokens, false);
+	}
+
+	std::string const& current() const {
+		if (*this)
+			return fTokens[fIndex];
+
+		static std::string const empty;
+		return empty;
+	}
+
+	std::string the_rest() const {
+		if (!*this)
+			return {};
+		return join(fTokens.begin()+static_cast<std::ptrdiff_t>(fIndex),
+			    fTokens.end(),
+			    " ");
+	}
+
+	std::string pop() {
+		return std::move(fTokens.at(fIndex++));
+	}
+
+	bool isParsingArgv() const { return fIsParsingArgv; }
+
+	struct OptionError : std::runtime_error { using runtime_error::runtime_error; };
+
+private:
+	std::vector<std::string> fTokens;
+	size_t fIndex = 0;
+	bool fIsParsingArgv;
+};
+
+// Get all instances of 'T' from the pattern
+template <typename T>
+std::vector<T*> flat_filter(Pattern& pattern) {
+	std::vector<Pattern*> flattened = pattern.flat([](Pattern const* p) -> bool {
+		return dynamic_cast<T const*>(p) != nullptr;
+	});
+
+	// now, we're guaranteed to have T*'s, so just use static_cast
+	std::vector<T*> ret;
+	std::transform(flattened.begin(), flattened.end(), std::back_inserter(ret), [](Pattern* p) {
+		return static_cast<T*>(p);
+	});
+	return ret;
+}
+
+static std::vector<std::string> parse_section(std::string const& name, std::string const& source) {
+	// ECMAScript regex only has "?=" for a non-matching lookahead. In order to make sure we always have
+	// a newline to anchor our matching, we have to avoid matching the final newline of each grouping.
+	// Therefore, our regex is adjusted from the docopt Python one to use ?= to match the newlines before
+	// the following lines, rather than after.
+	std::regex const re_section_pattern {
+		"(?:^|\\n)"  // anchored at a linebreak (or start of string)
+		"("
+		   "[^\\n]*" + name + "[^\\n]*(?=\\n?)" // a line that contains the name
+		   "(?:\\n[ \\t].*?(?=\\n|$))*"         // followed by any number of lines that are indented
+		")",
+		std::regex::icase
+	};
+
+	std::vector<std::string> ret;
+	std::for_each(std::sregex_iterator(source.begin(), source.end(), re_section_pattern),
+		      std::sregex_iterator(),
+		      [&](std::smatch const& match)
+	{
+		ret.push_back(trim(match[1].str()));
+	});
+
+	return ret;
+}
+
+static bool is_argument_spec(std::string const& token) {
+	if (token.empty())
+		return false;
+
+	if (token[0]=='<' && token[token.size()-1]=='>')
+		return true;
+
+	if (std::all_of(token.begin(), token.end(), &::isupper))
+		return true;
+
+	return false;
+}
+
+template <typename I>
+std::vector<std::string> longOptions(I iter, I end) {
+	std::vector<std::string> ret;
+	std::transform(iter, end,
+		       std::back_inserter(ret),
+		       [](typename I::reference opt) { return opt->longOption(); });
+	return ret;
+}
+
+static PatternList parse_long(Tokens& tokens, std::vector<Option>& options)
+{
+	// long ::= '--' chars [ ( ' ' | '=' ) chars ] ;
+	std::string longOpt, equal;
+	value val;
+	std::tie(longOpt, equal, val) = partition(tokens.pop(), "=");
+
+	assert(starts_with(longOpt, "--"));
+
+	if (equal.empty()) {
+		val = value{};
+	}
+
+	// detect with options match this long option
+	std::vector<Option const*> similar;
+	for(auto const& option : options) {
+		if (option.longOption()==longOpt)
+			similar.push_back(&option);
+	}
+
+	// maybe allow similar options that match by prefix
+	if (tokens.isParsingArgv() && similar.empty()) {
+		for(auto const& option : options) {
+			if (option.longOption().empty())
+				continue;
+			if (starts_with(option.longOption(), longOpt))
+				similar.push_back(&option);
+		}
+	}
+
+	PatternList ret;
+
+	if (similar.size() > 1) { // might be simply specified ambiguously 2+ times?
+		std::vector<std::string> prefixes = longOptions(similar.begin(), similar.end());
+		std::string error = "'" + longOpt + "' is not a unique prefix: ";
+		error.append(join(prefixes.begin(), prefixes.end(), ", "));
+		throw Tokens::OptionError(std::move(error));
+	} else if (similar.empty()) {
+		int argcount = equal.empty() ? 0 : 1;
+		options.emplace_back("", longOpt, argcount);
+
+		auto o = std::make_shared<Option>(options.back());
+		if (tokens.isParsingArgv()) {
+			o->setValue(argcount ? value{val} : value{true});
+		}
+		ret.push_back(o);
+	} else {
+		auto o = std::make_shared<Option>(*similar[0]);
+		if (o->argCount() == 0) {
+			if (val) {
+				std::string error = o->longOption() + " must not have an argument";
+				throw Tokens::OptionError(std::move(error));
+			}
+		} else {
+			if (!val) {
+				auto const& token = tokens.current();
+				if (token.empty() || token=="--") {
+					std::string error = o->longOption() + " requires an argument";
+					throw Tokens::OptionError(std::move(error));
+				}
+				val = tokens.pop();
+			}
+		}
+		if (tokens.isParsingArgv()) {
+			o->setValue(val ? std::move(val) : value{true});
+		}
+		ret.push_back(o);
+	}
+
+	return ret;
+}
+
+static PatternList parse_short(Tokens& tokens, std::vector<Option>& options)
+{
+	// shorts ::= '-' ( chars )* [ [ ' ' ] chars ] ;
+
+	auto token = tokens.pop();
+
+	assert(starts_with(token, "-"));
+	assert(!starts_with(token, "--"));
+
+	auto i = token.begin();
+	++i; // skip the leading '-'
+
+	PatternList ret;
+	while (i != token.end()) {
+		std::string shortOpt = { '-', *i };
+		++i;
+
+		std::vector<Option const*> similar;
+		for(auto const& option : options) {
+			if (option.shortOption()==shortOpt)
+				similar.push_back(&option);
+		}
+
+		if (similar.size() > 1) {
+			std::string error = shortOpt + " is specified ambiguously "
+			+ std::to_string(similar.size()) + " times";
+			throw Tokens::OptionError(std::move(error));
+		} else if (similar.empty()) {
+			options.emplace_back(shortOpt, "", 0);
+
+			auto o = std::make_shared<Option>(options.back());
+			if (tokens.isParsingArgv()) {
+				o->setValue(value{true});
+			}
+			ret.push_back(o);
+		} else {
+			auto o = std::make_shared<Option>(*similar[0]);
+			value val;
+			if (o->argCount()) {
+				if (i == token.end()) {
+					// consume the next token
+					auto const& ttoken = tokens.current();
+					if (ttoken.empty() || ttoken=="--") {
+						std::string error = shortOpt + " requires an argument";
+						throw Tokens::OptionError(std::move(error));
+					}
+					val = tokens.pop();
+				} else {
+					// consume all the rest
+					val = std::string{i, token.end()};
+					i = token.end();
+				}
+			}
+
+			if (tokens.isParsingArgv()) {
+				o->setValue(val ? std::move(val) : value{true});
+			}
+			ret.push_back(o);
+		}
+	}
+
+	return ret;
+}
+
+static PatternList parse_expr(Tokens& tokens, std::vector<Option>& options);
+
+static PatternList parse_atom(Tokens& tokens, std::vector<Option>& options)
+{
+	// atom ::= '(' expr ')' | '[' expr ']' | 'options'
+	//             | long | shorts | argument | command ;
+
+	std::string const& token = tokens.current();
+
+	PatternList ret;
+
+	if (token == "[") {
+		tokens.pop();
+
+		auto expr = parse_expr(tokens, options);
+
+		auto trailing = tokens.pop();
+		if (trailing != "]") {
+			throw DocoptLanguageError("Mismatched '['");
+		}
+
+		ret.emplace_back(std::make_shared<Optional>(std::move(expr)));
+	} else if (token=="(") {
+		tokens.pop();
+
+		auto expr = parse_expr(tokens, options);
+
+		auto trailing = tokens.pop();
+		if (trailing != ")") {
+			throw DocoptLanguageError("Mismatched '('");
+		}
+
+		ret.emplace_back(std::make_shared<Required>(std::move(expr)));
+	} else if (token == "options") {
+		tokens.pop();
+		ret.emplace_back(std::make_shared<OptionsShortcut>());
+	} else if (starts_with(token, "--") && token != "--") {
+		ret = parse_long(tokens, options);
+	} else if (starts_with(token, "-") && token != "-" && token != "--") {
+		ret = parse_short(tokens, options);
+	} else if (is_argument_spec(token)) {
+		ret.emplace_back(std::make_shared<Argument>(tokens.pop()));
+	} else {
+		ret.emplace_back(std::make_shared<Command>(tokens.pop()));
+	}
+
+	return ret;
+}
+
+static PatternList parse_seq(Tokens& tokens, std::vector<Option>& options)
+{
+	// seq ::= ( atom [ '...' ] )* ;"""
+
+	PatternList ret;
+
+	while (tokens) {
+		auto const& token = tokens.current();
+
+		if (token=="]" || token==")" || token=="|")
+			break;
+
+		auto atom = parse_atom(tokens, options);
+		if (tokens.current() == "...") {
+			ret.emplace_back(std::make_shared<OneOrMore>(std::move(atom)));
+			tokens.pop();
+		} else {
+			std::move(atom.begin(), atom.end(), std::back_inserter(ret));
+		}
+	}
+
+	return ret;
+}
+
+static std::shared_ptr<Pattern> maybe_collapse_to_required(PatternList&& seq)
+{
+	if (seq.size()==1) {
+		return std::move(seq[0]);
+	}
+	return std::make_shared<Required>(std::move(seq));
+}
+
+static std::shared_ptr<Pattern> maybe_collapse_to_either(PatternList&& seq)
+{
+	if (seq.size()==1) {
+		return std::move(seq[0]);
+	}
+	return std::make_shared<Either>(std::move(seq));
+}
+
+PatternList parse_expr(Tokens& tokens, std::vector<Option>& options)
+{
+	// expr ::= seq ( '|' seq )* ;
+
+	auto seq = parse_seq(tokens, options);
+
+	if (tokens.current() != "|")
+		return seq;
+
+	PatternList ret;
+	ret.emplace_back(maybe_collapse_to_required(std::move(seq)));
+
+	while (tokens.current() == "|") {
+		tokens.pop();
+		seq = parse_seq(tokens, options);
+		ret.emplace_back(maybe_collapse_to_required(std::move(seq)));
+	}
+
+	return { maybe_collapse_to_either(std::move(ret)) };
+}
+
+static Required parse_pattern(std::string const& source, std::vector<Option>& options)
+{
+	auto tokens = Tokens::from_pattern(source);
+	auto result = parse_expr(tokens, options);
+
+	if (tokens)
+		throw DocoptLanguageError("Unexpected ending: '" + tokens.the_rest() + "'");
+
+	assert(result.size() == 1  &&  "top level is always one big");
+	return Required{ std::move(result) };
+}
+
+
+static std::string formal_usage(std::string const& section) {
+	std::string ret = "(";
+
+	auto i = section.find(':')+1;  // skip past "usage:"
+	auto parts = split(section, i);
+	for(size_t ii = 1; ii < parts.size(); ++ii) {
+		if (parts[ii] == parts[0]) {
+			ret += " ) | (";
+		} else {
+			ret.push_back(' ');
+			ret += parts[ii];
+		}
+	}
+
+	ret += " )";
+	return ret;
+}
+
+static PatternList parse_argv(Tokens tokens, std::vector<Option>& options, bool options_first)
+{
+	// Parse command-line argument vector.
+	//
+	// If options_first:
+	//    argv ::= [ long | shorts ]* [ argument ]* [ '--' [ argument ]* ] ;
+	// else:
+	//    argv ::= [ long | shorts | argument ]* [ '--' [ argument ]* ] ;
+
+	PatternList ret;
+	while (tokens) {
+		auto const& token = tokens.current();
+
+		if (token=="--") {
+			// option list is done; convert all the rest to arguments
+			while (tokens) {
+				ret.emplace_back(std::make_shared<Argument>("", tokens.pop()));
+			}
+		} else if (starts_with(token, "--")) {
+			auto&& parsed = parse_long(tokens, options);
+			std::move(parsed.begin(), parsed.end(), std::back_inserter(ret));
+		} else if (token[0]=='-' && token != "-") {
+			auto&& parsed = parse_short(tokens, options);
+			std::move(parsed.begin(), parsed.end(), std::back_inserter(ret));
+		} else if (options_first) {
+			// option list is done; convert all the rest to arguments
+			while (tokens) {
+				ret.emplace_back(std::make_shared<Argument>("", tokens.pop()));
+			}
+		} else {
+			ret.emplace_back(std::make_shared<Argument>("", tokens.pop()));
+		}
+	}
+
+	return ret;
+}
+
+std::vector<Option> parse_defaults(std::string const& doc) {
+	// This pattern is a delimiter by which we split the options.
+	// The delimiter is a new line followed by a whitespace(s) followed by one or two hyphens.
+	static std::regex const re_delimiter{
+		"(?:^|\\n)[ \\t]*"  // a new line with leading whitespace
+		"(?=-{1,2})"        // [split happens here] (positive lookahead) ... and followed by one or two hyphes
+	};
+
+	std::vector<Option> defaults;
+	for (auto s : parse_section("options:", doc)) {
+		s.erase(s.begin(), s.begin() + static_cast<std::ptrdiff_t>(s.find(':')) + 1); // get rid of "options:"
+
+		for (const auto& opt : regex_split(s, re_delimiter)) {
+			if (starts_with(opt, "-")) {
+				defaults.emplace_back(Option::parse(opt));
+			}
+		}
+	}
+
+	return defaults;
+}
+
+static bool isOptionSet(PatternList const& options, std::string const& opt1, std::string const& opt2 = "") {
+	return std::any_of(options.begin(), options.end(), [&](std::shared_ptr<Pattern const> const& opt) -> bool {
+		auto const& name = opt->name();
+		if (name==opt1 || (!opt2.empty() && name==opt2)) {
+			return opt->hasValue();
+		}
+		return false;
+	});
+}
+
+static void extras(bool help, bool version, PatternList const& options) {
+	if (help && isOptionSet(options, "-h", "--help")) {
+		throw DocoptExitHelp();
+	}
+
+	if (version && isOptionSet(options, "--version")) {
+		throw DocoptExitVersion();
+	}
+}
+
+// Parse the doc string and generate the Pattern tree
+static std::pair<Required, std::vector<Option>> create_pattern_tree(std::string const& doc)
+{
+	auto usage_sections = parse_section("usage:", doc);
+	if (usage_sections.empty()) {
+		throw DocoptLanguageError("'usage:' (case-insensitive) not found.");
+	}
+	if (usage_sections.size() > 1) {
+		throw DocoptLanguageError("More than one 'usage:' (case-insensitive).");
+	}
+
+	std::vector<Option> options = parse_defaults(doc);
+	Required pattern = parse_pattern(formal_usage(usage_sections[0]), options);
+
+	std::vector<Option const*> pattern_options = flat_filter<Option const>(pattern);
+
+	using UniqueOptions = std::unordered_set<Option const*, PatternHasher, PatternPointerEquality>;
+	UniqueOptions const uniq_pattern_options { pattern_options.begin(), pattern_options.end() };
+
+	// Fix up any "[options]" shortcuts with the actual option tree
+	for(auto& options_shortcut : flat_filter<OptionsShortcut>(pattern)) {
+		std::vector<Option> doc_options = parse_defaults(doc);
+
+		// set(doc_options) - set(pattern_options)
+		UniqueOptions uniq_doc_options;
+		for(auto const& opt : doc_options) {
+			if (uniq_pattern_options.count(&opt))
+				continue;
+			uniq_doc_options.insert(&opt);
+		}
+
+		// turn into shared_ptr's and set as children
+		PatternList children;
+		std::transform(uniq_doc_options.begin(), uniq_doc_options.end(),
+			       std::back_inserter(children), [](Option const* opt) {
+				       return std::make_shared<Option>(*opt);
+			       });
+		options_shortcut->setChildren(std::move(children));
+	}
+
+	return { std::move(pattern), std::move(options) };
+}
+
+DOCOPT_INLINE
+std::map<std::string, value>
+docopt::docopt_parse(std::string const& doc,
+		     std::vector<std::string> const& argv,
+		     bool help,
+		     bool version,
+		     bool options_first)
+{
+	Required pattern;
+	std::vector<Option> options;
+	try {
+		std::tie(pattern, options) = create_pattern_tree(doc);
+	} catch (Tokens::OptionError const& error) {
+		throw DocoptLanguageError(error.what());
+	}
+
+	PatternList argv_patterns;
+	try {
+		argv_patterns = parse_argv(Tokens(argv), options, options_first);
+	} catch (Tokens::OptionError const& error) {
+		throw DocoptArgumentError(error.what());
+	}
+
+	extras(help, version, argv_patterns);
+
+	std::vector<std::shared_ptr<LeafPattern>> collected;
+	bool matched = pattern.fix().match(argv_patterns, collected);
+	if (matched && argv_patterns.empty()) {
+		std::map<std::string, value> ret;
+
+		// (a.name, a.value) for a in (pattern.flat() + collected)
+		for (auto* p : pattern.leaves()) {
+			ret[p->name()] = p->getValue();
+		}
+
+		for (auto const& p : collected) {
+			ret[p->name()] = p->getValue();
+		}
+
+		return ret;
+	}
+
+	if (matched) {
+		std::string leftover = join(argv.begin(), argv.end(), ", ");
+		throw DocoptArgumentError("Unexpected argument: " + leftover);
+	}
+
+	throw DocoptArgumentError("Arguments did not match expected patterns"); // BLEH. Bad error.
+}
+
+DOCOPT_INLINE
+std::map<std::string, value>
+docopt::docopt(std::string const& doc,
+	       std::vector<std::string> const& argv,
+	       bool help,
+	       std::string const& version,
+	       bool options_first) noexcept
+{
+	try {
+		return docopt_parse(doc, argv, help, !version.empty(), options_first);
+	} catch (DocoptExitHelp const&) {
+		std::cout << doc << std::endl;
+		std::exit(0);
+	} catch (DocoptExitVersion const&) {
+		std::cout << version << std::endl;
+		std::exit(0);
+	} catch (DocoptLanguageError const& error) {
+		std::cerr << "Docopt usage string could not be parsed" << std::endl;
+		std::cerr << error.what() << std::endl;
+		std::exit(-1);
+	} catch (DocoptArgumentError const& error) {
+		std::cerr << error.what();
+		std::cout << std::endl;
+		std::cout << doc << std::endl;
+		std::exit(-1);
+	} /* Any other exception is unexpected: let std::terminate grab it */
+}

+ 94 - 0
src/external/docopt/docopt.h

@@ -0,0 +1,94 @@
+//
+//  docopt.h
+//  docopt
+//
+//  Created by Jared Grubb on 2013-11-03.
+//  Copyright (c) 2013 Jared Grubb. All rights reserved.
+//
+
+#ifndef docopt__docopt_h_
+#define docopt__docopt_h_
+
+#include "docopt_value.h"
+
+#include <map>
+#include <vector>
+#include <string>
+
+#ifdef DOCOPT_HEADER_ONLY
+    #define DOCOPT_INLINE inline
+    #define DOCOPT_API
+#else 
+    #define DOCOPT_INLINE
+
+    // With Microsoft Visual Studio, export certain symbols so they 
+    // are available to users of docopt.dll (shared library). The DOCOPT_DLL
+    // macro should be defined if building a DLL (with Visual Studio),
+    // and by clients using the DLL. The CMakeLists.txt and the
+    // docopt-config.cmake it generates handle this.
+    #ifdef DOCOPT_DLL
+        // Whoever is *building* the DLL should define DOCOPT_EXPORTS.
+        // The CMakeLists.txt that comes with docopt does this.
+        // Clients of docopt.dll should NOT define DOCOPT_EXPORTS.
+        #ifdef DOCOPT_EXPORTS
+            #define DOCOPT_API __declspec(dllexport)
+        #else
+            #define DOCOPT_API __declspec(dllimport)
+        #endif
+    #else
+        #define DOCOPT_API
+    #endif
+#endif
+
+namespace docopt {
+	
+	// Usage string could not be parsed (ie, the developer did something wrong)
+	struct DocoptLanguageError : std::runtime_error { using runtime_error::runtime_error; };
+	
+	// Arguments passed by user were incorrect (ie, developer was good, user is wrong)
+	struct DocoptArgumentError : std::runtime_error { using runtime_error::runtime_error; };
+	
+	// Arguments contained '--help' and parsing was aborted early
+	struct DocoptExitHelp : std::runtime_error { DocoptExitHelp() : std::runtime_error("Docopt --help argument encountered"){} };
+
+	// Arguments contained '--version' and parsing was aborted early
+	struct DocoptExitVersion : std::runtime_error { DocoptExitVersion() : std::runtime_error("Docopt --version argument encountered") {} };
+	
+	/// Parse user options from the given option string.
+	///
+	/// @param doc   The usage string
+	/// @param argv  The user-supplied arguments
+	/// @param help  Whether to end early if '-h' or '--help' is in the argv
+	/// @param version Whether to end early if '--version' is in the argv
+	/// @param options_first  Whether options must precede all args (true), or if args and options
+	///                can be arbitrarily mixed.
+	///
+	/// @throws DocoptLanguageError if the doc usage string had errors itself
+	/// @throws DocoptExitHelp if 'help' is true and the user has passed the '--help' argument
+	/// @throws DocoptExitVersion if 'version' is true and the user has passed the '--version' argument
+	/// @throws DocoptArgumentError if the user's argv did not match the usage patterns
+	std::map<std::string, value> DOCOPT_API docopt_parse(std::string const& doc,
+					    std::vector<std::string> const& argv,
+					    bool help = true,
+					    bool version = true,
+					    bool options_first = false);
+	
+	/// Parse user options from the given string, and exit appropriately
+	///
+	/// Calls 'docopt_parse' and will terminate the program if any of the exceptions above occur:
+	///  * DocoptLanguageError - print error and terminate (with exit code -1)
+	///  * DocoptExitHelp - print usage string and terminate (with exit code 0)
+	///  * DocoptExitVersion - print version and terminate (with exit code 0)
+	///  * DocoptArgumentError - print error and usage string and terminate (with exit code -1)
+	std::map<std::string, value> DOCOPT_API docopt(std::string const& doc,
+					    std::vector<std::string> const& argv,
+					    bool help = true,
+					    std::string const& version = {},
+					    bool options_first = false) noexcept;
+}
+
+#ifdef DOCOPT_HEADER_ONLY
+    #include "docopt.cpp"
+#endif
+
+#endif /* defined(docopt__docopt_h_) */

+ 676 - 0
src/external/docopt/docopt_private.h

@@ -0,0 +1,676 @@
+//
+//  docopt_private.h
+//  docopt
+//
+//  Created by Jared Grubb on 2013-11-04.
+//  Copyright (c) 2013 Jared Grubb. All rights reserved.
+//
+
+#ifndef docopt_docopt_private_h
+#define docopt_docopt_private_h
+
+#include <vector>
+#include <memory>
+#include <unordered_set>
+#include <assert.h>
+
+// Workaround GCC 4.8 not having std::regex
+#if DOCTOPT_USE_BOOST_REGEX
+#include <boost/regex.hpp>
+namespace std {
+	using boost::regex;
+   	using boost::sregex_iterator;
+   	using boost::smatch;
+   	using boost::regex_search;
+   	namespace regex_constants {
+		using boost::regex_constants::match_not_null;
+   	}
+}
+#else
+#include <regex>
+#endif
+
+#include "docopt_value.h"
+
+namespace docopt {
+
+	class Pattern;
+	class LeafPattern;
+
+	using PatternList = std::vector<std::shared_ptr<Pattern>>;
+
+	// Utility to use Pattern types in std hash-containers
+	struct PatternHasher {
+		template <typename P>
+		size_t operator()(std::shared_ptr<P> const& pattern) const {
+			return pattern->hash();
+		}
+		template <typename P>
+		size_t operator()(P const* pattern) const {
+			return pattern->hash();
+		}
+		template <typename P>
+		size_t operator()(P const& pattern) const {
+			return pattern.hash();
+		}
+	};
+
+	// Utility to use 'hash' as the equality operator as well in std containers
+	struct PatternPointerEquality {
+		template <typename P1, typename P2>
+		bool operator()(std::shared_ptr<P1> const& p1, std::shared_ptr<P2> const& p2) const {
+			return p1->hash()==p2->hash();
+		}
+		template <typename P1, typename P2>
+		bool operator()(P1 const* p1, P2 const* p2) const {
+			return p1->hash()==p2->hash();
+		}
+	};
+
+	// A hash-set that uniques by hash value
+	using UniquePatternSet = std::unordered_set<std::shared_ptr<Pattern>, PatternHasher, PatternPointerEquality>;
+
+
+	class Pattern {
+	public:
+		// flatten out children, stopping descent when the given filter returns 'true'
+		virtual std::vector<Pattern*> flat(bool (*filter)(Pattern const*)) = 0;
+
+		// flatten out all children into a list of LeafPattern objects
+		virtual void collect_leaves(std::vector<LeafPattern*>&) = 0;
+
+		// flatten out all children into a list of LeafPattern objects
+		std::vector<LeafPattern*> leaves();
+
+		// Attempt to find something in 'left' that matches this pattern's spec, and if so, move it to 'collected'
+		virtual bool match(PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const = 0;
+
+		virtual std::string const& name() const = 0;
+
+		virtual bool hasValue() const { return false; }
+
+		virtual size_t hash() const = 0;
+
+		virtual ~Pattern() = default;
+	};
+
+	class LeafPattern
+	: public Pattern {
+	public:
+		LeafPattern(std::string name, value v = {})
+		: fName(std::move(name)),
+		  fValue(std::move(v))
+		{}
+
+		virtual std::vector<Pattern*> flat(bool (*filter)(Pattern const*)) override {
+			if (filter(this)) {
+				return { this };
+			}
+			return {};
+		}
+
+		virtual void collect_leaves(std::vector<LeafPattern*>& lst) override final {
+			lst.push_back(this);
+		}
+
+		virtual bool match(PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const override;
+
+		virtual bool hasValue() const override { return static_cast<bool>(fValue); }
+
+		value const& getValue() const { return fValue; }
+		void setValue(value&& v) { fValue = std::move(v); }
+
+		virtual std::string const& name() const override { return fName; }
+
+		virtual size_t hash() const override {
+			size_t seed = typeid(*this).hash_code();
+			hash_combine(seed, fName);
+			hash_combine(seed, fValue);
+			return seed;
+		}
+
+	protected:
+		virtual std::pair<size_t, std::shared_ptr<LeafPattern>> single_match(PatternList const&) const = 0;
+
+	private:
+		std::string fName;
+		value fValue;
+	};
+
+	class BranchPattern
+	: public Pattern {
+	public:
+		BranchPattern(PatternList children = {})
+		: fChildren(std::move(children))
+		{}
+
+		Pattern& fix() {
+			UniquePatternSet patterns;
+			fix_identities(patterns);
+			fix_repeating_arguments();
+			return *this;
+		}
+
+		virtual std::string const& name() const override {
+			throw std::runtime_error("Logic error: name() shouldnt be called on a BranchPattern");
+		}
+
+		virtual value const& getValue() const {
+			throw std::runtime_error("Logic error: name() shouldnt be called on a BranchPattern");
+		}
+
+		virtual std::vector<Pattern*> flat(bool (*filter)(Pattern const*)) override {
+			if (filter(this)) {
+				return {this};
+			}
+
+			std::vector<Pattern*> ret;
+			for(auto& child : fChildren) {
+				auto sublist = child->flat(filter);
+				ret.insert(ret.end(), sublist.begin(), sublist.end());
+			}
+			return ret;
+		}
+
+		virtual void collect_leaves(std::vector<LeafPattern*>& lst) override final {
+			for(auto& child : fChildren) {
+				child->collect_leaves(lst);
+			}
+		}
+
+		void setChildren(PatternList children) {
+			fChildren = std::move(children);
+		}
+
+		PatternList const& children() const { return fChildren; }
+
+		virtual void fix_identities(UniquePatternSet& patterns) {
+			for(auto& child : fChildren) {
+				// this will fix up all its children, if needed
+				if (auto bp = dynamic_cast<BranchPattern*>(child.get())) {
+					bp->fix_identities(patterns);
+				}
+
+				// then we try to add it to the list
+				auto inserted = patterns.insert(child);
+				if (!inserted.second) {
+					// already there? then reuse the existing shared_ptr for that thing
+					child = *inserted.first;
+				}
+			}
+		}
+
+		virtual size_t hash() const override {
+			size_t seed = typeid(*this).hash_code();
+			hash_combine(seed, fChildren.size());
+			for(auto const& child : fChildren) {
+				hash_combine(seed, child->hash());
+			}
+			return seed;
+		}
+	private:
+		void fix_repeating_arguments();
+
+	protected:
+		PatternList fChildren;
+	};
+
+	class Argument
+	: public LeafPattern {
+	public:
+		using LeafPattern::LeafPattern;
+
+	protected:
+		virtual std::pair<size_t, std::shared_ptr<LeafPattern>> single_match(PatternList const& left) const override;
+	};
+
+	class Command : public Argument {
+	public:
+		Command(std::string name, value v = value{false})
+		: Argument(std::move(name), std::move(v))
+		{}
+
+	protected:
+		virtual std::pair<size_t, std::shared_ptr<LeafPattern>> single_match(PatternList const& left) const override;
+	};
+
+	class Option final
+	: public LeafPattern
+	{
+	public:
+		static Option parse(std::string const& option_description);
+
+		Option(std::string shortOption,
+		       std::string longOption,
+		       int argcount = 0,
+		       value v = value{false})
+		: LeafPattern(longOption.empty() ? shortOption : longOption,
+			      std::move(v)),
+		  fShortOption(std::move(shortOption)),
+		  fLongOption(std::move(longOption)),
+		  fArgcount(argcount)
+		{
+			// From Python:
+			//   self.value = None if value is False and argcount else value
+			if (argcount && v.isBool() && !v.asBool()) {
+				setValue(value{});
+			}
+		}
+
+		Option(Option const&) = default;
+		Option(Option&&) = default;
+		Option& operator=(Option const&) = default;
+		Option& operator=(Option&&) = default;
+
+		using LeafPattern::setValue;
+
+		std::string const& longOption() const { return fLongOption; }
+		std::string const& shortOption() const { return fShortOption; }
+		int argCount() const { return fArgcount; }
+
+		virtual size_t hash() const override {
+			size_t seed = LeafPattern::hash();
+			hash_combine(seed, fShortOption);
+			hash_combine(seed, fLongOption);
+			hash_combine(seed, fArgcount);
+			return seed;
+		}
+
+	protected:
+		virtual std::pair<size_t, std::shared_ptr<LeafPattern>> single_match(PatternList const& left) const override;
+
+	private:
+		std::string fShortOption;
+		std::string fLongOption;
+		int fArgcount;
+	};
+
+	class Required : public BranchPattern {
+	public:
+		using BranchPattern::BranchPattern;
+
+		bool match(PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const override;
+	};
+
+	class Optional : public BranchPattern {
+	public:
+		using BranchPattern::BranchPattern;
+
+		bool match(PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const override {
+			for(auto const& pattern : fChildren) {
+				pattern->match(left, collected);
+			}
+			return true;
+		}
+	};
+
+	class OptionsShortcut : public Optional {
+		using Optional::Optional;
+	};
+
+	class OneOrMore : public BranchPattern {
+	public:
+		using BranchPattern::BranchPattern;
+
+		bool match(PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const override;
+	};
+
+	class Either : public BranchPattern {
+	public:
+		using BranchPattern::BranchPattern;
+
+		bool match(PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const override;
+	};
+
+#if 0
+#pragma mark -
+#pragma mark inline implementations
+#endif
+
+	inline std::vector<LeafPattern*> Pattern::leaves()
+	{
+		std::vector<LeafPattern*> ret;
+		collect_leaves(ret);
+		return ret;
+	}
+
+	static inline std::vector<PatternList> transform(PatternList pattern)
+	{
+		std::vector<PatternList> result;
+
+		std::vector<PatternList> groups;
+		groups.emplace_back(std::move(pattern));
+
+		while(!groups.empty()) {
+			// pop off the first element
+			auto children = std::move(groups[0]);
+			groups.erase(groups.begin());
+
+			// find the first branch node in the list
+			auto child_iter = std::find_if(children.begin(), children.end(), [](std::shared_ptr<Pattern> const& p) {
+				return dynamic_cast<BranchPattern const*>(p.get());
+			});
+
+			// no branch nodes left : expansion is complete for this grouping
+			if (child_iter == children.end()) {
+				result.emplace_back(std::move(children));
+				continue;
+			}
+
+			// pop the child from the list
+			auto child = std::move(*child_iter);
+			children.erase(child_iter);
+
+			// expand the branch in the appropriate way
+			if (Either* either = dynamic_cast<Either*>(child.get())) {
+				// "[e] + children" for each child 'e' in Either
+				for(auto const& eitherChild : either->children()) {
+					PatternList group = { eitherChild };
+					group.insert(group.end(), children.begin(), children.end());
+
+					groups.emplace_back(std::move(group));
+				}
+			} else if (OneOrMore* oneOrMore = dynamic_cast<OneOrMore*>(child.get())) {
+				// child.children * 2 + children
+				auto const& subchildren = oneOrMore->children();
+				PatternList group = subchildren;
+				group.insert(group.end(), subchildren.begin(), subchildren.end());
+				group.insert(group.end(), children.begin(), children.end());
+
+				groups.emplace_back(std::move(group));
+			} else { // Required, Optional, OptionsShortcut
+				BranchPattern* branch = dynamic_cast<BranchPattern*>(child.get());
+
+				// child.children + children
+				PatternList group = branch->children();
+				group.insert(group.end(), children.begin(), children.end());
+
+				groups.emplace_back(std::move(group));
+			}
+		}
+
+		return result;
+	}
+
+	inline void BranchPattern::fix_repeating_arguments()
+	{
+		std::vector<PatternList> either = transform(children());
+		for(auto const& group : either) {
+			// use multiset to help identify duplicate entries
+			std::unordered_multiset<std::shared_ptr<Pattern>, PatternHasher> group_set {group.begin(), group.end()};
+			for(auto const& e : group_set) {
+				if (group_set.count(e) == 1)
+					continue;
+
+				LeafPattern* leaf = dynamic_cast<LeafPattern*>(e.get());
+				if (!leaf) continue;
+
+				bool ensureList = false;
+				bool ensureInt = false;
+
+				if (dynamic_cast<Command*>(leaf)) {
+					ensureInt = true;
+				} else if (dynamic_cast<Argument*>(leaf)) {
+					ensureList = true;
+				} else if (Option* o = dynamic_cast<Option*>(leaf)) {
+					if (o->argCount()) {
+						ensureList = true;
+					} else {
+						ensureInt = true;
+					}
+				}
+
+				if (ensureList) {
+					std::vector<std::string> newValue;
+					if (leaf->getValue().isString()) {
+						newValue = split(leaf->getValue().asString());
+					}
+					if (!leaf->getValue().isStringList()) {
+						leaf->setValue(value{newValue});
+					}
+				} else if (ensureInt) {
+					leaf->setValue(value{0});
+				}
+			}
+		}
+	}
+
+	inline bool LeafPattern::match(PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const
+	{
+		auto match = single_match(left);
+		if (!match.second) {
+			return false;
+		}
+
+		left.erase(left.begin()+static_cast<std::ptrdiff_t>(match.first));
+
+		auto same_name = std::find_if(collected.begin(), collected.end(), [&](std::shared_ptr<LeafPattern> const& p) {
+			return p->name()==name();
+		});
+		if (getValue().isLong()) {
+			long val = 1;
+			if (same_name == collected.end()) {
+				collected.push_back(match.second);
+				match.second->setValue(value{val});
+			} else if ((**same_name).getValue().isLong()) {
+				val += (**same_name).getValue().asLong();
+				(**same_name).setValue(value{val});
+			} else {
+				(**same_name).setValue(value{val});
+			}
+		} else if (getValue().isStringList()) {
+			std::vector<std::string> val;
+			if (match.second->getValue().isString()) {
+				val.push_back(match.second->getValue().asString());
+			} else if (match.second->getValue().isStringList()) {
+				val = match.second->getValue().asStringList();
+			} else {
+				/// cant be!?
+			}
+
+			if (same_name == collected.end()) {
+				collected.push_back(match.second);
+				match.second->setValue(value{val});
+			} else if ((**same_name).getValue().isStringList()) {
+				std::vector<std::string> const& list = (**same_name).getValue().asStringList();
+				val.insert(val.begin(), list.begin(), list.end());
+				(**same_name).setValue(value{val});
+			} else {
+				(**same_name).setValue(value{val});
+			}
+		} else {
+			collected.push_back(match.second);
+		}
+		return true;
+	}
+
+	inline std::pair<size_t, std::shared_ptr<LeafPattern>> Argument::single_match(PatternList const& left) const
+	{
+		std::pair<size_t, std::shared_ptr<LeafPattern>> ret {};
+
+		for(size_t i = 0, size = left.size(); i < size; ++i)
+		{
+			auto arg = dynamic_cast<Argument const*>(left[i].get());
+			if (arg) {
+				ret.first = i;
+				ret.second = std::make_shared<Argument>(name(), arg->getValue());
+				break;
+			}
+		}
+
+		return ret;
+	}
+
+	inline std::pair<size_t, std::shared_ptr<LeafPattern>> Command::single_match(PatternList const& left) const
+	{
+		std::pair<size_t, std::shared_ptr<LeafPattern>> ret {};
+
+		for(size_t i = 0, size = left.size(); i < size; ++i)
+		{
+			auto arg = dynamic_cast<Argument const*>(left[i].get());
+			if (arg) {
+				if (name() == arg->getValue()) {
+					ret.first = i;
+					ret.second = std::make_shared<Command>(name(), value{true});
+				}
+				break;
+			}
+		}
+
+		return ret;
+	}
+
+	inline Option Option::parse(std::string const& option_description)
+	{
+		std::string shortOption, longOption;
+		int argcount = 0;
+		value val { false };
+
+		auto double_space = option_description.find("  ");
+		auto options_end = option_description.end();
+		if (double_space != std::string::npos) {
+			options_end = option_description.begin() + static_cast<std::ptrdiff_t>(double_space);
+		}
+
+		static const std::regex pattern {"(-{1,2})?(.*?)([,= ]|$)"};
+		for(std::sregex_iterator i {option_description.begin(), options_end, pattern, std::regex_constants::match_not_null},
+			   e{};
+			i != e;
+			++i)
+		{
+			std::smatch const& match = *i;
+			if (match[1].matched) { // [1] is optional.
+				if (match[1].length()==1) {
+						shortOption = "-" + match[2].str();
+				} else {
+						longOption =  "--" + match[2].str();
+				}
+			} else if (match[2].length() > 0) { // [2] always matches.
+				std::string m = match[2];
+				argcount = 1;
+			} else {
+				// delimeter
+			}
+
+			if (match[3].length() == 0) { // [3] always matches.
+				// Hit end of string. For some reason 'match_not_null' will let us match empty
+				// at the end, and then we'll spin in an infinite loop. So, if we hit an empty
+				// match, we know we must be at the end.
+				break;
+			}
+		}
+
+		if (argcount) {
+			std::smatch match;
+			if (std::regex_search(options_end, option_description.end(),
+						  match,
+						  std::regex{"\\[default: (.*)\\]", std::regex::icase}))
+			{
+				val = match[1].str();
+			}
+		}
+
+		return {std::move(shortOption),
+			std::move(longOption),
+			argcount,
+			std::move(val)};
+	}
+
+	inline std::pair<size_t, std::shared_ptr<LeafPattern>> Option::single_match(PatternList const& left) const
+	{
+		auto thematch = find_if(left.begin(), left.end(), [this](std::shared_ptr<Pattern> const& a) {
+			auto leaf = std::dynamic_pointer_cast<LeafPattern>(a);
+			return leaf && this->name() == leaf->name();
+		});
+		if (thematch == left.end()) {
+			return {};
+		}
+		return { std::distance(left.begin(), thematch), std::dynamic_pointer_cast<LeafPattern>(*thematch) };
+	}
+
+	inline bool Required::match(PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const {
+		auto l = left;
+		auto c = collected;
+		for(auto const& pattern : fChildren) {
+			bool ret = pattern->match(l, c);
+			if (!ret) {
+				// leave (left, collected) untouched
+				return false;
+			}
+		}
+
+		left = std::move(l);
+		collected = std::move(c);
+		return true;
+	}
+
+	inline bool OneOrMore::match(PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const
+	{
+		assert(fChildren.size() == 1);
+
+		auto l = left;
+		auto c = collected;
+
+		bool matched = true;
+		size_t times = 0;
+
+		decltype(l) l_;
+		bool firstLoop = true;
+
+		while (matched) {
+			// could it be that something didn't match but changed l or c?
+			matched = fChildren[0]->match(l, c);
+
+			if (matched)
+				++times;
+
+			if (firstLoop) {
+				firstLoop = false;
+			} else if (l == l_) {
+				break;
+			}
+
+			l_ = l;
+		}
+
+		if (times == 0) {
+			return false;
+		}
+
+		left = std::move(l);
+		collected = std::move(c);
+		return true;
+	}
+
+	inline bool Either::match(PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const
+	{
+		using Outcome = std::pair<PatternList, std::vector<std::shared_ptr<LeafPattern>>>;
+
+		std::vector<Outcome> outcomes;
+
+		for(auto const& pattern : fChildren) {
+			// need a copy so we apply the same one for every iteration
+			auto l = left;
+			auto c = collected;
+			bool matched = pattern->match(l, c);
+			if (matched) {
+				outcomes.emplace_back(std::move(l), std::move(c));
+			}
+		}
+
+		auto min = std::min_element(outcomes.begin(), outcomes.end(), [](Outcome const& o1, Outcome const& o2) {
+			return o1.first.size() < o2.first.size();
+		});
+
+		if (min == outcomes.end()) {
+			// (left, collected) unchanged
+			return false;
+		}
+
+		std::tie(left, collected) = std::move(*min);
+		return true;
+	}
+
+}
+
+#endif

+ 122 - 0
src/external/docopt/docopt_util.h

@@ -0,0 +1,122 @@
+//
+//  docopt_util.h
+//  docopt
+//
+//  Created by Jared Grubb on 2013-11-04.
+//  Copyright (c) 2013 Jared Grubb. All rights reserved.
+//
+
+#ifndef docopt_docopt_util_h
+#define docopt_docopt_util_h
+
+#if DOCTOPT_USE_BOOST_REGEX
+#include <boost/regex.hpp>
+namespace std {
+    using boost::regex;
+    using boost::sregex_token_iterator;
+}
+#else
+#include <regex>
+#endif
+
+#if 0
+#pragma mark -
+#pragma mark General utility
+#endif
+
+namespace {
+	bool starts_with(std::string const& str, std::string const& prefix)
+	{
+		if (str.length() < prefix.length())
+			return false;
+		return std::equal(prefix.begin(), prefix.end(),
+				  str.begin());
+	}
+
+	std::string trim(std::string&& str,
+			 const std::string& whitespace = " \t\n")
+	{
+		const auto strEnd = str.find_last_not_of(whitespace);
+		if (strEnd==std::string::npos)
+			return {}; // no content
+		str.erase(strEnd+1);
+
+		const auto strBegin = str.find_first_not_of(whitespace);
+		str.erase(0, strBegin);
+
+		return std::move(str);
+	}
+
+	std::vector<std::string> split(std::string const& str, size_t pos = 0)
+	{
+		const char* const anySpace = " \t\r\n\v\f";
+
+		std::vector<std::string> ret;
+		while (pos != std::string::npos) {
+			auto start = str.find_first_not_of(anySpace, pos);
+			if (start == std::string::npos) break;
+
+			auto end = str.find_first_of(anySpace, start);
+			auto size = end==std::string::npos ? end : end-start;
+			ret.emplace_back(str.substr(start, size));
+
+			pos = end;
+		}
+
+		return ret;
+	}
+
+	std::tuple<std::string, std::string, std::string> partition(std::string str, std::string const& point)
+	{
+		std::tuple<std::string, std::string, std::string> ret;
+
+		auto i = str.find(point);
+
+		if (i == std::string::npos) {
+			// no match: string goes in 0th spot only
+		} else {
+			std::get<2>(ret) = str.substr(i + point.size());
+			std::get<1>(ret) = point;
+			str.resize(i);
+		}
+		std::get<0>(ret) = std::move(str);
+
+		return ret;
+	}
+
+	template <typename I>
+	std::string join(I iter, I end, std::string const& delim) {
+		if (iter==end)
+			return {};
+
+		std::string ret = *iter;
+		for(++iter; iter!=end; ++iter) {
+			ret.append(delim);
+			ret.append(*iter);
+		}
+		return ret;
+	}
+
+	std::vector<std::string> regex_split(std::string const& text, std::regex const& re)
+	{
+		std::vector<std::string> ret;
+		for (auto it = std::sregex_token_iterator(text.begin(), text.end(), re, -1);
+			it != std::sregex_token_iterator();
+			++it) {
+			ret.emplace_back(*it);
+		}
+		return ret;
+	}
+}
+
+namespace docopt {
+	template <class T>
+	inline void hash_combine(std::size_t& seed, T const& v)
+	{
+		// stolen from boost::hash_combine
+		std::hash<T> hasher;
+		seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
+	}
+}
+
+#endif

+ 341 - 0
src/external/docopt/docopt_value.h

@@ -0,0 +1,341 @@
+//
+//  value.h
+//  docopt
+//
+//  Created by Jared Grubb on 2013-10-14.
+//  Copyright (c) 2013 Jared Grubb. All rights reserved.
+//
+
+#ifndef docopt__value_h_
+#define docopt__value_h_
+
+#include <string>
+#include <vector>
+#include <functional> // std::hash
+#include <iosfwd>
+#include <stdexcept>
+
+namespace docopt {
+
+	/// A generic type to hold the various types that can be produced by docopt.
+	///
+	/// This type can be one of: {bool, long, string, vector<string>}, or empty.
+	struct value {
+		/// An empty value
+		value() {}
+
+		value(std::string);
+		value(std::vector<std::string>);
+		
+		explicit value(bool);
+		explicit value(long);
+		explicit value(int v) : value(static_cast<long>(v)) {}
+
+		~value();
+		value(value const&);
+		value(value&&) noexcept;
+		value& operator=(value const&);
+		value& operator=(value&&) noexcept;
+		
+		// Test if this object has any contents at all
+		explicit operator bool() const { return kind != Kind::Empty; }
+		
+		// Test the type contained by this value object
+		bool isBool()       const { return kind==Kind::Bool; }
+		bool isString()     const { return kind==Kind::String; }
+		bool isLong()       const { return kind==Kind::Long; }
+		bool isStringList() const { return kind==Kind::StringList; }
+
+		// Throws std::invalid_argument if the type does not match
+		bool asBool() const;
+		long asLong() const;
+		std::string const& asString() const;
+		std::vector<std::string> const& asStringList() const;
+
+		size_t hash() const noexcept;
+		
+		// equality is based on hash-equality
+		friend bool operator==(value const&, value const&);
+		friend bool operator!=(value const&, value const&);
+
+	private:
+		enum class Kind {
+			Empty,
+			Bool,
+			Long,
+			String,
+			StringList
+		};
+		
+		union Variant {
+			Variant() {}
+			~Variant() {  /* do nothing; will be destroyed by ~value */ }
+			
+			bool boolValue;
+			long longValue;
+			std::string strValue;
+			std::vector<std::string> strList;
+		};
+		
+		static const char* kindAsString(Kind kind) {
+			switch (kind) {
+				case Kind::Empty: return "empty";
+				case Kind::Bool: return "bool";
+				case Kind::Long: return "long";
+				case Kind::String: return "string";
+				case Kind::StringList: return "string-list";
+			}
+			return "unknown";
+		}
+
+		void throwIfNotKind(Kind expected) const {
+			if (kind == expected)
+				return;
+
+			std::string error = "Illegal cast to ";
+			error += kindAsString(expected);
+			error += "; type is actually ";
+			error += kindAsString(kind);
+			throw std::runtime_error(std::move(error));
+		}
+
+	private:
+		Kind kind = Kind::Empty;
+		Variant variant {};
+	};
+
+	/// Write out the contents to the ostream
+	std::ostream& operator<<(std::ostream&, value const&);
+}
+
+namespace std {
+	template <>
+	struct hash<docopt::value> {
+		size_t operator()(docopt::value const& val) const noexcept {
+			return val.hash();
+		}
+	};
+}
+
+namespace docopt {
+	inline
+	value::value(bool v)
+	: kind(Kind::Bool)
+	{
+		variant.boolValue = v;
+	}
+
+	inline
+	value::value(long v)
+	: kind(Kind::Long)
+	{
+		variant.longValue = v;
+	}
+
+	inline
+	value::value(std::string v)
+	: kind(Kind::String)
+	{
+		new (&variant.strValue) std::string(std::move(v));
+	}
+
+	inline
+	value::value(std::vector<std::string> v)
+	: kind(Kind::StringList)
+	{
+		new (&variant.strList) std::vector<std::string>(std::move(v));
+	}
+
+	inline
+	value::value(value const& other)
+	: kind(other.kind)
+	{
+		switch (kind) {
+			case Kind::String:
+				new (&variant.strValue) std::string(other.variant.strValue);
+				break;
+
+			case Kind::StringList:
+				new (&variant.strList) std::vector<std::string>(other.variant.strList);
+				break;
+
+			case Kind::Bool:
+				variant.boolValue = other.variant.boolValue;
+				break;
+
+			case Kind::Long:
+				variant.longValue = other.variant.longValue;
+				break;
+
+			case Kind::Empty:
+			default:
+				break;
+		}
+	}
+
+	inline
+	value::value(value&& other) noexcept
+	: kind(other.kind)
+	{
+		switch (kind) {
+			case Kind::String:
+				new (&variant.strValue) std::string(std::move(other.variant.strValue));
+				break;
+
+			case Kind::StringList:
+				new (&variant.strList) std::vector<std::string>(std::move(other.variant.strList));
+				break;
+
+			case Kind::Bool:
+				variant.boolValue = other.variant.boolValue;
+				break;
+
+			case Kind::Long:
+				variant.longValue = other.variant.longValue;
+				break;
+
+			case Kind::Empty:
+			default:
+				break;
+		}
+	}
+
+	inline
+	value::~value()
+	{
+		switch (kind) {
+			case Kind::String:
+				variant.strValue.~basic_string();
+				break;
+
+			case Kind::StringList:
+				variant.strList.~vector();
+				break;
+
+			case Kind::Empty:
+			case Kind::Bool:
+			case Kind::Long:
+			default:
+				// trivial dtor
+				break;
+		}
+	}
+
+	inline
+	value& value::operator=(value const& other) {
+		// make a copy and move from it; way easier.
+		return *this = value{other};
+	}
+
+	inline
+	value& value::operator=(value&& other) noexcept {
+		// move of all the types involved is noexcept, so we dont have to worry about 
+		// these two statements throwing, which gives us a consistency guarantee.
+		this->~value();
+		new (this) value(std::move(other));
+
+		return *this;
+	}
+
+	template <class T>
+	void hash_combine(std::size_t& seed, const T& v);
+
+	inline
+	size_t value::hash() const noexcept
+	{
+		switch (kind) {
+			case Kind::String:
+				return std::hash<std::string>()(variant.strValue);
+
+			case Kind::StringList: {
+				size_t seed = std::hash<size_t>()(variant.strList.size());
+				for(auto const& str : variant.strList) {
+					hash_combine(seed, str);
+				}
+				return seed;
+			}
+
+			case Kind::Bool:
+				return std::hash<bool>()(variant.boolValue);
+
+			case Kind::Long:
+				return std::hash<long>()(variant.longValue);
+
+			case Kind::Empty:
+			default:
+				return std::hash<void*>()(nullptr);
+		}
+	}
+
+	inline
+	bool value::asBool() const
+	{
+		throwIfNotKind(Kind::Bool);
+		return variant.boolValue;
+	}
+
+	inline
+	long value::asLong() const
+	{
+		// Attempt to convert a string to a long
+		if (kind == Kind::String) {
+			const std::string& str = variant.strValue;
+			std::size_t pos;
+			const long ret = stol(str, &pos); // Throws if it can't convert
+			if (pos != str.length()) {
+				// The string ended in non-digits.
+				throw std::runtime_error( str + " contains non-numeric characters.");
+			}
+			return ret;
+		}
+		throwIfNotKind(Kind::Long);
+		return variant.longValue;
+	}
+
+	inline
+	std::string const& value::asString() const
+	{
+		throwIfNotKind(Kind::String);
+		return variant.strValue;
+	}
+
+	inline
+	std::vector<std::string> const& value::asStringList() const
+	{
+		throwIfNotKind(Kind::StringList);
+		return variant.strList;
+	}
+
+	inline
+	bool operator==(value const& v1, value const& v2)
+	{
+		if (v1.kind != v2.kind)
+			return false;
+		
+		switch (v1.kind) {
+			case value::Kind::String:
+				return v1.variant.strValue==v2.variant.strValue;
+
+			case value::Kind::StringList:
+				return v1.variant.strList==v2.variant.strList;
+
+			case value::Kind::Bool:
+				return v1.variant.boolValue==v2.variant.boolValue;
+
+			case value::Kind::Long:
+				return v1.variant.longValue==v2.variant.longValue;
+
+			case value::Kind::Empty:
+			default:
+				return true;
+		}
+	}
+
+	inline
+	bool operator!=(value const& v1, value const& v2)
+	{
+		return !(v1 == v2);
+	}
+}
+
+#endif /* defined(docopt__value_h_) */

+ 255 - 0
src/external/jsoncpp/dist/json/json-forwards.h

@@ -0,0 +1,255 @@
+/// Json-cpp amalgated forward header (http://jsoncpp.sourceforge.net/).
+/// It is intended to be used with #include "json/json-forwards.h"
+/// This header provides forward declaration for all JsonCpp types.
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: LICENSE
+// //////////////////////////////////////////////////////////////////////
+
+/*
+The JsonCpp library's source code, including accompanying documentation, 
+tests and demonstration applications, are licensed under the following
+conditions...
+
+The author (Baptiste Lepilleur) explicitly disclaims copyright in all 
+jurisdictions which recognize such a disclaimer. In such jurisdictions, 
+this software is released into the Public Domain.
+
+In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
+2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is
+released under the terms of the MIT License (see below).
+
+In jurisdictions which recognize Public Domain property, the user of this 
+software may choose to accept it either as 1) Public Domain, 2) under the 
+conditions of the MIT License (see below), or 3) under the terms of dual 
+Public Domain/MIT License conditions described here, as they choose.
+
+The MIT License is about as close to Public Domain as a license can get, and is
+described in clear, concise terms at:
+
+   http://en.wikipedia.org/wiki/MIT_License
+   
+The full text of the MIT License follows:
+
+========================================================================
+Copyright (c) 2007-2010 Baptiste Lepilleur
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use, copy,
+modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+========================================================================
+(END LICENSE TEXT)
+
+The MIT license is compatible with both the GPL and commercial
+software, affording one all of the rights of Public Domain with the
+minor nuisance of being required to keep the above copyright notice
+and license text in the source code. Note also that by accepting the
+Public Domain "license" you can re-license your copy using whatever
+license you like.
+
+*/
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: LICENSE
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+#ifndef JSON_FORWARD_AMALGATED_H_INCLUDED
+# define JSON_FORWARD_AMALGATED_H_INCLUDED
+/// If defined, indicates that the source file is amalgated
+/// to prevent private header inclusion.
+#define JSON_IS_AMALGAMATION
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/config.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_CONFIG_H_INCLUDED
+#define JSON_CONFIG_H_INCLUDED
+
+/// If defined, indicates that json library is embedded in CppTL library.
+//# define JSON_IN_CPPTL 1
+
+/// If defined, indicates that json may leverage CppTL library
+//#  define JSON_USE_CPPTL 1
+/// If defined, indicates that cpptl vector based map should be used instead of
+/// std::map
+/// as Value container.
+//#  define JSON_USE_CPPTL_SMALLMAP 1
+
+// If non-zero, the library uses exceptions to report bad input instead of C
+// assertion macros. The default is to use exceptions.
+#ifndef JSON_USE_EXCEPTION
+#define JSON_USE_EXCEPTION 1
+#endif
+
+/// If defined, indicates that the source file is amalgated
+/// to prevent private header inclusion.
+/// Remarks: it is automatically defined in the generated amalgated header.
+// #define JSON_IS_AMALGAMATION
+
+#ifdef JSON_IN_CPPTL
+#include <cpptl/config.h>
+#ifndef JSON_USE_CPPTL
+#define JSON_USE_CPPTL 1
+#endif
+#endif
+
+#ifdef JSON_IN_CPPTL
+#define JSON_API CPPTL_API
+#elif defined(JSON_DLL_BUILD)
+#if defined(_MSC_VER)
+#define JSON_API __declspec(dllexport)
+#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
+#endif // if defined(_MSC_VER)
+#elif defined(JSON_DLL)
+#if defined(_MSC_VER)
+#define JSON_API __declspec(dllimport)
+#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
+#endif // if defined(_MSC_VER)
+#endif // ifdef JSON_IN_CPPTL
+#if !defined(JSON_API)
+#define JSON_API
+#endif
+
+// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
+// integer
+// Storages, and 64 bits integer support is disabled.
+// #define JSON_NO_INT64 1
+
+#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6
+// Microsoft Visual Studio 6 only support conversion from __int64 to double
+// (no conversion from unsigned __int64).
+#define JSON_USE_INT64_DOUBLE_CONVERSION 1
+// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255'
+// characters in the debug information)
+// All projects I've ever seen with VS6 were using this globally (not bothering
+// with pragma push/pop).
+#pragma warning(disable : 4786)
+#endif // if defined(_MSC_VER)  &&  _MSC_VER < 1200 // MSVC 6
+
+#if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008
+/// Indicates that the following function is deprecated.
+#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
+#elif defined(__clang__) && defined(__has_feature)
+#if __has_feature(attribute_deprecated_with_message)
+#define JSONCPP_DEPRECATED(message)  __attribute__ ((deprecated(message)))
+#endif
+#elif defined(__GNUC__) &&  (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
+#define JSONCPP_DEPRECATED(message)  __attribute__ ((deprecated(message)))
+#elif defined(__GNUC__) &&  (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
+#define JSONCPP_DEPRECATED(message)  __attribute__((__deprecated__))
+#endif
+
+#if !defined(JSONCPP_DEPRECATED)
+#define JSONCPP_DEPRECATED(message)
+#endif // if !defined(JSONCPP_DEPRECATED)
+
+namespace Json {
+typedef int Int;
+typedef unsigned int UInt;
+#if defined(JSON_NO_INT64)
+typedef int LargestInt;
+typedef unsigned int LargestUInt;
+#undef JSON_HAS_INT64
+#else                 // if defined(JSON_NO_INT64)
+// For Microsoft Visual use specific types as long long is not supported
+#if defined(_MSC_VER) // Microsoft Visual Studio
+typedef __int64 Int64;
+typedef unsigned __int64 UInt64;
+#else                 // if defined(_MSC_VER) // Other platforms, use long long
+typedef long long int Int64;
+typedef unsigned long long int UInt64;
+#endif // if defined(_MSC_VER)
+typedef Int64 LargestInt;
+typedef UInt64 LargestUInt;
+#define JSON_HAS_INT64
+#endif // if defined(JSON_NO_INT64)
+} // end namespace Json
+
+#endif // JSON_CONFIG_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/config.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/forwards.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_FORWARDS_H_INCLUDED
+#define JSON_FORWARDS_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "config.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+namespace Json {
+
+// writer.h
+class FastWriter;
+class StyledWriter;
+
+// reader.h
+class Reader;
+
+// features.h
+class Features;
+
+// value.h
+typedef unsigned int ArrayIndex;
+class StaticString;
+class Path;
+class PathArgument;
+class Value;
+class ValueIteratorBase;
+class ValueIterator;
+class ValueConstIterator;
+
+} // namespace Json
+
+#endif // JSON_FORWARDS_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/forwards.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+#endif //ifndef JSON_FORWARD_AMALGATED_H_INCLUDED

+ 2014 - 0
src/external/jsoncpp/dist/json/json.h

@@ -0,0 +1,2014 @@
+/// Json-cpp amalgated header (http://jsoncpp.sourceforge.net/).
+/// It is intended to be used with #include "json/json.h"
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: LICENSE
+// //////////////////////////////////////////////////////////////////////
+
+/*
+The JsonCpp library's source code, including accompanying documentation, 
+tests and demonstration applications, are licensed under the following
+conditions...
+
+The author (Baptiste Lepilleur) explicitly disclaims copyright in all 
+jurisdictions which recognize such a disclaimer. In such jurisdictions, 
+this software is released into the Public Domain.
+
+In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
+2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is
+released under the terms of the MIT License (see below).
+
+In jurisdictions which recognize Public Domain property, the user of this 
+software may choose to accept it either as 1) Public Domain, 2) under the 
+conditions of the MIT License (see below), or 3) under the terms of dual 
+Public Domain/MIT License conditions described here, as they choose.
+
+The MIT License is about as close to Public Domain as a license can get, and is
+described in clear, concise terms at:
+
+   http://en.wikipedia.org/wiki/MIT_License
+   
+The full text of the MIT License follows:
+
+========================================================================
+Copyright (c) 2007-2010 Baptiste Lepilleur
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use, copy,
+modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+========================================================================
+(END LICENSE TEXT)
+
+The MIT license is compatible with both the GPL and commercial
+software, affording one all of the rights of Public Domain with the
+minor nuisance of being required to keep the above copyright notice
+and license text in the source code. Note also that by accepting the
+Public Domain "license" you can re-license your copy using whatever
+license you like.
+
+*/
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: LICENSE
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+#ifndef JSON_AMALGATED_H_INCLUDED
+# define JSON_AMALGATED_H_INCLUDED
+/// If defined, indicates that the source file is amalgated
+/// to prevent private header inclusion.
+#define JSON_IS_AMALGAMATION
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/version.h
+// //////////////////////////////////////////////////////////////////////
+
+// DO NOT EDIT. This file is generated by CMake from  "version"
+// and "version.h.in" files.
+// Run CMake configure step to update it.
+#ifndef JSON_VERSION_H_INCLUDED
+# define JSON_VERSION_H_INCLUDED
+
+# define JSONCPP_VERSION_STRING "1.6.2"
+# define JSONCPP_VERSION_MAJOR 1
+# define JSONCPP_VERSION_MINOR 6
+# define JSONCPP_VERSION_PATCH 2
+# define JSONCPP_VERSION_QUALIFIER
+# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8))
+
+#endif // JSON_VERSION_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/version.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/config.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_CONFIG_H_INCLUDED
+#define JSON_CONFIG_H_INCLUDED
+
+/// If defined, indicates that json library is embedded in CppTL library.
+//# define JSON_IN_CPPTL 1
+
+/// If defined, indicates that json may leverage CppTL library
+//#  define JSON_USE_CPPTL 1
+/// If defined, indicates that cpptl vector based map should be used instead of
+/// std::map
+/// as Value container.
+//#  define JSON_USE_CPPTL_SMALLMAP 1
+
+// If non-zero, the library uses exceptions to report bad input instead of C
+// assertion macros. The default is to use exceptions.
+#ifndef JSON_USE_EXCEPTION
+#define JSON_USE_EXCEPTION 1
+#endif
+
+/// If defined, indicates that the source file is amalgated
+/// to prevent private header inclusion.
+/// Remarks: it is automatically defined in the generated amalgated header.
+// #define JSON_IS_AMALGAMATION
+
+#ifdef JSON_IN_CPPTL
+#include <cpptl/config.h>
+#ifndef JSON_USE_CPPTL
+#define JSON_USE_CPPTL 1
+#endif
+#endif
+
+#ifdef JSON_IN_CPPTL
+#define JSON_API CPPTL_API
+#elif defined(JSON_DLL_BUILD)
+#if defined(_MSC_VER)
+#define JSON_API __declspec(dllexport)
+#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
+#endif // if defined(_MSC_VER)
+#elif defined(JSON_DLL)
+#if defined(_MSC_VER)
+#define JSON_API __declspec(dllimport)
+#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
+#endif // if defined(_MSC_VER)
+#endif // ifdef JSON_IN_CPPTL
+#if !defined(JSON_API)
+#define JSON_API
+#endif
+
+// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
+// integer
+// Storages, and 64 bits integer support is disabled.
+// #define JSON_NO_INT64 1
+
+#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6
+// Microsoft Visual Studio 6 only support conversion from __int64 to double
+// (no conversion from unsigned __int64).
+#define JSON_USE_INT64_DOUBLE_CONVERSION 1
+// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255'
+// characters in the debug information)
+// All projects I've ever seen with VS6 were using this globally (not bothering
+// with pragma push/pop).
+#pragma warning(disable : 4786)
+#endif // if defined(_MSC_VER)  &&  _MSC_VER < 1200 // MSVC 6
+
+#if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008
+/// Indicates that the following function is deprecated.
+#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
+#elif defined(__clang__) && defined(__has_feature)
+#if __has_feature(attribute_deprecated_with_message)
+#define JSONCPP_DEPRECATED(message)  __attribute__ ((deprecated(message)))
+#endif
+#elif defined(__GNUC__) &&  (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
+#define JSONCPP_DEPRECATED(message)  __attribute__ ((deprecated(message)))
+#elif defined(__GNUC__) &&  (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
+#define JSONCPP_DEPRECATED(message)  __attribute__((__deprecated__))
+#endif
+
+#if !defined(JSONCPP_DEPRECATED)
+#define JSONCPP_DEPRECATED(message)
+#endif // if !defined(JSONCPP_DEPRECATED)
+
+namespace Json {
+    typedef int Int;
+    typedef unsigned int UInt;
+#if defined(JSON_NO_INT64)
+    typedef int LargestInt;
+    typedef unsigned int LargestUInt;
+  #undef JSON_HAS_INT64
+#else                 // if defined(JSON_NO_INT64)
+  }
+  #include <cstdint>  // Amazon change to fix gcc build.
+  namespace Json {
+      typedef int64_t Int64;
+      typedef uint64_t UInt64;
+      typedef Int64 LargestInt;
+      typedef UInt64 LargestUInt;
+  #define JSON_HAS_INT64
+#endif // if defined(JSON_NO_INT64)
+} // end namespace Json
+
+#endif // JSON_CONFIG_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/config.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/forwards.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_FORWARDS_H_INCLUDED
+#define JSON_FORWARDS_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "config.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+namespace Json {
+
+// writer.h
+class FastWriter;
+class StyledWriter;
+
+// reader.h
+class Reader;
+
+// features.h
+class Features;
+
+// value.h
+typedef unsigned int ArrayIndex;
+class StaticString;
+class Path;
+class PathArgument;
+class Value;
+class ValueIteratorBase;
+class ValueIterator;
+class ValueConstIterator;
+
+} // namespace Json
+
+#endif // JSON_FORWARDS_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/forwards.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/features.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef CPPTL_JSON_FEATURES_H_INCLUDED
+#define CPPTL_JSON_FEATURES_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "forwards.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+namespace Json {
+
+/** \brief Configuration passed to reader and writer.
+ * This configuration object can be used to force the Reader or Writer
+ * to behave in a standard conforming way.
+ */
+class JSON_API Features {
+public:
+  /** \brief A configuration that allows all features and assumes all strings
+   * are UTF-8.
+   * - C & C++ comments are allowed
+   * - Root object can be any JSON value
+   * - Assumes Value strings are encoded in UTF-8
+   */
+  static Features all();
+
+  /** \brief A configuration that is strictly compatible with the JSON
+   * specification.
+   * - Comments are forbidden.
+   * - Root object must be either an array or an object value.
+   * - Assumes Value strings are encoded in UTF-8
+   */
+  static Features strictMode();
+
+  /** \brief Initialize the configuration like JsonConfig::allFeatures;
+   */
+  Features();
+
+  /// \c true if comments are allowed. Default: \c true.
+  bool allowComments_;
+
+  /// \c true if root must be either an array or an object value. Default: \c
+  /// false.
+  bool strictRoot_;
+
+  /// \c true if dropped null placeholders are allowed. Default: \c false.
+  bool allowDroppedNullPlaceholders_;
+
+  /// \c true if numeric object key are allowed. Default: \c false.
+  bool allowNumericKeys_;
+};
+
+} // namespace Json
+
+#endif // CPPTL_JSON_FEATURES_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/features.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/value.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef CPPTL_JSON_H_INCLUDED
+#define CPPTL_JSON_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "forwards.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+#include <string>
+#include <vector>
+#include <exception>
+
+#ifndef JSON_USE_CPPTL_SMALLMAP
+#include <map>
+#else
+#include <cpptl/smallmap.h>
+#endif
+#ifdef JSON_USE_CPPTL
+#include <cpptl/forwards.h>
+#endif
+
+// Disable warning C4251: <data member>: <type> needs to have dll-interface to
+// be used by...
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(push)
+#pragma warning(disable : 4251)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+/** \brief JSON (JavaScript Object Notation).
+ */
+namespace Json {
+
+/** Base class for all exceptions we throw.
+ *
+ * We use nothing but these internally. Of course, STL can throw others.
+ */
+class JSON_API Exception;
+/** Exceptions which the user cannot easily avoid.
+ *
+ * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input
+ * 
+ * \remark derived from Json::Exception
+ */
+class JSON_API RuntimeError;
+/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros.
+ *
+ * These are precondition-violations (user bugs) and internal errors (our bugs).
+ * 
+ * \remark derived from Json::Exception
+ */
+class JSON_API LogicError;
+
+/// used internally
+void throwRuntimeError(std::string const& msg);
+/// used internally
+void throwLogicError(std::string const& msg);
+
+/** \brief Type of the value held by a Value object.
+ */
+enum ValueType {
+  nullValue = 0, ///< 'null' value
+  intValue,      ///< signed integer value
+  uintValue,     ///< unsigned integer value
+  realValue,     ///< double value
+  stringValue,   ///< UTF-8 string value
+  booleanValue,  ///< bool value
+  arrayValue,    ///< array value (ordered list)
+  objectValue    ///< object value (collection of name/value pairs).
+};
+
+enum CommentPlacement {
+  commentBefore = 0,      ///< a comment placed on the line before a value
+  commentAfterOnSameLine, ///< a comment just after a value on the same line
+  commentAfter, ///< a comment on the line after a value (only make sense for
+  /// root value)
+  numberOfCommentPlacement
+};
+
+//# ifdef JSON_USE_CPPTL
+//   typedef CppTL::AnyEnumerator<const char *> EnumMemberNames;
+//   typedef CppTL::AnyEnumerator<const Value &> EnumValues;
+//# endif
+
+/** \brief Lightweight wrapper to tag static string.
+ *
+ * Value constructor and objectValue member assignement takes advantage of the
+ * StaticString and avoid the cost of string duplication when storing the
+ * string or the member name.
+ *
+ * Example of usage:
+ * \code
+ * Json::Value aValue( StaticString("some text") );
+ * Json::Value object;
+ * static const StaticString code("code");
+ * object[code] = 1234;
+ * \endcode
+ */
+class JSON_API StaticString {
+public:
+  explicit StaticString(const char* czstring) : c_str_(czstring) {}
+
+  operator const char*() const { return c_str_; }
+
+  const char* c_str() const { return c_str_; }
+
+private:
+  const char* c_str_;
+};
+
+/** \brief Represents a <a HREF="http://www.json.org">JSON</a> value.
+ *
+ * This class is a discriminated union wrapper that can represents a:
+ * - signed integer [range: Value::minInt - Value::maxInt]
+ * - unsigned integer (range: 0 - Value::maxUInt)
+ * - double
+ * - UTF-8 string
+ * - boolean
+ * - 'null'
+ * - an ordered list of Value
+ * - collection of name/value pairs (javascript object)
+ *
+ * The type of the held value is represented by a #ValueType and
+ * can be obtained using type().
+ *
+ * Values of an #objectValue or #arrayValue can be accessed using operator[]()
+ * methods.
+ * Non-const methods will automatically create the a #nullValue element
+ * if it does not exist.
+ * The sequence of an #arrayValue will be automatically resized and initialized
+ * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue.
+ *
+ * The get() methods can be used to obtain default value in the case the
+ * required element does not exist.
+ *
+ * It is possible to iterate over the list of a #objectValue values using
+ * the getMemberNames() method.
+ *
+ * \note #Value string-length fit in size_t, but keys must be < 2^30.
+ * (The reason is an implementation detail.) A #CharReader will raise an
+ * exception if a bound is exceeded to avoid security holes in your app,
+ * but the Value API does *not* check bounds. That is the responsibility
+ * of the caller.
+ */
+class JSON_API Value {
+  friend class ValueIteratorBase;
+public:
+  typedef std::vector<std::string> Members;
+  typedef ValueIterator iterator;
+  typedef ValueConstIterator const_iterator;
+  typedef Json::UInt UInt;
+  typedef Json::Int Int;
+#if defined(JSON_HAS_INT64)
+  typedef Json::UInt64 UInt64;
+  typedef Json::Int64 Int64;
+#endif // defined(JSON_HAS_INT64)
+  typedef Json::LargestInt LargestInt;
+  typedef Json::LargestUInt LargestUInt;
+  typedef Json::ArrayIndex ArrayIndex;
+
+  static const Value& null;  ///< We regret this reference to a global instance; prefer the simpler Value().
+  static const Value& nullRef;  ///< just a kludge for binary-compatibility; same as null
+  /// Minimum signed integer value that can be stored in a Json::Value.
+  static const LargestInt minLargestInt;
+  /// Maximum signed integer value that can be stored in a Json::Value.
+  static const LargestInt maxLargestInt;
+  /// Maximum unsigned integer value that can be stored in a Json::Value.
+  static const LargestUInt maxLargestUInt;
+
+  /// Minimum signed int value that can be stored in a Json::Value.
+  static const Int minInt;
+  /// Maximum signed int value that can be stored in a Json::Value.
+  static const Int maxInt;
+  /// Maximum unsigned int value that can be stored in a Json::Value.
+  static const UInt maxUInt;
+
+#if defined(JSON_HAS_INT64)
+  /// Minimum signed 64 bits int value that can be stored in a Json::Value.
+  static const Int64 minInt64;
+  /// Maximum signed 64 bits int value that can be stored in a Json::Value.
+  static const Int64 maxInt64;
+  /// Maximum unsigned 64 bits int value that can be stored in a Json::Value.
+  static const UInt64 maxUInt64;
+#endif // defined(JSON_HAS_INT64)
+
+private:
+#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
+  class CZString {
+  public:
+    enum DuplicationPolicy {
+      noDuplication = 0,
+      duplicate,
+      duplicateOnCopy
+    };
+    CZString(ArrayIndex index);
+    CZString(char const* str, unsigned length, DuplicationPolicy allocate);
+    CZString(CZString const& other);
+    ~CZString();
+    CZString& operator=(CZString other);
+    bool operator<(CZString const& other) const;
+    bool operator==(CZString const& other) const;
+    ArrayIndex index() const;
+    //const char* c_str() const; ///< \deprecated
+    char const* data() const;
+    unsigned length() const;
+    bool isStaticString() const;
+
+  private:
+    void swap(CZString& other);
+
+    struct StringStorage {
+      unsigned policy_: 2;
+      unsigned length_: 30; // 1GB max
+    };
+
+    char const* cstr_;  // actually, a prefixed string, unless policy is noDup
+    union {
+      ArrayIndex index_;
+      StringStorage storage_;
+    };
+  };
+
+public:
+#ifndef JSON_USE_CPPTL_SMALLMAP
+  typedef std::map<CZString, Value> ObjectValues;
+#else
+  typedef CppTL::SmallMap<CZString, Value> ObjectValues;
+#endif // ifndef JSON_USE_CPPTL_SMALLMAP
+#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
+
+public:
+  /** \brief Create a default Value of the given type.
+
+    This is a very useful constructor.
+    To create an empty array, pass arrayValue.
+    To create an empty object, pass objectValue.
+    Another Value can then be set to this one by assignment.
+This is useful since clear() and resize() will not alter types.
+
+    Examples:
+\code
+Json::Value null_value; // null
+Json::Value arr_value(Json::arrayValue); // []
+Json::Value obj_value(Json::objectValue); // {}
+\endcode
+  */
+  Value(ValueType type = nullValue);
+  Value(Int value);
+  Value(UInt value);
+#if defined(JSON_HAS_INT64)
+  Value(Int64 value);
+  Value(UInt64 value);
+#endif // if defined(JSON_HAS_INT64)
+  Value(double value);
+  Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.)
+  Value(const char* beginValue, const char* endValue); ///< Copy all, incl zeroes.
+  /** \brief Constructs a value from a static string.
+
+   * Like other value string constructor but do not duplicate the string for
+   * internal storage. The given string must remain alive after the call to this
+   * constructor.
+   * \note This works only for null-terminated strings. (We cannot change the
+   *   size of this class, so we have nowhere to store the length,
+   *   which might be computed later for various operations.)
+   *
+   * Example of usage:
+   * \code
+   * static StaticString foo("some text");
+   * Json::Value aValue(foo);
+   * \endcode
+   */
+  Value(const StaticString& value);
+  Value(const std::string& value); ///< Copy data() til size(). Embedded zeroes too.
+#ifdef JSON_USE_CPPTL
+  Value(const CppTL::ConstString& value);
+#endif
+  Value(bool value);
+  /// Deep copy.
+  Value(const Value& other);
+  ~Value();
+
+  /// Deep copy, then swap(other).
+  /// \note Over-write existing comments. To preserve comments, use #swapPayload().
+  Value& operator=(Value other);
+  /// Swap everything.
+  void swap(Value& other);
+  /// Swap values but leave comments and source offsets in place.
+  void swapPayload(Value& other);
+
+  ValueType type() const;
+
+  /// Compare payload only, not comments etc.
+  bool operator<(const Value& other) const;
+  bool operator<=(const Value& other) const;
+  bool operator>=(const Value& other) const;
+  bool operator>(const Value& other) const;
+  bool operator==(const Value& other) const;
+  bool operator!=(const Value& other) const;
+  int compare(const Value& other) const;
+
+  const char* asCString() const; ///< Embedded zeroes could cause you trouble!
+  std::string asString() const; ///< Embedded zeroes are possible.
+  /** Get raw char* of string-value.
+   *  \return false if !string. (Seg-fault if str or end are NULL.)
+   */
+  bool getString(
+      char const** str, char const** end) const;
+#ifdef JSON_USE_CPPTL
+  CppTL::ConstString asConstString() const;
+#endif
+  Int asInt() const;
+  UInt asUInt() const;
+#if defined(JSON_HAS_INT64)
+  Int64 asInt64() const;
+  UInt64 asUInt64() const;
+#endif // if defined(JSON_HAS_INT64)
+  LargestInt asLargestInt() const;
+  LargestUInt asLargestUInt() const;
+  float asFloat() const;
+  double asDouble() const;
+  bool asBool() const;
+
+  bool isNull() const;
+  bool isBool() const;
+  bool isInt() const;
+  bool isInt64() const;
+  bool isUInt() const;
+  bool isUInt64() const;
+  bool isIntegral() const;
+  bool isDouble() const;
+  bool isNumeric() const;
+  bool isString() const;
+  bool isArray() const;
+  bool isObject() const;
+
+  bool isConvertibleTo(ValueType other) const;
+
+  /// Number of values in array or object
+  ArrayIndex size() const;
+
+  /// \brief Return true if empty array, empty object, or null;
+  /// otherwise, false.
+  bool empty() const;
+
+  /// Return isNull()
+  bool operator!() const;
+
+  /// Remove all object members and array elements.
+  /// \pre type() is arrayValue, objectValue, or nullValue
+  /// \post type() is unchanged
+  void clear();
+
+  /// Resize the array to size elements.
+  /// New elements are initialized to null.
+  /// May only be called on nullValue or arrayValue.
+  /// \pre type() is arrayValue or nullValue
+  /// \post type() is arrayValue
+  void resize(ArrayIndex size);
+
+  /// Access an array element (zero based index ).
+  /// If the array contains less than index element, then null value are
+  /// inserted
+  /// in the array so that its size is index+1.
+  /// (You may need to say 'value[0u]' to get your compiler to distinguish
+  ///  this from the operator[] which takes a string.)
+  Value& operator[](ArrayIndex index);
+
+  /// Access an array element (zero based index ).
+  /// If the array contains less than index element, then null value are
+  /// inserted
+  /// in the array so that its size is index+1.
+  /// (You may need to say 'value[0u]' to get your compiler to distinguish
+  ///  this from the operator[] which takes a string.)
+  Value& operator[](int index);
+
+  /// Access an array element (zero based index )
+  /// (You may need to say 'value[0u]' to get your compiler to distinguish
+  ///  this from the operator[] which takes a string.)
+  const Value& operator[](ArrayIndex index) const;
+
+  /// Access an array element (zero based index )
+  /// (You may need to say 'value[0u]' to get your compiler to distinguish
+  ///  this from the operator[] which takes a string.)
+  const Value& operator[](int index) const;
+
+  /// If the array contains at least index+1 elements, returns the element
+  /// value,
+  /// otherwise returns defaultValue.
+  Value get(ArrayIndex index, const Value& defaultValue) const;
+  /// Return true if index < size().
+  bool isValidIndex(ArrayIndex index) const;
+  /// \brief Append value to array at the end.
+  ///
+  /// Equivalent to jsonvalue[jsonvalue.size()] = value;
+  Value& append(const Value& value);
+
+  /// Access an object value by name, create a null member if it does not exist.
+  /// \note Because of our implementation, keys are limited to 2^30 -1 chars.
+  ///  Exceeding that will cause an exception.
+  Value& operator[](const char* key);
+  /// Access an object value by name, returns null if there is no member with
+  /// that name.
+  const Value& operator[](const char* key) const;
+  /// Access an object value by name, create a null member if it does not exist.
+  /// \param key may contain embedded nulls.
+  Value& operator[](const std::string& key);
+  /// Access an object value by name, returns null if there is no member with
+  /// that name.
+  /// \param key may contain embedded nulls.
+  const Value& operator[](const std::string& key) const;
+  /** \brief Access an object value by name, create a null member if it does not
+   exist.
+
+   * If the object has no entry for that name, then the member name used to store
+   * the new entry is not duplicated.
+   * Example of use:
+   * \code
+   * Json::Value object;
+   * static const StaticString code("code");
+   * object[code] = 1234;
+   * \endcode
+   */
+  Value& operator[](const StaticString& key);
+#ifdef JSON_USE_CPPTL
+  /// Access an object value by name, create a null member if it does not exist.
+  Value& operator[](const CppTL::ConstString& key);
+  /// Access an object value by name, returns null if there is no member with
+  /// that name.
+  const Value& operator[](const CppTL::ConstString& key) const;
+#endif
+  /// Return the member named key if it exist, defaultValue otherwise.
+  /// \note deep copy
+  Value get(const char* key, const Value& defaultValue) const;
+  /// Return the member named key if it exist, defaultValue otherwise.
+  /// \note deep copy
+  /// \param key may contain embedded nulls.
+  Value get(const char* key, const char* end, const Value& defaultValue) const;
+  /// Return the member named key if it exist, defaultValue otherwise.
+  /// \note deep copy
+  /// \param key may contain embedded nulls.
+  Value get(const std::string& key, const Value& defaultValue) const;
+#ifdef JSON_USE_CPPTL
+  /// Return the member named key if it exist, defaultValue otherwise.
+  /// \note deep copy
+  Value get(const CppTL::ConstString& key, const Value& defaultValue) const;
+#endif
+  /// Most general and efficient version of isMember()const, get()const,
+  /// and operator[]const
+  /// \note As stated elsewhere, behavior is undefined if (end-key) >= 2^30
+  Value const* find(char const* key, char const* end) const;
+  /// Most general and efficient version of object-mutators.
+  /// \note As stated elsewhere, behavior is undefined if (end-key) >= 2^30
+  /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue.
+  Value const* demand(char const* key, char const* end);
+  /// \brief Remove and return the named member.
+  ///
+  /// Do nothing if it did not exist.
+  /// \return the removed Value, or null.
+  /// \pre type() is objectValue or nullValue
+  /// \post type() is unchanged
+  /// \deprecated
+  Value removeMember(const char* key);
+  /// Same as removeMember(const char*)
+  /// \param key may contain embedded nulls.
+  /// \deprecated
+  Value removeMember(const std::string& key);
+  /// Same as removeMember(const char* key, const char* end, Value* removed),
+  /// but 'key' is null-terminated.
+  bool removeMember(const char* key, Value* removed);
+  /** \brief Remove the named map member.
+
+      Update 'removed' iff removed.
+      \param key may contain embedded nulls.
+      \return true iff removed (no exceptions)
+  */
+  bool removeMember(std::string const& key, Value* removed);
+  /// Same as removeMember(std::string const& key, Value* removed)
+  bool removeMember(const char* key, const char* end, Value* removed);
+  /** \brief Remove the indexed array element.
+
+      O(n) expensive operations.
+      Update 'removed' iff removed.
+      \return true iff removed (no exceptions)
+  */
+  bool removeIndex(ArrayIndex i, Value* removed);
+
+  /// Return true if the object has a member named key.
+  /// \note 'key' must be null-terminated.
+  bool isMember(const char* key) const;
+  /// Return true if the object has a member named key.
+  /// \param key may contain embedded nulls.
+  bool isMember(const std::string& key) const;
+  /// Same as isMember(std::string const& key)const
+  bool isMember(const char* key, const char* end) const;
+#ifdef JSON_USE_CPPTL
+  /// Return true if the object has a member named key.
+  bool isMember(const CppTL::ConstString& key) const;
+#endif
+
+  /// \brief Return a list of the member names.
+  ///
+  /// If null, return an empty list.
+  /// \pre type() is objectValue or nullValue
+  /// \post if type() was nullValue, it remains nullValue
+  Members getMemberNames() const;
+
+  //# ifdef JSON_USE_CPPTL
+  //      EnumMemberNames enumMemberNames() const;
+  //      EnumValues enumValues() const;
+  //# endif
+
+  /// \deprecated Always pass len.
+  JSONCPP_DEPRECATED("Use setComment(std::string const&) instead.")
+  void setComment(const char* comment, CommentPlacement placement);
+  /// Comments must be //... or /* ... */
+  void setComment(const char* comment, size_t len, CommentPlacement placement);
+  /// Comments must be //... or /* ... */
+  void setComment(const std::string& comment, CommentPlacement placement);
+  bool hasComment(CommentPlacement placement) const;
+  /// Include delimiters and embedded newlines.
+  std::string getComment(CommentPlacement placement) const;
+
+  std::string toStyledString() const;
+
+  const_iterator begin() const;
+  const_iterator end() const;
+
+  iterator begin();
+  iterator end();
+
+  // Accessors for the [start, limit) range of bytes within the JSON text from
+  // which this value was parsed, if any.
+  void setOffsetStart(size_t start);
+  void setOffsetLimit(size_t limit);
+  size_t getOffsetStart() const;
+  size_t getOffsetLimit() const;
+
+private:
+  void initBasic(ValueType type, bool allocated = false);
+
+  Value& resolveReference(const char* key);
+  Value& resolveReference(const char* key, const char* end);
+
+  struct CommentInfo {
+    CommentInfo();
+    ~CommentInfo();
+
+    void setComment(const char* text, size_t len);
+
+    char* comment_;
+  };
+
+  // struct MemberNamesTransform
+  //{
+  //   typedef const char *result_type;
+  //   const char *operator()( const CZString &name ) const
+  //   {
+  //      return name.c_str();
+  //   }
+  //};
+
+  union ValueHolder {
+    LargestInt int_;
+    LargestUInt uint_;
+    double real_;
+    bool bool_;
+    char* string_;  // actually ptr to unsigned, followed by str, unless !allocated_
+    ObjectValues* map_;
+  } value_;
+  ValueType type_ : 8;
+  unsigned int allocated_ : 1; // Notes: if declared as bool, bitfield is useless.
+                               // If not allocated_, string_ must be null-terminated.
+  CommentInfo* comments_;
+
+  // [start, limit) byte offsets in the source JSON text from which this Value
+  // was extracted.
+  size_t start_;
+  size_t limit_;
+};
+
+/** \brief Experimental and untested: represents an element of the "path" to
+ * access a node.
+ */
+class JSON_API PathArgument {
+public:
+  friend class Path;
+
+  PathArgument();
+  PathArgument(ArrayIndex index);
+  PathArgument(const char* key);
+  PathArgument(const std::string& key);
+
+private:
+  enum Kind {
+    kindNone = 0,
+    kindIndex,
+    kindKey
+  };
+  std::string key_;
+  ArrayIndex index_;
+  Kind kind_;
+};
+
+/** \brief Experimental and untested: represents a "path" to access a node.
+ *
+ * Syntax:
+ * - "." => root node
+ * - ".[n]" => elements at index 'n' of root node (an array value)
+ * - ".name" => member named 'name' of root node (an object value)
+ * - ".name1.name2.name3"
+ * - ".[0][1][2].name1[3]"
+ * - ".%" => member name is provided as parameter
+ * - ".[%]" => index is provied as parameter
+ */
+class JSON_API Path {
+public:
+  Path(const std::string& path,
+       const PathArgument& a1 = PathArgument(),
+       const PathArgument& a2 = PathArgument(),
+       const PathArgument& a3 = PathArgument(),
+       const PathArgument& a4 = PathArgument(),
+       const PathArgument& a5 = PathArgument());
+
+  const Value& resolve(const Value& root) const;
+  Value resolve(const Value& root, const Value& defaultValue) const;
+  /// Creates the "path" to access the specified node and returns a reference on
+  /// the node.
+  Value& make(Value& root) const;
+
+private:
+  typedef std::vector<const PathArgument*> InArgs;
+  typedef std::vector<PathArgument> Args;
+
+  void makePath(const std::string& path, const InArgs& in);
+  void addPathInArg(const std::string& path,
+                    const InArgs& in,
+                    InArgs::const_iterator& itInArg,
+                    PathArgument::Kind kind);
+  void invalidPath(const std::string& path, int location);
+
+  Args args_;
+};
+
+/** \brief base class for Value iterators.
+ *
+ */
+class JSON_API ValueIteratorBase {
+public:
+  typedef std::bidirectional_iterator_tag iterator_category;
+  typedef unsigned int size_t;
+  typedef int difference_type;
+  typedef ValueIteratorBase SelfType;
+
+  bool operator==(const SelfType& other) const { return isEqual(other); }
+
+  bool operator!=(const SelfType& other) const { return !isEqual(other); }
+
+  difference_type operator-(const SelfType& other) const {
+    return other.computeDistance(*this);
+  }
+
+  /// Return either the index or the member name of the referenced value as a
+  /// Value.
+  Value key() const;
+
+  /// Return the index of the referenced Value, or -1 if it is not an arrayValue.
+  UInt index() const;
+
+  /// Return the member name of the referenced Value, or "" if it is not an
+  /// objectValue.
+  /// \note Avoid `c_str()` on result, as embedded zeroes are possible.
+  std::string name() const;
+
+  /// Return the member name of the referenced Value. "" if it is not an
+  /// objectValue.
+  /// \deprecated This cannot be used for UTF-8 strings, since there can be embedded nulls.
+  JSONCPP_DEPRECATED("Use `key = name();` instead.")
+  char const* memberName() const;
+  /// Return the member name of the referenced Value, or NULL if it is not an
+  /// objectValue.
+  /// \note Better version than memberName(). Allows embedded nulls.
+  char const* memberName(char const** end) const;
+
+protected:
+  Value& deref() const;
+
+  void increment();
+
+  void decrement();
+
+  difference_type computeDistance(const SelfType& other) const;
+
+  bool isEqual(const SelfType& other) const;
+
+  void copy(const SelfType& other);
+
+private:
+  Value::ObjectValues::iterator current_;
+  // Indicates that iterator is for a null value.
+  bool isNull_;
+
+public:
+  // For some reason, BORLAND needs these at the end, rather
+  // than earlier. No idea why.
+  ValueIteratorBase();
+  explicit ValueIteratorBase(const Value::ObjectValues::iterator& current);
+};
+
+/** \brief const iterator for object and array value.
+ *
+ */
+class JSON_API ValueConstIterator : public ValueIteratorBase {
+  friend class Value;
+
+public:
+  typedef const Value value_type;
+  //typedef unsigned int size_t;
+  //typedef int difference_type;
+  typedef const Value& reference;
+  typedef const Value* pointer;
+  typedef ValueConstIterator SelfType;
+
+  ValueConstIterator();
+
+private:
+/*! \internal Use by Value to create an iterator.
+ */
+  explicit ValueConstIterator(const Value::ObjectValues::iterator& current);
+public:
+  SelfType& operator=(const ValueIteratorBase& other);
+
+  SelfType operator++(int) {
+    SelfType temp(*this);
+    ++*this;
+    return temp;
+  }
+
+  SelfType operator--(int) {
+    SelfType temp(*this);
+    --*this;
+    return temp;
+  }
+
+  SelfType& operator--() {
+    decrement();
+    return *this;
+  }
+
+  SelfType& operator++() {
+    increment();
+    return *this;
+  }
+
+  reference operator*() const { return deref(); }
+
+  pointer operator->() const { return &deref(); }
+};
+
+/** \brief Iterator for object and array value.
+ */
+class JSON_API ValueIterator : public ValueIteratorBase {
+  friend class Value;
+
+public:
+  typedef Value value_type;
+  typedef unsigned int size_t;
+  typedef int difference_type;
+  typedef Value& reference;
+  typedef Value* pointer;
+  typedef ValueIterator SelfType;
+
+  ValueIterator();
+  ValueIterator(const ValueConstIterator& other);
+  ValueIterator(const ValueIterator& other);
+
+private:
+/*! \internal Use by Value to create an iterator.
+ */
+  explicit ValueIterator(const Value::ObjectValues::iterator& current);
+public:
+  SelfType& operator=(const SelfType& other);
+
+  SelfType operator++(int) {
+    SelfType temp(*this);
+    ++*this;
+    return temp;
+  }
+
+  SelfType operator--(int) {
+    SelfType temp(*this);
+    --*this;
+    return temp;
+  }
+
+  SelfType& operator--() {
+    decrement();
+    return *this;
+  }
+
+  SelfType& operator++() {
+    increment();
+    return *this;
+  }
+
+  reference operator*() const { return deref(); }
+
+  pointer operator->() const { return &deref(); }
+};
+
+} // namespace Json
+
+
+namespace std {
+/// Specialize std::swap() for Json::Value.
+template<>
+inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); }
+}
+
+
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(pop)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+#endif // CPPTL_JSON_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/value.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/reader.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef CPPTL_JSON_READER_H_INCLUDED
+#define CPPTL_JSON_READER_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "features.h"
+#include "value.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+#include <deque>
+#include <iosfwd>
+#include <stack>
+#include <string>
+#include <istream>
+
+// Disable warning C4251: <data member>: <type> needs to have dll-interface to
+// be used by...
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(push)
+#pragma warning(disable : 4251)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+namespace Json {
+
+/** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a
+ *Value.
+ *
+ * \deprecated Use CharReader and CharReaderBuilder.
+ */
+class JSON_API Reader {
+public:
+  typedef char Char;
+  typedef const Char* Location;
+
+  /** \brief An error tagged with where in the JSON text it was encountered.
+   *
+   * The offsets give the [start, limit) range of bytes within the text. Note
+   * that this is bytes, not codepoints.
+   *
+   */
+  struct StructuredError {
+    size_t offset_start;
+    size_t offset_limit;
+    std::string message;
+  };
+
+  /** \brief Constructs a Reader allowing all features
+   * for parsing.
+   */
+  Reader();
+
+  /** \brief Constructs a Reader allowing the specified feature set
+   * for parsing.
+   */
+  Reader(const Features& features);
+
+  /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
+   * document.
+   * \param document UTF-8 encoded string containing the document to read.
+   * \param root [out] Contains the root value of the document if it was
+   *             successfully parsed.
+   * \param collectComments \c true to collect comment and allow writing them
+   * back during
+   *                        serialization, \c false to discard comments.
+   *                        This parameter is ignored if
+   * Features::allowComments_
+   *                        is \c false.
+   * \return \c true if the document was successfully parsed, \c false if an
+   * error occurred.
+   */
+  bool
+  parse(const std::string& document, Value& root, bool collectComments = true);
+
+  /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
+   document.
+   * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the
+   document to read.
+   * \param endDoc Pointer on the end of the UTF-8 encoded string of the
+   document to read.
+   *               Must be >= beginDoc.
+   * \param root [out] Contains the root value of the document if it was
+   *             successfully parsed.
+   * \param collectComments \c true to collect comment and allow writing them
+   back during
+   *                        serialization, \c false to discard comments.
+   *                        This parameter is ignored if
+   Features::allowComments_
+   *                        is \c false.
+   * \return \c true if the document was successfully parsed, \c false if an
+   error occurred.
+   */
+  bool parse(const char* beginDoc,
+             const char* endDoc,
+             Value& root,
+             bool collectComments = true);
+
+  /// \brief Parse from input stream.
+  /// \see Json::operator>>(std::istream&, Json::Value&).
+  bool parse(std::istream& is, Value& root, bool collectComments = true);
+
+  /** \brief Returns a user friendly string that list errors in the parsed
+   * document.
+   * \return Formatted error message with the list of errors with their location
+   * in
+   *         the parsed document. An empty string is returned if no error
+   * occurred
+   *         during parsing.
+   * \deprecated Use getFormattedErrorMessages() instead (typo fix).
+   */
+  JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.")
+  std::string getFormatedErrorMessages() const;
+
+  /** \brief Returns a user friendly string that list errors in the parsed
+   * document.
+   * \return Formatted error message with the list of errors with their location
+   * in
+   *         the parsed document. An empty string is returned if no error
+   * occurred
+   *         during parsing.
+   */
+  std::string getFormattedErrorMessages() const;
+
+  /** \brief Returns a vector of structured erros encounted while parsing.
+   * \return A (possibly empty) vector of StructuredError objects. Currently
+   *         only one error can be returned, but the caller should tolerate
+   * multiple
+   *         errors.  This can occur if the parser recovers from a non-fatal
+   *         parse error and then encounters additional errors.
+   */
+  std::vector<StructuredError> getStructuredErrors() const;
+
+  /** \brief Add a semantic error message.
+   * \param value JSON Value location associated with the error
+   * \param message The error message.
+   * \return \c true if the error was successfully added, \c false if the
+   * Value offset exceeds the document size.
+   */
+  bool pushError(const Value& value, const std::string& message);
+
+  /** \brief Add a semantic error message with extra context.
+   * \param value JSON Value location associated with the error
+   * \param message The error message.
+   * \param extra Additional JSON Value location to contextualize the error
+   * \return \c true if the error was successfully added, \c false if either
+   * Value offset exceeds the document size.
+   */
+  bool pushError(const Value& value, const std::string& message, const Value& extra);
+
+  /** \brief Return whether there are any errors.
+   * \return \c true if there are no errors to report \c false if
+   * errors have occurred.
+   */
+  bool good() const;
+
+private:
+  enum TokenType {
+    tokenEndOfStream = 0,
+    tokenObjectBegin,
+    tokenObjectEnd,
+    tokenArrayBegin,
+    tokenArrayEnd,
+    tokenString,
+    tokenNumber,
+    tokenTrue,
+    tokenFalse,
+    tokenNull,
+    tokenArraySeparator,
+    tokenMemberSeparator,
+    tokenComment,
+    tokenError
+  };
+
+  class Token {
+  public:
+    TokenType type_;
+    Location start_;
+    Location end_;
+  };
+
+  class ErrorInfo {
+  public:
+    Token token_;
+    std::string message_;
+    Location extra_;
+  };
+
+  typedef std::deque<ErrorInfo> Errors;
+
+  bool readToken(Token& token);
+  void skipSpaces();
+  bool match(Location pattern, int patternLength);
+  bool readComment();
+  bool readCStyleComment();
+  bool readCppStyleComment();
+  bool readString();
+  void readNumber();
+  bool readValue();
+  bool readObject(Token& token);
+  bool readArray(Token& token);
+  bool decodeNumber(Token& token);
+  bool decodeNumber(Token& token, Value& decoded);
+  bool decodeString(Token& token);
+  bool decodeString(Token& token, std::string& decoded);
+  bool decodeDouble(Token& token);
+  bool decodeDouble(Token& token, Value& decoded);
+  bool decodeUnicodeCodePoint(Token& token,
+                              Location& current,
+                              Location end,
+                              unsigned int& unicode);
+  bool decodeUnicodeEscapeSequence(Token& token,
+                                   Location& current,
+                                   Location end,
+                                   unsigned int& unicode);
+  bool addError(const std::string& message, Token& token, Location extra = 0);
+  bool recoverFromError(TokenType skipUntilToken);
+  bool addErrorAndRecover(const std::string& message,
+                          Token& token,
+                          TokenType skipUntilToken);
+  void skipUntilSpace();
+  Value& currentValue();
+  Char getNextChar();
+  void
+  getLocationLineAndColumn(Location location, int& line, int& column) const;
+  std::string getLocationLineAndColumn(Location location) const;
+  void addComment(Location begin, Location end, CommentPlacement placement);
+  void skipCommentTokens(Token& token);
+
+  typedef std::stack<Value*> Nodes;
+  Nodes nodes_;
+  Errors errors_;
+  std::string document_;
+  Location begin_;
+  Location end_;
+  Location current_;
+  Location lastValueEnd_;
+  Value* lastValue_;
+  std::string commentsBefore_;
+  Features features_;
+  bool collectComments_;
+};  // Reader
+
+/** Interface for reading JSON from a char array.
+ */
+class JSON_API CharReader {
+public:
+  virtual ~CharReader() {}
+  /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
+   document.
+   * The document must be a UTF-8 encoded string containing the document to read.
+   *
+   * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the
+   document to read.
+   * \param endDoc Pointer on the end of the UTF-8 encoded string of the
+   document to read.
+   *        Must be >= beginDoc.
+   * \param root [out] Contains the root value of the document if it was
+   *             successfully parsed.
+   * \param errs [out] Formatted error messages (if not NULL)
+   *        a user friendly string that lists errors in the parsed
+   * document.
+   * \return \c true if the document was successfully parsed, \c false if an
+   error occurred.
+   */
+  virtual bool parse(
+      char const* beginDoc, char const* endDoc,
+      Value* root, std::string* errs) = 0;
+
+  class Factory {
+  public:
+    virtual ~Factory() {}
+    /** \brief Allocate a CharReader via operator new().
+     * \throw std::exception if something goes wrong (e.g. invalid settings)
+     */
+    virtual CharReader* newCharReader() const = 0;
+  };  // Factory
+};  // CharReader
+
+/** \brief Build a CharReader implementation.
+
+Usage:
+\code
+  using namespace Json;
+  CharReaderBuilder builder;
+  builder["collectComments"] = false;
+  Value value;
+  std::string errs;
+  bool ok = parseFromStream(builder, std::cin, &value, &errs);
+\endcode
+*/
+class JSON_API CharReaderBuilder : public CharReader::Factory {
+public:
+  // Note: We use a Json::Value so that we can add data-members to this class
+  // without a major version bump.
+  /** Configuration of this builder.
+    These are case-sensitive.
+    Available settings (case-sensitive):
+    - `"collectComments": false or true`
+      - true to collect comment and allow writing them
+        back during serialization, false to discard comments.
+        This parameter is ignored if allowComments is false.
+    - `"allowComments": false or true`
+      - true if comments are allowed.
+    - `"strictRoot": false or true`
+      - true if root must be either an array or an object value
+    - `"allowDroppedNullPlaceholders": false or true`
+      - true if dropped null placeholders are allowed. (See StreamWriterBuilder.)
+    - `"allowNumericKeys": false or true`
+      - true if numeric object keys are allowed.
+    - `"allowSingleQuotes": false or true`
+      - true if '' are allowed for strings (both keys and values)
+    - `"stackLimit": integer`
+      - Exceeding stackLimit (recursive depth of `readValue()`) will
+        cause an exception.
+      - This is a security issue (seg-faults caused by deeply nested JSON),
+        so the default is low.
+    - `"failIfExtra": false or true`
+      - If true, `parse()` returns false when extra non-whitespace trails
+        the JSON value in the input string.
+    - `"rejectDupKeys": false or true`
+      - If true, `parse()` returns false when a key is duplicated within an object.
+
+    You can examine 'settings_` yourself
+    to see the defaults. You can also write and read them just like any
+    JSON Value.
+    \sa setDefaults()
+    */
+  Json::Value settings_;
+
+  CharReaderBuilder();
+  virtual ~CharReaderBuilder();
+
+  virtual CharReader* newCharReader() const;
+
+  /** \return true if 'settings' are legal and consistent;
+   *   otherwise, indicate bad settings via 'invalid'.
+   */
+  bool validate(Json::Value* invalid) const;
+
+  /** A simple way to update a specific setting.
+   */
+  Value& operator[](std::string key);
+
+  /** Called by ctor, but you can use this to reset settings_.
+   * \pre 'settings' != NULL (but Json::null is fine)
+   * \remark Defaults:
+   * \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults
+   */
+  static void setDefaults(Json::Value* settings);
+  /** Same as old Features::strictMode().
+   * \pre 'settings' != NULL (but Json::null is fine)
+   * \remark Defaults:
+   * \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode
+   */
+  static void strictMode(Json::Value* settings);
+};
+
+/** Consume entire stream and use its begin/end.
+  * Someday we might have a real StreamReader, but for now this
+  * is convenient.
+  */
+bool JSON_API parseFromStream(
+    CharReader::Factory const&,
+    std::istream&,
+    Value* root, std::string* errs);
+
+/** \brief Read from 'sin' into 'root'.
+
+ Always keep comments from the input JSON.
+
+ This can be used to read a file into a particular sub-object.
+ For example:
+ \code
+ Json::Value root;
+ cin >> root["dir"]["file"];
+ cout << root;
+ \endcode
+ Result:
+ \verbatim
+ {
+ "dir": {
+     "file": {
+     // The input stream JSON would be nested here.
+     }
+ }
+ }
+ \endverbatim
+ \throw std::exception on parse error.
+ \see Json::operator<<()
+*/
+JSON_API std::istream& operator>>(std::istream&, Value&);
+
+} // namespace Json
+
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(pop)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+#endif // CPPTL_JSON_READER_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/reader.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/writer.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_WRITER_H_INCLUDED
+#define JSON_WRITER_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "value.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+#include <vector>
+#include <string>
+#include <ostream>
+
+// Disable warning C4251: <data member>: <type> needs to have dll-interface to
+// be used by...
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(push)
+#pragma warning(disable : 4251)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+namespace Json {
+
+class Value;
+
+/**
+
+Usage:
+\code
+  using namespace Json;
+  void writeToStdout(StreamWriter::Factory const& factory, Value const& value) {
+    std::unique_ptr<StreamWriter> const writer(
+      factory.newStreamWriter());
+    writer->write(value, &std::cout);
+    std::cout << std::endl;  // add lf and flush
+  }
+\endcode
+*/
+class JSON_API StreamWriter {
+protected:
+  std::ostream* sout_;  // not owned; will not delete
+public:
+  StreamWriter();
+  virtual ~StreamWriter();
+  /** Write Value into document as configured in sub-class.
+      Do not take ownership of sout, but maintain a reference during function.
+      \pre sout != NULL
+      \return zero on success (For now, we always return zero, so check the stream instead.)
+      \throw std::exception possibly, depending on configuration
+   */
+  virtual int write(Value const& root, std::ostream* sout) = 0;
+
+  /** \brief A simple abstract factory.
+   */
+  class JSON_API Factory {
+  public:
+    virtual ~Factory();
+    /** \brief Allocate a CharReader via operator new().
+     * \throw std::exception if something goes wrong (e.g. invalid settings)
+     */
+    virtual StreamWriter* newStreamWriter() const = 0;
+  };  // Factory
+};  // StreamWriter
+
+/** \brief Write into stringstream, then return string, for convenience.
+ * A StreamWriter will be created from the factory, used, and then deleted.
+ */
+std::string JSON_API writeString(StreamWriter::Factory const& factory, Value const& root);
+
+
+/** \brief Build a StreamWriter implementation.
+
+Usage:
+\code
+  using namespace Json;
+  Value value = ...;
+  StreamWriterBuilder builder;
+  builder["commentStyle"] = "None";
+  builder["indentation"] = "   ";  // or whatever you like
+  std::unique_ptr<Json::StreamWriter> writer(
+      builder.newStreamWriter());
+  writer->write(value, &std::cout);
+  std::cout << std::endl;  // add lf and flush
+\endcode
+*/
+class JSON_API StreamWriterBuilder : public StreamWriter::Factory {
+public:
+  // Note: We use a Json::Value so that we can add data-members to this class
+  // without a major version bump.
+  /** Configuration of this builder.
+    Available settings (case-sensitive):
+    - "commentStyle": "None" or "All"
+    - "indentation":  "<anything>"
+    - "enableYAMLCompatibility": false or true
+      - slightly change the whitespace around colons
+    - "dropNullPlaceholders": false or true
+      - Drop the "null" string from the writer's output for nullValues.
+        Strictly speaking, this is not valid JSON. But when the output is being
+        fed to a browser's Javascript, it makes for smaller output and the
+        browser can handle the output just fine.
+
+    You can examine 'settings_` yourself
+    to see the defaults. You can also write and read them just like any
+    JSON Value.
+    \sa setDefaults()
+    */
+  Json::Value settings_;
+
+  StreamWriterBuilder();
+  virtual ~StreamWriterBuilder();
+
+  /**
+   * \throw std::exception if something goes wrong (e.g. invalid settings)
+   */
+  virtual StreamWriter* newStreamWriter() const;
+
+  /** \return true if 'settings' are legal and consistent;
+   *   otherwise, indicate bad settings via 'invalid'.
+   */
+  bool validate(Json::Value* invalid) const;
+  /** A simple way to update a specific setting.
+   */
+  Value& operator[](std::string key);
+
+  /** Called by ctor, but you can use this to reset settings_.
+   * \pre 'settings' != NULL (but Json::null is fine)
+   * \remark Defaults:
+   * \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults
+   */
+  static void setDefaults(Json::Value* settings);
+};
+
+/** \brief Abstract class for writers.
+ * \deprecated Use StreamWriter. (And really, this is an implementation detail.)
+ */
+class JSON_API Writer {
+public:
+  virtual ~Writer();
+
+  virtual std::string write(const Value& root) = 0;
+};
+
+/** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format
+ *without formatting (not human friendly).
+ *
+ * The JSON document is written in a single line. It is not intended for 'human'
+ *consumption,
+ * but may be usefull to support feature such as RPC where bandwith is limited.
+ * \sa Reader, Value
+ * \deprecated Use StreamWriterBuilder.
+ */
+class JSON_API FastWriter : public Writer {
+
+public:
+  FastWriter();
+  virtual ~FastWriter() {}
+
+  void enableYAMLCompatibility();
+
+  /** \brief Drop the "null" string from the writer's output for nullValues.
+   * Strictly speaking, this is not valid JSON. But when the output is being
+   * fed to a browser's Javascript, it makes for smaller output and the
+   * browser can handle the output just fine.
+   */
+  void dropNullPlaceholders();
+
+  void omitEndingLineFeed();
+
+public: // overridden from Writer
+  virtual std::string write(const Value& root);
+
+private:
+  void writeValue(const Value& value);
+
+  std::string document_;
+  bool yamlCompatiblityEnabled_;
+  bool dropNullPlaceholders_;
+  bool omitEndingLineFeed_;
+};
+
+/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a
+ *human friendly way.
+ *
+ * The rules for line break and indent are as follow:
+ * - Object value:
+ *     - if empty then print {} without indent and line break
+ *     - if not empty the print '{', line break & indent, print one value per
+ *line
+ *       and then unindent and line break and print '}'.
+ * - Array value:
+ *     - if empty then print [] without indent and line break
+ *     - if the array contains no object value, empty array or some other value
+ *types,
+ *       and all the values fit on one lines, then print the array on a single
+ *line.
+ *     - otherwise, it the values do not fit on one line, or the array contains
+ *       object or non empty array, then print one value per line.
+ *
+ * If the Value have comments then they are outputed according to their
+ *#CommentPlacement.
+ *
+ * \sa Reader, Value, Value::setComment()
+ * \deprecated Use StreamWriterBuilder.
+ */
+class JSON_API StyledWriter : public Writer {
+public:
+  StyledWriter();
+  virtual ~StyledWriter() {}
+
+public: // overridden from Writer
+  /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
+   * \param root Value to serialize.
+   * \return String containing the JSON document that represents the root value.
+   */
+  virtual std::string write(const Value& root);
+
+private:
+  void writeValue(const Value& value);
+  void writeArrayValue(const Value& value);
+  bool isMultineArray(const Value& value);
+  void pushValue(const std::string& value);
+  void writeIndent();
+  void writeWithIndent(const std::string& value);
+  void indent();
+  void unindent();
+  void writeCommentBeforeValue(const Value& root);
+  void writeCommentAfterValueOnSameLine(const Value& root);
+  bool hasCommentForValue(const Value& value);
+  static std::string normalizeEOL(const std::string& text);
+
+  typedef std::vector<std::string> ChildValues;
+
+  ChildValues childValues_;
+  std::string document_;
+  std::string indentString_;
+  int rightMargin_;
+  int indentSize_;
+  bool addChildValues_;
+};
+
+/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a
+ human friendly way,
+     to a stream rather than to a string.
+ *
+ * The rules for line break and indent are as follow:
+ * - Object value:
+ *     - if empty then print {} without indent and line break
+ *     - if not empty the print '{', line break & indent, print one value per
+ line
+ *       and then unindent and line break and print '}'.
+ * - Array value:
+ *     - if empty then print [] without indent and line break
+ *     - if the array contains no object value, empty array or some other value
+ types,
+ *       and all the values fit on one lines, then print the array on a single
+ line.
+ *     - otherwise, it the values do not fit on one line, or the array contains
+ *       object or non empty array, then print one value per line.
+ *
+ * If the Value have comments then they are outputed according to their
+ #CommentPlacement.
+ *
+ * \param indentation Each level will be indented by this amount extra.
+ * \sa Reader, Value, Value::setComment()
+ * \deprecated Use StreamWriterBuilder.
+ */
+class JSON_API StyledStreamWriter {
+public:
+  StyledStreamWriter(std::string indentation = "\t");
+  ~StyledStreamWriter() {}
+
+public:
+  /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
+   * \param out Stream to write to. (Can be ostringstream, e.g.)
+   * \param root Value to serialize.
+   * \note There is no point in deriving from Writer, since write() should not
+   * return a value.
+   */
+  void write(std::ostream& out, const Value& root);
+
+private:
+  void writeValue(const Value& value);
+  void writeArrayValue(const Value& value);
+  bool isMultineArray(const Value& value);
+  void pushValue(const std::string& value);
+  void writeIndent();
+  void writeWithIndent(const std::string& value);
+  void indent();
+  void unindent();
+  void writeCommentBeforeValue(const Value& root);
+  void writeCommentAfterValueOnSameLine(const Value& root);
+  bool hasCommentForValue(const Value& value);
+  static std::string normalizeEOL(const std::string& text);
+
+  typedef std::vector<std::string> ChildValues;
+
+  ChildValues childValues_;
+  std::ostream* document_;
+  std::string indentString_;
+  int rightMargin_;
+  std::string indentation_;
+  bool addChildValues_ : 1;
+  bool indented_ : 1;
+};
+
+#if defined(JSON_HAS_INT64)
+std::string JSON_API valueToString(Int value);
+std::string JSON_API valueToString(UInt value);
+#endif // if defined(JSON_HAS_INT64)
+std::string JSON_API valueToString(LargestInt value);
+std::string JSON_API valueToString(LargestUInt value);
+std::string JSON_API valueToString(double value);
+std::string JSON_API valueToString(bool value);
+std::string JSON_API valueToQuotedString(const char* value);
+
+/// \brief Output using the StyledStreamWriter.
+/// \see Json::operator>>()
+JSON_API std::ostream& operator<<(std::ostream&, const Value& root);
+
+} // namespace Json
+
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(pop)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+#endif // JSON_WRITER_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/writer.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/assertions.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED
+#define CPPTL_JSON_ASSERTIONS_H_INCLUDED
+
+#include <stdlib.h>
+#include <sstream>
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "config.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+/** It should not be possible for a maliciously designed file to
+ *  cause an abort() or seg-fault, so these macros are used only
+ *  for pre-condition violations and internal logic errors.
+ */
+#if JSON_USE_EXCEPTION
+
+// @todo <= add detail about condition in exception
+# define JSON_ASSERT(condition)                                                \
+  {if (!(condition)) {Json::throwLogicError( "assert json failed" );}}
+
+# define JSON_FAIL_MESSAGE(message)                                            \
+  {                                                                            \
+    std::ostringstream oss; oss << message;                                    \
+    Json::throwLogicError(oss.str());                                          \
+    abort();                                                                   \
+  }
+
+#else // JSON_USE_EXCEPTION
+
+# define JSON_ASSERT(condition) assert(condition)
+
+// The call to assert() will show the failure message in debug builds. In
+// release builds we abort, for a core-dump or debugger.
+# define JSON_FAIL_MESSAGE(message)                                            \
+  {                                                                            \
+    std::ostringstream oss; oss << message;                                    \
+    assert(false && oss.str().c_str());                                        \
+    abort();                                                                   \
+  }
+
+
+#endif
+
+#define JSON_ASSERT_MESSAGE(condition, message)                                \
+  if (!(condition)) {                                                          \
+    JSON_FAIL_MESSAGE(message);                                                \
+  }
+
+#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/assertions.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+#endif //ifndef JSON_AMALGATED_H_INCLUDED

+ 5124 - 0
src/external/jsoncpp/dist/jsoncpp.cpp

@@ -0,0 +1,5124 @@
+/// Json-cpp amalgated source (http://jsoncpp.sourceforge.net/).
+/// It is intended to be used with #include "json/json.h"
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: LICENSE
+// //////////////////////////////////////////////////////////////////////
+
+/*
+The JsonCpp library's source code, including accompanying documentation, 
+tests and demonstration applications, are licensed under the following
+conditions...
+
+The author (Baptiste Lepilleur) explicitly disclaims copyright in all 
+jurisdictions which recognize such a disclaimer. In such jurisdictions, 
+this software is released into the Public Domain.
+
+In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
+2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is
+released under the terms of the MIT License (see below).
+
+In jurisdictions which recognize Public Domain property, the user of this 
+software may choose to accept it either as 1) Public Domain, 2) under the 
+conditions of the MIT License (see below), or 3) under the terms of dual 
+Public Domain/MIT License conditions described here, as they choose.
+
+The MIT License is about as close to Public Domain as a license can get, and is
+described in clear, concise terms at:
+
+   http://en.wikipedia.org/wiki/MIT_License
+   
+The full text of the MIT License follows:
+
+========================================================================
+Copyright (c) 2007-2010 Baptiste Lepilleur
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use, copy,
+modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+========================================================================
+(END LICENSE TEXT)
+
+The MIT license is compatible with both the GPL and commercial
+software, affording one all of the rights of Public Domain with the
+minor nuisance of being required to keep the above copyright notice
+and license text in the source code. Note also that by accepting the
+Public Domain "license" you can re-license your copy using whatever
+license you like.
+
+*/
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: LICENSE
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+#include "json/json.h"
+
+#ifndef JSON_IS_AMALGAMATION
+#error "Compile with -I PATH_TO_JSON_DIRECTORY"
+#endif
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: src/lib_json/json_tool.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED
+#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED
+
+/* This header provides common string manipulation support, such as UTF-8,
+ * portable conversion from/to string...
+ *
+ * It is an internal header that must not be exposed.
+ */
+
+namespace Json {
+
+/// Converts a unicode code-point to UTF-8.
+static inline std::string codePointToUTF8(unsigned int cp) {
+  std::string result;
+
+  // based on description from http://en.wikipedia.org/wiki/UTF-8
+
+  if (cp <= 0x7f) {
+    result.resize(1);
+    result[0] = static_cast<char>(cp);
+  } else if (cp <= 0x7FF) {
+    result.resize(2);
+    result[1] = static_cast<char>(0x80 | (0x3f & cp));
+    result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));
+  } else if (cp <= 0xFFFF) {
+    result.resize(3);
+    result[2] = static_cast<char>(0x80 | (0x3f & cp));
+    result[1] = 0x80 | static_cast<char>((0x3f & (cp >> 6)));
+    result[0] = 0xE0 | static_cast<char>((0xf & (cp >> 12)));
+  } else if (cp <= 0x10FFFF) {
+    result.resize(4);
+    result[3] = static_cast<char>(0x80 | (0x3f & cp));
+    result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
+    result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));
+    result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18)));
+  }
+
+  return result;
+}
+
+/// Returns true if ch is a control character (in range [0,32[).
+static inline bool isControlCharacter(char ch) { return ch > 0 && ch <= 0x1F; }
+
+enum {
+  /// Constant that specify the size of the buffer that must be passed to
+  /// uintToString.
+  uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1
+};
+
+// Defines a char buffer for use with uintToString().
+typedef char UIntToStringBuffer[uintToStringBufferSize];
+
+/** Converts an unsigned integer to string.
+ * @param value Unsigned interger to convert to string
+ * @param current Input/Output string buffer.
+ *        Must have at least uintToStringBufferSize chars free.
+ */
+static inline void uintToString(LargestUInt value, char*& current) {
+  *--current = 0;
+  do {
+    *--current = char(value % 10) + '0';
+    value /= 10;
+  } while (value != 0);
+}
+
+/** Change ',' to '.' everywhere in buffer.
+ *
+ * We had a sophisticated way, but it did not work in WinCE.
+ * @see https://github.com/open-source-parsers/jsoncpp/pull/9
+ */
+static inline void fixNumericLocale(char* begin, char* end) {
+  while (begin < end) {
+    if (*begin == ',') {
+      *begin = '.';
+    }
+    ++begin;
+  }
+}
+
+} // namespace Json {
+
+#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: src/lib_json/json_tool.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: src/lib_json/json_reader.cpp
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2011 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include <json/assertions.h>
+#include <json/reader.h>
+#include <json/value.h>
+#include "json_tool.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+#include <utility>
+#include <cstdio>
+#include <cassert>
+#include <cstring>
+#include <istream>
+#include <sstream>
+#include <memory>
+#include <set>
+
+#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below
+#define snprintf _snprintf
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
+// Disable warning about strdup being deprecated.
+#pragma warning(disable : 4996)
+#endif
+
+static int const stackLimit_g = 1000;
+static int       stackDepth_g = 0;  // see readValue()
+
+namespace Json {
+
+#if __cplusplus >= 201103L
+typedef std::unique_ptr<CharReader> CharReaderPtr;
+#else
+typedef std::auto_ptr<CharReader>   CharReaderPtr;
+#endif
+
+// Implementation of class Features
+// ////////////////////////////////
+
+Features::Features()
+    : allowComments_(true), strictRoot_(false),
+      allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) {}
+
+Features Features::all() { return Features(); }
+
+Features Features::strictMode() {
+  Features features;
+  features.allowComments_ = false;
+  features.strictRoot_ = true;
+  features.allowDroppedNullPlaceholders_ = false;
+  features.allowNumericKeys_ = false;
+  return features;
+}
+
+// Implementation of class Reader
+// ////////////////////////////////
+
+static bool containsNewLine(Reader::Location begin, Reader::Location end) {
+  for (; begin < end; ++begin)
+    if (*begin == '\n' || *begin == '\r')
+      return true;
+  return false;
+}
+
+// Class Reader
+// //////////////////////////////////////////////////////////////////
+
+Reader::Reader()
+    : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
+      lastValue_(), commentsBefore_(), features_(Features::all()),
+      collectComments_() {}
+
+Reader::Reader(const Features& features)
+    : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
+      lastValue_(), commentsBefore_(), features_(features), collectComments_() {
+}
+
+bool
+Reader::parse(const std::string& document, Value& root, bool collectComments) {
+  document_ = document;
+  const char* begin = document_.c_str();
+  const char* end = begin + document_.length();
+  return parse(begin, end, root, collectComments);
+}
+
+bool Reader::parse(std::istream& sin, Value& root, bool collectComments) {
+  // std::istream_iterator<char> begin(sin);
+  // std::istream_iterator<char> end;
+  // Those would allow streamed input from a file, if parse() were a
+  // template function.
+
+  // Since std::string is reference-counted, this at least does not
+  // create an extra copy.
+  std::string doc;
+  std::getline(sin, doc, (char)EOF);
+  return parse(doc, root, collectComments);
+}
+
+bool Reader::parse(const char* beginDoc,
+                   const char* endDoc,
+                   Value& root,
+                   bool collectComments) {
+  if (!features_.allowComments_) {
+    collectComments = false;
+  }
+
+  begin_ = beginDoc;
+  end_ = endDoc;
+  collectComments_ = collectComments;
+  current_ = begin_;
+  lastValueEnd_ = 0;
+  lastValue_ = 0;
+  commentsBefore_ = "";
+  errors_.clear();
+  while (!nodes_.empty())
+    nodes_.pop();
+  nodes_.push(&root);
+
+  stackDepth_g = 0;  // Yes, this is bad coding, but options are limited.
+  bool successful = readValue();
+  Token token;
+  skipCommentTokens(token);
+  if (collectComments_ && !commentsBefore_.empty())
+    root.setComment(commentsBefore_, commentAfter);
+  if (features_.strictRoot_) {
+    if (!root.isArray() && !root.isObject()) {
+      // Set error location to start of doc, ideally should be first token found
+      // in doc
+      token.type_ = tokenError;
+      token.start_ = beginDoc;
+      token.end_ = endDoc;
+      addError(
+          "A valid JSON document must be either an array or an object value.",
+          token);
+      return false;
+    }
+  }
+  return successful;
+}
+
+bool Reader::readValue() {
+  // This is a non-reentrant way to support a stackLimit. Terrible!
+  // But this deprecated class has a security problem: Bad input can
+  // cause a seg-fault. This seems like a fair, binary-compatible way
+  // to prevent the problem.
+  if (stackDepth_g >= stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue().");
+  ++stackDepth_g;
+
+  Token token;
+  skipCommentTokens(token);
+  bool successful = true;
+
+  if (collectComments_ && !commentsBefore_.empty()) {
+    currentValue().setComment(commentsBefore_, commentBefore);
+    commentsBefore_ = "";
+  }
+
+  switch (token.type_) {
+  case tokenObjectBegin:
+    successful = readObject(token);
+    currentValue().setOffsetLimit(current_ - begin_);
+    break;
+  case tokenArrayBegin:
+    successful = readArray(token);
+    currentValue().setOffsetLimit(current_ - begin_);
+    break;
+  case tokenNumber:
+    successful = decodeNumber(token);
+    break;
+  case tokenString:
+    successful = decodeString(token);
+    break;
+  case tokenTrue:
+    {
+    Value v(true);
+    currentValue().swapPayload(v);
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+    }
+    break;
+  case tokenFalse:
+    {
+    Value v(false);
+    currentValue().swapPayload(v);
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+    }
+    break;
+  case tokenNull:
+    {
+    Value v;
+    currentValue().swapPayload(v);
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+    }
+    break;
+  case tokenArraySeparator:
+  case tokenObjectEnd:
+  case tokenArrayEnd:
+    if (features_.allowDroppedNullPlaceholders_) {
+      // "Un-read" the current token and mark the current value as a null
+      // token.
+      current_--;
+      Value v;
+      currentValue().swapPayload(v);
+      currentValue().setOffsetStart(current_ - begin_ - 1);
+      currentValue().setOffsetLimit(current_ - begin_);
+      break;
+    } // Else, fall through...
+  default:
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+    return addError("Syntax error: value, object or array expected.", token);
+  }
+
+  if (collectComments_) {
+    lastValueEnd_ = current_;
+    lastValue_ = &currentValue();
+  }
+
+  --stackDepth_g;
+  return successful;
+}
+
+void Reader::skipCommentTokens(Token& token) {
+  if (features_.allowComments_) {
+    do {
+      readToken(token);
+    } while (token.type_ == tokenComment);
+  } else {
+    readToken(token);
+  }
+}
+
+bool Reader::readToken(Token& token) {
+  skipSpaces();
+  token.start_ = current_;
+  Char c = getNextChar();
+  bool ok = true;
+  switch (c) {
+  case '{':
+    token.type_ = tokenObjectBegin;
+    break;
+  case '}':
+    token.type_ = tokenObjectEnd;
+    break;
+  case '[':
+    token.type_ = tokenArrayBegin;
+    break;
+  case ']':
+    token.type_ = tokenArrayEnd;
+    break;
+  case '"':
+    token.type_ = tokenString;
+    ok = readString();
+    break;
+  case '/':
+    token.type_ = tokenComment;
+    ok = readComment();
+    break;
+  case '0':
+  case '1':
+  case '2':
+  case '3':
+  case '4':
+  case '5':
+  case '6':
+  case '7':
+  case '8':
+  case '9':
+  case '-':
+    token.type_ = tokenNumber;
+    readNumber();
+    break;
+  case 't':
+    token.type_ = tokenTrue;
+    ok = match("rue", 3);
+    break;
+  case 'f':
+    token.type_ = tokenFalse;
+    ok = match("alse", 4);
+    break;
+  case 'n':
+    token.type_ = tokenNull;
+    ok = match("ull", 3);
+    break;
+  case ',':
+    token.type_ = tokenArraySeparator;
+    break;
+  case ':':
+    token.type_ = tokenMemberSeparator;
+    break;
+  case 0:
+    token.type_ = tokenEndOfStream;
+    break;
+  default:
+    ok = false;
+    break;
+  }
+  if (!ok)
+    token.type_ = tokenError;
+  token.end_ = current_;
+  return true;
+}
+
+void Reader::skipSpaces() {
+  while (current_ != end_) {
+    Char c = *current_;
+    if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
+      ++current_;
+    else
+      break;
+  }
+}
+
+bool Reader::match(Location pattern, int patternLength) {
+  if (end_ - current_ < patternLength)
+    return false;
+  int index = patternLength;
+  while (index--)
+    if (current_[index] != pattern[index])
+      return false;
+  current_ += patternLength;
+  return true;
+}
+
+bool Reader::readComment() {
+  Location commentBegin = current_ - 1;
+  Char c = getNextChar();
+  bool successful = false;
+  if (c == '*')
+    successful = readCStyleComment();
+  else if (c == '/')
+    successful = readCppStyleComment();
+  if (!successful)
+    return false;
+
+  if (collectComments_) {
+    CommentPlacement placement = commentBefore;
+    if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
+      if (c != '*' || !containsNewLine(commentBegin, current_))
+        placement = commentAfterOnSameLine;
+    }
+
+    addComment(commentBegin, current_, placement);
+  }
+  return true;
+}
+
+static std::string normalizeEOL(Reader::Location begin, Reader::Location end) {
+  std::string normalized;
+  normalized.reserve(end - begin);
+  Reader::Location current = begin;
+  while (current != end) {
+    char c = *current++;
+    if (c == '\r') {
+      if (current != end && *current == '\n')
+         // convert dos EOL
+         ++current;
+      // convert Mac EOL
+      normalized += '\n';
+    } else {
+      normalized += c;
+    }
+  }
+  return normalized;
+}
+
+void
+Reader::addComment(Location begin, Location end, CommentPlacement placement) {
+  assert(collectComments_);
+  const std::string& normalized = normalizeEOL(begin, end);
+  if (placement == commentAfterOnSameLine) {
+    assert(lastValue_ != 0);
+    lastValue_->setComment(normalized, placement);
+  } else {
+    commentsBefore_ += normalized;
+  }
+}
+
+bool Reader::readCStyleComment() {
+  while (current_ != end_) {
+    Char c = getNextChar();
+    if (c == '*' && *current_ == '/')
+      break;
+  }
+  return getNextChar() == '/';
+}
+
+bool Reader::readCppStyleComment() {
+  while (current_ != end_) {
+    Char c = getNextChar();
+    if (c == '\n')
+      break;
+    if (c == '\r') {
+      // Consume DOS EOL. It will be normalized in addComment.
+      if (current_ != end_ && *current_ == '\n')
+        getNextChar();
+      // Break on Moc OS 9 EOL.
+      break;
+    }
+  }
+  return true;
+}
+
+void Reader::readNumber() {
+  const char *p = current_;
+  char c = '0'; // stopgap for already consumed character
+  // integral part
+  while (c >= '0' && c <= '9')
+    c = (current_ = p) < end_ ? *p++ : 0;
+  // fractional part
+  if (c == '.') {
+    c = (current_ = p) < end_ ? *p++ : 0;
+    while (c >= '0' && c <= '9')
+      c = (current_ = p) < end_ ? *p++ : 0;
+  }
+  // exponential part
+  if (c == 'e' || c == 'E') {
+    c = (current_ = p) < end_ ? *p++ : 0;
+    if (c == '+' || c == '-')
+      c = (current_ = p) < end_ ? *p++ : 0;
+    while (c >= '0' && c <= '9')
+      c = (current_ = p) < end_ ? *p++ : 0;
+  }
+}
+
+bool Reader::readString() {
+  Char c = 0;
+  while (current_ != end_) {
+    c = getNextChar();
+    if (c == '\\')
+      getNextChar();
+    else if (c == '"')
+      break;
+  }
+  return c == '"';
+}
+
+bool Reader::readObject(Token& tokenStart) {
+  Token tokenName;
+  std::string name;
+  Value init(objectValue);
+  currentValue().swapPayload(init);
+  currentValue().setOffsetStart(tokenStart.start_ - begin_);
+  while (readToken(tokenName)) {
+    bool initialTokenOk = true;
+    while (tokenName.type_ == tokenComment && initialTokenOk)
+      initialTokenOk = readToken(tokenName);
+    if (!initialTokenOk)
+      break;
+    if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
+      return true;
+    name = "";
+    if (tokenName.type_ == tokenString) {
+      if (!decodeString(tokenName, name))
+        return recoverFromError(tokenObjectEnd);
+    } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
+      Value numberName;
+      if (!decodeNumber(tokenName, numberName))
+        return recoverFromError(tokenObjectEnd);
+      name = numberName.asString();
+    } else {
+      break;
+    }
+
+    Token colon;
+    if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
+      return addErrorAndRecover(
+          "Missing ':' after object member name", colon, tokenObjectEnd);
+    }
+    Value& value = currentValue()[name];
+    nodes_.push(&value);
+    bool ok = readValue();
+    nodes_.pop();
+    if (!ok) // error already set
+      return recoverFromError(tokenObjectEnd);
+
+    Token comma;
+    if (!readToken(comma) ||
+        (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
+         comma.type_ != tokenComment)) {
+      return addErrorAndRecover(
+          "Missing ',' or '}' in object declaration", comma, tokenObjectEnd);
+    }
+    bool finalizeTokenOk = true;
+    while (comma.type_ == tokenComment && finalizeTokenOk)
+      finalizeTokenOk = readToken(comma);
+    if (comma.type_ == tokenObjectEnd)
+      return true;
+  }
+  return addErrorAndRecover(
+      "Missing '}' or object member name", tokenName, tokenObjectEnd);
+}
+
+bool Reader::readArray(Token& tokenStart) {
+  Value init(arrayValue);
+  currentValue().swapPayload(init);
+  currentValue().setOffsetStart(tokenStart.start_ - begin_);
+  skipSpaces();
+  if (*current_ == ']') // empty array
+  {
+    Token endArray;
+    readToken(endArray);
+    return true;
+  }
+  int index = 0;
+  for (;;) {
+    Value& value = currentValue()[index++];
+    nodes_.push(&value);
+    bool ok = readValue();
+    nodes_.pop();
+    if (!ok) // error already set
+      return recoverFromError(tokenArrayEnd);
+
+    Token token;
+    // Accept Comment after last item in the array.
+    ok = readToken(token);
+    while (token.type_ == tokenComment && ok) {
+      ok = readToken(token);
+    }
+    bool badTokenType =
+        (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd);
+    if (!ok || badTokenType) {
+      return addErrorAndRecover(
+          "Missing ',' or ']' in array declaration", token, tokenArrayEnd);
+    }
+    if (token.type_ == tokenArrayEnd)
+      break;
+  }
+  return true;
+}
+
+bool Reader::decodeNumber(Token& token) {
+  Value decoded;
+  if (!decodeNumber(token, decoded))
+    return false;
+  currentValue().swapPayload(decoded);
+  currentValue().setOffsetStart(token.start_ - begin_);
+  currentValue().setOffsetLimit(token.end_ - begin_);
+  return true;
+}
+
+bool Reader::decodeNumber(Token& token, Value& decoded) {
+  // Attempts to parse the number as an integer. If the number is
+  // larger than the maximum supported value of an integer then
+  // we decode the number as a double.
+  Location current = token.start_;
+  bool isNegative = *current == '-';
+  if (isNegative)
+    ++current;
+  // TODO: Help the compiler do the div and mod at compile time or get rid of them.
+  Value::LargestUInt maxIntegerValue =
+      isNegative ? Value::LargestUInt(-Value::minLargestInt)
+                 : Value::maxLargestUInt;
+  Value::LargestUInt threshold = maxIntegerValue / 10;
+  Value::LargestUInt value = 0;
+  while (current < token.end_) {
+    Char c = *current++;
+    if (c < '0' || c > '9')
+      return decodeDouble(token, decoded);
+    Value::UInt digit(c - '0');
+    if (value >= threshold) {
+      // We've hit or exceeded the max value divided by 10 (rounded down). If
+      // a) we've only just touched the limit, b) this is the last digit, and
+      // c) it's small enough to fit in that rounding delta, we're okay.
+      // Otherwise treat this number as a double to avoid overflow.
+      if (value > threshold || current != token.end_ ||
+          digit > maxIntegerValue % 10) {
+        return decodeDouble(token, decoded);
+      }
+    }
+    value = value * 10 + digit;
+  }
+  if (isNegative)
+    decoded = -Value::LargestInt(value);
+  else if (value <= Value::LargestUInt(Value::maxInt))
+    decoded = Value::LargestInt(value);
+  else
+    decoded = value;
+  return true;
+}
+
+bool Reader::decodeDouble(Token& token) {
+  Value decoded;
+  if (!decodeDouble(token, decoded))
+    return false;
+  currentValue().swapPayload(decoded);
+  currentValue().setOffsetStart(token.start_ - begin_);
+  currentValue().setOffsetLimit(token.end_ - begin_);
+  return true;
+}
+
+bool Reader::decodeDouble(Token& token, Value& decoded) {
+  double value = 0;
+  const int bufferSize = 32;
+  int count;
+  int length = int(token.end_ - token.start_);
+
+  // Sanity check to avoid buffer overflow exploits.
+  if (length < 0) {
+    return addError("Unable to parse token length", token);
+  }
+
+  // Avoid using a string constant for the format control string given to
+  // sscanf, as this can cause hard to debug crashes on OS X. See here for more
+  // info:
+  //
+  //     http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html
+  char format[] = "%lf";
+
+  if (length <= bufferSize) {
+    Char buffer[bufferSize + 1];
+    memcpy(buffer, token.start_, length);
+    buffer[length] = 0;
+    count = sscanf(buffer, format, &value);
+  } else {
+    std::string buffer(token.start_, token.end_);
+    count = sscanf(buffer.c_str(), format, &value);
+  }
+
+  if (count != 1)
+    return addError("'" + std::string(token.start_, token.end_) +
+                        "' is not a number.",
+                    token);
+  decoded = value;
+  return true;
+}
+
+bool Reader::decodeString(Token& token) {
+  std::string decoded_string;
+  if (!decodeString(token, decoded_string))
+    return false;
+  Value decoded(decoded_string);
+  currentValue().swapPayload(decoded);
+  currentValue().setOffsetStart(token.start_ - begin_);
+  currentValue().setOffsetLimit(token.end_ - begin_);
+  return true;
+}
+
+bool Reader::decodeString(Token& token, std::string& decoded) {
+  decoded.reserve(token.end_ - token.start_ - 2);
+  Location current = token.start_ + 1; // skip '"'
+  Location end = token.end_ - 1;       // do not include '"'
+  while (current != end) {
+    Char c = *current++;
+    if (c == '"')
+      break;
+    else if (c == '\\') {
+      if (current == end)
+        return addError("Empty escape sequence in string", token, current);
+      Char escape = *current++;
+      switch (escape) {
+      case '"':
+        decoded += '"';
+        break;
+      case '/':
+        decoded += '/';
+        break;
+      case '\\':
+        decoded += '\\';
+        break;
+      case 'b':
+        decoded += '\b';
+        break;
+      case 'f':
+        decoded += '\f';
+        break;
+      case 'n':
+        decoded += '\n';
+        break;
+      case 'r':
+        decoded += '\r';
+        break;
+      case 't':
+        decoded += '\t';
+        break;
+      case 'u': {
+        unsigned int unicode;
+        if (!decodeUnicodeCodePoint(token, current, end, unicode))
+          return false;
+        decoded += codePointToUTF8(unicode);
+      } break;
+      default:
+        return addError("Bad escape sequence in string", token, current);
+      }
+    } else {
+      decoded += c;
+    }
+  }
+  return true;
+}
+
+bool Reader::decodeUnicodeCodePoint(Token& token,
+                                    Location& current,
+                                    Location end,
+                                    unsigned int& unicode) {
+
+  if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
+    return false;
+  if (unicode >= 0xD800 && unicode <= 0xDBFF) {
+    // surrogate pairs
+    if (end - current < 6)
+      return addError(
+          "additional six characters expected to parse unicode surrogate pair.",
+          token,
+          current);
+    unsigned int surrogatePair;
+    if (*(current++) == '\\' && *(current++) == 'u') {
+      if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
+        unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
+      } else
+        return false;
+    } else
+      return addError("expecting another \\u token to begin the second half of "
+                      "a unicode surrogate pair",
+                      token,
+                      current);
+  }
+  return true;
+}
+
+bool Reader::decodeUnicodeEscapeSequence(Token& token,
+                                         Location& current,
+                                         Location end,
+                                         unsigned int& unicode) {
+  if (end - current < 4)
+    return addError(
+        "Bad unicode escape sequence in string: four digits expected.",
+        token,
+        current);
+  unicode = 0;
+  for (int index = 0; index < 4; ++index) {
+    Char c = *current++;
+    unicode *= 16;
+    if (c >= '0' && c <= '9')
+      unicode += c - '0';
+    else if (c >= 'a' && c <= 'f')
+      unicode += c - 'a' + 10;
+    else if (c >= 'A' && c <= 'F')
+      unicode += c - 'A' + 10;
+    else
+      return addError(
+          "Bad unicode escape sequence in string: hexadecimal digit expected.",
+          token,
+          current);
+  }
+  return true;
+}
+
+bool
+Reader::addError(const std::string& message, Token& token, Location extra) {
+  ErrorInfo info;
+  info.token_ = token;
+  info.message_ = message;
+  info.extra_ = extra;
+  errors_.push_back(info);
+  return false;
+}
+
+bool Reader::recoverFromError(TokenType skipUntilToken) {
+  int errorCount = int(errors_.size());
+  Token skip;
+  for (;;) {
+    if (!readToken(skip))
+      errors_.resize(errorCount); // discard errors caused by recovery
+    if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
+      break;
+  }
+  errors_.resize(errorCount);
+  return false;
+}
+
+bool Reader::addErrorAndRecover(const std::string& message,
+                                Token& token,
+                                TokenType skipUntilToken) {
+  addError(message, token);
+  return recoverFromError(skipUntilToken);
+}
+
+Value& Reader::currentValue() { return *(nodes_.top()); }
+
+Reader::Char Reader::getNextChar() {
+  if (current_ == end_)
+    return 0;
+  return *current_++;
+}
+
+void Reader::getLocationLineAndColumn(Location location,
+                                      int& line,
+                                      int& column) const {
+  Location current = begin_;
+  Location lastLineStart = current;
+  line = 0;
+  while (current < location && current != end_) {
+    Char c = *current++;
+    if (c == '\r') {
+      if (*current == '\n')
+        ++current;
+      lastLineStart = current;
+      ++line;
+    } else if (c == '\n') {
+      lastLineStart = current;
+      ++line;
+    }
+  }
+  // column & line start at 1
+  column = int(location - lastLineStart) + 1;
+  ++line;
+}
+
+std::string Reader::getLocationLineAndColumn(Location location) const {
+  int line, column;
+  getLocationLineAndColumn(location, line, column);
+  char buffer[18 + 16 + 16 + 1];
+#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
+#if defined(WINCE)
+  _snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
+#else
+  sprintf_s(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
+#endif
+#else
+  snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
+#endif
+  return buffer;
+}
+
+// Deprecated. Preserved for backward compatibility
+std::string Reader::getFormatedErrorMessages() const {
+  return getFormattedErrorMessages();
+}
+
+std::string Reader::getFormattedErrorMessages() const {
+  std::string formattedMessage;
+  for (Errors::const_iterator itError = errors_.begin();
+       itError != errors_.end();
+       ++itError) {
+    const ErrorInfo& error = *itError;
+    formattedMessage +=
+        "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
+    formattedMessage += "  " + error.message_ + "\n";
+    if (error.extra_)
+      formattedMessage +=
+          "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
+  }
+  return formattedMessage;
+}
+
+std::vector<Reader::StructuredError> Reader::getStructuredErrors() const {
+  std::vector<Reader::StructuredError> allErrors;
+  for (Errors::const_iterator itError = errors_.begin();
+       itError != errors_.end();
+       ++itError) {
+    const ErrorInfo& error = *itError;
+    Reader::StructuredError structured;
+    structured.offset_start = error.token_.start_ - begin_;
+    structured.offset_limit = error.token_.end_ - begin_;
+    structured.message = error.message_;
+    allErrors.push_back(structured);
+  }
+  return allErrors;
+}
+
+bool Reader::pushError(const Value& value, const std::string& message) {
+  size_t length = end_ - begin_;
+  if(value.getOffsetStart() > length
+    || value.getOffsetLimit() > length)
+    return false;
+  Token token;
+  token.type_ = tokenError;
+  token.start_ = begin_ + value.getOffsetStart();
+  token.end_ = end_ + value.getOffsetLimit();
+  ErrorInfo info;
+  info.token_ = token;
+  info.message_ = message;
+  info.extra_ = 0;
+  errors_.push_back(info);
+  return true;
+}
+
+bool Reader::pushError(const Value& value, const std::string& message, const Value& extra) {
+  size_t length = end_ - begin_;
+  if(value.getOffsetStart() > length
+    || value.getOffsetLimit() > length
+    || extra.getOffsetLimit() > length)
+    return false;
+  Token token;
+  token.type_ = tokenError;
+  token.start_ = begin_ + value.getOffsetStart();
+  token.end_ = begin_ + value.getOffsetLimit();
+  ErrorInfo info;
+  info.token_ = token;
+  info.message_ = message;
+  info.extra_ = begin_ + extra.getOffsetStart();
+  errors_.push_back(info);
+  return true;
+}
+
+bool Reader::good() const {
+  return !errors_.size();
+}
+
+// exact copy of Features
+class OurFeatures {
+public:
+  static OurFeatures all();
+  OurFeatures();
+  bool allowComments_;
+  bool strictRoot_;
+  bool allowDroppedNullPlaceholders_;
+  bool allowNumericKeys_;
+  bool allowSingleQuotes_;
+  bool failIfExtra_;
+  bool rejectDupKeys_;
+  int stackLimit_;
+};  // OurFeatures
+
+// exact copy of Implementation of class Features
+// ////////////////////////////////
+
+OurFeatures::OurFeatures()
+    : allowComments_(true), strictRoot_(false)
+    , allowDroppedNullPlaceholders_(false), allowNumericKeys_(false)
+    , allowSingleQuotes_(false)
+    , failIfExtra_(false)
+{
+}
+
+OurFeatures OurFeatures::all() { return OurFeatures(); }
+
+// Implementation of class Reader
+// ////////////////////////////////
+
+// exact copy of Reader, renamed to OurReader
+class OurReader {
+public:
+  typedef char Char;
+  typedef const Char* Location;
+  struct StructuredError {
+    size_t offset_start;
+    size_t offset_limit;
+    std::string message;
+  };
+
+  OurReader(OurFeatures const& features);
+  bool parse(const char* beginDoc,
+             const char* endDoc,
+             Value& root,
+             bool collectComments = true);
+  std::string getFormattedErrorMessages() const;
+  std::vector<StructuredError> getStructuredErrors() const;
+  bool pushError(const Value& value, const std::string& message);
+  bool pushError(const Value& value, const std::string& message, const Value& extra);
+  bool good() const;
+
+private:
+  OurReader(OurReader const&);  // no impl
+  void operator=(OurReader const&);  // no impl
+
+  enum TokenType {
+    tokenEndOfStream = 0,
+    tokenObjectBegin,
+    tokenObjectEnd,
+    tokenArrayBegin,
+    tokenArrayEnd,
+    tokenString,
+    tokenNumber,
+    tokenTrue,
+    tokenFalse,
+    tokenNull,
+    tokenArraySeparator,
+    tokenMemberSeparator,
+    tokenComment,
+    tokenError
+  };
+
+  class Token {
+  public:
+    TokenType type_;
+    Location start_;
+    Location end_;
+  };
+
+  class ErrorInfo {
+  public:
+    Token token_;
+    std::string message_;
+    Location extra_;
+  };
+
+  typedef std::deque<ErrorInfo> Errors;
+
+  bool readToken(Token& token);
+  void skipSpaces();
+  bool match(Location pattern, int patternLength);
+  bool readComment();
+  bool readCStyleComment();
+  bool readCppStyleComment();
+  bool readString();
+  bool readStringSingleQuote();
+  void readNumber();
+  bool readValue();
+  bool readObject(Token& token);
+  bool readArray(Token& token);
+  bool decodeNumber(Token& token);
+  bool decodeNumber(Token& token, Value& decoded);
+  bool decodeString(Token& token);
+  bool decodeString(Token& token, std::string& decoded);
+  bool decodeDouble(Token& token);
+  bool decodeDouble(Token& token, Value& decoded);
+  bool decodeUnicodeCodePoint(Token& token,
+                              Location& current,
+                              Location end,
+                              unsigned int& unicode);
+  bool decodeUnicodeEscapeSequence(Token& token,
+                                   Location& current,
+                                   Location end,
+                                   unsigned int& unicode);
+  bool addError(const std::string& message, Token& token, Location extra = 0);
+  bool recoverFromError(TokenType skipUntilToken);
+  bool addErrorAndRecover(const std::string& message,
+                          Token& token,
+                          TokenType skipUntilToken);
+  void skipUntilSpace();
+  Value& currentValue();
+  Char getNextChar();
+  void
+  getLocationLineAndColumn(Location location, int& line, int& column) const;
+  std::string getLocationLineAndColumn(Location location) const;
+  void addComment(Location begin, Location end, CommentPlacement placement);
+  void skipCommentTokens(Token& token);
+
+  typedef std::stack<Value*> Nodes;
+  Nodes nodes_;
+  Errors errors_;
+  std::string document_;
+  Location begin_;
+  Location end_;
+  Location current_;
+  Location lastValueEnd_;
+  Value* lastValue_;
+  std::string commentsBefore_;
+  int stackDepth_;
+
+  OurFeatures const features_;
+  bool collectComments_;
+};  // OurReader
+
+// complete copy of Read impl, for OurReader
+
+OurReader::OurReader(OurFeatures const& features)
+    : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
+      lastValue_(), commentsBefore_(), features_(features), collectComments_() {
+}
+
+bool OurReader::parse(const char* beginDoc,
+                   const char* endDoc,
+                   Value& root,
+                   bool collectComments) {
+  if (!features_.allowComments_) {
+    collectComments = false;
+  }
+
+  begin_ = beginDoc;
+  end_ = endDoc;
+  collectComments_ = collectComments;
+  current_ = begin_;
+  lastValueEnd_ = 0;
+  lastValue_ = 0;
+  commentsBefore_ = "";
+  errors_.clear();
+  while (!nodes_.empty())
+    nodes_.pop();
+  nodes_.push(&root);
+
+  stackDepth_ = 0;
+  bool successful = readValue();
+  Token token;
+  skipCommentTokens(token);
+  if (features_.failIfExtra_) {
+    if (token.type_ != tokenError && token.type_ != tokenEndOfStream) {
+      addError("Extra non-whitespace after JSON value.", token);
+      return false;
+    }
+  }
+  if (collectComments_ && !commentsBefore_.empty())
+    root.setComment(commentsBefore_, commentAfter);
+  if (features_.strictRoot_) {
+    if (!root.isArray() && !root.isObject()) {
+      // Set error location to start of doc, ideally should be first token found
+      // in doc
+      token.type_ = tokenError;
+      token.start_ = beginDoc;
+      token.end_ = endDoc;
+      addError(
+          "A valid JSON document must be either an array or an object value.",
+          token);
+      return false;
+    }
+  }
+  return successful;
+}
+
+bool OurReader::readValue() {
+  if (stackDepth_ >= features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue().");
+  ++stackDepth_;
+  Token token;
+  skipCommentTokens(token);
+  bool successful = true;
+
+  if (collectComments_ && !commentsBefore_.empty()) {
+    currentValue().setComment(commentsBefore_, commentBefore);
+    commentsBefore_ = "";
+  }
+
+  switch (token.type_) {
+  case tokenObjectBegin:
+    successful = readObject(token);
+    currentValue().setOffsetLimit(current_ - begin_);
+    break;
+  case tokenArrayBegin:
+    successful = readArray(token);
+    currentValue().setOffsetLimit(current_ - begin_);
+    break;
+  case tokenNumber:
+    successful = decodeNumber(token);
+    break;
+  case tokenString:
+    successful = decodeString(token);
+    break;
+  case tokenTrue:
+    {
+    Value v(true);
+    currentValue().swapPayload(v);
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+    }
+    break;
+  case tokenFalse:
+    {
+    Value v(false);
+    currentValue().swapPayload(v);
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+    }
+    break;
+  case tokenNull:
+    {
+    Value v;
+    currentValue().swapPayload(v);
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+    }
+    break;
+  case tokenArraySeparator:
+  case tokenObjectEnd:
+  case tokenArrayEnd:
+    if (features_.allowDroppedNullPlaceholders_) {
+      // "Un-read" the current token and mark the current value as a null
+      // token.
+      current_--;
+      Value v;
+      currentValue().swapPayload(v);
+      currentValue().setOffsetStart(current_ - begin_ - 1);
+      currentValue().setOffsetLimit(current_ - begin_);
+      break;
+    } // else, fall through ...
+  default:
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+    return addError("Syntax error: value, object or array expected.", token);
+  }
+
+  if (collectComments_) {
+    lastValueEnd_ = current_;
+    lastValue_ = &currentValue();
+  }
+
+  --stackDepth_;
+  return successful;
+}
+
+void OurReader::skipCommentTokens(Token& token) {
+  if (features_.allowComments_) {
+    do {
+      readToken(token);
+    } while (token.type_ == tokenComment);
+  } else {
+    readToken(token);
+  }
+}
+
+bool OurReader::readToken(Token& token) {
+  skipSpaces();
+  token.start_ = current_;
+  Char c = getNextChar();
+  bool ok = true;
+  switch (c) {
+  case '{':
+    token.type_ = tokenObjectBegin;
+    break;
+  case '}':
+    token.type_ = tokenObjectEnd;
+    break;
+  case '[':
+    token.type_ = tokenArrayBegin;
+    break;
+  case ']':
+    token.type_ = tokenArrayEnd;
+    break;
+  case '"':
+    token.type_ = tokenString;
+    ok = readString();
+    break;
+  case '\'':
+    if (features_.allowSingleQuotes_) {
+    token.type_ = tokenString;
+    ok = readStringSingleQuote();
+    break;
+    } // else continue
+  case '/':
+    token.type_ = tokenComment;
+    ok = readComment();
+    break;
+  case '0':
+  case '1':
+  case '2':
+  case '3':
+  case '4':
+  case '5':
+  case '6':
+  case '7':
+  case '8':
+  case '9':
+  case '-':
+    token.type_ = tokenNumber;
+    readNumber();
+    break;
+  case 't':
+    token.type_ = tokenTrue;
+    ok = match("rue", 3);
+    break;
+  case 'f':
+    token.type_ = tokenFalse;
+    ok = match("alse", 4);
+    break;
+  case 'n':
+    token.type_ = tokenNull;
+    ok = match("ull", 3);
+    break;
+  case ',':
+    token.type_ = tokenArraySeparator;
+    break;
+  case ':':
+    token.type_ = tokenMemberSeparator;
+    break;
+  case 0:
+    token.type_ = tokenEndOfStream;
+    break;
+  default:
+    ok = false;
+    break;
+  }
+  if (!ok)
+    token.type_ = tokenError;
+  token.end_ = current_;
+  return true;
+}
+
+void OurReader::skipSpaces() {
+  while (current_ != end_) {
+    Char c = *current_;
+    if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
+      ++current_;
+    else
+      break;
+  }
+}
+
+bool OurReader::match(Location pattern, int patternLength) {
+  if (end_ - current_ < patternLength)
+    return false;
+  int index = patternLength;
+  while (index--)
+    if (current_[index] != pattern[index])
+      return false;
+  current_ += patternLength;
+  return true;
+}
+
+bool OurReader::readComment() {
+  Location commentBegin = current_ - 1;
+  Char c = getNextChar();
+  bool successful = false;
+  if (c == '*')
+    successful = readCStyleComment();
+  else if (c == '/')
+    successful = readCppStyleComment();
+  if (!successful)
+    return false;
+
+  if (collectComments_) {
+    CommentPlacement placement = commentBefore;
+    if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
+      if (c != '*' || !containsNewLine(commentBegin, current_))
+        placement = commentAfterOnSameLine;
+    }
+
+    addComment(commentBegin, current_, placement);
+  }
+  return true;
+}
+
+void
+OurReader::addComment(Location begin, Location end, CommentPlacement placement) {
+  assert(collectComments_);
+  const std::string& normalized = normalizeEOL(begin, end);
+  if (placement == commentAfterOnSameLine) {
+    assert(lastValue_ != 0);
+    lastValue_->setComment(normalized, placement);
+  } else {
+    commentsBefore_ += normalized;
+  }
+}
+
+bool OurReader::readCStyleComment() {
+  while (current_ != end_) {
+    Char c = getNextChar();
+    if (c == '*' && *current_ == '/')
+      break;
+  }
+  return getNextChar() == '/';
+}
+
+bool OurReader::readCppStyleComment() {
+  while (current_ != end_) {
+    Char c = getNextChar();
+    if (c == '\n')
+      break;
+    if (c == '\r') {
+      // Consume DOS EOL. It will be normalized in addComment.
+      if (current_ != end_ && *current_ == '\n')
+        getNextChar();
+      // Break on Moc OS 9 EOL.
+      break;
+    }
+  }
+  return true;
+}
+
+void OurReader::readNumber() {
+  const char *p = current_;
+  char c = '0'; // stopgap for already consumed character
+  // integral part
+  while (c >= '0' && c <= '9')
+    c = (current_ = p) < end_ ? *p++ : 0;
+  // fractional part
+  if (c == '.') {
+    c = (current_ = p) < end_ ? *p++ : 0;
+    while (c >= '0' && c <= '9')
+      c = (current_ = p) < end_ ? *p++ : 0;
+  }
+  // exponential part
+  if (c == 'e' || c == 'E') {
+    c = (current_ = p) < end_ ? *p++ : 0;
+    if (c == '+' || c == '-')
+      c = (current_ = p) < end_ ? *p++ : 0;
+    while (c >= '0' && c <= '9')
+      c = (current_ = p) < end_ ? *p++ : 0;
+  }
+}
+bool OurReader::readString() {
+  Char c = 0;
+  while (current_ != end_) {
+    c = getNextChar();
+    if (c == '\\')
+      getNextChar();
+    else if (c == '"')
+      break;
+  }
+  return c == '"';
+}
+
+
+bool OurReader::readStringSingleQuote() {
+  Char c = 0;
+  while (current_ != end_) {
+    c = getNextChar();
+    if (c == '\\')
+      getNextChar();
+    else if (c == '\'')
+      break;
+  }
+  return c == '\'';
+}
+
+bool OurReader::readObject(Token& tokenStart) {
+  Token tokenName;
+  std::string name;
+  Value init(objectValue);
+  currentValue().swapPayload(init);
+  currentValue().setOffsetStart(tokenStart.start_ - begin_);
+  while (readToken(tokenName)) {
+    bool initialTokenOk = true;
+    while (tokenName.type_ == tokenComment && initialTokenOk)
+      initialTokenOk = readToken(tokenName);
+    if (!initialTokenOk)
+      break;
+    if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
+      return true;
+    name = "";
+    if (tokenName.type_ == tokenString) {
+      if (!decodeString(tokenName, name))
+        return recoverFromError(tokenObjectEnd);
+    } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
+      Value numberName;
+      if (!decodeNumber(tokenName, numberName))
+        return recoverFromError(tokenObjectEnd);
+      name = numberName.asString();
+    } else {
+      break;
+    }
+
+    Token colon;
+    if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
+      return addErrorAndRecover(
+          "Missing ':' after object member name", colon, tokenObjectEnd);
+    }
+    if (name.length() >= (1U<<30)) throwRuntimeError("keylength >= 2^30");
+    if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
+      std::string msg = "Duplicate key: '" + name + "'";
+      return addErrorAndRecover(
+          msg, tokenName, tokenObjectEnd);
+    }
+    Value& value = currentValue()[name];
+    nodes_.push(&value);
+    bool ok = readValue();
+    nodes_.pop();
+    if (!ok) // error already set
+      return recoverFromError(tokenObjectEnd);
+
+    Token comma;
+    if (!readToken(comma) ||
+        (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
+         comma.type_ != tokenComment)) {
+      return addErrorAndRecover(
+          "Missing ',' or '}' in object declaration", comma, tokenObjectEnd);
+    }
+    bool finalizeTokenOk = true;
+    while (comma.type_ == tokenComment && finalizeTokenOk)
+      finalizeTokenOk = readToken(comma);
+    if (comma.type_ == tokenObjectEnd)
+      return true;
+  }
+  return addErrorAndRecover(
+      "Missing '}' or object member name", tokenName, tokenObjectEnd);
+}
+
+bool OurReader::readArray(Token& tokenStart) {
+  Value init(arrayValue);
+  currentValue().swapPayload(init);
+  currentValue().setOffsetStart(tokenStart.start_ - begin_);
+  skipSpaces();
+  if (*current_ == ']') // empty array
+  {
+    Token endArray;
+    readToken(endArray);
+    return true;
+  }
+  int index = 0;
+  for (;;) {
+    Value& value = currentValue()[index++];
+    nodes_.push(&value);
+    bool ok = readValue();
+    nodes_.pop();
+    if (!ok) // error already set
+      return recoverFromError(tokenArrayEnd);
+
+    Token token;
+    // Accept Comment after last item in the array.
+    ok = readToken(token);
+    while (token.type_ == tokenComment && ok) {
+      ok = readToken(token);
+    }
+    bool badTokenType =
+        (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd);
+    if (!ok || badTokenType) {
+      return addErrorAndRecover(
+          "Missing ',' or ']' in array declaration", token, tokenArrayEnd);
+    }
+    if (token.type_ == tokenArrayEnd)
+      break;
+  }
+  return true;
+}
+
+bool OurReader::decodeNumber(Token& token) {
+  Value decoded;
+  if (!decodeNumber(token, decoded))
+    return false;
+  currentValue().swapPayload(decoded);
+  currentValue().setOffsetStart(token.start_ - begin_);
+  currentValue().setOffsetLimit(token.end_ - begin_);
+  return true;
+}
+
+bool OurReader::decodeNumber(Token& token, Value& decoded) {
+  // Attempts to parse the number as an integer. If the number is
+  // larger than the maximum supported value of an integer then
+  // we decode the number as a double.
+  Location current = token.start_;
+  bool isNegative = *current == '-';
+  if (isNegative)
+    ++current;
+  // TODO: Help the compiler do the div and mod at compile time or get rid of them.
+  Value::LargestUInt maxIntegerValue =
+      isNegative ? Value::LargestUInt(-Value::minLargestInt)
+                 : Value::maxLargestUInt;
+  Value::LargestUInt threshold = maxIntegerValue / 10;
+  Value::LargestUInt value = 0;
+  while (current < token.end_) {
+    Char c = *current++;
+    if (c < '0' || c > '9')
+      return decodeDouble(token, decoded);
+    Value::UInt digit(c - '0');
+    if (value >= threshold) {
+      // We've hit or exceeded the max value divided by 10 (rounded down). If
+      // a) we've only just touched the limit, b) this is the last digit, and
+      // c) it's small enough to fit in that rounding delta, we're okay.
+      // Otherwise treat this number as a double to avoid overflow.
+      if (value > threshold || current != token.end_ ||
+          digit > maxIntegerValue % 10) {
+        return decodeDouble(token, decoded);
+      }
+    }
+    value = value * 10 + digit;
+  }
+  if (isNegative)
+    decoded = -Value::LargestInt(value);
+  else if (value <= Value::LargestUInt(Value::maxInt))
+    decoded = Value::LargestInt(value);
+  else
+    decoded = value;
+  return true;
+}
+
+bool OurReader::decodeDouble(Token& token) {
+  Value decoded;
+  if (!decodeDouble(token, decoded))
+    return false;
+  currentValue().swapPayload(decoded);
+  currentValue().setOffsetStart(token.start_ - begin_);
+  currentValue().setOffsetLimit(token.end_ - begin_);
+  return true;
+}
+
+bool OurReader::decodeDouble(Token& token, Value& decoded) {
+  double value = 0;
+  const int bufferSize = 32;
+  int count;
+  int length = int(token.end_ - token.start_);
+
+  // Sanity check to avoid buffer overflow exploits.
+  if (length < 0) {
+    return addError("Unable to parse token length", token);
+  }
+
+  // Avoid using a string constant for the format control string given to
+  // sscanf, as this can cause hard to debug crashes on OS X. See here for more
+  // info:
+  //
+  //     http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html
+  char format[] = "%lf";
+
+  if (length <= bufferSize) {
+    Char buffer[bufferSize + 1];
+    memcpy(buffer, token.start_, length);
+    buffer[length] = 0;
+    count = sscanf(buffer, format, &value);
+  } else {
+    std::string buffer(token.start_, token.end_);
+    count = sscanf(buffer.c_str(), format, &value);
+  }
+
+  if (count != 1)
+    return addError("'" + std::string(token.start_, token.end_) +
+                        "' is not a number.",
+                    token);
+  decoded = value;
+  return true;
+}
+
+bool OurReader::decodeString(Token& token) {
+  std::string decoded_string;
+  if (!decodeString(token, decoded_string))
+    return false;
+  Value decoded(decoded_string);
+  currentValue().swapPayload(decoded);
+  currentValue().setOffsetStart(token.start_ - begin_);
+  currentValue().setOffsetLimit(token.end_ - begin_);
+  return true;
+}
+
+bool OurReader::decodeString(Token& token, std::string& decoded) {
+  decoded.reserve(token.end_ - token.start_ - 2);
+  Location current = token.start_ + 1; // skip '"'
+  Location end = token.end_ - 1;       // do not include '"'
+  while (current != end) {
+    Char c = *current++;
+    if (c == '"')
+      break;
+    else if (c == '\\') {
+      if (current == end)
+        return addError("Empty escape sequence in string", token, current);
+      Char escape = *current++;
+      switch (escape) {
+      case '"':
+        decoded += '"';
+        break;
+      case '/':
+        decoded += '/';
+        break;
+      case '\\':
+        decoded += '\\';
+        break;
+      case 'b':
+        decoded += '\b';
+        break;
+      case 'f':
+        decoded += '\f';
+        break;
+      case 'n':
+        decoded += '\n';
+        break;
+      case 'r':
+        decoded += '\r';
+        break;
+      case 't':
+        decoded += '\t';
+        break;
+      case 'u': {
+        unsigned int unicode;
+        if (!decodeUnicodeCodePoint(token, current, end, unicode))
+          return false;
+        decoded += codePointToUTF8(unicode);
+      } break;
+      default:
+        return addError("Bad escape sequence in string", token, current);
+      }
+    } else {
+      decoded += c;
+    }
+  }
+  return true;
+}
+
+bool OurReader::decodeUnicodeCodePoint(Token& token,
+                                    Location& current,
+                                    Location end,
+                                    unsigned int& unicode) {
+
+  if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
+    return false;
+  if (unicode >= 0xD800 && unicode <= 0xDBFF) {
+    // surrogate pairs
+    if (end - current < 6)
+      return addError(
+          "additional six characters expected to parse unicode surrogate pair.",
+          token,
+          current);
+    unsigned int surrogatePair;
+    if (*(current++) == '\\' && *(current++) == 'u') {
+      if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
+        unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
+      } else
+        return false;
+    } else
+      return addError("expecting another \\u token to begin the second half of "
+                      "a unicode surrogate pair",
+                      token,
+                      current);
+  }
+  return true;
+}
+
+bool OurReader::decodeUnicodeEscapeSequence(Token& token,
+                                         Location& current,
+                                         Location end,
+                                         unsigned int& unicode) {
+  if (end - current < 4)
+    return addError(
+        "Bad unicode escape sequence in string: four digits expected.",
+        token,
+        current);
+  unicode = 0;
+  for (int index = 0; index < 4; ++index) {
+    Char c = *current++;
+    unicode *= 16;
+    if (c >= '0' && c <= '9')
+      unicode += c - '0';
+    else if (c >= 'a' && c <= 'f')
+      unicode += c - 'a' + 10;
+    else if (c >= 'A' && c <= 'F')
+      unicode += c - 'A' + 10;
+    else
+      return addError(
+          "Bad unicode escape sequence in string: hexadecimal digit expected.",
+          token,
+          current);
+  }
+  return true;
+}
+
+bool
+OurReader::addError(const std::string& message, Token& token, Location extra) {
+  ErrorInfo info;
+  info.token_ = token;
+  info.message_ = message;
+  info.extra_ = extra;
+  errors_.push_back(info);
+  return false;
+}
+
+bool OurReader::recoverFromError(TokenType skipUntilToken) {
+  int errorCount = int(errors_.size());
+  Token skip;
+  for (;;) {
+    if (!readToken(skip))
+      errors_.resize(errorCount); // discard errors caused by recovery
+    if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
+      break;
+  }
+  errors_.resize(errorCount);
+  return false;
+}
+
+bool OurReader::addErrorAndRecover(const std::string& message,
+                                Token& token,
+                                TokenType skipUntilToken) {
+  addError(message, token);
+  return recoverFromError(skipUntilToken);
+}
+
+Value& OurReader::currentValue() { return *(nodes_.top()); }
+
+OurReader::Char OurReader::getNextChar() {
+  if (current_ == end_)
+    return 0;
+  return *current_++;
+}
+
+void OurReader::getLocationLineAndColumn(Location location,
+                                      int& line,
+                                      int& column) const {
+  Location current = begin_;
+  Location lastLineStart = current;
+  line = 0;
+  while (current < location && current != end_) {
+    Char c = *current++;
+    if (c == '\r') {
+      if (*current == '\n')
+        ++current;
+      lastLineStart = current;
+      ++line;
+    } else if (c == '\n') {
+      lastLineStart = current;
+      ++line;
+    }
+  }
+  // column & line start at 1
+  column = int(location - lastLineStart) + 1;
+  ++line;
+}
+
+std::string OurReader::getLocationLineAndColumn(Location location) const {
+  int line, column;
+  getLocationLineAndColumn(location, line, column);
+  char buffer[18 + 16 + 16 + 1];
+#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
+#if defined(WINCE)
+  _snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
+#else
+  sprintf_s(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
+#endif
+#else
+  snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
+#endif
+  return buffer;
+}
+
+std::string OurReader::getFormattedErrorMessages() const {
+  std::string formattedMessage;
+  for (Errors::const_iterator itError = errors_.begin();
+       itError != errors_.end();
+       ++itError) {
+    const ErrorInfo& error = *itError;
+    formattedMessage +=
+        "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
+    formattedMessage += "  " + error.message_ + "\n";
+    if (error.extra_)
+      formattedMessage +=
+          "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
+  }
+  return formattedMessage;
+}
+
+std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {
+  std::vector<OurReader::StructuredError> allErrors;
+  for (Errors::const_iterator itError = errors_.begin();
+       itError != errors_.end();
+       ++itError) {
+    const ErrorInfo& error = *itError;
+    OurReader::StructuredError structured;
+    structured.offset_start = error.token_.start_ - begin_;
+    structured.offset_limit = error.token_.end_ - begin_;
+    structured.message = error.message_;
+    allErrors.push_back(structured);
+  }
+  return allErrors;
+}
+
+bool OurReader::pushError(const Value& value, const std::string& message) {
+  size_t length = end_ - begin_;
+  if(value.getOffsetStart() > length
+    || value.getOffsetLimit() > length)
+    return false;
+  Token token;
+  token.type_ = tokenError;
+  token.start_ = begin_ + value.getOffsetStart();
+  token.end_ = end_ + value.getOffsetLimit();
+  ErrorInfo info;
+  info.token_ = token;
+  info.message_ = message;
+  info.extra_ = 0;
+  errors_.push_back(info);
+  return true;
+}
+
+bool OurReader::pushError(const Value& value, const std::string& message, const Value& extra) {
+  size_t length = end_ - begin_;
+  if(value.getOffsetStart() > length
+    || value.getOffsetLimit() > length
+    || extra.getOffsetLimit() > length)
+    return false;
+  Token token;
+  token.type_ = tokenError;
+  token.start_ = begin_ + value.getOffsetStart();
+  token.end_ = begin_ + value.getOffsetLimit();
+  ErrorInfo info;
+  info.token_ = token;
+  info.message_ = message;
+  info.extra_ = begin_ + extra.getOffsetStart();
+  errors_.push_back(info);
+  return true;
+}
+
+bool OurReader::good() const {
+  return !errors_.size();
+}
+
+
+class OurCharReader : public CharReader {
+  bool const collectComments_;
+  OurReader reader_;
+public:
+  OurCharReader(
+    bool collectComments,
+    OurFeatures const& features)
+  : collectComments_(collectComments)
+  , reader_(features)
+  {}
+  virtual bool parse(
+      char const* beginDoc, char const* endDoc,
+      Value* root, std::string* errs) {
+    bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
+    if (errs) {
+      *errs = reader_.getFormattedErrorMessages();
+    }
+    return ok;
+  }
+};
+
+CharReaderBuilder::CharReaderBuilder()
+{
+  setDefaults(&settings_);
+}
+CharReaderBuilder::~CharReaderBuilder()
+{}
+CharReader* CharReaderBuilder::newCharReader() const
+{
+  bool collectComments = settings_["collectComments"].asBool();
+  OurFeatures features = OurFeatures::all();
+  features.allowComments_ = settings_["allowComments"].asBool();
+  features.strictRoot_ = settings_["strictRoot"].asBool();
+  features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool();
+  features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
+  features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool();
+  features.stackLimit_ = settings_["stackLimit"].asInt();
+  features.failIfExtra_ = settings_["failIfExtra"].asBool();
+  features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
+  return new OurCharReader(collectComments, features);
+}
+static void getValidReaderKeys(std::set<std::string>* valid_keys)
+{
+  valid_keys->clear();
+  valid_keys->insert("collectComments");
+  valid_keys->insert("allowComments");
+  valid_keys->insert("strictRoot");
+  valid_keys->insert("allowDroppedNullPlaceholders");
+  valid_keys->insert("allowNumericKeys");
+  valid_keys->insert("allowSingleQuotes");
+  valid_keys->insert("stackLimit");
+  valid_keys->insert("failIfExtra");
+  valid_keys->insert("rejectDupKeys");
+}
+bool CharReaderBuilder::validate(Json::Value* invalid) const
+{
+  Json::Value my_invalid;
+  if (!invalid) invalid = &my_invalid;  // so we do not need to test for NULL
+  Json::Value& inv = *invalid;
+  std::set<std::string> valid_keys;
+  getValidReaderKeys(&valid_keys);
+  Value::Members keys = settings_.getMemberNames();
+  size_t n = keys.size();
+  for (size_t i = 0; i < n; ++i) {
+    std::string const& key = keys[i];
+    if (valid_keys.find(key) == valid_keys.end()) {
+      inv[key] = settings_[key];
+    }
+  }
+  return 0u == inv.size();
+}
+Value& CharReaderBuilder::operator[](std::string key)
+{
+  return settings_[key];
+}
+// static
+void CharReaderBuilder::strictMode(Json::Value* settings)
+{
+//! [CharReaderBuilderStrictMode]
+  (*settings)["allowComments"] = false;
+  (*settings)["strictRoot"] = true;
+  (*settings)["allowDroppedNullPlaceholders"] = false;
+  (*settings)["allowNumericKeys"] = false;
+  (*settings)["allowSingleQuotes"] = false;
+  (*settings)["failIfExtra"] = true;
+  (*settings)["rejectDupKeys"] = true;
+//! [CharReaderBuilderStrictMode]
+}
+// static
+void CharReaderBuilder::setDefaults(Json::Value* settings)
+{
+//! [CharReaderBuilderDefaults]
+  (*settings)["collectComments"] = true;
+  (*settings)["allowComments"] = true;
+  (*settings)["strictRoot"] = false;
+  (*settings)["allowDroppedNullPlaceholders"] = false;
+  (*settings)["allowNumericKeys"] = false;
+  (*settings)["allowSingleQuotes"] = false;
+  (*settings)["stackLimit"] = 1000;
+  (*settings)["failIfExtra"] = false;
+  (*settings)["rejectDupKeys"] = false;
+//! [CharReaderBuilderDefaults]
+}
+
+//////////////////////////////////
+// global functions
+
+bool parseFromStream(
+    CharReader::Factory const& fact, std::istream& sin,
+    Value* root, std::string* errs)
+{
+  std::ostringstream ssin;
+  ssin << sin.rdbuf();
+  std::string doc = ssin.str();
+  char const* begin = doc.data();
+  char const* end = begin + doc.size();
+  // Note that we do not actually need a null-terminator.
+  CharReaderPtr const reader(fact.newCharReader());
+  return reader->parse(begin, end, root, errs);
+}
+
+std::istream& operator>>(std::istream& sin, Value& root) {
+  CharReaderBuilder b;
+  std::string errs;
+  bool ok = parseFromStream(b, sin, &root, &errs);
+  if (!ok) {
+    fprintf(stderr,
+            "Error from reader: %s",
+            errs.c_str());
+
+    throwRuntimeError("reader error");
+  }
+  return sin;
+}
+
+} // namespace Json
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: src/lib_json/json_reader.cpp
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: src/lib_json/json_valueiterator.inl
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+// included by json_value.cpp
+
+namespace Json {
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class ValueIteratorBase
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+ValueIteratorBase::ValueIteratorBase()
+    : current_(), isNull_(true) {
+}
+
+ValueIteratorBase::ValueIteratorBase(
+    const Value::ObjectValues::iterator& current)
+    : current_(current), isNull_(false) {}
+
+Value& ValueIteratorBase::deref() const {
+  return current_->second;
+}
+
+void ValueIteratorBase::increment() {
+  ++current_;
+}
+
+void ValueIteratorBase::decrement() {
+  --current_;
+}
+
+ValueIteratorBase::difference_type
+ValueIteratorBase::computeDistance(const SelfType& other) const {
+#ifdef JSON_USE_CPPTL_SMALLMAP
+  return other.current_ - current_;
+#else
+  // Iterator for null value are initialized using the default
+  // constructor, which initialize current_ to the default
+  // std::map::iterator. As begin() and end() are two instance
+  // of the default std::map::iterator, they can not be compared.
+  // To allow this, we handle this comparison specifically.
+  if (isNull_ && other.isNull_) {
+    return 0;
+  }
+
+  // Usage of std::distance is not portable (does not compile with Sun Studio 12
+  // RogueWave STL,
+  // which is the one used by default).
+  // Using a portable hand-made version for non random iterator instead:
+  //   return difference_type( std::distance( current_, other.current_ ) );
+  difference_type myDistance = 0;
+  for (Value::ObjectValues::iterator it = current_; it != other.current_;
+       ++it) {
+    ++myDistance;
+  }
+  return myDistance;
+#endif
+}
+
+bool ValueIteratorBase::isEqual(const SelfType& other) const {
+  if (isNull_) {
+    return other.isNull_;
+  }
+  return current_ == other.current_;
+}
+
+void ValueIteratorBase::copy(const SelfType& other) {
+  current_ = other.current_;
+  isNull_ = other.isNull_;
+}
+
+Value ValueIteratorBase::key() const {
+  const Value::CZString czstring = (*current_).first;
+  if (czstring.data()) {
+    if (czstring.isStaticString())
+      return Value(StaticString(czstring.data()));
+    return Value(czstring.data(), czstring.data() + czstring.length());
+  }
+  return Value(czstring.index());
+}
+
+UInt ValueIteratorBase::index() const {
+  const Value::CZString czstring = (*current_).first;
+  if (!czstring.data())
+    return czstring.index();
+  return Value::UInt(-1);
+}
+
+std::string ValueIteratorBase::name() const {
+  char const* key;
+  char const* end;
+  key = memberName(&end);
+  if (!key) return std::string();
+  return std::string(key, end);
+}
+
+char const* ValueIteratorBase::memberName() const {
+  const char* name = (*current_).first.data();
+  return name ? name : "";
+}
+
+char const* ValueIteratorBase::memberName(char const** end) const {
+  const char* name = (*current_).first.data();
+  if (!name) {
+    *end = NULL;
+    return NULL;
+  }
+  *end = name + (*current_).first.length();
+  return name;
+}
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class ValueConstIterator
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+ValueConstIterator::ValueConstIterator() {}
+
+ValueConstIterator::ValueConstIterator(
+    const Value::ObjectValues::iterator& current)
+    : ValueIteratorBase(current) {}
+
+ValueConstIterator& ValueConstIterator::
+operator=(const ValueIteratorBase& other) {
+  copy(other);
+  return *this;
+}
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class ValueIterator
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+ValueIterator::ValueIterator() {}
+
+ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current)
+    : ValueIteratorBase(current) {}
+
+ValueIterator::ValueIterator(const ValueConstIterator& other)
+    : ValueIteratorBase(other) {}
+
+ValueIterator::ValueIterator(const ValueIterator& other)
+    : ValueIteratorBase(other) {}
+
+ValueIterator& ValueIterator::operator=(const SelfType& other) {
+  copy(other);
+  return *this;
+}
+
+} // namespace Json
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: src/lib_json/json_valueiterator.inl
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: src/lib_json/json_value.cpp
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2011 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include <json/assertions.h>
+#include <json/value.h>
+#include <json/writer.h>
+#endif // if !defined(JSON_IS_AMALGAMATION)
+#include <math.h>
+#include <sstream>
+#include <utility>
+#include <cstring>
+#include <cassert>
+#ifdef JSON_USE_CPPTL
+#include <cpptl/conststring.h>
+#endif
+#include <cstddef> // size_t
+#include <algorithm> // min()
+
+#define JSON_ASSERT_UNREACHABLE assert(false)
+
+namespace Json {
+
+// This is a walkaround to avoid the static initialization of Value::null.
+// kNull must be word-aligned to avoid crashing on ARM.  We use an alignment of
+// 8 (instead of 4) as a bit of future-proofing.
+#if defined(__ARMEL__)
+#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment)))
+#else
+#define ALIGNAS(byte_alignment)
+#endif
+static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 };
+const unsigned char& kNullRef = kNull[0];
+const Value& Value::null = reinterpret_cast<const Value&>(kNullRef);
+const Value& Value::nullRef = null;
+
+const Int Value::minInt = Int(~(UInt(-1) / 2));
+const Int Value::maxInt = Int(UInt(-1) / 2);
+const UInt Value::maxUInt = UInt(-1);
+#if defined(JSON_HAS_INT64)
+const Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2));
+const Int64 Value::maxInt64 = Int64(UInt64(-1) / 2);
+const UInt64 Value::maxUInt64 = UInt64(-1);
+// The constant is hard-coded because some compiler have trouble
+// converting Value::maxUInt64 to a double correctly (AIX/xlC).
+// Assumes that UInt64 is a 64 bits integer.
+static const double maxUInt64AsDouble = 18446744073709551615.0;
+#endif // defined(JSON_HAS_INT64)
+const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2));
+const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2);
+const LargestUInt Value::maxLargestUInt = LargestUInt(-1);
+
+#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+template <typename T, typename U>
+static inline bool InRange(double d, T min, U max) {
+  return d >= min && d <= max;
+}
+#else  // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+static inline double integerToDouble(Json::UInt64 value) {
+  return static_cast<double>(Int64(value / 2)) * 2.0 + Int64(value & 1);
+}
+
+template <typename T> static inline double integerToDouble(T value) {
+  return static_cast<double>(value);
+}
+
+template <typename T, typename U>
+static inline bool InRange(double d, T min, U max) {
+  return d >= integerToDouble(min) && d <= integerToDouble(max);
+}
+#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+
+/** Duplicates the specified string value.
+ * @param value Pointer to the string to duplicate. Must be zero-terminated if
+ *              length is "unknown".
+ * @param length Length of the value. if equals to unknown, then it will be
+ *               computed using strlen(value).
+ * @return Pointer on the duplicate instance of string.
+ */
+static inline char* duplicateStringValue(const char* value,
+                                         size_t length) {
+  // Avoid an integer overflow in the call to malloc below by limiting length
+  // to a sane value.
+  if (length >= (size_t)Value::maxInt)
+    length = Value::maxInt - 1;
+
+  char* newString = static_cast<char*>(malloc(length + 1));
+  if (newString == NULL) {
+    throwRuntimeError(
+        "in Json::Value::duplicateStringValue(): "
+        "Failed to allocate string value buffer");
+  }
+  memcpy(newString, value, length);
+  newString[length] = 0;
+  return newString;
+}
+
+/* Record the length as a prefix.
+ */
+static inline char* duplicateAndPrefixStringValue(
+    const char* value,
+    unsigned int length)
+{
+  // Avoid an integer overflow in the call to malloc below by limiting length
+  // to a sane value.
+  JSON_ASSERT_MESSAGE(length <= (unsigned)Value::maxInt - sizeof(unsigned) - 1U,
+                      "in Json::Value::duplicateAndPrefixStringValue(): "
+                      "length too big for prefixing");
+  unsigned actualLength = length + sizeof(unsigned) + 1U;
+  char* newString = static_cast<char*>(malloc(actualLength));
+  if (newString == 0) {
+    throwRuntimeError(
+        "in Json::Value::duplicateAndPrefixStringValue(): "
+        "Failed to allocate string value buffer");
+  }
+  *reinterpret_cast<unsigned*>(newString) = length;
+  memcpy(newString + sizeof(unsigned), value, length);
+  newString[actualLength - 1U] = 0; // to avoid buffer over-run accidents by users later
+  return newString;
+}
+inline static void decodePrefixedString(
+    bool isPrefixed, char const* prefixed,
+    unsigned* length, char const** value)
+{
+  if (!isPrefixed) {
+    *length = (unsigned)strlen(prefixed);
+    *value = prefixed;
+  } else {
+    *length = *reinterpret_cast<unsigned const*>(prefixed);
+    *value = prefixed + sizeof(unsigned);
+  }
+}
+/** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue().
+ */
+static inline void releaseStringValue(char* value) { free(value); }
+
+} // namespace Json
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// ValueInternals...
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+#if !defined(JSON_IS_AMALGAMATION)
+
+#include "json_valueiterator.inl"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+namespace Json {
+
+class JSON_API Exception : public std::exception {
+public:
+  Exception(std::string const& msg);
+  virtual ~Exception() throw();
+  virtual char const* what() const throw();
+protected:
+  std::string const msg_;
+};
+class JSON_API RuntimeError : public Exception {
+public:
+  RuntimeError(std::string const& msg);
+};
+class JSON_API LogicError : public Exception {
+public:
+  LogicError(std::string const& msg);
+};
+
+Exception::Exception(std::string const& msg)
+  : msg_(msg)
+{}
+Exception::~Exception() throw()
+{}
+char const* Exception::what() const throw()
+{
+  return msg_.c_str();
+}
+RuntimeError::RuntimeError(std::string const& msg)
+  : Exception(msg)
+{}
+LogicError::LogicError(std::string const& msg)
+  : Exception(msg)
+{}
+void throwRuntimeError(std::string const& msg)
+{
+  throw RuntimeError(msg);
+}
+void throwLogicError(std::string const& msg)
+{
+  throw LogicError(msg);
+}
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class Value::CommentInfo
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+Value::CommentInfo::CommentInfo() : comment_(0) {}
+
+Value::CommentInfo::~CommentInfo() {
+  if (comment_)
+    releaseStringValue(comment_);
+}
+
+void Value::CommentInfo::setComment(const char* text, size_t len) {
+  if (comment_) {
+    releaseStringValue(comment_);
+    comment_ = 0;
+  }
+  JSON_ASSERT(text != 0);
+  JSON_ASSERT_MESSAGE(
+      text[0] == '\0' || text[0] == '/',
+      "in Json::Value::setComment(): Comments must start with /");
+  // It seems that /**/ style comments are acceptable as well.
+  comment_ = duplicateStringValue(text, len);
+}
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class Value::CZString
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+// Notes: policy_ indicates if the string was allocated when
+// a string is stored.
+
+Value::CZString::CZString(ArrayIndex index) : cstr_(0), index_(index) {}
+
+Value::CZString::CZString(char const* str, unsigned length, DuplicationPolicy allocate)
+    : cstr_(str)
+{
+  // allocate != duplicate
+  storage_.policy_ = allocate;
+  storage_.length_ = length;
+}
+
+Value::CZString::CZString(const CZString& other)
+    : cstr_(other.storage_.policy_ != noDuplication && other.cstr_ != 0
+                ? duplicateStringValue(other.cstr_, other.storage_.length_)
+                : other.cstr_)
+{
+  storage_.policy_ = (other.cstr_
+                 ? (other.storage_.policy_ == noDuplication
+                     ? noDuplication : duplicate)
+                 : other.storage_.policy_);
+  storage_.length_ = other.storage_.length_;
+}
+
+Value::CZString::~CZString() {
+  if (cstr_ && storage_.policy_ == duplicate)
+    releaseStringValue(const_cast<char*>(cstr_));
+}
+
+void Value::CZString::swap(CZString& other) {
+  std::swap(cstr_, other.cstr_);
+  std::swap(index_, other.index_);
+}
+
+Value::CZString& Value::CZString::operator=(CZString other) {
+  swap(other);
+  return *this;
+}
+
+bool Value::CZString::operator<(const CZString& other) const {
+  if (!cstr_) return index_ < other.index_;
+  //return strcmp(cstr_, other.cstr_) < 0;
+  // Assume both are strings.
+  unsigned this_len = this->storage_.length_;
+  unsigned other_len = other.storage_.length_;
+  unsigned min_len = std::min(this_len, other_len);
+  int comp = memcmp(this->cstr_, other.cstr_, min_len);
+  if (comp < 0) return true;
+  if (comp > 0) return false;
+  return (this_len < other_len);
+}
+
+bool Value::CZString::operator==(const CZString& other) const {
+  if (!cstr_) return index_ == other.index_;
+  //return strcmp(cstr_, other.cstr_) == 0;
+  // Assume both are strings.
+  unsigned this_len = this->storage_.length_;
+  unsigned other_len = other.storage_.length_;
+  if (this_len != other_len) return false;
+  int comp = memcmp(this->cstr_, other.cstr_, this_len);
+  return comp == 0;
+}
+
+ArrayIndex Value::CZString::index() const { return index_; }
+
+//const char* Value::CZString::c_str() const { return cstr_; }
+const char* Value::CZString::data() const { return cstr_; }
+unsigned Value::CZString::length() const { return storage_.length_; }
+bool Value::CZString::isStaticString() const { return storage_.policy_ == noDuplication; }
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class Value::Value
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+/*! \internal Default constructor initialization must be equivalent to:
+ * memset( this, 0, sizeof(Value) )
+ * This optimization is used in ValueInternalMap fast allocator.
+ */
+Value::Value(ValueType type) {
+  initBasic(type);
+  switch (type) {
+  case nullValue:
+    break;
+  case intValue:
+  case uintValue:
+    value_.int_ = 0;
+    break;
+  case realValue:
+    value_.real_ = 0.0;
+    break;
+  case stringValue:
+    value_.string_ = 0;
+    break;
+  case arrayValue:
+  case objectValue:
+    value_.map_ = new ObjectValues();
+    break;
+  case booleanValue:
+    value_.bool_ = false;
+    break;
+  default:
+    JSON_ASSERT_UNREACHABLE;
+  }
+}
+
+Value::Value(Int value) {
+  initBasic(intValue);
+  value_.int_ = value;
+}
+
+Value::Value(UInt value) {
+  initBasic(uintValue);
+  value_.uint_ = value;
+}
+#if defined(JSON_HAS_INT64)
+Value::Value(Int64 value) {
+  initBasic(intValue);
+  value_.int_ = value;
+}
+Value::Value(UInt64 value) {
+  initBasic(uintValue);
+  value_.uint_ = value;
+}
+#endif // defined(JSON_HAS_INT64)
+
+Value::Value(double value) {
+  initBasic(realValue);
+  value_.real_ = value;
+}
+
+Value::Value(const char* value) {
+  initBasic(stringValue, true);
+  value_.string_ = duplicateAndPrefixStringValue(value, static_cast<unsigned>(strlen(value)));
+}
+
+Value::Value(const char* beginValue, const char* endValue) {
+  initBasic(stringValue, true);
+  value_.string_ =
+      duplicateAndPrefixStringValue(beginValue, static_cast<unsigned>(endValue - beginValue));
+}
+
+Value::Value(const std::string& value) {
+  initBasic(stringValue, true);
+  value_.string_ =
+      duplicateAndPrefixStringValue(value.data(), static_cast<unsigned>(value.length()));
+}
+
+Value::Value(const StaticString& value) {
+  initBasic(stringValue);
+  value_.string_ = const_cast<char*>(value.c_str());
+}
+
+#ifdef JSON_USE_CPPTL
+Value::Value(const CppTL::ConstString& value) {
+  initBasic(stringValue, true);
+  value_.string_ = duplicateAndPrefixStringValue(value, static_cast<unsigned>(value.length()));
+}
+#endif
+
+Value::Value(bool value) {
+  initBasic(booleanValue);
+  value_.bool_ = value;
+}
+
+Value::Value(Value const& other)
+    : type_(other.type_), allocated_(false)
+      ,
+      comments_(0), start_(other.start_), limit_(other.limit_)
+{
+  switch (type_) {
+  case nullValue:
+  case intValue:
+  case uintValue:
+  case realValue:
+  case booleanValue:
+    value_ = other.value_;
+    break;
+  case stringValue:
+    if (other.value_.string_ && other.allocated_) {
+      unsigned len;
+      char const* str;
+      decodePrefixedString(other.allocated_, other.value_.string_,
+          &len, &str);
+      value_.string_ = duplicateAndPrefixStringValue(str, len);
+      allocated_ = true;
+    } else {
+      value_.string_ = other.value_.string_;
+      allocated_ = false;
+    }
+    break;
+  case arrayValue:
+  case objectValue:
+    value_.map_ = new ObjectValues(*other.value_.map_);
+    break;
+  default:
+    JSON_ASSERT_UNREACHABLE;
+  }
+  if (other.comments_) {
+    comments_ = new CommentInfo[numberOfCommentPlacement];
+    for (int comment = 0; comment < numberOfCommentPlacement; ++comment) {
+      const CommentInfo& otherComment = other.comments_[comment];
+      if (otherComment.comment_)
+        comments_[comment].setComment(
+            otherComment.comment_, strlen(otherComment.comment_));
+    }
+  }
+}
+
+Value::~Value() {
+  switch (type_) {
+  case nullValue:
+  case intValue:
+  case uintValue:
+  case realValue:
+  case booleanValue:
+    break;
+  case stringValue:
+    if (allocated_)
+      releaseStringValue(value_.string_);
+    break;
+  case arrayValue:
+  case objectValue:
+    delete value_.map_;
+    break;
+  default:
+    JSON_ASSERT_UNREACHABLE;
+  }
+
+  if (comments_)
+    delete[] comments_;
+}
+
+Value& Value::operator=(Value other) {
+  swap(other);
+  return *this;
+}
+
+void Value::swapPayload(Value& other) {
+  ValueType temp = type_;
+  type_ = other.type_;
+  other.type_ = temp;
+  std::swap(value_, other.value_);
+  int temp2 = allocated_;
+  allocated_ = other.allocated_;
+  other.allocated_ = temp2;
+}
+
+void Value::swap(Value& other) {
+  swapPayload(other);
+  std::swap(comments_, other.comments_);
+  std::swap(start_, other.start_);
+  std::swap(limit_, other.limit_);
+}
+
+ValueType Value::type() const { return type_; }
+
+int Value::compare(const Value& other) const {
+  if (*this < other)
+    return -1;
+  if (*this > other)
+    return 1;
+  return 0;
+}
+
+bool Value::operator<(const Value& other) const {
+  int typeDelta = type_ - other.type_;
+  if (typeDelta)
+    return typeDelta < 0 ? true : false;
+  switch (type_) {
+  case nullValue:
+    return false;
+  case intValue:
+    return value_.int_ < other.value_.int_;
+  case uintValue:
+    return value_.uint_ < other.value_.uint_;
+  case realValue:
+    return value_.real_ < other.value_.real_;
+  case booleanValue:
+    return value_.bool_ < other.value_.bool_;
+  case stringValue:
+  {
+    if ((value_.string_ == 0) || (other.value_.string_ == 0)) {
+      if (other.value_.string_) return true;
+      else return false;
+    }
+    unsigned this_len;
+    unsigned other_len;
+    char const* this_str;
+    char const* other_str;
+    decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);
+    decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str);
+    unsigned min_len = std::min(this_len, other_len);
+    int comp = memcmp(this_str, other_str, min_len);
+    if (comp < 0) return true;
+    if (comp > 0) return false;
+    return (this_len < other_len);
+  }
+  case arrayValue:
+  case objectValue: {
+    int delta = int(value_.map_->size() - other.value_.map_->size());
+    if (delta)
+      return delta < 0;
+    return (*value_.map_) < (*other.value_.map_);
+  }
+  default:
+    JSON_ASSERT_UNREACHABLE;
+  }
+  return false; // unreachable
+}
+
+bool Value::operator<=(const Value& other) const { return !(other < *this); }
+
+bool Value::operator>=(const Value& other) const { return !(*this < other); }
+
+bool Value::operator>(const Value& other) const { return other < *this; }
+
+bool Value::operator==(const Value& other) const {
+  // if ( type_ != other.type_ )
+  // GCC 2.95.3 says:
+  // attempt to take address of bit-field structure member `Json::Value::type_'
+  // Beats me, but a temp solves the problem.
+  int temp = other.type_;
+  if (type_ != temp)
+    return false;
+  switch (type_) {
+  case nullValue:
+    return true;
+  case intValue:
+    return value_.int_ == other.value_.int_;
+  case uintValue:
+    return value_.uint_ == other.value_.uint_;
+  case realValue:
+    return value_.real_ == other.value_.real_;
+  case booleanValue:
+    return value_.bool_ == other.value_.bool_;
+  case stringValue:
+  {
+    if ((value_.string_ == 0) || (other.value_.string_ == 0)) {
+      return (value_.string_ == other.value_.string_);
+    }
+    unsigned this_len;
+    unsigned other_len;
+    char const* this_str;
+    char const* other_str;
+    decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);
+    decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str);
+    if (this_len != other_len) return false;
+    int comp = memcmp(this_str, other_str, this_len);
+    return comp == 0;
+  }
+  case arrayValue:
+  case objectValue:
+    return value_.map_->size() == other.value_.map_->size() &&
+           (*value_.map_) == (*other.value_.map_);
+  default:
+    JSON_ASSERT_UNREACHABLE;
+  }
+  return false; // unreachable
+}
+
+bool Value::operator!=(const Value& other) const { return !(*this == other); }
+
+const char* Value::asCString() const {
+  JSON_ASSERT_MESSAGE(type_ == stringValue,
+                      "in Json::Value::asCString(): requires stringValue");
+  if (value_.string_ == 0) return 0;
+  unsigned this_len;
+  char const* this_str;
+  decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);
+  return this_str;
+}
+
+bool Value::getString(char const** str, char const** end) const {
+  if (type_ != stringValue) return false;
+  if (value_.string_ == 0) return false;
+  unsigned length;
+  decodePrefixedString(this->allocated_, this->value_.string_, &length, str);
+  *end = *str + length;
+  return true;
+}
+
+std::string Value::asString() const {
+  switch (type_) {
+  case nullValue:
+    return "";
+  case stringValue:
+  {
+    if (value_.string_ == 0) return "";
+    unsigned this_len;
+    char const* this_str;
+    decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);
+    return std::string(this_str, this_len);
+  }
+  case booleanValue:
+    return value_.bool_ ? "true" : "false";
+  case intValue:
+    return valueToString(value_.int_);
+  case uintValue:
+    return valueToString(value_.uint_);
+  case realValue:
+    return valueToString(value_.real_);
+  default:
+    JSON_FAIL_MESSAGE("Type is not convertible to string");
+  }
+}
+
+#ifdef JSON_USE_CPPTL
+CppTL::ConstString Value::asConstString() const {
+  unsigned len;
+  char const* str;
+  decodePrefixedString(allocated_, value_.string_,
+      &len, &str);
+  return CppTL::ConstString(str, len);
+}
+#endif
+
+Value::Int Value::asInt() const {
+  switch (type_) {
+  case intValue:
+    JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range");
+    return Int(value_.int_);
+  case uintValue:
+    JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range");
+    return Int(value_.uint_);
+  case realValue:
+    JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt),
+                        "double out of Int range");
+    return Int(value_.real_);
+  case nullValue:
+    return 0;
+  case booleanValue:
+    return value_.bool_ ? 1 : 0;
+  default:
+    break;
+  }
+  JSON_FAIL_MESSAGE("Value is not convertible to Int.");
+}
+
+Value::UInt Value::asUInt() const {
+  switch (type_) {
+  case intValue:
+    JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range");
+    return UInt(value_.int_);
+  case uintValue:
+    JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range");
+    return UInt(value_.uint_);
+  case realValue:
+    JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt),
+                        "double out of UInt range");
+    return UInt(value_.real_);
+  case nullValue:
+    return 0;
+  case booleanValue:
+    return value_.bool_ ? 1 : 0;
+  default:
+    break;
+  }
+  JSON_FAIL_MESSAGE("Value is not convertible to UInt.");
+}
+
+#if defined(JSON_HAS_INT64)
+
+Value::Int64 Value::asInt64() const {
+  switch (type_) {
+  case intValue:
+    return Int64(value_.int_);
+  case uintValue:
+    JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range");
+    return Int64(value_.uint_);
+  case realValue:
+    JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64),
+                        "double out of Int64 range");
+    return Int64(value_.real_);
+  case nullValue:
+    return 0;
+  case booleanValue:
+    return value_.bool_ ? 1 : 0;
+  default:
+    break;
+  }
+  JSON_FAIL_MESSAGE("Value is not convertible to Int64.");
+}
+
+Value::UInt64 Value::asUInt64() const {
+  switch (type_) {
+  case intValue:
+    JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range");
+    return UInt64(value_.int_);
+  case uintValue:
+    return UInt64(value_.uint_);
+  case realValue:
+    JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64),
+                        "double out of UInt64 range");
+    return UInt64(value_.real_);
+  case nullValue:
+    return 0;
+  case booleanValue:
+    return value_.bool_ ? 1 : 0;
+  default:
+    break;
+  }
+  JSON_FAIL_MESSAGE("Value is not convertible to UInt64.");
+}
+#endif // if defined(JSON_HAS_INT64)
+
+LargestInt Value::asLargestInt() const {
+#if defined(JSON_NO_INT64)
+  return asInt();
+#else
+  return asInt64();
+#endif
+}
+
+LargestUInt Value::asLargestUInt() const {
+#if defined(JSON_NO_INT64)
+  return asUInt();
+#else
+  return asUInt64();
+#endif
+}
+
+double Value::asDouble() const {
+  switch (type_) {
+  case intValue:
+    return static_cast<double>(value_.int_);
+  case uintValue:
+#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+    return static_cast<double>(value_.uint_);
+#else  // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+    return integerToDouble(value_.uint_);
+#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+  case realValue:
+    return value_.real_;
+  case nullValue:
+    return 0.0;
+  case booleanValue:
+    return value_.bool_ ? 1.0 : 0.0;
+  default:
+    break;
+  }
+  JSON_FAIL_MESSAGE("Value is not convertible to double.");
+}
+
+float Value::asFloat() const {
+  switch (type_) {
+  case intValue:
+    return static_cast<float>(value_.int_);
+  case uintValue:
+#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+    return static_cast<float>(value_.uint_);
+#else  // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+    return integerToDouble(value_.uint_);
+#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+  case realValue:
+    return static_cast<float>(value_.real_);
+  case nullValue:
+    return 0.0;
+  case booleanValue:
+    return value_.bool_ ? 1.0f : 0.0f;
+  default:
+    break;
+  }
+  JSON_FAIL_MESSAGE("Value is not convertible to float.");
+}
+
+bool Value::asBool() const {
+  switch (type_) {
+  case booleanValue:
+    return value_.bool_;
+  case nullValue:
+    return false;
+  case intValue:
+    return value_.int_ ? true : false;
+  case uintValue:
+    return value_.uint_ ? true : false;
+  case realValue:
+    return value_.real_ ? true : false;
+  default:
+    break;
+  }
+  JSON_FAIL_MESSAGE("Value is not convertible to bool.");
+}
+
+bool Value::isConvertibleTo(ValueType other) const {
+  switch (other) {
+  case nullValue:
+    return (isNumeric() && asDouble() == 0.0) ||
+           (type_ == booleanValue && value_.bool_ == false) ||
+           (type_ == stringValue && asString() == "") ||
+           (type_ == arrayValue && value_.map_->size() == 0) ||
+           (type_ == objectValue && value_.map_->size() == 0) ||
+           type_ == nullValue;
+  case intValue:
+    return isInt() ||
+           (type_ == realValue && InRange(value_.real_, minInt, maxInt)) ||
+           type_ == booleanValue || type_ == nullValue;
+  case uintValue:
+    return isUInt() ||
+           (type_ == realValue && InRange(value_.real_, 0, maxUInt)) ||
+           type_ == booleanValue || type_ == nullValue;
+  case realValue:
+    return isNumeric() || type_ == booleanValue || type_ == nullValue;
+  case booleanValue:
+    return isNumeric() || type_ == booleanValue || type_ == nullValue;
+  case stringValue:
+    return isNumeric() || type_ == booleanValue || type_ == stringValue ||
+           type_ == nullValue;
+  case arrayValue:
+    return type_ == arrayValue || type_ == nullValue;
+  case objectValue:
+    return type_ == objectValue || type_ == nullValue;
+  }
+  JSON_ASSERT_UNREACHABLE;
+  return false;
+}
+
+/// Number of values in array or object
+ArrayIndex Value::size() const {
+  switch (type_) {
+  case nullValue:
+  case intValue:
+  case uintValue:
+  case realValue:
+  case booleanValue:
+  case stringValue:
+    return 0;
+  case arrayValue: // size of the array is highest index + 1
+    if (!value_.map_->empty()) {
+      ObjectValues::const_iterator itLast = value_.map_->end();
+      --itLast;
+      return (*itLast).first.index() + 1;
+    }
+    return 0;
+  case objectValue:
+    return ArrayIndex(value_.map_->size());
+  }
+  JSON_ASSERT_UNREACHABLE;
+  return 0; // unreachable;
+}
+
+bool Value::empty() const {
+  if (isNull() || isArray() || isObject())
+    return size() == 0u;
+  else
+    return false;
+}
+
+bool Value::operator!() const { return isNull(); }
+
+void Value::clear() {
+  JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue ||
+                          type_ == objectValue,
+                      "in Json::Value::clear(): requires complex value");
+  start_ = 0;
+  limit_ = 0;
+  switch (type_) {
+  case arrayValue:
+  case objectValue:
+    value_.map_->clear();
+    break;
+  default:
+    break;
+  }
+}
+
+void Value::resize(ArrayIndex newSize) {
+  JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue,
+                      "in Json::Value::resize(): requires arrayValue");
+  if (type_ == nullValue)
+    *this = Value(arrayValue);
+  ArrayIndex oldSize = size();
+  if (newSize == 0)
+    clear();
+  else if (newSize > oldSize)
+    (*this)[newSize - 1];
+  else {
+    for (ArrayIndex index = newSize; index < oldSize; ++index) {
+      value_.map_->erase(index);
+    }
+    assert(size() == newSize);
+  }
+}
+
+Value& Value::operator[](ArrayIndex index) {
+  JSON_ASSERT_MESSAGE(
+      type_ == nullValue || type_ == arrayValue,
+      "in Json::Value::operator[](ArrayIndex): requires arrayValue");
+  if (type_ == nullValue)
+    *this = Value(arrayValue);
+  CZString key(index);
+  ObjectValues::iterator it = value_.map_->lower_bound(key);
+  if (it != value_.map_->end() && (*it).first == key)
+    return (*it).second;
+
+  ObjectValues::value_type defaultValue(key, nullRef);
+  it = value_.map_->insert(it, defaultValue);
+  return (*it).second;
+}
+
+Value& Value::operator[](int index) {
+  JSON_ASSERT_MESSAGE(
+      index >= 0,
+      "in Json::Value::operator[](int index): index cannot be negative");
+  return (*this)[ArrayIndex(index)];
+}
+
+const Value& Value::operator[](ArrayIndex index) const {
+  JSON_ASSERT_MESSAGE(
+      type_ == nullValue || type_ == arrayValue,
+      "in Json::Value::operator[](ArrayIndex)const: requires arrayValue");
+  if (type_ == nullValue)
+    return nullRef;
+  CZString key(index);
+  ObjectValues::const_iterator it = value_.map_->find(key);
+  if (it == value_.map_->end())
+    return nullRef;
+  return (*it).second;
+}
+
+const Value& Value::operator[](int index) const {
+  JSON_ASSERT_MESSAGE(
+      index >= 0,
+      "in Json::Value::operator[](int index) const: index cannot be negative");
+  return (*this)[ArrayIndex(index)];
+}
+
+void Value::initBasic(ValueType type, bool allocated) {
+  type_ = type;
+  allocated_ = allocated;
+  comments_ = 0;
+  start_ = 0;
+  limit_ = 0;
+}
+
+// Access an object value by name, create a null member if it does not exist.
+// @pre Type of '*this' is object or null.
+// @param key is null-terminated.
+Value& Value::resolveReference(const char* key) {
+  JSON_ASSERT_MESSAGE(
+      type_ == nullValue || type_ == objectValue,
+      "in Json::Value::resolveReference(): requires objectValue");
+  if (type_ == nullValue)
+    *this = Value(objectValue);
+  CZString actualKey(
+      key, static_cast<unsigned>(strlen(key)), CZString::noDuplication); // NOTE!
+  ObjectValues::iterator it = value_.map_->lower_bound(actualKey);
+  if (it != value_.map_->end() && (*it).first == actualKey)
+    return (*it).second;
+
+  ObjectValues::value_type defaultValue(actualKey, nullRef);
+  it = value_.map_->insert(it, defaultValue);
+  Value& value = (*it).second;
+  return value;
+}
+
+// @param key is not null-terminated.
+Value& Value::resolveReference(char const* key, char const* end)
+{
+  JSON_ASSERT_MESSAGE(
+      type_ == nullValue || type_ == objectValue,
+      "in Json::Value::resolveReference(key, end): requires objectValue");
+  if (type_ == nullValue)
+    *this = Value(objectValue);
+  CZString actualKey(
+      key, static_cast<unsigned>(end-key), CZString::duplicateOnCopy);
+  ObjectValues::iterator it = value_.map_->lower_bound(actualKey);
+  if (it != value_.map_->end() && (*it).first == actualKey)
+    return (*it).second;
+
+  ObjectValues::value_type defaultValue(actualKey, nullRef);
+  it = value_.map_->insert(it, defaultValue);
+  Value& value = (*it).second;
+  return value;
+}
+
+Value Value::get(ArrayIndex index, const Value& defaultValue) const {
+  const Value* value = &((*this)[index]);
+  return value == &nullRef ? defaultValue : *value;
+}
+
+bool Value::isValidIndex(ArrayIndex index) const { return index < size(); }
+
+Value const* Value::find(char const* key, char const* end) const
+{
+  JSON_ASSERT_MESSAGE(
+      type_ == nullValue || type_ == objectValue,
+      "in Json::Value::find(key, end, found): requires objectValue or nullValue");
+  if (type_ == nullValue) return NULL;
+  CZString actualKey(key, static_cast<unsigned>(end-key), CZString::noDuplication);
+  ObjectValues::const_iterator it = value_.map_->find(actualKey);
+  if (it == value_.map_->end()) return NULL;
+  return &(*it).second;
+}
+const Value& Value::operator[](const char* key) const
+{
+  Value const* found = find(key, key + strlen(key));
+  if (!found) return nullRef;
+  return *found;
+}
+Value const& Value::operator[](std::string const& key) const
+{
+  Value const* found = find(key.data(), key.data() + key.length());
+  if (!found) return nullRef;
+  return *found;
+}
+
+Value& Value::operator[](const char* key) {
+  return resolveReference(key, key + strlen(key));
+}
+
+Value& Value::operator[](const std::string& key) {
+  return resolveReference(key.data(), key.data() + key.length());
+}
+
+Value& Value::operator[](const StaticString& key) {
+  return resolveReference(key.c_str());
+}
+
+#ifdef JSON_USE_CPPTL
+Value& Value::operator[](const CppTL::ConstString& key) {
+  return resolveReference(key.c_str(), key.end_c_str());
+}
+Value const& Value::operator[](CppTL::ConstString const& key) const
+{
+  Value const* found = find(key.c_str(), key.end_c_str());
+  if (!found) return nullRef;
+  return *found;
+}
+#endif
+
+Value& Value::append(const Value& value) { return (*this)[size()] = value; }
+
+Value Value::get(char const* key, char const* end, Value const& defaultValue) const
+{
+  Value const* found = find(key, end);
+  return !found ? defaultValue : *found;
+}
+Value Value::get(char const* key, Value const& defaultValue) const
+{
+  return get(key, key + strlen(key), defaultValue);
+}
+Value Value::get(std::string const& key, Value const& defaultValue) const
+{
+  return get(key.data(), key.data() + key.length(), defaultValue);
+}
+
+
+bool Value::removeMember(const char* key, const char* end, Value* removed)
+{
+  if (type_ != objectValue) {
+    return false;
+  }
+  CZString actualKey(key, static_cast<unsigned>(end-key), CZString::noDuplication);
+  ObjectValues::iterator it = value_.map_->find(actualKey);
+  if (it == value_.map_->end())
+    return false;
+  *removed = it->second;
+  value_.map_->erase(it);
+  return true;
+}
+bool Value::removeMember(const char* key, Value* removed)
+{
+  return removeMember(key, key + strlen(key), removed);
+}
+bool Value::removeMember(std::string const& key, Value* removed)
+{
+  return removeMember(key.data(), key.data() + key.length(), removed);
+}
+Value Value::removeMember(const char* key)
+{
+  JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue,
+                      "in Json::Value::removeMember(): requires objectValue");
+  if (type_ == nullValue)
+    return nullRef;
+
+  Value removed;  // null
+  removeMember(key, key + strlen(key), &removed);
+  return removed; // still null if removeMember() did nothing
+}
+Value Value::removeMember(const std::string& key)
+{
+  return removeMember(key.c_str());
+}
+
+bool Value::removeIndex(ArrayIndex index, Value* removed) {
+  if (type_ != arrayValue) {
+    return false;
+  }
+  CZString key(index);
+  ObjectValues::iterator it = value_.map_->find(key);
+  if (it == value_.map_->end()) {
+    return false;
+  }
+  *removed = it->second;
+  ArrayIndex oldSize = size();
+  // shift left all items left, into the place of the "removed"
+  for (ArrayIndex i = index; i < (oldSize - 1); ++i){
+    CZString key(i);
+    (*value_.map_)[key] = (*this)[i + 1];
+  }
+  // erase the last one ("leftover")
+  CZString keyLast(oldSize - 1);
+  ObjectValues::iterator itLast = value_.map_->find(keyLast);
+  value_.map_->erase(itLast);
+  return true;
+}
+
+#ifdef JSON_USE_CPPTL
+Value Value::get(const CppTL::ConstString& key,
+                 const Value& defaultValue) const {
+  return get(key.c_str(), key.end_c_str(), defaultValue);
+}
+#endif
+
+bool Value::isMember(char const* key, char const* end) const
+{
+  Value const* value = find(key, end);
+  return NULL != value;
+}
+bool Value::isMember(char const* key) const
+{
+  return isMember(key, key + strlen(key));
+}
+bool Value::isMember(std::string const& key) const
+{
+  return isMember(key.data(), key.data() + key.length());
+}
+
+#ifdef JSON_USE_CPPTL
+bool Value::isMember(const CppTL::ConstString& key) const {
+  return isMember(key.c_str(), key.end_c_str());
+}
+#endif
+
+Value::Members Value::getMemberNames() const {
+  JSON_ASSERT_MESSAGE(
+      type_ == nullValue || type_ == objectValue,
+      "in Json::Value::getMemberNames(), value must be objectValue");
+  if (type_ == nullValue)
+    return Value::Members();
+  Members members;
+  members.reserve(value_.map_->size());
+  ObjectValues::const_iterator it = value_.map_->begin();
+  ObjectValues::const_iterator itEnd = value_.map_->end();
+  for (; it != itEnd; ++it) {
+    members.push_back(std::string((*it).first.data(),
+                                  (*it).first.length()));
+  }
+  return members;
+}
+//
+//# ifdef JSON_USE_CPPTL
+// EnumMemberNames
+// Value::enumMemberNames() const
+//{
+//   if ( type_ == objectValue )
+//   {
+//      return CppTL::Enum::any(  CppTL::Enum::transform(
+//         CppTL::Enum::keys( *(value_.map_), CppTL::Type<const CZString &>() ),
+//         MemberNamesTransform() ) );
+//   }
+//   return EnumMemberNames();
+//}
+//
+//
+// EnumValues
+// Value::enumValues() const
+//{
+//   if ( type_ == objectValue  ||  type_ == arrayValue )
+//      return CppTL::Enum::anyValues( *(value_.map_),
+//                                     CppTL::Type<const Value &>() );
+//   return EnumValues();
+//}
+//
+//# endif
+
+static bool IsIntegral(double d) {
+  double integral_part;
+  return modf(d, &integral_part) == 0.0;
+}
+
+bool Value::isNull() const { return type_ == nullValue; }
+
+bool Value::isBool() const { return type_ == booleanValue; }
+
+bool Value::isInt() const {
+  switch (type_) {
+  case intValue:
+    return value_.int_ >= minInt && value_.int_ <= maxInt;
+  case uintValue:
+    return value_.uint_ <= UInt(maxInt);
+  case realValue:
+    return value_.real_ >= minInt && value_.real_ <= maxInt &&
+           IsIntegral(value_.real_);
+  default:
+    break;
+  }
+  return false;
+}
+
+bool Value::isUInt() const {
+  switch (type_) {
+  case intValue:
+    return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt);
+  case uintValue:
+    return value_.uint_ <= maxUInt;
+  case realValue:
+    return value_.real_ >= 0 && value_.real_ <= maxUInt &&
+           IsIntegral(value_.real_);
+  default:
+    break;
+  }
+  return false;
+}
+
+bool Value::isInt64() const {
+#if defined(JSON_HAS_INT64)
+  switch (type_) {
+  case intValue:
+    return true;
+  case uintValue:
+    return value_.uint_ <= UInt64(maxInt64);
+  case realValue:
+    // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a
+    // double, so double(maxInt64) will be rounded up to 2^63. Therefore we
+    // require the value to be strictly less than the limit.
+    return value_.real_ >= double(minInt64) &&
+           value_.real_ < double(maxInt64) && IsIntegral(value_.real_);
+  default:
+    break;
+  }
+#endif // JSON_HAS_INT64
+  return false;
+}
+
+bool Value::isUInt64() const {
+#if defined(JSON_HAS_INT64)
+  switch (type_) {
+  case intValue:
+    return value_.int_ >= 0;
+  case uintValue:
+    return true;
+  case realValue:
+    // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a
+    // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we
+    // require the value to be strictly less than the limit.
+    return value_.real_ >= 0 && value_.real_ < maxUInt64AsDouble &&
+           IsIntegral(value_.real_);
+  default:
+    break;
+  }
+#endif // JSON_HAS_INT64
+  return false;
+}
+
+bool Value::isIntegral() const {
+#if defined(JSON_HAS_INT64)
+  return isInt64() || isUInt64();
+#else
+  return isInt() || isUInt();
+#endif
+}
+
+bool Value::isDouble() const { return type_ == realValue || isIntegral(); }
+
+bool Value::isNumeric() const { return isIntegral() || isDouble(); }
+
+bool Value::isString() const { return type_ == stringValue; }
+
+bool Value::isArray() const { return type_ == arrayValue; }
+
+bool Value::isObject() const { return type_ == objectValue; }
+
+void Value::setComment(const char* comment, size_t len, CommentPlacement placement) {
+  if (!comments_)
+    comments_ = new CommentInfo[numberOfCommentPlacement];
+  if ((len > 0) && (comment[len-1] == '\n')) {
+    // Always discard trailing newline, to aid indentation.
+    len -= 1;
+  }
+  comments_[placement].setComment(comment, len);
+}
+
+void Value::setComment(const char* comment, CommentPlacement placement) {
+  setComment(comment, strlen(comment), placement);
+}
+
+void Value::setComment(const std::string& comment, CommentPlacement placement) {
+  setComment(comment.c_str(), comment.length(), placement);
+}
+
+bool Value::hasComment(CommentPlacement placement) const {
+  return comments_ != 0 && comments_[placement].comment_ != 0;
+}
+
+std::string Value::getComment(CommentPlacement placement) const {
+  if (hasComment(placement))
+    return comments_[placement].comment_;
+  return "";
+}
+
+void Value::setOffsetStart(size_t start) { start_ = start; }
+
+void Value::setOffsetLimit(size_t limit) { limit_ = limit; }
+
+size_t Value::getOffsetStart() const { return start_; }
+
+size_t Value::getOffsetLimit() const { return limit_; }
+
+std::string Value::toStyledString() const {
+  StyledWriter writer;
+  return writer.write(*this);
+}
+
+Value::const_iterator Value::begin() const {
+  switch (type_) {
+  case arrayValue:
+  case objectValue:
+    if (value_.map_)
+      return const_iterator(value_.map_->begin());
+    break;
+  default:
+    break;
+  }
+  return const_iterator();
+}
+
+Value::const_iterator Value::end() const {
+  switch (type_) {
+  case arrayValue:
+  case objectValue:
+    if (value_.map_)
+      return const_iterator(value_.map_->end());
+    break;
+  default:
+    break;
+  }
+  return const_iterator();
+}
+
+Value::iterator Value::begin() {
+  switch (type_) {
+  case arrayValue:
+  case objectValue:
+    if (value_.map_)
+      return iterator(value_.map_->begin());
+    break;
+  default:
+    break;
+  }
+  return iterator();
+}
+
+Value::iterator Value::end() {
+  switch (type_) {
+  case arrayValue:
+  case objectValue:
+    if (value_.map_)
+      return iterator(value_.map_->end());
+    break;
+  default:
+    break;
+  }
+  return iterator();
+}
+
+// class PathArgument
+// //////////////////////////////////////////////////////////////////
+
+PathArgument::PathArgument() : key_(), index_(), kind_(kindNone) {}
+
+PathArgument::PathArgument(ArrayIndex index)
+    : key_(), index_(index), kind_(kindIndex) {}
+
+PathArgument::PathArgument(const char* key)
+    : key_(key), index_(), kind_(kindKey) {}
+
+PathArgument::PathArgument(const std::string& key)
+    : key_(key.c_str()), index_(), kind_(kindKey) {}
+
+// class Path
+// //////////////////////////////////////////////////////////////////
+
+Path::Path(const std::string& path,
+           const PathArgument& a1,
+           const PathArgument& a2,
+           const PathArgument& a3,
+           const PathArgument& a4,
+           const PathArgument& a5) {
+  InArgs in;
+  in.push_back(&a1);
+  in.push_back(&a2);
+  in.push_back(&a3);
+  in.push_back(&a4);
+  in.push_back(&a5);
+  makePath(path, in);
+}
+
+void Path::makePath(const std::string& path, const InArgs& in) {
+  const char* current = path.c_str();
+  const char* end = current + path.length();
+  InArgs::const_iterator itInArg = in.begin();
+  while (current != end) {
+    if (*current == '[') {
+      ++current;
+      if (*current == '%')
+        addPathInArg(path, in, itInArg, PathArgument::kindIndex);
+      else {
+        ArrayIndex index = 0;
+        for (; current != end && *current >= '0' && *current <= '9'; ++current)
+          index = index * 10 + ArrayIndex(*current - '0');
+        args_.push_back(index);
+      }
+      if (current == end || *current++ != ']')
+        invalidPath(path, int(current - path.c_str()));
+    } else if (*current == '%') {
+      addPathInArg(path, in, itInArg, PathArgument::kindKey);
+      ++current;
+    } else if (*current == '.') {
+      ++current;
+    } else {
+      const char* beginName = current;
+      while (current != end && !strchr("[.", *current))
+        ++current;
+      args_.push_back(std::string(beginName, current));
+    }
+  }
+}
+
+void Path::addPathInArg(const std::string& /*path*/,
+                        const InArgs& in,
+                        InArgs::const_iterator& itInArg,
+                        PathArgument::Kind kind) {
+  if (itInArg == in.end()) {
+    // Error: missing argument %d
+  } else if ((*itInArg)->kind_ != kind) {
+    // Error: bad argument type
+  } else {
+    args_.push_back(**itInArg);
+  }
+}
+
+void Path::invalidPath(const std::string& /*path*/, int /*location*/) {
+  // Error: invalid path.
+}
+
+const Value& Path::resolve(const Value& root) const {
+  const Value* node = &root;
+  for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) {
+    const PathArgument& arg = *it;
+    if (arg.kind_ == PathArgument::kindIndex) {
+      if (!node->isArray() || !node->isValidIndex(arg.index_)) {
+        // Error: unable to resolve path (array value expected at position...
+      }
+      node = &((*node)[arg.index_]);
+    } else if (arg.kind_ == PathArgument::kindKey) {
+      if (!node->isObject()) {
+        // Error: unable to resolve path (object value expected at position...)
+      }
+      node = &((*node)[arg.key_]);
+      if (node == &Value::nullRef) {
+        // Error: unable to resolve path (object has no member named '' at
+        // position...)
+      }
+    }
+  }
+  return *node;
+}
+
+Value Path::resolve(const Value& root, const Value& defaultValue) const {
+  const Value* node = &root;
+  for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) {
+    const PathArgument& arg = *it;
+    if (arg.kind_ == PathArgument::kindIndex) {
+      if (!node->isArray() || !node->isValidIndex(arg.index_))
+        return defaultValue;
+      node = &((*node)[arg.index_]);
+    } else if (arg.kind_ == PathArgument::kindKey) {
+      if (!node->isObject())
+        return defaultValue;
+      node = &((*node)[arg.key_]);
+      if (node == &Value::nullRef)
+        return defaultValue;
+    }
+  }
+  return *node;
+}
+
+Value& Path::make(Value& root) const {
+  Value* node = &root;
+  for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) {
+    const PathArgument& arg = *it;
+    if (arg.kind_ == PathArgument::kindIndex) {
+      if (!node->isArray()) {
+        // Error: node is not an array at position ...
+      }
+      node = &((*node)[arg.index_]);
+    } else if (arg.kind_ == PathArgument::kindKey) {
+      if (!node->isObject()) {
+        // Error: node is not an object at position...
+      }
+      node = &((*node)[arg.key_]);
+    }
+  }
+  return *node;
+}
+
+} // namespace Json
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: src/lib_json/json_value.cpp
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: src/lib_json/json_writer.cpp
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2011 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include <json/writer.h>
+#include "json_tool.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+#include <iomanip>
+#include <memory>
+#include <sstream>
+#include <utility>
+#include <set>
+#include <cassert>
+#include <cstring>
+#include <cstdio>
+
+#if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0
+#include <float.h>
+#define isfinite _finite
+#elif defined(__sun) && defined(__SVR4) //Solaris
+#include <ieeefp.h>
+#define isfinite finite
+#else
+#include <cmath>
+#define isfinite std::isfinite
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below
+#define snprintf _snprintf
+#elif defined(__ANDROID__)
+#define snprintf snprintf
+#elif __cplusplus >= 201103L
+#define snprintf std::snprintf
+#endif
+
+#if defined(__BORLANDC__)  
+#include <float.h>
+#define isfinite _finite
+#define snprintf _snprintf
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
+// Disable warning about strdup being deprecated.
+#pragma warning(disable : 4996)
+#endif
+
+namespace Json {
+
+#if __cplusplus >= 201103L
+typedef std::unique_ptr<StreamWriter> StreamWriterPtr;
+#else
+typedef std::auto_ptr<StreamWriter>   StreamWriterPtr;
+#endif
+
+static bool containsControlCharacter(const char* str) {
+  while (*str) {
+    if (isControlCharacter(*(str++)))
+      return true;
+  }
+  return false;
+}
+
+static bool containsControlCharacter0(const char* str, unsigned len) {
+  char const* end = str + len;
+  while (end != str) {
+    if (isControlCharacter(*str) || 0==*str)
+      return true;
+    ++str;
+  }
+  return false;
+}
+
+std::string valueToString(LargestInt value) {
+  UIntToStringBuffer buffer;
+  char* current = buffer + sizeof(buffer);
+  bool isNegative = value < 0;
+  if (isNegative)
+    value = -value;
+  uintToString(LargestUInt(value), current);
+  if (isNegative)
+    *--current = '-';
+  assert(current >= buffer);
+  return current;
+}
+
+std::string valueToString(LargestUInt value) {
+  UIntToStringBuffer buffer;
+  char* current = buffer + sizeof(buffer);
+  uintToString(value, current);
+  assert(current >= buffer);
+  return current;
+}
+
+#if defined(JSON_HAS_INT64)
+
+std::string valueToString(Int value) {
+  return valueToString(LargestInt(value));
+}
+
+std::string valueToString(UInt value) {
+  return valueToString(LargestUInt(value));
+}
+
+#endif // # if defined(JSON_HAS_INT64)
+
+std::string valueToString(double value) {
+  // Allocate a buffer that is more than large enough to store the 16 digits of
+  // precision requested below.
+  char buffer[32];
+  int len = -1;
+
+// Print into the buffer. We need not request the alternative representation
+// that always has a decimal point because JSON doesn't distingish the
+// concepts of reals and integers.
+#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with
+                                                      // visual studio 2005 to
+                                                      // avoid warning.
+#if defined(WINCE)
+  len = _snprintf(buffer, sizeof(buffer), "%.17g", value);
+#else
+  len = sprintf_s(buffer, sizeof(buffer), "%.17g", value);
+#endif
+#else
+  if (isfinite(value)) {
+    len = snprintf(buffer, sizeof(buffer), "%.17g", value);
+  } else {
+    // IEEE standard states that NaN values will not compare to themselves
+    if (value != value) {
+      len = snprintf(buffer, sizeof(buffer), "null");
+    } else if (value < 0) {
+      len = snprintf(buffer, sizeof(buffer), "-1e+9999");
+    } else {
+      len = snprintf(buffer, sizeof(buffer), "1e+9999");
+    }
+    // For those, we do not need to call fixNumLoc, but it is fast.
+  }
+#endif
+  assert(len >= 0);
+  fixNumericLocale(buffer, buffer + len);
+  return buffer;
+}
+
+std::string valueToString(bool value) { return value ? "true" : "false"; }
+
+std::string valueToQuotedString(const char* value) {
+  if (value == NULL)
+    return "";
+  // Not sure how to handle unicode...
+  if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL &&
+      !containsControlCharacter(value))
+    return std::string("\"") + value + "\"";
+  // We have to walk value and escape any special characters.
+  // Appending to std::string is not efficient, but this should be rare.
+  // (Note: forward slashes are *not* rare, but I am not escaping them.)
+  std::string::size_type maxsize =
+      strlen(value) * 2 + 3; // allescaped+quotes+NULL
+  std::string result;
+  result.reserve(maxsize); // to avoid lots of mallocs
+  result += "\"";
+  for (const char* c = value; *c != 0; ++c) {
+    switch (*c) {
+    case '\"':
+      result += "\\\"";
+      break;
+    case '\\':
+      result += "\\\\";
+      break;
+    case '\b':
+      result += "\\b";
+      break;
+    case '\f':
+      result += "\\f";
+      break;
+    case '\n':
+      result += "\\n";
+      break;
+    case '\r':
+      result += "\\r";
+      break;
+    case '\t':
+      result += "\\t";
+      break;
+    // case '/':
+    // Even though \/ is considered a legal escape in JSON, a bare
+    // slash is also legal, so I see no reason to escape it.
+    // (I hope I am not misunderstanding something.
+    // blep notes: actually escaping \/ may be useful in javascript to avoid </
+    // sequence.
+    // Should add a flag to allow this compatibility mode and prevent this
+    // sequence from occurring.
+    default:
+      if (isControlCharacter(*c)) {
+        std::ostringstream oss;
+        oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
+            << std::setw(4) << static_cast<int>(*c);
+        result += oss.str();
+      } else {
+        result += *c;
+      }
+      break;
+    }
+  }
+  result += "\"";
+  return result;
+}
+
+// https://github.com/upcaste/upcaste/blob/master/src/upcore/src/cstring/strnpbrk.cpp
+static char const* strnpbrk(char const* s, char const* accept, size_t n) {
+  assert((s || !n) && accept);
+
+  char const* const end = s + n;
+  for (char const* cur = s; cur < end; ++cur) {
+    int const c = *cur;
+    for (char const* a = accept; *a; ++a) {
+      if (*a == c) {
+        return cur;
+      }
+    }
+  }
+  return NULL;
+}
+static std::string valueToQuotedStringN(const char* value, unsigned length) {
+  if (value == NULL)
+    return "";
+  // Not sure how to handle unicode...
+  if (strnpbrk(value, "\"\\\b\f\n\r\t", length) == NULL &&
+      !containsControlCharacter0(value, length))
+    return std::string("\"") + value + "\"";
+  // We have to walk value and escape any special characters.
+  // Appending to std::string is not efficient, but this should be rare.
+  // (Note: forward slashes are *not* rare, but I am not escaping them.)
+  std::string::size_type maxsize =
+      length * 2 + 3; // allescaped+quotes+NULL
+  std::string result;
+  result.reserve(maxsize); // to avoid lots of mallocs
+  result += "\"";
+  char const* end = value + length;
+  for (const char* c = value; c != end; ++c) {
+    switch (*c) {
+    case '\"':
+      result += "\\\"";
+      break;
+    case '\\':
+      result += "\\\\";
+      break;
+    case '\b':
+      result += "\\b";
+      break;
+    case '\f':
+      result += "\\f";
+      break;
+    case '\n':
+      result += "\\n";
+      break;
+    case '\r':
+      result += "\\r";
+      break;
+    case '\t':
+      result += "\\t";
+      break;
+    // case '/':
+    // Even though \/ is considered a legal escape in JSON, a bare
+    // slash is also legal, so I see no reason to escape it.
+    // (I hope I am not misunderstanding something.)
+    // blep notes: actually escaping \/ may be useful in javascript to avoid </
+    // sequence.
+    // Should add a flag to allow this compatibility mode and prevent this
+    // sequence from occurring.
+    default:
+      if ((isControlCharacter(*c)) || (*c == 0)) {
+        std::ostringstream oss;
+        oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
+            << std::setw(4) << static_cast<int>(*c);
+        result += oss.str();
+      } else {
+        result += *c;
+      }
+      break;
+    }
+  }
+  result += "\"";
+  return result;
+}
+
+// Class Writer
+// //////////////////////////////////////////////////////////////////
+Writer::~Writer() {}
+
+// Class FastWriter
+// //////////////////////////////////////////////////////////////////
+
+FastWriter::FastWriter()
+    : yamlCompatiblityEnabled_(false), dropNullPlaceholders_(false),
+      omitEndingLineFeed_(false) {}
+
+void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; }
+
+void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
+
+void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }
+
+std::string FastWriter::write(const Value& root) {
+  document_ = "";
+  writeValue(root);
+  if (!omitEndingLineFeed_)
+    document_ += "\n";
+  return document_;
+}
+
+void FastWriter::writeValue(const Value& value) {
+  switch (value.type()) {
+  case nullValue:
+    if (!dropNullPlaceholders_)
+      document_ += "null";
+    break;
+  case intValue:
+    document_ += valueToString(value.asLargestInt());
+    break;
+  case uintValue:
+    document_ += valueToString(value.asLargestUInt());
+    break;
+  case realValue:
+    document_ += valueToString(value.asDouble());
+    break;
+  case stringValue:
+  {
+    // Is NULL possible for value.string_?
+    char const* str;
+    char const* end;
+    bool ok = value.getString(&str, &end);
+    if (ok) document_ += valueToQuotedStringN(str, static_cast<unsigned>(end-str));
+    break;
+  }
+  case booleanValue:
+    document_ += valueToString(value.asBool());
+    break;
+  case arrayValue: {
+    document_ += '[';
+    int size = value.size();
+    for (int index = 0; index < size; ++index) {
+      if (index > 0)
+        document_ += ',';
+      writeValue(value[index]);
+    }
+    document_ += ']';
+  } break;
+  case objectValue: {
+    Value::Members members(value.getMemberNames());
+    document_ += '{';
+    for (Value::Members::iterator it = members.begin(); it != members.end();
+         ++it) {
+      const std::string& name = *it;
+      if (it != members.begin())
+        document_ += ',';
+      document_ += valueToQuotedStringN(name.data(), (unsigned)name.length());
+      document_ += yamlCompatiblityEnabled_ ? ": " : ":";
+      writeValue(value[name]);
+    }
+    document_ += '}';
+  } break;
+  }
+}
+
+// Class StyledWriter
+// //////////////////////////////////////////////////////////////////
+
+StyledWriter::StyledWriter()
+    : rightMargin_(74), indentSize_(3), addChildValues_() {}
+
+std::string StyledWriter::write(const Value& root) {
+  document_ = "";
+  addChildValues_ = false;
+  indentString_ = "";
+  writeCommentBeforeValue(root);
+  writeValue(root);
+  writeCommentAfterValueOnSameLine(root);
+  document_ += "\n";
+  return document_;
+}
+
+void StyledWriter::writeValue(const Value& value) {
+  switch (value.type()) {
+  case nullValue:
+    pushValue("null");
+    break;
+  case intValue:
+    pushValue(valueToString(value.asLargestInt()));
+    break;
+  case uintValue:
+    pushValue(valueToString(value.asLargestUInt()));
+    break;
+  case realValue:
+    pushValue(valueToString(value.asDouble()));
+    break;
+  case stringValue:
+  {
+    // Is NULL possible for value.string_?
+    char const* str;
+    char const* end;
+    bool ok = value.getString(&str, &end);
+    if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
+    else pushValue("");
+    break;
+  }
+  case booleanValue:
+    pushValue(valueToString(value.asBool()));
+    break;
+  case arrayValue:
+    writeArrayValue(value);
+    break;
+  case objectValue: {
+    Value::Members members(value.getMemberNames());
+    if (members.empty())
+      pushValue("{}");
+    else {
+      writeWithIndent("{");
+      indent();
+      Value::Members::iterator it = members.begin();
+      for (;;) {
+        const std::string& name = *it;
+        const Value& childValue = value[name];
+        writeCommentBeforeValue(childValue);
+        writeWithIndent(valueToQuotedString(name.c_str()));
+        document_ += " : ";
+        writeValue(childValue);
+        if (++it == members.end()) {
+          writeCommentAfterValueOnSameLine(childValue);
+          break;
+        }
+        document_ += ',';
+        writeCommentAfterValueOnSameLine(childValue);
+      }
+      unindent();
+      writeWithIndent("}");
+    }
+  } break;
+  }
+}
+
+void StyledWriter::writeArrayValue(const Value& value) {
+  unsigned size = value.size();
+  if (size == 0)
+    pushValue("[]");
+  else {
+    bool isArrayMultiLine = isMultineArray(value);
+    if (isArrayMultiLine) {
+      writeWithIndent("[");
+      indent();
+      bool hasChildValue = !childValues_.empty();
+      unsigned index = 0;
+      for (;;) {
+        const Value& childValue = value[index];
+        writeCommentBeforeValue(childValue);
+        if (hasChildValue)
+          writeWithIndent(childValues_[index]);
+        else {
+          writeIndent();
+          writeValue(childValue);
+        }
+        if (++index == size) {
+          writeCommentAfterValueOnSameLine(childValue);
+          break;
+        }
+        document_ += ',';
+        writeCommentAfterValueOnSameLine(childValue);
+      }
+      unindent();
+      writeWithIndent("]");
+    } else // output on a single line
+    {
+      assert(childValues_.size() == size);
+      document_ += "[ ";
+      for (unsigned index = 0; index < size; ++index) {
+        if (index > 0)
+          document_ += ", ";
+        document_ += childValues_[index];
+      }
+      document_ += " ]";
+    }
+  }
+}
+
+bool StyledWriter::isMultineArray(const Value& value) {
+  int size = value.size();
+  bool isMultiLine = size * 3 >= rightMargin_;
+  childValues_.clear();
+  for (int index = 0; index < size && !isMultiLine; ++index) {
+    const Value& childValue = value[index];
+    isMultiLine =
+        isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
+                        childValue.size() > 0);
+  }
+  if (!isMultiLine) // check if line length > max line length
+  {
+    childValues_.reserve(size);
+    addChildValues_ = true;
+    int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
+    for (int index = 0; index < size; ++index) {
+      if (hasCommentForValue(value[index])) {
+        isMultiLine = true;
+      }
+      writeValue(value[index]);
+      lineLength += int(childValues_[index].length());
+    }
+    addChildValues_ = false;
+    isMultiLine = isMultiLine || lineLength >= rightMargin_;
+  }
+  return isMultiLine;
+}
+
+void StyledWriter::pushValue(const std::string& value) {
+  if (addChildValues_)
+    childValues_.push_back(value);
+  else
+    document_ += value;
+}
+
+void StyledWriter::writeIndent() {
+  if (!document_.empty()) {
+    char last = document_[document_.length() - 1];
+    if (last == ' ') // already indented
+      return;
+    if (last != '\n') // Comments may add new-line
+      document_ += '\n';
+  }
+  document_ += indentString_;
+}
+
+void StyledWriter::writeWithIndent(const std::string& value) {
+  writeIndent();
+  document_ += value;
+}
+
+void StyledWriter::indent() { indentString_ += std::string(indentSize_, ' '); }
+
+void StyledWriter::unindent() {
+  assert(int(indentString_.size()) >= indentSize_);
+  indentString_.resize(indentString_.size() - indentSize_);
+}
+
+void StyledWriter::writeCommentBeforeValue(const Value& root) {
+  if (!root.hasComment(commentBefore))
+    return;
+
+  document_ += "\n";
+  writeIndent();
+  const std::string& comment = root.getComment(commentBefore);
+  std::string::const_iterator iter = comment.begin();
+  while (iter != comment.end()) {
+    document_ += *iter;
+    if (*iter == '\n' &&
+       (iter != comment.end() && *(iter + 1) == '/'))
+      writeIndent();
+    ++iter;
+  }
+
+  // Comments are stripped of trailing newlines, so add one here
+  document_ += "\n";
+}
+
+void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
+  if (root.hasComment(commentAfterOnSameLine))
+    document_ += " " + root.getComment(commentAfterOnSameLine);
+
+  if (root.hasComment(commentAfter)) {
+    document_ += "\n";
+    document_ += root.getComment(commentAfter);
+    document_ += "\n";
+  }
+}
+
+bool StyledWriter::hasCommentForValue(const Value& value) {
+  return value.hasComment(commentBefore) ||
+         value.hasComment(commentAfterOnSameLine) ||
+         value.hasComment(commentAfter);
+}
+
+// Class StyledStreamWriter
+// //////////////////////////////////////////////////////////////////
+
+StyledStreamWriter::StyledStreamWriter(std::string indentation)
+    : document_(NULL), rightMargin_(74), indentation_(indentation),
+      addChildValues_() {}
+
+void StyledStreamWriter::write(std::ostream& out, const Value& root) {
+  document_ = &out;
+  addChildValues_ = false;
+  indentString_ = "";
+  indented_ = true;
+  writeCommentBeforeValue(root);
+  if (!indented_) writeIndent();
+  indented_ = true;
+  writeValue(root);
+  writeCommentAfterValueOnSameLine(root);
+  *document_ << "\n";
+  document_ = NULL; // Forget the stream, for safety.
+}
+
+void StyledStreamWriter::writeValue(const Value& value) {
+  switch (value.type()) {
+  case nullValue:
+    pushValue("null");
+    break;
+  case intValue:
+    pushValue(valueToString(value.asLargestInt()));
+    break;
+  case uintValue:
+    pushValue(valueToString(value.asLargestUInt()));
+    break;
+  case realValue:
+    pushValue(valueToString(value.asDouble()));
+    break;
+  case stringValue:
+  {
+    // Is NULL possible for value.string_?
+    char const* str;
+    char const* end;
+    bool ok = value.getString(&str, &end);
+    if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
+    else pushValue("");
+    break;
+  }
+  case booleanValue:
+    pushValue(valueToString(value.asBool()));
+    break;
+  case arrayValue:
+    writeArrayValue(value);
+    break;
+  case objectValue: {
+    Value::Members members(value.getMemberNames());
+    if (members.empty())
+      pushValue("{}");
+    else {
+      writeWithIndent("{");
+      indent();
+      Value::Members::iterator it = members.begin();
+      for (;;) {
+        const std::string& name = *it;
+        const Value& childValue = value[name];
+        writeCommentBeforeValue(childValue);
+        writeWithIndent(valueToQuotedString(name.c_str()));
+        *document_ << " : ";
+        writeValue(childValue);
+        if (++it == members.end()) {
+          writeCommentAfterValueOnSameLine(childValue);
+          break;
+        }
+        *document_ << ",";
+        writeCommentAfterValueOnSameLine(childValue);
+      }
+      unindent();
+      writeWithIndent("}");
+    }
+  } break;
+  }
+}
+
+void StyledStreamWriter::writeArrayValue(const Value& value) {
+  unsigned size = value.size();
+  if (size == 0)
+    pushValue("[]");
+  else {
+    bool isArrayMultiLine = isMultineArray(value);
+    if (isArrayMultiLine) {
+      writeWithIndent("[");
+      indent();
+      bool hasChildValue = !childValues_.empty();
+      unsigned index = 0;
+      for (;;) {
+        const Value& childValue = value[index];
+        writeCommentBeforeValue(childValue);
+        if (hasChildValue)
+          writeWithIndent(childValues_[index]);
+        else {
+          if (!indented_) writeIndent();
+          indented_ = true;
+          writeValue(childValue);
+          indented_ = false;
+        }
+        if (++index == size) {
+          writeCommentAfterValueOnSameLine(childValue);
+          break;
+        }
+        *document_ << ",";
+        writeCommentAfterValueOnSameLine(childValue);
+      }
+      unindent();
+      writeWithIndent("]");
+    } else // output on a single line
+    {
+      assert(childValues_.size() == size);
+      *document_ << "[ ";
+      for (unsigned index = 0; index < size; ++index) {
+        if (index > 0)
+          *document_ << ", ";
+        *document_ << childValues_[index];
+      }
+      *document_ << " ]";
+    }
+  }
+}
+
+bool StyledStreamWriter::isMultineArray(const Value& value) {
+  int size = value.size();
+  bool isMultiLine = size * 3 >= rightMargin_;
+  childValues_.clear();
+  for (int index = 0; index < size && !isMultiLine; ++index) {
+    const Value& childValue = value[index];
+    isMultiLine =
+        isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
+                        childValue.size() > 0);
+  }
+  if (!isMultiLine) // check if line length > max line length
+  {
+    childValues_.reserve(size);
+    addChildValues_ = true;
+    int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
+    for (int index = 0; index < size; ++index) {
+      if (hasCommentForValue(value[index])) {
+        isMultiLine = true;
+      }
+      writeValue(value[index]);
+      lineLength += int(childValues_[index].length());
+    }
+    addChildValues_ = false;
+    isMultiLine = isMultiLine || lineLength >= rightMargin_;
+  }
+  return isMultiLine;
+}
+
+void StyledStreamWriter::pushValue(const std::string& value) {
+  if (addChildValues_)
+    childValues_.push_back(value);
+  else
+    *document_ << value;
+}
+
+void StyledStreamWriter::writeIndent() {
+  // blep intended this to look at the so-far-written string
+  // to determine whether we are already indented, but
+  // with a stream we cannot do that. So we rely on some saved state.
+  // The caller checks indented_.
+  *document_ << '\n' << indentString_;
+}
+
+void StyledStreamWriter::writeWithIndent(const std::string& value) {
+  if (!indented_) writeIndent();
+  *document_ << value;
+  indented_ = false;
+}
+
+void StyledStreamWriter::indent() { indentString_ += indentation_; }
+
+void StyledStreamWriter::unindent() {
+  assert(indentString_.size() >= indentation_.size());
+  indentString_.resize(indentString_.size() - indentation_.size());
+}
+
+void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
+  if (!root.hasComment(commentBefore))
+    return;
+
+  if (!indented_) writeIndent();
+  const std::string& comment = root.getComment(commentBefore);
+  std::string::const_iterator iter = comment.begin();
+  while (iter != comment.end()) {
+    *document_ << *iter;
+    if (*iter == '\n' &&
+       (iter != comment.end() && *(iter + 1) == '/'))
+      // writeIndent();  // would include newline
+      *document_ << indentString_;
+    ++iter;
+  }
+  indented_ = false;
+}
+
+void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
+  if (root.hasComment(commentAfterOnSameLine))
+    *document_ << ' ' << root.getComment(commentAfterOnSameLine);
+
+  if (root.hasComment(commentAfter)) {
+    writeIndent();
+    *document_ << root.getComment(commentAfter);
+  }
+  indented_ = false;
+}
+
+bool StyledStreamWriter::hasCommentForValue(const Value& value) {
+  return value.hasComment(commentBefore) ||
+         value.hasComment(commentAfterOnSameLine) ||
+         value.hasComment(commentAfter);
+}
+
+//////////////////////////
+// BuiltStyledStreamWriter
+
+/// Scoped enums are not available until C++11.
+struct CommentStyle {
+  /// Decide whether to write comments.
+  enum Enum {
+    None,  ///< Drop all comments.
+    Most,  ///< Recover odd behavior of previous versions (not implemented yet).
+    All  ///< Keep all comments.
+  };
+};
+
+struct BuiltStyledStreamWriter : public StreamWriter
+{
+  BuiltStyledStreamWriter(
+      std::string const& indentation,
+      CommentStyle::Enum cs,
+      std::string const& colonSymbol,
+      std::string const& nullSymbol,
+      std::string const& endingLineFeedSymbol);
+  virtual int write(Value const& root, std::ostream* sout);
+private:
+  void writeValue(Value const& value);
+  void writeArrayValue(Value const& value);
+  bool isMultineArray(Value const& value);
+  void pushValue(std::string const& value);
+  void writeIndent();
+  void writeWithIndent(std::string const& value);
+  void indent();
+  void unindent();
+  void writeCommentBeforeValue(Value const& root);
+  void writeCommentAfterValueOnSameLine(Value const& root);
+  static bool hasCommentForValue(const Value& value);
+
+  typedef std::vector<std::string> ChildValues;
+
+  ChildValues childValues_;
+  std::string indentString_;
+  int rightMargin_;
+  std::string indentation_;
+  CommentStyle::Enum cs_;
+  std::string colonSymbol_;
+  std::string nullSymbol_;
+  std::string endingLineFeedSymbol_;
+  bool addChildValues_ : 1;
+  bool indented_ : 1;
+};
+BuiltStyledStreamWriter::BuiltStyledStreamWriter(
+      std::string const& indentation,
+      CommentStyle::Enum cs,
+      std::string const& colonSymbol,
+      std::string const& nullSymbol,
+      std::string const& endingLineFeedSymbol)
+  : rightMargin_(74)
+  , indentation_(indentation)
+  , cs_(cs)
+  , colonSymbol_(colonSymbol)
+  , nullSymbol_(nullSymbol)
+  , endingLineFeedSymbol_(endingLineFeedSymbol)
+  , addChildValues_(false)
+  , indented_(false)
+{
+}
+int BuiltStyledStreamWriter::write(Value const& root, std::ostream* sout)
+{
+  sout_ = sout;
+  addChildValues_ = false;
+  indented_ = true;
+  indentString_ = "";
+  writeCommentBeforeValue(root);
+  if (!indented_) writeIndent();
+  indented_ = true;
+  writeValue(root);
+  writeCommentAfterValueOnSameLine(root);
+  *sout_ << endingLineFeedSymbol_;
+  sout_ = NULL;
+  return 0;
+}
+void BuiltStyledStreamWriter::writeValue(Value const& value) {
+  switch (value.type()) {
+  case nullValue:
+    pushValue(nullSymbol_);
+    break;
+  case intValue:
+    pushValue(valueToString(value.asLargestInt()));
+    break;
+  case uintValue:
+    pushValue(valueToString(value.asLargestUInt()));
+    break;
+  case realValue:
+    pushValue(valueToString(value.asDouble()));
+    break;
+  case stringValue:
+  {
+    // Is NULL is possible for value.string_?
+    char const* str;
+    char const* end;
+    bool ok = value.getString(&str, &end);
+    if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
+    else pushValue("");
+    break;
+  }
+  case booleanValue:
+    pushValue(valueToString(value.asBool()));
+    break;
+  case arrayValue:
+    writeArrayValue(value);
+    break;
+  case objectValue: {
+    Value::Members members(value.getMemberNames());
+    if (members.empty())
+      pushValue("{}");
+    else {
+      writeWithIndent("{");
+      indent();
+      Value::Members::iterator it = members.begin();
+      for (;;) {
+        std::string const& name = *it;
+        Value const& childValue = value[name];
+        writeCommentBeforeValue(childValue);
+        writeWithIndent(valueToQuotedStringN(name.data(), (unsigned)name.length()));
+        *sout_ << colonSymbol_;
+        writeValue(childValue);
+        if (++it == members.end()) {
+          writeCommentAfterValueOnSameLine(childValue);
+          break;
+        }
+        *sout_ << ",";
+        writeCommentAfterValueOnSameLine(childValue);
+      }
+      unindent();
+      writeWithIndent("}");
+    }
+  } break;
+  }
+}
+
+void BuiltStyledStreamWriter::writeArrayValue(Value const& value) {
+  unsigned size = value.size();
+  if (size == 0)
+    pushValue("[]");
+  else {
+    bool isMultiLine = (cs_ == CommentStyle::All) || isMultineArray(value);
+    if (isMultiLine) {
+      writeWithIndent("[");
+      indent();
+      bool hasChildValue = !childValues_.empty();
+      unsigned index = 0;
+      for (;;) {
+        Value const& childValue = value[index];
+        writeCommentBeforeValue(childValue);
+        if (hasChildValue)
+          writeWithIndent(childValues_[index]);
+        else {
+          if (!indented_) writeIndent();
+          indented_ = true;
+          writeValue(childValue);
+          indented_ = false;
+        }
+        if (++index == size) {
+          writeCommentAfterValueOnSameLine(childValue);
+          break;
+        }
+        *sout_ << ",";
+        writeCommentAfterValueOnSameLine(childValue);
+      }
+      unindent();
+      writeWithIndent("]");
+    } else // output on a single line
+    {
+      assert(childValues_.size() == size);
+      *sout_ << "[";
+      if (!indentation_.empty()) *sout_ << " ";
+      for (unsigned index = 0; index < size; ++index) {
+        if (index > 0)
+          *sout_ << ", ";
+        *sout_ << childValues_[index];
+      }
+      if (!indentation_.empty()) *sout_ << " ";
+      *sout_ << "]";
+    }
+  }
+}
+
+bool BuiltStyledStreamWriter::isMultineArray(Value const& value) {
+  int size = value.size();
+  bool isMultiLine = size * 3 >= rightMargin_;
+  childValues_.clear();
+  for (int index = 0; index < size && !isMultiLine; ++index) {
+    Value const& childValue = value[index];
+    isMultiLine =
+        isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
+                        childValue.size() > 0);
+  }
+  if (!isMultiLine) // check if line length > max line length
+  {
+    childValues_.reserve(size);
+    addChildValues_ = true;
+    int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
+    for (int index = 0; index < size; ++index) {
+      if (hasCommentForValue(value[index])) {
+        isMultiLine = true;
+      }
+      writeValue(value[index]);
+      lineLength += int(childValues_[index].length());
+    }
+    addChildValues_ = false;
+    isMultiLine = isMultiLine || lineLength >= rightMargin_;
+  }
+  return isMultiLine;
+}
+
+void BuiltStyledStreamWriter::pushValue(std::string const& value) {
+  if (addChildValues_)
+    childValues_.push_back(value);
+  else
+    *sout_ << value;
+}
+
+void BuiltStyledStreamWriter::writeIndent() {
+  // blep intended this to look at the so-far-written string
+  // to determine whether we are already indented, but
+  // with a stream we cannot do that. So we rely on some saved state.
+  // The caller checks indented_.
+
+  if (!indentation_.empty()) {
+    // In this case, drop newlines too.
+    *sout_ << '\n' << indentString_;
+  }
+}
+
+void BuiltStyledStreamWriter::writeWithIndent(std::string const& value) {
+  if (!indented_) writeIndent();
+  *sout_ << value;
+  indented_ = false;
+}
+
+void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; }
+
+void BuiltStyledStreamWriter::unindent() {
+  assert(indentString_.size() >= indentation_.size());
+  indentString_.resize(indentString_.size() - indentation_.size());
+}
+
+void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) {
+  if (cs_ == CommentStyle::None) return;
+  if (!root.hasComment(commentBefore))
+    return;
+
+  if (!indented_) writeIndent();
+  const std::string& comment = root.getComment(commentBefore);
+  std::string::const_iterator iter = comment.begin();
+  while (iter != comment.end()) {
+    *sout_ << *iter;
+    if (*iter == '\n' &&
+       (iter != comment.end() && *(iter + 1) == '/'))
+      // writeIndent();  // would write extra newline
+      *sout_ << indentString_;
+    ++iter;
+  }
+  indented_ = false;
+}
+
+void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) {
+  if (cs_ == CommentStyle::None) return;
+  if (root.hasComment(commentAfterOnSameLine))
+    *sout_ << " " + root.getComment(commentAfterOnSameLine);
+
+  if (root.hasComment(commentAfter)) {
+    writeIndent();
+    *sout_ << root.getComment(commentAfter);
+  }
+}
+
+// static
+bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {
+  return value.hasComment(commentBefore) ||
+         value.hasComment(commentAfterOnSameLine) ||
+         value.hasComment(commentAfter);
+}
+
+///////////////
+// StreamWriter
+
+StreamWriter::StreamWriter()
+    : sout_(NULL)
+{
+}
+StreamWriter::~StreamWriter()
+{
+}
+StreamWriter::Factory::~Factory()
+{}
+StreamWriterBuilder::StreamWriterBuilder()
+{
+  setDefaults(&settings_);
+}
+StreamWriterBuilder::~StreamWriterBuilder()
+{}
+StreamWriter* StreamWriterBuilder::newStreamWriter() const
+{
+  std::string indentation = settings_["indentation"].asString();
+  std::string cs_str = settings_["commentStyle"].asString();
+  bool eyc = settings_["enableYAMLCompatibility"].asBool();
+  bool dnp = settings_["dropNullPlaceholders"].asBool();
+  CommentStyle::Enum cs = CommentStyle::All;
+  if (cs_str == "All") {
+    cs = CommentStyle::All;
+  } else if (cs_str == "None") {
+    cs = CommentStyle::None;
+  } else {
+    throwRuntimeError("commentStyle must be 'All' or 'None'");
+  }
+  std::string colonSymbol = " : ";
+  if (eyc) {
+    colonSymbol = ": ";
+  } else if (indentation.empty()) {
+    colonSymbol = ":";
+  }
+  std::string nullSymbol = "null";
+  if (dnp) {
+    nullSymbol = "";
+  }
+  std::string endingLineFeedSymbol = "";
+  return new BuiltStyledStreamWriter(
+      indentation, cs,
+      colonSymbol, nullSymbol, endingLineFeedSymbol);
+}
+static void getValidWriterKeys(std::set<std::string>* valid_keys)
+{
+  valid_keys->clear();
+  valid_keys->insert("indentation");
+  valid_keys->insert("commentStyle");
+  valid_keys->insert("enableYAMLCompatibility");
+  valid_keys->insert("dropNullPlaceholders");
+}
+bool StreamWriterBuilder::validate(Json::Value* invalid) const
+{
+  Json::Value my_invalid;
+  if (!invalid) invalid = &my_invalid;  // so we do not need to test for NULL
+  Json::Value& inv = *invalid;
+  std::set<std::string> valid_keys;
+  getValidWriterKeys(&valid_keys);
+  Value::Members keys = settings_.getMemberNames();
+  size_t n = keys.size();
+  for (size_t i = 0; i < n; ++i) {
+    std::string const& key = keys[i];
+    if (valid_keys.find(key) == valid_keys.end()) {
+      inv[key] = settings_[key];
+    }
+  }
+  return 0u == inv.size();
+}
+Value& StreamWriterBuilder::operator[](std::string key)
+{
+  return settings_[key];
+}
+// static
+void StreamWriterBuilder::setDefaults(Json::Value* settings)
+{
+  //! [StreamWriterBuilderDefaults]
+  (*settings)["commentStyle"] = "All";
+  (*settings)["indentation"] = "\t";
+  (*settings)["enableYAMLCompatibility"] = false;
+  (*settings)["dropNullPlaceholders"] = false;
+  //! [StreamWriterBuilderDefaults]
+}
+
+std::string writeString(StreamWriter::Factory const& builder, Value const& root) {
+  std::ostringstream sout;
+  StreamWriterPtr const writer(builder.newStreamWriter());
+  writer->write(root, &sout);
+  return sout.str();
+}
+
+std::ostream& operator<<(std::ostream& sout, Value const& root) {
+  StreamWriterBuilder builder;
+  StreamWriterPtr const writer(builder.newStreamWriter());
+  writer->write(root, &sout);
+  return sout;
+}
+
+} // namespace Json
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: src/lib_json/json_writer.cpp
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+

+ 23 - 0
src/external/mpark-variant/LICENSE.md

@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.

+ 37 - 0
src/external/mpark-variant/README.md

@@ -0,0 +1,37 @@
+# MPark.Variant
+
+> __C++17__ `std::variant` for __C++11__/__14__/__17__
+
+[![release][badge.release]][release]
+[![header][badge.header]][header]
+[![travis][badge.travis]][travis]
+[![appveyor][badge.appveyor]][appveyor]
+[![license][badge.license]][license]
+[![godbolt][badge.godbolt]][godbolt]
+[![wandbox][badge.wandbox]][wandbox]
+
+[badge.release]: https://img.shields.io/github/release/mpark/variant.svg
+[badge.header]: https://img.shields.io/badge/single%20header-master-blue.svg
+[badge.travis]: https://travis-ci.org/mpark/variant.svg?branch=master
+[badge.appveyor]: https://ci.appveyor.com/api/projects/status/github/mpark/variant?branch=master&svg=true
+[badge.license]: https://img.shields.io/badge/license-boost-blue.svg
+[badge.godbolt]: https://img.shields.io/badge/try%20it-on%20godbolt-222266.svg
+[badge.wandbox]: https://img.shields.io/badge/try%20it-on%20wandbox-5cb85c.svg
+
+[release]: https://github.com/mpark/variant/releases/latest
+[header]: https://github.com/mpark/variant/blob/single-header/master/variant.hpp
+[travis]: https://travis-ci.org/mpark/variant
+[appveyor]: https://ci.appveyor.com/project/mpark/variant
+[license]: https://github.com/mpark/variant/blob/master/LICENSE.md
+[godbolt]: https://godbolt.org/z/4r7hEy
+[wandbox]: https://wandbox.org/permlink/dTZxf85MVhehOqx1
+
+## Single Header
+
+This branch provides a standalone `variant.hpp` file for each
+[release](https://github.com/mpark/variant/releases).
+Copy it and `#include` away!
+
+## License
+
+Distributed under the [Boost Software License, Version 1.0](LICENSE.md).

+ 2784 - 0
src/external/mpark-variant/master/variant.hpp

@@ -0,0 +1,2784 @@
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_VARIANT_HPP
+#define MPARK_VARIANT_HPP
+
+/*
+   variant synopsis
+
+namespace std {
+
+  // 20.7.2, class template variant
+  template <class... Types>
+  class variant {
+  public:
+
+    // 20.7.2.1, constructors
+    constexpr variant() noexcept(see below);
+    variant(const variant&);
+    variant(variant&&) noexcept(see below);
+
+    template <class T> constexpr variant(T&&) noexcept(see below);
+
+    template <class T, class... Args>
+    constexpr explicit variant(in_place_type_t<T>, Args&&...);
+
+    template <class T, class U, class... Args>
+    constexpr explicit variant(
+        in_place_type_t<T>, initializer_list<U>, Args&&...);
+
+    template <size_t I, class... Args>
+    constexpr explicit variant(in_place_index_t<I>, Args&&...);
+
+    template <size_t I, class U, class... Args>
+    constexpr explicit variant(
+        in_place_index_t<I>, initializer_list<U>, Args&&...);
+
+    // 20.7.2.2, destructor
+    ~variant();
+
+    // 20.7.2.3, assignment
+    variant& operator=(const variant&);
+    variant& operator=(variant&&) noexcept(see below);
+
+    template <class T> variant& operator=(T&&) noexcept(see below);
+
+    // 20.7.2.4, modifiers
+    template <class T, class... Args>
+    T& emplace(Args&&...);
+
+    template <class T, class U, class... Args>
+    T& emplace(initializer_list<U>, Args&&...);
+
+    template <size_t I, class... Args>
+    variant_alternative<I, variant>& emplace(Args&&...);
+
+    template <size_t I, class U, class...  Args>
+    variant_alternative<I, variant>& emplace(initializer_list<U>, Args&&...);
+
+    // 20.7.2.5, value status
+    constexpr bool valueless_by_exception() const noexcept;
+    constexpr size_t index() const noexcept;
+
+    // 20.7.2.6, swap
+    void swap(variant&) noexcept(see below);
+  };
+
+  // 20.7.3, variant helper classes
+  template <class T> struct variant_size; // undefined
+
+  template <class T>
+  constexpr size_t variant_size_v = variant_size<T>::value;
+
+  template <class T> struct variant_size<const T>;
+  template <class T> struct variant_size<volatile T>;
+  template <class T> struct variant_size<const volatile T>;
+
+  template <class... Types>
+  struct variant_size<variant<Types...>>;
+
+  template <size_t I, class T> struct variant_alternative; // undefined
+
+  template <size_t I, class T>
+  using variant_alternative_t = typename variant_alternative<I, T>::type;
+
+  template <size_t I, class T> struct variant_alternative<I, const T>;
+  template <size_t I, class T> struct variant_alternative<I, volatile T>;
+  template <size_t I, class T> struct variant_alternative<I, const volatile T>;
+
+  template <size_t I, class... Types>
+  struct variant_alternative<I, variant<Types...>>;
+
+  constexpr size_t variant_npos = -1;
+
+  // 20.7.4, value access
+  template <class T, class... Types>
+  constexpr bool holds_alternative(const variant<Types...>&) noexcept;
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>>&
+  get(variant<Types...>&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>>&&
+  get(variant<Types...>&&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>> const&
+  get(const variant<Types...>&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>> const&&
+  get(const variant<Types...>&&);
+
+  template <class T, class...  Types>
+  constexpr T& get(variant<Types...>&);
+
+  template <class T, class... Types>
+  constexpr T&& get(variant<Types...>&&);
+
+  template <class T, class... Types>
+  constexpr const T& get(const variant<Types...>&);
+
+  template <class T, class... Types>
+  constexpr const T&& get(const variant<Types...>&&);
+
+  template <size_t I, class... Types>
+  constexpr add_pointer_t<variant_alternative_t<I, variant<Types...>>>
+  get_if(variant<Types...>*) noexcept;
+
+  template <size_t I, class... Types>
+  constexpr add_pointer_t<const variant_alternative_t<I, variant<Types...>>>
+  get_if(const variant<Types...>*) noexcept;
+
+  template <class T, class... Types>
+  constexpr add_pointer_t<T>
+  get_if(variant<Types...>*) noexcept;
+
+  template <class T, class... Types>
+  constexpr add_pointer_t<const T>
+  get_if(const variant<Types...>*) noexcept;
+
+  // 20.7.5, relational operators
+  template <class... Types>
+  constexpr bool operator==(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator!=(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator<(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator>(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator<=(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator>=(const variant<Types...>&, const variant<Types...>&);
+
+  // 20.7.6, visitation
+  template <class Visitor, class... Variants>
+  constexpr see below visit(Visitor&&, Variants&&...);
+
+  // 20.7.7, class monostate
+  struct monostate;
+
+  // 20.7.8, monostate relational operators
+  constexpr bool operator<(monostate, monostate) noexcept;
+  constexpr bool operator>(monostate, monostate) noexcept;
+  constexpr bool operator<=(monostate, monostate) noexcept;
+  constexpr bool operator>=(monostate, monostate) noexcept;
+  constexpr bool operator==(monostate, monostate) noexcept;
+  constexpr bool operator!=(monostate, monostate) noexcept;
+
+  // 20.7.9, specialized algorithms
+  template <class... Types>
+  void swap(variant<Types...>&, variant<Types...>&) noexcept(see below);
+
+  // 20.7.10, class bad_variant_access
+  class bad_variant_access;
+
+  // 20.7.11, hash support
+  template <class T> struct hash;
+  template <class... Types> struct hash<variant<Types...>>;
+  template <> struct hash<monostate>;
+
+} // namespace std
+
+*/
+
+#include <cstddef>
+#include <exception>
+#include <functional>
+#include <initializer_list>
+#include <limits>
+#include <new>
+#include <type_traits>
+#include <utility>
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_CONFIG_HPP
+#define MPARK_CONFIG_HPP
+
+// MSVC 2015 Update 3.
+#if __cplusplus < 201103L && (!defined(_MSC_VER) || _MSC_FULL_VER < 190024210)
+#error "MPark.Variant requires C++11 support."
+#endif
+
+#ifndef __has_attribute
+#define __has_attribute(x) 0
+#endif
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+#ifndef __has_include
+#define __has_include(x) 0
+#endif
+
+#ifndef __has_feature
+#define __has_feature(x) 0
+#endif
+
+#if __has_attribute(always_inline) || defined(__GNUC__)
+#define MPARK_ALWAYS_INLINE __attribute__((__always_inline__)) inline
+#elif defined(_MSC_VER)
+#define MPARK_ALWAYS_INLINE __forceinline
+#else
+#define MPARK_ALWAYS_INLINE inline
+#endif
+
+#if __has_builtin(__builtin_addressof) || \
+    (defined(__GNUC__) && __GNUC__ >= 7) || defined(_MSC_VER)
+#define MPARK_BUILTIN_ADDRESSOF
+#endif
+
+#if __has_builtin(__builtin_unreachable) || defined(__GNUC__)
+#define MPARK_BUILTIN_UNREACHABLE __builtin_unreachable()
+#elif defined(_MSC_VER)
+#define MPARK_BUILTIN_UNREACHABLE __assume(false)
+#else
+#define MPARK_BUILTIN_UNREACHABLE
+#endif
+
+#if __has_builtin(__type_pack_element)
+#define MPARK_TYPE_PACK_ELEMENT
+#endif
+
+#if defined(__cpp_constexpr) && __cpp_constexpr >= 200704 && \
+    !(defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 9)
+#define MPARK_CPP11_CONSTEXPR
+#endif
+
+#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304
+#define MPARK_CPP14_CONSTEXPR
+#endif
+
+#if __has_feature(cxx_exceptions) || defined(__cpp_exceptions) || \
+    (defined(_MSC_VER) && defined(_CPPUNWIND))
+#define MPARK_EXCEPTIONS
+#endif
+
+#if defined(__cpp_generic_lambdas) || defined(_MSC_VER)
+#define MPARK_GENERIC_LAMBDAS
+#endif
+
+#if defined(__cpp_lib_integer_sequence)
+#define MPARK_INTEGER_SEQUENCE
+#endif
+
+#if defined(__cpp_return_type_deduction) || defined(_MSC_VER)
+#define MPARK_RETURN_TYPE_DEDUCTION
+#endif
+
+#if defined(__cpp_lib_transparent_operators) || defined(_MSC_VER)
+#define MPARK_TRANSPARENT_OPERATORS
+#endif
+
+#if defined(__cpp_variable_templates) || defined(_MSC_VER)
+#define MPARK_VARIABLE_TEMPLATES
+#endif
+
+#if !defined(__GLIBCXX__) || __has_include(<codecvt>)  // >= libstdc++-5
+#define MPARK_TRIVIALITY_TYPE_TRAITS
+#define MPARK_INCOMPLETE_TYPE_TRAITS
+#endif
+
+#endif  // MPARK_CONFIG_HPP
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_IN_PLACE_HPP
+#define MPARK_IN_PLACE_HPP
+
+#include <cstddef>
+
+
+namespace mpark {
+
+  struct in_place_t { explicit in_place_t() = default; };
+
+  template <std::size_t I>
+  struct in_place_index_t { explicit in_place_index_t() = default; };
+
+  template <typename T>
+  struct in_place_type_t { explicit in_place_type_t() = default; };
+
+#ifdef MPARK_VARIABLE_TEMPLATES
+  constexpr in_place_t in_place{};
+
+  template <std::size_t I> constexpr in_place_index_t<I> in_place_index{};
+
+  template <typename T> constexpr in_place_type_t<T> in_place_type{};
+#endif
+
+}  // namespace mpark
+
+#endif  // MPARK_IN_PLACE_HPP
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_LIB_HPP
+#define MPARK_LIB_HPP
+
+#include <memory>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+
+#define MPARK_RETURN(...) \
+  noexcept(noexcept(__VA_ARGS__)) -> decltype(__VA_ARGS__) { return __VA_ARGS__; }
+
+namespace mpark {
+  namespace lib {
+    template <typename T>
+    struct identity { using type = T; };
+
+    inline namespace cpp14 {
+      template <typename T, std::size_t N>
+      struct array {
+        constexpr const T &operator[](std::size_t index) const {
+          return data[index];
+        }
+
+        T data[N == 0 ? 1 : N];
+      };
+
+      template <typename T>
+      using add_pointer_t = typename std::add_pointer<T>::type;
+
+      template <typename... Ts>
+      using common_type_t = typename std::common_type<Ts...>::type;
+
+      template <typename T>
+      using decay_t = typename std::decay<T>::type;
+
+      template <bool B, typename T = void>
+      using enable_if_t = typename std::enable_if<B, T>::type;
+
+      template <typename T>
+      using remove_const_t = typename std::remove_const<T>::type;
+
+      template <typename T>
+      using remove_reference_t = typename std::remove_reference<T>::type;
+
+      template <typename T>
+      inline constexpr T &&forward(remove_reference_t<T> &t) noexcept {
+        return static_cast<T &&>(t);
+      }
+
+      template <typename T>
+      inline constexpr T &&forward(remove_reference_t<T> &&t) noexcept {
+        static_assert(!std::is_lvalue_reference<T>::value,
+                      "can not forward an rvalue as an lvalue");
+        return static_cast<T &&>(t);
+      }
+
+      template <typename T>
+      inline constexpr remove_reference_t<T> &&move(T &&t) noexcept {
+        return static_cast<remove_reference_t<T> &&>(t);
+      }
+
+#ifdef MPARK_INTEGER_SEQUENCE
+      using std::integer_sequence;
+      using std::index_sequence;
+      using std::make_index_sequence;
+      using std::index_sequence_for;
+#else
+      template <typename T, T... Is>
+      struct integer_sequence {
+        using value_type = T;
+        static constexpr std::size_t size() noexcept { return sizeof...(Is); }
+      };
+
+      template <std::size_t... Is>
+      using index_sequence = integer_sequence<std::size_t, Is...>;
+
+      template <typename Lhs, typename Rhs>
+      struct make_index_sequence_concat;
+
+      template <std::size_t... Lhs, std::size_t... Rhs>
+      struct make_index_sequence_concat<index_sequence<Lhs...>,
+                                        index_sequence<Rhs...>>
+          : identity<index_sequence<Lhs..., (sizeof...(Lhs) + Rhs)...>> {};
+
+      template <std::size_t N>
+      struct make_index_sequence_impl;
+
+      template <std::size_t N>
+      using make_index_sequence = typename make_index_sequence_impl<N>::type;
+
+      template <std::size_t N>
+      struct make_index_sequence_impl
+          : make_index_sequence_concat<make_index_sequence<N / 2>,
+                                       make_index_sequence<N - (N / 2)>> {};
+
+      template <>
+      struct make_index_sequence_impl<0> : identity<index_sequence<>> {};
+
+      template <>
+      struct make_index_sequence_impl<1> : identity<index_sequence<0>> {};
+
+      template <typename... Ts>
+      using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
+#endif
+
+      // <functional>
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using equal_to = std::equal_to<>;
+#else
+      struct equal_to {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          MPARK_RETURN(lib::forward<Lhs>(lhs) == lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using not_equal_to = std::not_equal_to<>;
+#else
+      struct not_equal_to {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          MPARK_RETURN(lib::forward<Lhs>(lhs) != lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using less = std::less<>;
+#else
+      struct less {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          MPARK_RETURN(lib::forward<Lhs>(lhs) < lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using greater = std::greater<>;
+#else
+      struct greater {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          MPARK_RETURN(lib::forward<Lhs>(lhs) > lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using less_equal = std::less_equal<>;
+#else
+      struct less_equal {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          MPARK_RETURN(lib::forward<Lhs>(lhs) <= lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using greater_equal = std::greater_equal<>;
+#else
+      struct greater_equal {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          MPARK_RETURN(lib::forward<Lhs>(lhs) >= lib::forward<Rhs>(rhs))
+      };
+#endif
+    }  // namespace cpp14
+
+    inline namespace cpp17 {
+
+      // <type_traits>
+      template <bool B>
+      using bool_constant = std::integral_constant<bool, B>;
+
+      template <typename...>
+      struct voider : identity<void> {};
+
+      template <typename... Ts>
+      using void_t = typename voider<Ts...>::type;
+
+      namespace detail {
+        namespace swappable {
+
+          using std::swap;
+
+          template <typename T>
+          struct is_swappable {
+            private:
+            template <typename U,
+                      typename = decltype(swap(std::declval<U &>(),
+                                               std::declval<U &>()))>
+            inline static std::true_type test(int);
+
+            template <typename U>
+            inline static std::false_type test(...);
+
+            public:
+            static constexpr bool value = decltype(test<T>(0))::value;
+          };
+
+          template <bool IsSwappable, typename T>
+          struct is_nothrow_swappable {
+            static constexpr bool value =
+                noexcept(swap(std::declval<T &>(), std::declval<T &>()));
+          };
+
+          template <typename T>
+          struct is_nothrow_swappable<false, T> : std::false_type {};
+
+        }  // namespace swappable
+      }  // namespace detail
+
+      using detail::swappable::is_swappable;
+
+      template <typename T>
+      using is_nothrow_swappable =
+          detail::swappable::is_nothrow_swappable<is_swappable<T>::value, T>;
+
+      // <functional>
+      namespace detail {
+
+        template <typename T>
+        struct is_reference_wrapper : std::false_type {};
+
+        template <typename T>
+        struct is_reference_wrapper<std::reference_wrapper<T>>
+            : std::true_type {};
+
+        template <bool, int>
+        struct Invoke;
+
+        template <>
+        struct Invoke<true /* pmf */, 0 /* is_base_of */> {
+          template <typename R, typename T, typename Arg, typename... Args>
+          inline static constexpr auto invoke(R T::*pmf, Arg &&arg, Args &&... args)
+            MPARK_RETURN((lib::forward<Arg>(arg).*pmf)(lib::forward<Args>(args)...))
+        };
+
+        template <>
+        struct Invoke<true /* pmf */, 1 /* is_reference_wrapper */> {
+          template <typename R, typename T, typename Arg, typename... Args>
+          inline static constexpr auto invoke(R T::*pmf, Arg &&arg, Args &&... args)
+            MPARK_RETURN((lib::forward<Arg>(arg).get().*pmf)(lib::forward<Args>(args)...))
+        };
+
+        template <>
+        struct Invoke<true /* pmf */, 2 /* otherwise */> {
+          template <typename R, typename T, typename Arg, typename... Args>
+          inline static constexpr auto invoke(R T::*pmf, Arg &&arg, Args &&... args)
+            MPARK_RETURN(((*lib::forward<Arg>(arg)).*pmf)(lib::forward<Args>(args)...))
+        };
+
+        template <>
+        struct Invoke<false /* pmo */, 0 /* is_base_of */> {
+          template <typename R, typename T, typename Arg>
+          inline static constexpr auto invoke(R T::*pmo, Arg &&arg)
+            MPARK_RETURN(lib::forward<Arg>(arg).*pmo)
+        };
+
+        template <>
+        struct Invoke<false /* pmo */, 1 /* is_reference_wrapper */> {
+          template <typename R, typename T, typename Arg>
+          inline static constexpr auto invoke(R T::*pmo, Arg &&arg)
+            MPARK_RETURN(lib::forward<Arg>(arg).get().*pmo)
+        };
+
+        template <>
+        struct Invoke<false /* pmo */, 2 /* otherwise */> {
+          template <typename R, typename T, typename Arg>
+          inline static constexpr auto invoke(R T::*pmo, Arg &&arg)
+              MPARK_RETURN((*lib::forward<Arg>(arg)).*pmo)
+        };
+
+        template <typename R, typename T, typename Arg, typename... Args>
+        inline constexpr auto invoke(R T::*f, Arg &&arg, Args &&... args)
+          MPARK_RETURN(
+              Invoke<std::is_function<R>::value,
+                     (std::is_base_of<T, lib::decay_t<Arg>>::value
+                          ? 0
+                          : is_reference_wrapper<lib::decay_t<Arg>>::value
+                                ? 1
+                                : 2)>::invoke(f,
+                                              lib::forward<Arg>(arg),
+                                              lib::forward<Args>(args)...))
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+        template <typename F, typename... Args>
+        inline constexpr auto invoke(F &&f, Args &&... args)
+          MPARK_RETURN(lib::forward<F>(f)(lib::forward<Args>(args)...))
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+      }  // namespace detail
+
+      template <typename F, typename... Args>
+      inline constexpr auto invoke(F &&f, Args &&... args)
+        MPARK_RETURN(detail::invoke(lib::forward<F>(f),
+                                    lib::forward<Args>(args)...))
+
+      namespace detail {
+
+        template <typename Void, typename, typename...>
+        struct invoke_result {};
+
+        template <typename F, typename... Args>
+        struct invoke_result<void_t<decltype(lib::invoke(
+                                 std::declval<F>(), std::declval<Args>()...))>,
+                             F,
+                             Args...>
+            : identity<decltype(
+                  lib::invoke(std::declval<F>(), std::declval<Args>()...))> {};
+
+      }  // namespace detail
+
+      template <typename F, typename... Args>
+      using invoke_result = detail::invoke_result<void, F, Args...>;
+
+      template <typename F, typename... Args>
+      using invoke_result_t = typename invoke_result<F, Args...>::type;
+
+      namespace detail {
+
+        template <typename Void, typename, typename...>
+        struct is_invocable : std::false_type {};
+
+        template <typename F, typename... Args>
+        struct is_invocable<void_t<invoke_result_t<F, Args...>>, F, Args...>
+            : std::true_type {};
+
+        template <typename Void, typename, typename, typename...>
+        struct is_invocable_r : std::false_type {};
+
+        template <typename R, typename F, typename... Args>
+        struct is_invocable_r<void_t<invoke_result_t<F, Args...>>,
+                              R,
+                              F,
+                              Args...>
+            : std::is_convertible<invoke_result_t<F, Args...>, R> {};
+
+      }  // namespace detail
+
+      template <typename F, typename... Args>
+      using is_invocable = detail::is_invocable<void, F, Args...>;
+
+      template <typename R, typename F, typename... Args>
+      using is_invocable_r = detail::is_invocable_r<void, R, F, Args...>;
+
+      // <memory>
+#ifdef MPARK_BUILTIN_ADDRESSOF
+      template <typename T>
+      inline constexpr T *addressof(T &arg) noexcept {
+        return __builtin_addressof(arg);
+      }
+#else
+      namespace detail {
+
+        namespace has_addressof_impl {
+
+          struct fail;
+
+          template <typename T>
+          inline fail operator&(T &&);
+
+          template <typename T>
+          inline static constexpr bool impl() {
+            return (std::is_class<T>::value || std::is_union<T>::value) &&
+                   !std::is_same<decltype(&std::declval<T &>()), fail>::value;
+          }
+
+        }  // namespace has_addressof_impl
+
+        template <typename T>
+        using has_addressof = bool_constant<has_addressof_impl::impl<T>()>;
+
+        template <typename T>
+        inline constexpr T *addressof(T &arg, std::true_type) noexcept {
+          return std::addressof(arg);
+        }
+
+        template <typename T>
+        inline constexpr T *addressof(T &arg, std::false_type) noexcept {
+          return &arg;
+        }
+
+      }  // namespace detail
+
+      template <typename T>
+      inline constexpr T *addressof(T &arg) noexcept {
+        return detail::addressof(arg, detail::has_addressof<T>{});
+      }
+#endif
+
+      template <typename T>
+      inline constexpr T *addressof(const T &&) = delete;
+
+    }  // namespace cpp17
+
+    template <typename T>
+    struct remove_all_extents : identity<T> {};
+
+    template <typename T, std::size_t N>
+    struct remove_all_extents<array<T, N>> : remove_all_extents<T> {};
+
+    template <typename T>
+    using remove_all_extents_t = typename remove_all_extents<T>::type;
+
+    template <std::size_t N>
+    using size_constant = std::integral_constant<std::size_t, N>;
+
+    template <std::size_t I, typename T>
+    struct indexed_type : size_constant<I> { using type = T; };
+
+    template <bool... Bs>
+    using all = std::is_same<integer_sequence<bool, true, Bs...>,
+                             integer_sequence<bool, Bs..., true>>;
+
+#ifdef MPARK_TYPE_PACK_ELEMENT
+    template <std::size_t I, typename... Ts>
+    using type_pack_element_t = __type_pack_element<I, Ts...>;
+#else
+    template <std::size_t I, typename... Ts>
+    struct type_pack_element_impl {
+      private:
+      template <typename>
+      struct set;
+
+      template <std::size_t... Is>
+      struct set<index_sequence<Is...>> : indexed_type<Is, Ts>... {};
+
+      template <typename T>
+      inline static std::enable_if<true, T> impl(indexed_type<I, T>);
+
+      inline static std::enable_if<false> impl(...);
+
+      public:
+      using type = decltype(impl(set<index_sequence_for<Ts...>>{}));
+    };
+
+    template <std::size_t I, typename... Ts>
+    using type_pack_element = typename type_pack_element_impl<I, Ts...>::type;
+
+    template <std::size_t I, typename... Ts>
+    using type_pack_element_t = typename type_pack_element<I, Ts...>::type;
+#endif
+
+#ifdef MPARK_TRIVIALITY_TYPE_TRAITS
+    using std::is_trivially_copy_constructible;
+    using std::is_trivially_move_constructible;
+    using std::is_trivially_copy_assignable;
+    using std::is_trivially_move_assignable;
+#else
+    template <typename T>
+    struct is_trivially_copy_constructible
+        : bool_constant<
+              std::is_copy_constructible<T>::value && __has_trivial_copy(T)> {};
+
+    template <typename T>
+    struct is_trivially_move_constructible : bool_constant<__is_trivial(T)> {};
+
+    template <typename T>
+    struct is_trivially_copy_assignable
+        : bool_constant<
+              std::is_copy_assignable<T>::value && __has_trivial_assign(T)> {};
+
+    template <typename T>
+    struct is_trivially_move_assignable : bool_constant<__is_trivial(T)> {};
+#endif
+
+    template <typename T, bool>
+    struct dependent_type : T {};
+
+    template <typename Is, std::size_t J>
+    struct push_back;
+
+    template <typename Is, std::size_t J>
+    using push_back_t = typename push_back<Is, J>::type;
+
+    template <std::size_t... Is, std::size_t J>
+    struct push_back<index_sequence<Is...>, J> {
+      using type = index_sequence<Is..., J>;
+    };
+
+  }  // namespace lib
+}  // namespace mpark
+
+#undef MPARK_RETURN
+
+#endif  // MPARK_LIB_HPP
+
+
+namespace mpark {
+
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+
+#define AUTO auto
+#define AUTO_RETURN(...) { return __VA_ARGS__; }
+
+#define AUTO_REFREF auto &&
+#define AUTO_REFREF_RETURN(...) { return __VA_ARGS__; }
+
+#define DECLTYPE_AUTO decltype(auto)
+#define DECLTYPE_AUTO_RETURN(...) { return __VA_ARGS__; }
+
+#else
+
+#define AUTO auto
+#define AUTO_RETURN(...) \
+  -> lib::decay_t<decltype(__VA_ARGS__)> { return __VA_ARGS__; }
+
+#define AUTO_REFREF auto
+#define AUTO_REFREF_RETURN(...)                                           \
+  -> decltype((__VA_ARGS__)) {                                            \
+    static_assert(std::is_reference<decltype((__VA_ARGS__))>::value, ""); \
+    return __VA_ARGS__;                                                   \
+  }
+
+#define DECLTYPE_AUTO auto
+#define DECLTYPE_AUTO_RETURN(...) \
+  -> decltype(__VA_ARGS__) { return __VA_ARGS__; }
+
+#endif
+
+  class bad_variant_access : public std::exception {
+    public:
+    virtual const char *what() const noexcept override { return "bad_variant_access"; }
+  };
+
+  [[noreturn]] inline void throw_bad_variant_access() {
+#ifdef MPARK_EXCEPTIONS
+    throw bad_variant_access{};
+#else
+    std::terminate();
+    MPARK_BUILTIN_UNREACHABLE;
+#endif
+  }
+
+  template <typename... Ts>
+  class variant;
+
+  template <typename T>
+  struct variant_size;
+
+#ifdef MPARK_VARIABLE_TEMPLATES
+  template <typename T>
+  constexpr std::size_t variant_size_v = variant_size<T>::value;
+#endif
+
+  template <typename T>
+  struct variant_size<const T> : variant_size<T> {};
+
+  template <typename T>
+  struct variant_size<volatile T> : variant_size<T> {};
+
+  template <typename T>
+  struct variant_size<const volatile T> : variant_size<T> {};
+
+  template <typename... Ts>
+  struct variant_size<variant<Ts...>> : lib::size_constant<sizeof...(Ts)> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative;
+
+  template <std::size_t I, typename T>
+  using variant_alternative_t = typename variant_alternative<I, T>::type;
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, const T>
+      : std::add_const<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, volatile T>
+      : std::add_volatile<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, const volatile T>
+      : std::add_cv<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename... Ts>
+  struct variant_alternative<I, variant<Ts...>> {
+    static_assert(I < sizeof...(Ts),
+                  "index out of bounds in `std::variant_alternative<>`");
+    using type = lib::type_pack_element_t<I, Ts...>;
+  };
+
+  constexpr std::size_t variant_npos = static_cast<std::size_t>(-1);
+
+  namespace detail {
+
+    constexpr std::size_t not_found = static_cast<std::size_t>(-1);
+    constexpr std::size_t ambiguous = static_cast<std::size_t>(-2);
+
+#ifdef MPARK_CPP14_CONSTEXPR
+    template <typename T, typename... Ts>
+    inline constexpr std::size_t find_index() {
+      constexpr lib::array<bool, sizeof...(Ts)> matches = {
+          {std::is_same<T, Ts>::value...}
+      };
+      std::size_t result = not_found;
+      for (std::size_t i = 0; i < sizeof...(Ts); ++i) {
+        if (matches[i]) {
+          if (result != not_found) {
+            return ambiguous;
+          }
+          result = i;
+        }
+      }
+      return result;
+    }
+#else
+    inline constexpr std::size_t find_index_impl(std::size_t result,
+                                                 std::size_t) {
+      return result;
+    }
+
+    template <typename... Bs>
+    inline constexpr std::size_t find_index_impl(std::size_t result,
+                                                 std::size_t idx,
+                                                 bool b,
+                                                 Bs... bs) {
+      return b ? (result != not_found ? ambiguous
+                                      : find_index_impl(idx, idx + 1, bs...))
+               : find_index_impl(result, idx + 1, bs...);
+    }
+
+    template <typename T, typename... Ts>
+    inline constexpr std::size_t find_index() {
+      return find_index_impl(not_found, 0, std::is_same<T, Ts>::value...);
+    }
+#endif
+
+    template <std::size_t I>
+    using find_index_sfinae_impl =
+        lib::enable_if_t<I != not_found && I != ambiguous,
+                         lib::size_constant<I>>;
+
+    template <typename T, typename... Ts>
+    using find_index_sfinae = find_index_sfinae_impl<find_index<T, Ts...>()>;
+
+    template <std::size_t I>
+    struct find_index_checked_impl : lib::size_constant<I> {
+      static_assert(I != not_found, "the specified type is not found.");
+      static_assert(I != ambiguous, "the specified type is ambiguous.");
+    };
+
+    template <typename T, typename... Ts>
+    using find_index_checked = find_index_checked_impl<find_index<T, Ts...>()>;
+
+    struct valueless_t {};
+
+    enum class Trait { TriviallyAvailable, Available, Unavailable };
+
+    template <typename T,
+              template <typename> class IsTriviallyAvailable,
+              template <typename> class IsAvailable>
+    inline constexpr Trait trait() {
+      return IsTriviallyAvailable<T>::value
+                 ? Trait::TriviallyAvailable
+                 : IsAvailable<T>::value ? Trait::Available
+                                         : Trait::Unavailable;
+    }
+
+#ifdef MPARK_CPP14_CONSTEXPR
+    template <typename... Traits>
+    inline constexpr Trait common_trait(Traits... traits_) {
+      Trait result = Trait::TriviallyAvailable;
+      lib::array<Trait, sizeof...(Traits)> traits = {{traits_...}};
+      for (std::size_t i = 0; i < sizeof...(Traits); ++i) {
+        Trait t = traits[i];
+        if (static_cast<int>(t) > static_cast<int>(result)) {
+          result = t;
+        }
+      }
+      return result;
+    }
+#else
+    inline constexpr Trait common_trait_impl(Trait result) { return result; }
+
+    template <typename... Traits>
+    inline constexpr Trait common_trait_impl(Trait result,
+                                             Trait t,
+                                             Traits... ts) {
+      return static_cast<int>(t) > static_cast<int>(result)
+                 ? common_trait_impl(t, ts...)
+                 : common_trait_impl(result, ts...);
+    }
+
+    template <typename... Traits>
+    inline constexpr Trait common_trait(Traits... ts) {
+      return common_trait_impl(Trait::TriviallyAvailable, ts...);
+    }
+#endif
+
+    template <typename... Ts>
+    struct traits {
+      static constexpr Trait copy_constructible_trait =
+          common_trait(trait<Ts,
+                             lib::is_trivially_copy_constructible,
+                             std::is_copy_constructible>()...);
+
+      static constexpr Trait move_constructible_trait =
+          common_trait(trait<Ts,
+                             lib::is_trivially_move_constructible,
+                             std::is_move_constructible>()...);
+
+      static constexpr Trait copy_assignable_trait =
+          common_trait(copy_constructible_trait,
+                       trait<Ts,
+                             lib::is_trivially_copy_assignable,
+                             std::is_copy_assignable>()...);
+
+      static constexpr Trait move_assignable_trait =
+          common_trait(move_constructible_trait,
+                       trait<Ts,
+                             lib::is_trivially_move_assignable,
+                             std::is_move_assignable>()...);
+
+      static constexpr Trait destructible_trait =
+          common_trait(trait<Ts,
+                             std::is_trivially_destructible,
+                             std::is_destructible>()...);
+    };
+
+    namespace access {
+
+      struct recursive_union {
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+        template <typename V>
+        inline static constexpr auto &&get_alt(V &&v, in_place_index_t<0>) {
+          return lib::forward<V>(v).head_;
+        }
+
+        template <typename V, std::size_t I>
+        inline static constexpr auto &&get_alt(V &&v, in_place_index_t<I>) {
+          return get_alt(lib::forward<V>(v).tail_, in_place_index_t<I - 1>{});
+        }
+#else
+        template <std::size_t I, bool Dummy = true>
+        struct get_alt_impl {
+          template <typename V>
+          inline constexpr AUTO_REFREF operator()(V &&v) const
+            AUTO_REFREF_RETURN(get_alt_impl<I - 1>{}(lib::forward<V>(v).tail_))
+        };
+
+        template <bool Dummy>
+        struct get_alt_impl<0, Dummy> {
+          template <typename V>
+          inline constexpr AUTO_REFREF operator()(V &&v) const
+            AUTO_REFREF_RETURN(lib::forward<V>(v).head_)
+        };
+
+        template <typename V, std::size_t I>
+        inline static constexpr AUTO_REFREF get_alt(V &&v, in_place_index_t<I>)
+          AUTO_REFREF_RETURN(get_alt_impl<I>{}(lib::forward<V>(v)))
+#endif
+      };
+
+      struct base {
+        template <std::size_t I, typename V>
+        inline static constexpr AUTO_REFREF get_alt(V &&v)
+#ifdef _MSC_VER
+          AUTO_REFREF_RETURN(recursive_union::get_alt(
+              lib::forward<V>(v).data_, in_place_index_t<I>{}))
+#else
+          AUTO_REFREF_RETURN(recursive_union::get_alt(
+              data(lib::forward<V>(v)), in_place_index_t<I>{}))
+#endif
+      };
+
+      struct variant {
+        template <std::size_t I, typename V>
+        inline static constexpr AUTO_REFREF get_alt(V &&v)
+          AUTO_REFREF_RETURN(base::get_alt<I>(lib::forward<V>(v).impl_))
+      };
+
+    }  // namespace access
+
+    namespace visitation {
+
+#if defined(MPARK_CPP14_CONSTEXPR) && !defined(_MSC_VER)
+#define MPARK_VARIANT_SWITCH_VISIT
+#endif
+
+      struct base {
+        template <typename Visitor, typename... Vs>
+        using dispatch_result_t = decltype(
+            lib::invoke(std::declval<Visitor>(),
+                        access::base::get_alt<0>(std::declval<Vs>())...));
+
+        template <typename Expected>
+        struct expected {
+          template <typename Actual>
+          inline static constexpr bool but_got() {
+            return std::is_same<Expected, Actual>::value;
+          }
+        };
+
+        template <typename Expected, typename Actual>
+        struct visit_return_type_check {
+          static_assert(
+              expected<Expected>::template but_got<Actual>(),
+              "`visit` requires the visitor to have a single return type");
+
+          template <typename Visitor, typename... Alts>
+          inline static constexpr DECLTYPE_AUTO invoke(Visitor &&visitor,
+                                                       Alts &&... alts)
+            DECLTYPE_AUTO_RETURN(lib::invoke(lib::forward<Visitor>(visitor),
+                                             lib::forward<Alts>(alts)...))
+        };
+
+#ifdef MPARK_VARIANT_SWITCH_VISIT
+        template <bool B, typename R, typename... ITs>
+        struct dispatcher;
+
+        template <typename R, typename... ITs>
+        struct dispatcher<false, R, ITs...> {
+          template <std::size_t B, typename F, typename... Vs>
+          MPARK_ALWAYS_INLINE static constexpr R dispatch(
+              F &&, typename ITs::type &&..., Vs &&...) {
+            MPARK_BUILTIN_UNREACHABLE;
+          }
+
+          template <std::size_t I, typename F, typename... Vs>
+          MPARK_ALWAYS_INLINE static constexpr R dispatch_case(F &&, Vs &&...) {
+            MPARK_BUILTIN_UNREACHABLE;
+          }
+
+          template <std::size_t B, typename F, typename... Vs>
+          MPARK_ALWAYS_INLINE static constexpr R dispatch_at(std::size_t,
+                                                             F &&,
+                                                             Vs &&...) {
+            MPARK_BUILTIN_UNREACHABLE;
+          }
+        };
+
+        template <typename R, typename... ITs>
+        struct dispatcher<true, R, ITs...> {
+          template <std::size_t B, typename F>
+          MPARK_ALWAYS_INLINE static constexpr R dispatch(
+              F &&f, typename ITs::type &&... visited_vs) {
+            using Expected = R;
+            using Actual = decltype(lib::invoke(
+                lib::forward<F>(f),
+                access::base::get_alt<ITs::value>(
+                    lib::forward<typename ITs::type>(visited_vs))...));
+            return visit_return_type_check<Expected, Actual>::invoke(
+                lib::forward<F>(f),
+                access::base::get_alt<ITs::value>(
+                    lib::forward<typename ITs::type>(visited_vs))...);
+          }
+
+          template <std::size_t B, typename F, typename V, typename... Vs>
+          MPARK_ALWAYS_INLINE static constexpr R dispatch(
+              F &&f, typename ITs::type &&... visited_vs, V &&v, Vs &&... vs) {
+#define MPARK_DISPATCH(I)                                                   \
+  dispatcher<(I < lib::decay_t<V>::size()),                                 \
+             R,                                                             \
+             ITs...,                                                        \
+             lib::indexed_type<I, V>>::                                     \
+      template dispatch<0>(lib::forward<F>(f),                              \
+                           lib::forward<typename ITs::type>(visited_vs)..., \
+                           lib::forward<V>(v),                              \
+                           lib::forward<Vs>(vs)...)
+
+#define MPARK_DEFAULT(I)                                                      \
+  dispatcher<(I < lib::decay_t<V>::size()), R, ITs...>::template dispatch<I>( \
+      lib::forward<F>(f),                                                     \
+      lib::forward<typename ITs::type>(visited_vs)...,                        \
+      lib::forward<V>(v),                                                     \
+      lib::forward<Vs>(vs)...)
+
+            switch (v.index()) {
+              case B + 0: return MPARK_DISPATCH(B + 0);
+              case B + 1: return MPARK_DISPATCH(B + 1);
+              case B + 2: return MPARK_DISPATCH(B + 2);
+              case B + 3: return MPARK_DISPATCH(B + 3);
+              case B + 4: return MPARK_DISPATCH(B + 4);
+              case B + 5: return MPARK_DISPATCH(B + 5);
+              case B + 6: return MPARK_DISPATCH(B + 6);
+              case B + 7: return MPARK_DISPATCH(B + 7);
+              case B + 8: return MPARK_DISPATCH(B + 8);
+              case B + 9: return MPARK_DISPATCH(B + 9);
+              case B + 10: return MPARK_DISPATCH(B + 10);
+              case B + 11: return MPARK_DISPATCH(B + 11);
+              case B + 12: return MPARK_DISPATCH(B + 12);
+              case B + 13: return MPARK_DISPATCH(B + 13);
+              case B + 14: return MPARK_DISPATCH(B + 14);
+              case B + 15: return MPARK_DISPATCH(B + 15);
+              case B + 16: return MPARK_DISPATCH(B + 16);
+              case B + 17: return MPARK_DISPATCH(B + 17);
+              case B + 18: return MPARK_DISPATCH(B + 18);
+              case B + 19: return MPARK_DISPATCH(B + 19);
+              case B + 20: return MPARK_DISPATCH(B + 20);
+              case B + 21: return MPARK_DISPATCH(B + 21);
+              case B + 22: return MPARK_DISPATCH(B + 22);
+              case B + 23: return MPARK_DISPATCH(B + 23);
+              case B + 24: return MPARK_DISPATCH(B + 24);
+              case B + 25: return MPARK_DISPATCH(B + 25);
+              case B + 26: return MPARK_DISPATCH(B + 26);
+              case B + 27: return MPARK_DISPATCH(B + 27);
+              case B + 28: return MPARK_DISPATCH(B + 28);
+              case B + 29: return MPARK_DISPATCH(B + 29);
+              case B + 30: return MPARK_DISPATCH(B + 30);
+              case B + 31: return MPARK_DISPATCH(B + 31);
+              default: return MPARK_DEFAULT(B + 32);
+            }
+
+#undef MPARK_DEFAULT
+#undef MPARK_DISPATCH
+          }
+
+          template <std::size_t I, typename F, typename... Vs>
+          MPARK_ALWAYS_INLINE static constexpr R dispatch_case(F &&f,
+                                                               Vs &&... vs) {
+            using Expected = R;
+            using Actual = decltype(
+                lib::invoke(lib::forward<F>(f),
+                            access::base::get_alt<I>(lib::forward<Vs>(vs))...));
+            return visit_return_type_check<Expected, Actual>::invoke(
+                lib::forward<F>(f),
+                access::base::get_alt<I>(lib::forward<Vs>(vs))...);
+          }
+
+          template <std::size_t B, typename F, typename V, typename... Vs>
+          MPARK_ALWAYS_INLINE static constexpr R dispatch_at(std::size_t index,
+                                                             F &&f,
+                                                             V &&v,
+                                                             Vs &&... vs) {
+            static_assert(lib::all<(lib::decay_t<V>::size() ==
+                                    lib::decay_t<Vs>::size())...>::value,
+                          "all of the variants must be the same size.");
+#define MPARK_DISPATCH_AT(I)                                               \
+  dispatcher<(I < lib::decay_t<V>::size()), R>::template dispatch_case<I>( \
+      lib::forward<F>(f), lib::forward<V>(v), lib::forward<Vs>(vs)...)
+
+#define MPARK_DEFAULT(I)                                                 \
+  dispatcher<(I < lib::decay_t<V>::size()), R>::template dispatch_at<I>( \
+      index, lib::forward<F>(f), lib::forward<V>(v), lib::forward<Vs>(vs)...)
+
+            switch (index) {
+              case B + 0: return MPARK_DISPATCH_AT(B + 0);
+              case B + 1: return MPARK_DISPATCH_AT(B + 1);
+              case B + 2: return MPARK_DISPATCH_AT(B + 2);
+              case B + 3: return MPARK_DISPATCH_AT(B + 3);
+              case B + 4: return MPARK_DISPATCH_AT(B + 4);
+              case B + 5: return MPARK_DISPATCH_AT(B + 5);
+              case B + 6: return MPARK_DISPATCH_AT(B + 6);
+              case B + 7: return MPARK_DISPATCH_AT(B + 7);
+              case B + 8: return MPARK_DISPATCH_AT(B + 8);
+              case B + 9: return MPARK_DISPATCH_AT(B + 9);
+              case B + 10: return MPARK_DISPATCH_AT(B + 10);
+              case B + 11: return MPARK_DISPATCH_AT(B + 11);
+              case B + 12: return MPARK_DISPATCH_AT(B + 12);
+              case B + 13: return MPARK_DISPATCH_AT(B + 13);
+              case B + 14: return MPARK_DISPATCH_AT(B + 14);
+              case B + 15: return MPARK_DISPATCH_AT(B + 15);
+              case B + 16: return MPARK_DISPATCH_AT(B + 16);
+              case B + 17: return MPARK_DISPATCH_AT(B + 17);
+              case B + 18: return MPARK_DISPATCH_AT(B + 18);
+              case B + 19: return MPARK_DISPATCH_AT(B + 19);
+              case B + 20: return MPARK_DISPATCH_AT(B + 20);
+              case B + 21: return MPARK_DISPATCH_AT(B + 21);
+              case B + 22: return MPARK_DISPATCH_AT(B + 22);
+              case B + 23: return MPARK_DISPATCH_AT(B + 23);
+              case B + 24: return MPARK_DISPATCH_AT(B + 24);
+              case B + 25: return MPARK_DISPATCH_AT(B + 25);
+              case B + 26: return MPARK_DISPATCH_AT(B + 26);
+              case B + 27: return MPARK_DISPATCH_AT(B + 27);
+              case B + 28: return MPARK_DISPATCH_AT(B + 28);
+              case B + 29: return MPARK_DISPATCH_AT(B + 29);
+              case B + 30: return MPARK_DISPATCH_AT(B + 30);
+              case B + 31: return MPARK_DISPATCH_AT(B + 31);
+              default: return MPARK_DEFAULT(B + 32);
+            }
+
+#undef MPARK_DEFAULT
+#undef MPARK_DISPATCH_AT
+          }
+        };
+#else
+        template <typename T>
+        inline static constexpr const T &at(const T &elem) noexcept {
+          return elem;
+        }
+
+        template <typename T, std::size_t N, typename... Is>
+        inline static constexpr const lib::remove_all_extents_t<T> &at(
+            const lib::array<T, N> &elems, std::size_t i, Is... is) noexcept {
+          return at(elems[i], is...);
+        }
+
+        template <typename F, typename... Fs>
+        inline static constexpr lib::array<lib::decay_t<F>, sizeof...(Fs) + 1>
+        make_farray(F &&f, Fs &&... fs) {
+          return {{lib::forward<F>(f), lib::forward<Fs>(fs)...}};
+        }
+
+        template <typename F, typename... Vs>
+        struct make_fmatrix_impl {
+
+          template <std::size_t... Is>
+          inline static constexpr dispatch_result_t<F, Vs...> dispatch(
+              F &&f, Vs &&... vs) {
+            using Expected = dispatch_result_t<F, Vs...>;
+            using Actual = decltype(lib::invoke(
+                lib::forward<F>(f),
+                access::base::get_alt<Is>(lib::forward<Vs>(vs))...));
+            return visit_return_type_check<Expected, Actual>::invoke(
+                lib::forward<F>(f),
+                access::base::get_alt<Is>(lib::forward<Vs>(vs))...);
+          }
+
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+          template <std::size_t... Is>
+          inline static constexpr auto impl(lib::index_sequence<Is...>) {
+            return &dispatch<Is...>;
+          }
+
+          template <typename Is, std::size_t... Js, typename... Ls>
+          inline static constexpr auto impl(Is,
+                                            lib::index_sequence<Js...>,
+                                            Ls... ls) {
+            return make_farray(impl(lib::push_back_t<Is, Js>{}, ls...)...);
+          }
+#else
+          template <typename...>
+          struct impl;
+
+          template <std::size_t... Is>
+          struct impl<lib::index_sequence<Is...>> {
+            inline constexpr AUTO operator()() const
+              AUTO_RETURN(&dispatch<Is...>)
+          };
+
+          template <typename Is, std::size_t... Js, typename... Ls>
+          struct impl<Is, lib::index_sequence<Js...>, Ls...> {
+            inline constexpr AUTO operator()() const
+              AUTO_RETURN(
+                  make_farray(impl<lib::push_back_t<Is, Js>, Ls...>{}()...))
+          };
+#endif
+        };
+
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+        template <typename F, typename... Vs>
+        inline static constexpr auto make_fmatrix() {
+          return make_fmatrix_impl<F, Vs...>::impl(
+              lib::index_sequence<>{},
+              lib::make_index_sequence<lib::decay_t<Vs>::size()>{}...);
+        }
+#else
+        template <typename F, typename... Vs>
+        inline static constexpr AUTO make_fmatrix()
+          AUTO_RETURN(
+              typename make_fmatrix_impl<F, Vs...>::template impl<
+                  lib::index_sequence<>,
+                  lib::make_index_sequence<lib::decay_t<Vs>::size()>...>{}())
+#endif
+
+        template <typename F, typename... Vs>
+        struct make_fdiagonal_impl {
+          template <std::size_t I>
+          inline static constexpr dispatch_result_t<F, Vs...> dispatch(
+              F &&f, Vs &&... vs) {
+            using Expected = dispatch_result_t<F, Vs...>;
+            using Actual = decltype(
+                lib::invoke(lib::forward<F>(f),
+                            access::base::get_alt<I>(lib::forward<Vs>(vs))...));
+            return visit_return_type_check<Expected, Actual>::invoke(
+                lib::forward<F>(f),
+                access::base::get_alt<I>(lib::forward<Vs>(vs))...);
+          }
+
+          template <std::size_t... Is>
+          inline static constexpr AUTO impl(lib::index_sequence<Is...>)
+            AUTO_RETURN(make_farray(&dispatch<Is>...))
+        };
+
+        template <typename F, typename V, typename... Vs>
+        inline static constexpr auto make_fdiagonal()
+            -> decltype(make_fdiagonal_impl<F, V, Vs...>::impl(
+                lib::make_index_sequence<lib::decay_t<V>::size()>{})) {
+          static_assert(lib::all<(lib::decay_t<V>::size() ==
+                                  lib::decay_t<Vs>::size())...>::value,
+                        "all of the variants must be the same size.");
+          return make_fdiagonal_impl<F, V, Vs...>::impl(
+              lib::make_index_sequence<lib::decay_t<V>::size()>{});
+        }
+#endif
+      };
+
+#if !defined(MPARK_VARIANT_SWITCH_VISIT) && \
+    (!defined(_MSC_VER) || _MSC_VER >= 1910)
+      template <typename F, typename... Vs>
+      using fmatrix_t = decltype(base::make_fmatrix<F, Vs...>());
+
+      template <typename F, typename... Vs>
+      struct fmatrix {
+        static constexpr fmatrix_t<F, Vs...> value =
+            base::make_fmatrix<F, Vs...>();
+      };
+
+      template <typename F, typename... Vs>
+      constexpr fmatrix_t<F, Vs...> fmatrix<F, Vs...>::value;
+
+      template <typename F, typename... Vs>
+      using fdiagonal_t = decltype(base::make_fdiagonal<F, Vs...>());
+
+      template <typename F, typename... Vs>
+      struct fdiagonal {
+        static constexpr fdiagonal_t<F, Vs...> value =
+            base::make_fdiagonal<F, Vs...>();
+      };
+
+      template <typename F, typename... Vs>
+      constexpr fdiagonal_t<F, Vs...> fdiagonal<F, Vs...>::value;
+#endif
+
+      struct alt {
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor,
+                                                        Vs &&... vs)
+#ifdef MPARK_VARIANT_SWITCH_VISIT
+          DECLTYPE_AUTO_RETURN(
+              base::dispatcher<
+                  true,
+                  base::dispatch_result_t<Visitor,
+                                          decltype(as_base(
+                                              lib::forward<Vs>(vs)))...>>::
+                  template dispatch<0>(lib::forward<Visitor>(visitor),
+                                       as_base(lib::forward<Vs>(vs))...))
+#elif !defined(_MSC_VER) || _MSC_VER >= 1910
+          DECLTYPE_AUTO_RETURN(base::at(
+              fmatrix<Visitor &&,
+                      decltype(as_base(lib::forward<Vs>(vs)))...>::value,
+              vs.index()...)(lib::forward<Visitor>(visitor),
+                             as_base(lib::forward<Vs>(vs))...))
+#else
+          DECLTYPE_AUTO_RETURN(base::at(
+              base::make_fmatrix<Visitor &&,
+                      decltype(as_base(lib::forward<Vs>(vs)))...>(),
+              vs.index()...)(lib::forward<Visitor>(visitor),
+                             as_base(lib::forward<Vs>(vs))...))
+#endif
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index,
+                                                           Visitor &&visitor,
+                                                           Vs &&... vs)
+#ifdef MPARK_VARIANT_SWITCH_VISIT
+          DECLTYPE_AUTO_RETURN(
+              base::dispatcher<
+                  true,
+                  base::dispatch_result_t<Visitor,
+                                          decltype(as_base(
+                                              lib::forward<Vs>(vs)))...>>::
+                  template dispatch_at<0>(index,
+                                          lib::forward<Visitor>(visitor),
+                                          as_base(lib::forward<Vs>(vs))...))
+#elif !defined(_MSC_VER) || _MSC_VER >= 1910
+          DECLTYPE_AUTO_RETURN(base::at(
+              fdiagonal<Visitor &&,
+                        decltype(as_base(lib::forward<Vs>(vs)))...>::value,
+              index)(lib::forward<Visitor>(visitor),
+                     as_base(lib::forward<Vs>(vs))...))
+#else
+          DECLTYPE_AUTO_RETURN(base::at(
+              base::make_fdiagonal<Visitor &&,
+                        decltype(as_base(lib::forward<Vs>(vs)))...>(),
+              index)(lib::forward<Visitor>(visitor),
+                     as_base(lib::forward<Vs>(vs))...))
+#endif
+      };
+
+      struct variant {
+        private:
+        template <typename Visitor>
+        struct visitor {
+          template <typename... Values>
+          inline static constexpr bool does_not_handle() {
+            return lib::is_invocable<Visitor, Values...>::value;
+          }
+        };
+
+        template <typename Visitor, typename... Values>
+        struct visit_exhaustiveness_check {
+          static_assert(visitor<Visitor>::template does_not_handle<Values...>(),
+                        "`visit` requires the visitor to be exhaustive.");
+
+          inline static constexpr DECLTYPE_AUTO invoke(Visitor &&visitor,
+                                                       Values &&... values)
+            DECLTYPE_AUTO_RETURN(lib::invoke(lib::forward<Visitor>(visitor),
+                                             lib::forward<Values>(values)...))
+        };
+
+        template <typename Visitor>
+        struct value_visitor {
+          Visitor &&visitor_;
+
+          template <typename... Alts>
+          inline constexpr DECLTYPE_AUTO operator()(Alts &&... alts) const
+            DECLTYPE_AUTO_RETURN(
+                visit_exhaustiveness_check<
+                    Visitor,
+                    decltype((lib::forward<Alts>(alts).value))...>::
+                    invoke(lib::forward<Visitor>(visitor_),
+                           lib::forward<Alts>(alts).value...))
+        };
+
+        template <typename Visitor>
+        inline static constexpr AUTO make_value_visitor(Visitor &&visitor)
+          AUTO_RETURN(value_visitor<Visitor>{lib::forward<Visitor>(visitor)})
+
+        public:
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor,
+                                                        Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(alt::visit_alt(lib::forward<Visitor>(visitor),
+                                              lib::forward<Vs>(vs).impl_...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index,
+                                                           Visitor &&visitor,
+                                                           Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              alt::visit_alt_at(index,
+                                lib::forward<Visitor>(visitor),
+                                lib::forward<Vs>(vs).impl_...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_value(Visitor &&visitor,
+                                                          Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              visit_alt(make_value_visitor(lib::forward<Visitor>(visitor)),
+                        lib::forward<Vs>(vs)...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_value_at(std::size_t index,
+                                                             Visitor &&visitor,
+                                                             Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              visit_alt_at(index,
+                           make_value_visitor(lib::forward<Visitor>(visitor)),
+                           lib::forward<Vs>(vs)...))
+      };
+
+    }  // namespace visitation
+
+    template <std::size_t Index, typename T>
+    struct alt {
+      using value_type = T;
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4244)
+#endif
+      template <typename... Args>
+      inline explicit constexpr alt(in_place_t, Args &&... args)
+          : value(lib::forward<Args>(args)...) {}
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+      T value;
+    };
+
+    template <Trait DestructibleTrait, std::size_t Index, typename... Ts>
+    union recursive_union;
+
+    template <Trait DestructibleTrait, std::size_t Index>
+    union recursive_union<DestructibleTrait, Index> {};
+
+#define MPARK_VARIANT_RECURSIVE_UNION(destructible_trait, destructor)      \
+  template <std::size_t Index, typename T, typename... Ts>                 \
+  union recursive_union<destructible_trait, Index, T, Ts...> {             \
+    public:                                                                \
+    inline explicit constexpr recursive_union(valueless_t) noexcept        \
+        : dummy_{} {}                                                      \
+                                                                           \
+    template <typename... Args>                                            \
+    inline explicit constexpr recursive_union(in_place_index_t<0>,         \
+                                              Args &&... args)             \
+        : head_(in_place_t{}, lib::forward<Args>(args)...) {}              \
+                                                                           \
+    template <std::size_t I, typename... Args>                             \
+    inline explicit constexpr recursive_union(in_place_index_t<I>,         \
+                                              Args &&... args)             \
+        : tail_(in_place_index_t<I - 1>{}, lib::forward<Args>(args)...) {} \
+                                                                           \
+    recursive_union(const recursive_union &) = default;                    \
+    recursive_union(recursive_union &&) = default;                         \
+                                                                           \
+    destructor                                                             \
+                                                                           \
+    recursive_union &operator=(const recursive_union &) = default;         \
+    recursive_union &operator=(recursive_union &&) = default;              \
+                                                                           \
+    private:                                                               \
+    char dummy_;                                                           \
+    alt<Index, T> head_;                                                   \
+    recursive_union<destructible_trait, Index + 1, Ts...> tail_;           \
+                                                                           \
+    friend struct access::recursive_union;                                 \
+  }
+
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::TriviallyAvailable,
+                                  ~recursive_union() = default;);
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::Available,
+                                  ~recursive_union() {});
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::Unavailable,
+                                  ~recursive_union() = delete;);
+
+#undef MPARK_VARIANT_RECURSIVE_UNION
+
+    template <typename... Ts>
+    using index_t = typename std::conditional<
+            sizeof...(Ts) < std::numeric_limits<unsigned char>::max(),
+            unsigned char,
+            typename std::conditional<
+                sizeof...(Ts) < std::numeric_limits<unsigned short>::max(),
+                unsigned short,
+                unsigned int>::type
+            >::type;
+
+    template <Trait DestructibleTrait, typename... Ts>
+    class base {
+      public:
+      inline explicit constexpr base(valueless_t tag) noexcept
+          : data_(tag), index_(static_cast<index_t<Ts...>>(-1)) {}
+
+      template <std::size_t I, typename... Args>
+      inline explicit constexpr base(in_place_index_t<I>, Args &&... args)
+          : data_(in_place_index_t<I>{}, lib::forward<Args>(args)...),
+            index_(I) {}
+
+      inline constexpr bool valueless_by_exception() const noexcept {
+        return index_ == static_cast<index_t<Ts...>>(-1);
+      }
+
+      inline constexpr std::size_t index() const noexcept {
+        return valueless_by_exception() ? variant_npos : index_;
+      }
+
+      protected:
+      using data_t = recursive_union<DestructibleTrait, 0, Ts...>;
+
+      friend inline constexpr base &as_base(base &b) { return b; }
+      friend inline constexpr const base &as_base(const base &b) { return b; }
+      friend inline constexpr base &&as_base(base &&b) { return lib::move(b); }
+      friend inline constexpr const base &&as_base(const base &&b) { return lib::move(b); }
+
+      friend inline constexpr data_t &data(base &b) { return b.data_; }
+      friend inline constexpr const data_t &data(const base &b) { return b.data_; }
+      friend inline constexpr data_t &&data(base &&b) { return lib::move(b).data_; }
+      friend inline constexpr const data_t &&data(const base &&b) { return lib::move(b).data_; }
+
+      inline static constexpr std::size_t size() { return sizeof...(Ts); }
+
+      data_t data_;
+      index_t<Ts...> index_;
+
+      friend struct access::base;
+      friend struct visitation::base;
+    };
+
+    struct dtor {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+      template <typename Alt>
+      inline void operator()(Alt &alt) const noexcept { alt.~Alt(); }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+    };
+
+#if !defined(_MSC_VER) || _MSC_VER >= 1910
+#define MPARK_INHERITING_CTOR(type, base) using base::base;
+#else
+#define MPARK_INHERITING_CTOR(type, base)         \
+  template <typename... Args>                     \
+  inline explicit constexpr type(Args &&... args) \
+      : base(lib::forward<Args>(args)...) {}
+#endif
+
+    template <typename Traits, Trait = Traits::destructible_trait>
+    class destructor;
+
+#define MPARK_VARIANT_DESTRUCTOR(destructible_trait, definition, destroy) \
+  template <typename... Ts>                                               \
+  class destructor<traits<Ts...>, destructible_trait>                     \
+      : public base<destructible_trait, Ts...> {                          \
+    using super = base<destructible_trait, Ts...>;                        \
+                                                                          \
+    public:                                                               \
+    MPARK_INHERITING_CTOR(destructor, super)                              \
+    using super::operator=;                                               \
+                                                                          \
+    destructor(const destructor &) = default;                             \
+    destructor(destructor &&) = default;                                  \
+    definition                                                            \
+    destructor &operator=(const destructor &) = default;                  \
+    destructor &operator=(destructor &&) = default;                       \
+                                                                          \
+    protected:                                                            \
+    destroy                                                               \
+  }
+
+    MPARK_VARIANT_DESTRUCTOR(
+        Trait::TriviallyAvailable,
+        ~destructor() = default;,
+        inline void destroy() noexcept {
+          this->index_ = static_cast<index_t<Ts...>>(-1);
+        });
+
+    MPARK_VARIANT_DESTRUCTOR(
+        Trait::Available,
+        ~destructor() { destroy(); },
+        inline void destroy() noexcept {
+          if (!this->valueless_by_exception()) {
+            visitation::alt::visit_alt(dtor{}, *this);
+          }
+          this->index_ = static_cast<index_t<Ts...>>(-1);
+        });
+
+    MPARK_VARIANT_DESTRUCTOR(
+        Trait::Unavailable,
+        ~destructor() = delete;,
+        inline void destroy() noexcept = delete;);
+
+#undef MPARK_VARIANT_DESTRUCTOR
+
+    template <typename Traits>
+    class constructor : public destructor<Traits> {
+      using super = destructor<Traits>;
+
+      public:
+      MPARK_INHERITING_CTOR(constructor, super)
+      using super::operator=;
+
+      protected:
+#ifndef MPARK_GENERIC_LAMBDAS
+      struct ctor {
+        template <typename LhsAlt, typename RhsAlt>
+        inline void operator()(LhsAlt &lhs_alt, RhsAlt &&rhs_alt) const {
+          constructor::construct_alt(lhs_alt,
+                                     lib::forward<RhsAlt>(rhs_alt).value);
+        }
+      };
+#endif
+
+      template <std::size_t I, typename T, typename... Args>
+      inline static T &construct_alt(alt<I, T> &a, Args &&... args) {
+        auto *result = ::new (static_cast<void *>(lib::addressof(a)))
+            alt<I, T>(in_place_t{}, lib::forward<Args>(args)...);
+        return result->value;
+      }
+
+      template <typename Rhs>
+      inline static void generic_construct(constructor &lhs, Rhs &&rhs) {
+        lhs.destroy();
+        if (!rhs.valueless_by_exception()) {
+          visitation::alt::visit_alt_at(
+              rhs.index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+              [](auto &lhs_alt, auto &&rhs_alt) {
+                constructor::construct_alt(
+                    lhs_alt, lib::forward<decltype(rhs_alt)>(rhs_alt).value);
+              }
+#else
+              ctor{}
+#endif
+              ,
+              lhs,
+              lib::forward<Rhs>(rhs));
+          lhs.index_ = rhs.index_;
+        }
+      }
+    };
+
+    template <typename Traits, Trait = Traits::move_constructible_trait>
+    class move_constructor;
+
+#define MPARK_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, definition) \
+  template <typename... Ts>                                                  \
+  class move_constructor<traits<Ts...>, move_constructible_trait>            \
+      : public constructor<traits<Ts...>> {                                  \
+    using super = constructor<traits<Ts...>>;                                \
+                                                                             \
+    public:                                                                  \
+    MPARK_INHERITING_CTOR(move_constructor, super)                           \
+    using super::operator=;                                                  \
+                                                                             \
+    move_constructor(const move_constructor &) = default;                    \
+    definition                                                               \
+    ~move_constructor() = default;                                           \
+    move_constructor &operator=(const move_constructor &) = default;         \
+    move_constructor &operator=(move_constructor &&) = default;              \
+  }
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::TriviallyAvailable,
+        move_constructor(move_constructor &&that) = default;);
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::Available,
+        move_constructor(move_constructor &&that) noexcept(
+            lib::all<std::is_nothrow_move_constructible<Ts>::value...>::value)
+            : move_constructor(valueless_t{}) {
+          this->generic_construct(*this, lib::move(that));
+        });
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::Unavailable,
+        move_constructor(move_constructor &&) = delete;);
+
+#undef MPARK_VARIANT_MOVE_CONSTRUCTOR
+
+    template <typename Traits, Trait = Traits::copy_constructible_trait>
+    class copy_constructor;
+
+#define MPARK_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, definition) \
+  template <typename... Ts>                                                  \
+  class copy_constructor<traits<Ts...>, copy_constructible_trait>            \
+      : public move_constructor<traits<Ts...>> {                             \
+    using super = move_constructor<traits<Ts...>>;                           \
+                                                                             \
+    public:                                                                  \
+    MPARK_INHERITING_CTOR(copy_constructor, super)                           \
+    using super::operator=;                                                  \
+                                                                             \
+    definition                                                               \
+    copy_constructor(copy_constructor &&) = default;                         \
+    ~copy_constructor() = default;                                           \
+    copy_constructor &operator=(const copy_constructor &) = default;         \
+    copy_constructor &operator=(copy_constructor &&) = default;              \
+  }
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::TriviallyAvailable,
+        copy_constructor(const copy_constructor &that) = default;);
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::Available,
+        copy_constructor(const copy_constructor &that)
+            : copy_constructor(valueless_t{}) {
+          this->generic_construct(*this, that);
+        });
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::Unavailable,
+        copy_constructor(const copy_constructor &) = delete;);
+
+#undef MPARK_VARIANT_COPY_CONSTRUCTOR
+
+    template <typename Traits>
+    class assignment : public copy_constructor<Traits> {
+      using super = copy_constructor<Traits>;
+
+      public:
+      MPARK_INHERITING_CTOR(assignment, super)
+      using super::operator=;
+
+      template <std::size_t I, typename... Args>
+      inline /* auto & */ auto emplace(Args &&... args)
+          -> decltype(this->construct_alt(access::base::get_alt<I>(*this),
+                                          lib::forward<Args>(args)...)) {
+        this->destroy();
+        auto &result = this->construct_alt(access::base::get_alt<I>(*this),
+                                           lib::forward<Args>(args)...);
+        this->index_ = I;
+        return result;
+      }
+
+      protected:
+#ifndef MPARK_GENERIC_LAMBDAS
+      template <typename That>
+      struct assigner {
+        template <typename ThisAlt, typename ThatAlt>
+        inline void operator()(ThisAlt &this_alt, ThatAlt &&that_alt) const {
+          self->assign_alt(this_alt, lib::forward<ThatAlt>(that_alt).value);
+        }
+        assignment *self;
+      };
+#endif
+
+      template <std::size_t I, typename T, typename Arg>
+      inline void assign_alt(alt<I, T> &a, Arg &&arg) {
+        if (this->index() == I) {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4244)
+#endif
+          a.value = lib::forward<Arg>(arg);
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+        } else {
+          struct {
+            void operator()(std::true_type) const {
+              this_->emplace<I>(lib::forward<Arg>(arg_));
+            }
+            void operator()(std::false_type) const {
+              this_->emplace<I>(T(lib::forward<Arg>(arg_)));
+            }
+            assignment *this_;
+            Arg &&arg_;
+          } impl{this, lib::forward<Arg>(arg)};
+          impl(lib::bool_constant<
+                   std::is_nothrow_constructible<T, Arg>::value ||
+                   !std::is_nothrow_move_constructible<T>::value>{});
+        }
+      }
+
+      template <typename That>
+      inline void generic_assign(That &&that) {
+        if (this->valueless_by_exception() && that.valueless_by_exception()) {
+          // do nothing.
+        } else if (that.valueless_by_exception()) {
+          this->destroy();
+        } else {
+          visitation::alt::visit_alt_at(
+              that.index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+              [this](auto &this_alt, auto &&that_alt) {
+                this->assign_alt(
+                    this_alt, lib::forward<decltype(that_alt)>(that_alt).value);
+              }
+#else
+              assigner<That>{this}
+#endif
+              ,
+              *this,
+              lib::forward<That>(that));
+        }
+      }
+    };
+
+    template <typename Traits, Trait = Traits::move_assignable_trait>
+    class move_assignment;
+
+#define MPARK_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, definition) \
+  template <typename... Ts>                                              \
+  class move_assignment<traits<Ts...>, move_assignable_trait>            \
+      : public assignment<traits<Ts...>> {                               \
+    using super = assignment<traits<Ts...>>;                             \
+                                                                         \
+    public:                                                              \
+    MPARK_INHERITING_CTOR(move_assignment, super)                        \
+    using super::operator=;                                              \
+                                                                         \
+    move_assignment(const move_assignment &) = default;                  \
+    move_assignment(move_assignment &&) = default;                       \
+    ~move_assignment() = default;                                        \
+    move_assignment &operator=(const move_assignment &) = default;       \
+    definition                                                           \
+  }
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::TriviallyAvailable,
+        move_assignment &operator=(move_assignment &&that) = default;);
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::Available,
+        move_assignment &
+        operator=(move_assignment &&that) noexcept(
+            lib::all<(std::is_nothrow_move_constructible<Ts>::value &&
+                      std::is_nothrow_move_assignable<Ts>::value)...>::value) {
+          this->generic_assign(lib::move(that));
+          return *this;
+        });
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::Unavailable,
+        move_assignment &operator=(move_assignment &&) = delete;);
+
+#undef MPARK_VARIANT_MOVE_ASSIGNMENT
+
+    template <typename Traits, Trait = Traits::copy_assignable_trait>
+    class copy_assignment;
+
+#define MPARK_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, definition) \
+  template <typename... Ts>                                              \
+  class copy_assignment<traits<Ts...>, copy_assignable_trait>            \
+      : public move_assignment<traits<Ts...>> {                          \
+    using super = move_assignment<traits<Ts...>>;                        \
+                                                                         \
+    public:                                                              \
+    MPARK_INHERITING_CTOR(copy_assignment, super)                        \
+    using super::operator=;                                              \
+                                                                         \
+    copy_assignment(const copy_assignment &) = default;                  \
+    copy_assignment(copy_assignment &&) = default;                       \
+    ~copy_assignment() = default;                                        \
+    definition                                                           \
+    copy_assignment &operator=(copy_assignment &&) = default;            \
+  }
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::TriviallyAvailable,
+        copy_assignment &operator=(const copy_assignment &that) = default;);
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::Available,
+        copy_assignment &operator=(const copy_assignment &that) {
+          this->generic_assign(that);
+          return *this;
+        });
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::Unavailable,
+        copy_assignment &operator=(const copy_assignment &) = delete;);
+
+#undef MPARK_VARIANT_COPY_ASSIGNMENT
+
+    template <typename... Ts>
+    class impl : public copy_assignment<traits<Ts...>> {
+      using super = copy_assignment<traits<Ts...>>;
+
+      public:
+      MPARK_INHERITING_CTOR(impl, super)
+      using super::operator=;
+
+      template <std::size_t I, typename Arg>
+      inline void assign(Arg &&arg) {
+        this->assign_alt(access::base::get_alt<I>(*this),
+                         lib::forward<Arg>(arg));
+      }
+
+      inline void swap(impl &that) {
+        if (this->valueless_by_exception() && that.valueless_by_exception()) {
+          // do nothing.
+        } else if (this->index() == that.index()) {
+          visitation::alt::visit_alt_at(this->index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+                                        [](auto &this_alt, auto &that_alt) {
+                                          using std::swap;
+                                          swap(this_alt.value,
+                                               that_alt.value);
+                                        }
+#else
+                                        swapper{}
+#endif
+                                        ,
+                                        *this,
+                                        that);
+        } else {
+          impl *lhs = this;
+          impl *rhs = lib::addressof(that);
+          if (lhs->move_nothrow() && !rhs->move_nothrow()) {
+            std::swap(lhs, rhs);
+          }
+          impl tmp(lib::move(*rhs));
+#ifdef MPARK_EXCEPTIONS
+          // EXTENSION: When the move construction of `lhs` into `rhs` throws
+          // and `tmp` is nothrow move constructible then we move `tmp` back
+          // into `rhs` and provide the strong exception safety guarantee.
+          try {
+            this->generic_construct(*rhs, lib::move(*lhs));
+          } catch (...) {
+            if (tmp.move_nothrow()) {
+              this->generic_construct(*rhs, lib::move(tmp));
+            }
+            throw;
+          }
+#else
+          this->generic_construct(*rhs, lib::move(*lhs));
+#endif
+          this->generic_construct(*lhs, lib::move(tmp));
+        }
+      }
+
+      private:
+#ifndef MPARK_GENERIC_LAMBDAS
+      struct swapper {
+        template <typename ThisAlt, typename ThatAlt>
+        inline void operator()(ThisAlt &this_alt, ThatAlt &that_alt) const {
+          using std::swap;
+          swap(this_alt.value, that_alt.value);
+        }
+      };
+#endif
+
+      inline constexpr bool move_nothrow() const {
+        return this->valueless_by_exception() ||
+               lib::array<bool, sizeof...(Ts)>{
+                   {std::is_nothrow_move_constructible<Ts>::value...}
+               }[this->index()];
+      }
+    };
+
+#undef MPARK_INHERITING_CTOR
+
+    template <std::size_t I, typename T>
+    struct overload_leaf {
+      using F = lib::size_constant<I> (*)(T);
+      operator F() const { return nullptr; }
+    };
+
+    template <typename... Ts>
+    struct overload_impl {
+      private:
+      template <typename>
+      struct impl;
+
+      template <std::size_t... Is>
+      struct impl<lib::index_sequence<Is...>> : overload_leaf<Is, Ts>... {};
+
+      public:
+      using type = impl<lib::index_sequence_for<Ts...>>;
+    };
+
+    template <typename... Ts>
+    using overload = typename overload_impl<Ts...>::type;
+
+    template <typename T, typename... Ts>
+    using best_match = lib::invoke_result_t<overload<Ts...>, T &&>;
+
+    template <typename T>
+    struct is_in_place_index : std::false_type {};
+
+    template <std::size_t I>
+    struct is_in_place_index<in_place_index_t<I>> : std::true_type {};
+
+    template <typename T>
+    struct is_in_place_type : std::false_type {};
+
+    template <typename T>
+    struct is_in_place_type<in_place_type_t<T>> : std::true_type {};
+
+  }  // detail
+
+  template <typename... Ts>
+  class variant {
+    static_assert(0 < sizeof...(Ts),
+                  "variant must consist of at least one alternative.");
+
+    static_assert(lib::all<!std::is_array<Ts>::value...>::value,
+                  "variant can not have an array type as an alternative.");
+
+    static_assert(lib::all<!std::is_reference<Ts>::value...>::value,
+                  "variant can not have a reference type as an alternative.");
+
+    static_assert(lib::all<!std::is_void<Ts>::value...>::value,
+                  "variant can not have a void type as an alternative.");
+
+    public:
+    template <
+        typename Front = lib::type_pack_element_t<0, Ts...>,
+        lib::enable_if_t<std::is_default_constructible<Front>::value, int> = 0>
+    inline constexpr variant() noexcept(
+        std::is_nothrow_default_constructible<Front>::value)
+        : impl_(in_place_index_t<0>{}) {}
+
+    variant(const variant &) = default;
+    variant(variant &&) = default;
+
+    template <
+        typename Arg,
+        typename Decayed = lib::decay_t<Arg>,
+        lib::enable_if_t<!std::is_same<Decayed, variant>::value, int> = 0,
+        lib::enable_if_t<!detail::is_in_place_index<Decayed>::value, int> = 0,
+        lib::enable_if_t<!detail::is_in_place_type<Decayed>::value, int> = 0,
+        std::size_t I = detail::best_match<Arg, Ts...>::value,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T, Arg>::value, int> = 0>
+    inline constexpr variant(Arg &&arg) noexcept(
+        std::is_nothrow_constructible<T, Arg>::value)
+        : impl_(in_place_index_t<I>{}, lib::forward<Arg>(arg)) {}
+
+    template <
+        std::size_t I,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline explicit constexpr variant(
+        in_place_index_t<I>,
+        Args &&... args) noexcept(std::is_nothrow_constructible<T,
+                                                                Args...>::value)
+        : impl_(in_place_index_t<I>{}, lib::forward<Args>(args)...) {}
+
+    template <
+        std::size_t I,
+        typename Up,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline explicit constexpr variant(
+        in_place_index_t<I>,
+        std::initializer_list<Up> il,
+        Args &&... args) noexcept(std::
+                                      is_nothrow_constructible<
+                                          T,
+                                          std::initializer_list<Up> &,
+                                          Args...>::value)
+        : impl_(in_place_index_t<I>{}, il, lib::forward<Args>(args)...) {}
+
+    template <
+        typename T,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline explicit constexpr variant(
+        in_place_type_t<T>,
+        Args &&... args) noexcept(std::is_nothrow_constructible<T,
+                                                                Args...>::value)
+        : impl_(in_place_index_t<I>{}, lib::forward<Args>(args)...) {}
+
+    template <
+        typename T,
+        typename Up,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline explicit constexpr variant(
+        in_place_type_t<T>,
+        std::initializer_list<Up> il,
+        Args &&... args) noexcept(std::
+                                      is_nothrow_constructible<
+                                          T,
+                                          std::initializer_list<Up> &,
+                                          Args...>::value)
+        : impl_(in_place_index_t<I>{}, il, lib::forward<Args>(args)...) {}
+
+    ~variant() = default;
+
+    variant &operator=(const variant &) = default;
+    variant &operator=(variant &&) = default;
+
+    template <typename Arg,
+              lib::enable_if_t<!std::is_same<lib::decay_t<Arg>, variant>::value,
+                               int> = 0,
+              std::size_t I = detail::best_match<Arg, Ts...>::value,
+              typename T = lib::type_pack_element_t<I, Ts...>,
+              lib::enable_if_t<(std::is_assignable<T &, Arg>::value &&
+                                std::is_constructible<T, Arg>::value),
+                               int> = 0>
+    inline variant &operator=(Arg &&arg) noexcept(
+        (std::is_nothrow_assignable<T &, Arg>::value &&
+         std::is_nothrow_constructible<T, Arg>::value)) {
+      impl_.template assign<I>(lib::forward<Arg>(arg));
+      return *this;
+    }
+
+    template <
+        std::size_t I,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline T &emplace(Args &&... args) {
+      return impl_.template emplace<I>(lib::forward<Args>(args)...);
+    }
+
+    template <
+        std::size_t I,
+        typename Up,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline T &emplace(std::initializer_list<Up> il, Args &&... args) {
+      return impl_.template emplace<I>(il, lib::forward<Args>(args)...);
+    }
+
+    template <
+        typename T,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline T &emplace(Args &&... args) {
+      return impl_.template emplace<I>(lib::forward<Args>(args)...);
+    }
+
+    template <
+        typename T,
+        typename Up,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline T &emplace(std::initializer_list<Up> il, Args &&... args) {
+      return impl_.template emplace<I>(il, lib::forward<Args>(args)...);
+    }
+
+    inline constexpr bool valueless_by_exception() const noexcept {
+      return impl_.valueless_by_exception();
+    }
+
+    inline constexpr std::size_t index() const noexcept {
+      return impl_.index();
+    }
+
+    template <bool Dummy = true,
+              lib::enable_if_t<
+                  lib::all<Dummy,
+                           (lib::dependent_type<std::is_move_constructible<Ts>,
+                                                Dummy>::value &&
+                            lib::dependent_type<lib::is_swappable<Ts>,
+                                                Dummy>::value)...>::value,
+                  int> = 0>
+    inline void swap(variant &that) noexcept(
+        lib::all<(std::is_nothrow_move_constructible<Ts>::value &&
+                  lib::is_nothrow_swappable<Ts>::value)...>::value) {
+      impl_.swap(that.impl_);
+    }
+
+    private:
+    detail::impl<Ts...> impl_;
+
+    friend struct detail::access::variant;
+    friend struct detail::visitation::variant;
+  };
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept {
+    return v.index() == I;
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept {
+    return holds_alternative<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  namespace detail {
+    template <std::size_t I, typename V>
+    struct generic_get_impl {
+      constexpr generic_get_impl(int) noexcept {}
+
+      constexpr AUTO_REFREF operator()(V &&v) const
+        AUTO_REFREF_RETURN(
+            access::variant::get_alt<I>(lib::forward<V>(v)).value)
+    };
+
+    template <std::size_t I, typename V>
+    inline constexpr AUTO_REFREF generic_get(V &&v)
+      AUTO_REFREF_RETURN(generic_get_impl<I, V>(
+          holds_alternative<I>(v) ? 0 : (throw_bad_variant_access(), 0))(
+          lib::forward<V>(v)))
+  }  // namespace detail
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr variant_alternative_t<I, variant<Ts...>> &get(
+      variant<Ts...> &v) {
+    return detail::generic_get<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr variant_alternative_t<I, variant<Ts...>> &&get(
+      variant<Ts...> &&v) {
+    return detail::generic_get<I>(lib::move(v));
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr const variant_alternative_t<I, variant<Ts...>> &get(
+      const variant<Ts...> &v) {
+    return detail::generic_get<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr const variant_alternative_t<I, variant<Ts...>> &&get(
+      const variant<Ts...> &&v) {
+    return detail::generic_get<I>(lib::move(v));
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr T &get(variant<Ts...> &v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr T &&get(variant<Ts...> &&v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(lib::move(v));
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr const T &get(const variant<Ts...> &v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr const T &&get(const variant<Ts...> &&v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(lib::move(v));
+  }
+
+  namespace detail {
+
+    template <std::size_t I, typename V>
+    inline constexpr /* auto * */ AUTO generic_get_if(V *v) noexcept
+      AUTO_RETURN(v && holds_alternative<I>(*v)
+                      ? lib::addressof(access::variant::get_alt<I>(*v).value)
+                      : nullptr)
+
+  }  // namespace detail
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr lib::add_pointer_t<variant_alternative_t<I, variant<Ts...>>>
+  get_if(variant<Ts...> *v) noexcept {
+    return detail::generic_get_if<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr lib::add_pointer_t<
+      const variant_alternative_t<I, variant<Ts...>>>
+  get_if(const variant<Ts...> *v) noexcept {
+    return detail::generic_get_if<I>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr lib::add_pointer_t<T>
+  get_if(variant<Ts...> *v) noexcept {
+    return get_if<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr lib::add_pointer_t<const T>
+  get_if(const variant<Ts...> *v) noexcept {
+    return get_if<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  namespace detail {
+    template <typename RelOp>
+    struct convert_to_bool {
+      template <typename Lhs, typename Rhs>
+      inline constexpr bool operator()(Lhs &&lhs, Rhs &&rhs) const {
+        static_assert(std::is_convertible<lib::invoke_result_t<RelOp, Lhs, Rhs>,
+                                          bool>::value,
+                      "relational operators must return a type"
+                      " implicitly convertible to bool");
+        return lib::invoke(
+            RelOp{}, lib::forward<Lhs>(lhs), lib::forward<Rhs>(rhs));
+      }
+    };
+  }  // namespace detail
+
+  template <typename... Ts>
+  inline constexpr bool operator==(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using equal_to = detail::convert_to_bool<lib::equal_to>;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.index() != rhs.index()) return false;
+    if (lhs.valueless_by_exception()) return true;
+    return variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs);
+#else
+    return lhs.index() == rhs.index() &&
+           (lhs.valueless_by_exception() ||
+            variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator!=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using not_equal_to = detail::convert_to_bool<lib::not_equal_to>;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.index() != rhs.index()) return true;
+    if (lhs.valueless_by_exception()) return false;
+    return variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs);
+#else
+    return lhs.index() != rhs.index() ||
+           (!lhs.valueless_by_exception() &&
+            variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator<(const variant<Ts...> &lhs,
+                                  const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using less = detail::convert_to_bool<lib::less>;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (rhs.valueless_by_exception()) return false;
+    if (lhs.valueless_by_exception()) return true;
+    if (lhs.index() < rhs.index()) return true;
+    if (lhs.index() > rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), less{}, lhs, rhs);
+#else
+    return !rhs.valueless_by_exception() &&
+           (lhs.valueless_by_exception() || lhs.index() < rhs.index() ||
+            (lhs.index() == rhs.index() &&
+             variant::visit_value_at(lhs.index(), less{}, lhs, rhs)));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator>(const variant<Ts...> &lhs,
+                                  const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using greater = detail::convert_to_bool<lib::greater>;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.valueless_by_exception()) return false;
+    if (rhs.valueless_by_exception()) return true;
+    if (lhs.index() > rhs.index()) return true;
+    if (lhs.index() < rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), greater{}, lhs, rhs);
+#else
+    return !lhs.valueless_by_exception() &&
+           (rhs.valueless_by_exception() || lhs.index() > rhs.index() ||
+            (lhs.index() == rhs.index() &&
+             variant::visit_value_at(lhs.index(), greater{}, lhs, rhs)));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator<=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using less_equal = detail::convert_to_bool<lib::less_equal>;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.valueless_by_exception()) return true;
+    if (rhs.valueless_by_exception()) return false;
+    if (lhs.index() < rhs.index()) return true;
+    if (lhs.index() > rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs);
+#else
+    return lhs.valueless_by_exception() ||
+           (!rhs.valueless_by_exception() &&
+            (lhs.index() < rhs.index() ||
+             (lhs.index() == rhs.index() &&
+              variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs))));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator>=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using greater_equal = detail::convert_to_bool<lib::greater_equal>;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (rhs.valueless_by_exception()) return true;
+    if (lhs.valueless_by_exception()) return false;
+    if (lhs.index() > rhs.index()) return true;
+    if (lhs.index() < rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), greater_equal{}, lhs, rhs);
+#else
+    return rhs.valueless_by_exception() ||
+           (!lhs.valueless_by_exception() &&
+            (lhs.index() > rhs.index() ||
+             (lhs.index() == rhs.index() &&
+              variant::visit_value_at(
+                  lhs.index(), greater_equal{}, lhs, rhs))));
+#endif
+  }
+
+  struct monostate {};
+
+  inline constexpr bool operator<(monostate, monostate) noexcept {
+    return false;
+  }
+
+  inline constexpr bool operator>(monostate, monostate) noexcept {
+    return false;
+  }
+
+  inline constexpr bool operator<=(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator>=(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator==(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator!=(monostate, monostate) noexcept {
+    return false;
+  }
+
+#ifdef MPARK_CPP14_CONSTEXPR
+  namespace detail {
+
+    inline constexpr bool all(std::initializer_list<bool> bs) {
+      for (bool b : bs) {
+        if (!b) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+  }  // namespace detail
+
+  template <typename Visitor, typename... Vs>
+  inline constexpr decltype(auto) visit(Visitor &&visitor, Vs &&... vs) {
+    return (detail::all({!vs.valueless_by_exception()...})
+                ? (void)0
+                : throw_bad_variant_access()),
+           detail::visitation::variant::visit_value(
+               lib::forward<Visitor>(visitor), lib::forward<Vs>(vs)...);
+  }
+#else
+  namespace detail {
+
+    template <std::size_t N>
+    inline constexpr bool all_impl(const lib::array<bool, N> &bs,
+                                   std::size_t idx) {
+      return idx >= N || (bs[idx] && all_impl(bs, idx + 1));
+    }
+
+    template <std::size_t N>
+    inline constexpr bool all(const lib::array<bool, N> &bs) {
+      return all_impl(bs, 0);
+    }
+
+  }  // namespace detail
+
+  template <typename Visitor, typename... Vs>
+  inline constexpr DECLTYPE_AUTO visit(Visitor &&visitor, Vs &&... vs)
+    DECLTYPE_AUTO_RETURN(
+        (detail::all(
+             lib::array<bool, sizeof...(Vs)>{{!vs.valueless_by_exception()...}})
+             ? (void)0
+             : throw_bad_variant_access()),
+        detail::visitation::variant::visit_value(lib::forward<Visitor>(visitor),
+                                                 lib::forward<Vs>(vs)...))
+#endif
+
+  template <typename... Ts>
+  inline auto swap(variant<Ts...> &lhs,
+                   variant<Ts...> &rhs) noexcept(noexcept(lhs.swap(rhs)))
+      -> decltype(lhs.swap(rhs)) {
+    lhs.swap(rhs);
+  }
+
+  namespace detail {
+
+    template <typename T, typename...>
+    using enabled_type = T;
+
+    namespace hash {
+
+      template <typename H, typename K>
+      constexpr bool meets_requirements() noexcept {
+        return std::is_copy_constructible<H>::value &&
+               std::is_move_constructible<H>::value &&
+               lib::is_invocable_r<std::size_t, H, const K &>::value;
+      }
+
+      template <typename K>
+      constexpr bool is_enabled() noexcept {
+        using H = std::hash<K>;
+        return meets_requirements<H, K>() &&
+               std::is_default_constructible<H>::value &&
+               std::is_copy_assignable<H>::value &&
+               std::is_move_assignable<H>::value;
+      }
+
+    }  // namespace hash
+
+  }  // namespace detail
+
+#undef AUTO
+#undef AUTO_RETURN
+
+#undef AUTO_REFREF
+#undef AUTO_REFREF_RETURN
+
+#undef DECLTYPE_AUTO
+#undef DECLTYPE_AUTO_RETURN
+
+}  // namespace mpark
+
+namespace std {
+
+  template <typename... Ts>
+  struct hash<mpark::detail::enabled_type<
+      mpark::variant<Ts...>,
+      mpark::lib::enable_if_t<mpark::lib::all<mpark::detail::hash::is_enabled<
+          mpark::lib::remove_const_t<Ts>>()...>::value>>> {
+    using argument_type = mpark::variant<Ts...>;
+    using result_type = std::size_t;
+
+    inline result_type operator()(const argument_type &v) const {
+      using mpark::detail::visitation::variant;
+      std::size_t result =
+          v.valueless_by_exception()
+              ? 299792458  // Random value chosen by the universe upon creation
+              : variant::visit_alt(
+#ifdef MPARK_GENERIC_LAMBDAS
+                    [](const auto &alt) {
+                      using alt_type = mpark::lib::decay_t<decltype(alt)>;
+                      using value_type = mpark::lib::remove_const_t<
+                          typename alt_type::value_type>;
+                      return hash<value_type>{}(alt.value);
+                    }
+#else
+                    hasher{}
+#endif
+                    ,
+                    v);
+      return hash_combine(result, hash<std::size_t>{}(v.index()));
+    }
+
+    private:
+#ifndef MPARK_GENERIC_LAMBDAS
+    struct hasher {
+      template <typename Alt>
+      inline std::size_t operator()(const Alt &alt) const {
+        using alt_type = mpark::lib::decay_t<Alt>;
+        using value_type =
+            mpark::lib::remove_const_t<typename alt_type::value_type>;
+        return hash<value_type>{}(alt.value);
+      }
+    };
+#endif
+
+    static std::size_t hash_combine(std::size_t lhs, std::size_t rhs) {
+      return lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
+    }
+  };
+
+  template <>
+  struct hash<mpark::monostate> {
+    using argument_type = mpark::monostate;
+    using result_type = std::size_t;
+
+    inline result_type operator()(const argument_type &) const noexcept {
+      return 66740831;  // return a fundamentally attractive random value.
+    }
+  };
+
+}  // namespace std
+
+#endif  // MPARK_VARIANT_HPP

+ 1729 - 0
src/external/mpark-variant/v1.0.0/variant.hpp

@@ -0,0 +1,1729 @@
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2016
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_VARIANT_HPP
+#define MPARK_VARIANT_HPP
+
+/*
+   variant synopsis
+
+namespace std {
+
+  // 20.7.2, class template variant
+  template <class... Types>
+  class variant {
+  public:
+
+    // 20.7.2.1, constructors
+    constexpr variant() noexcept(see below);
+    variant(const variant&);
+    variant(variant&&) noexcept(see below);
+
+    template <class T> constexpr variant(T&&) noexcept(see below);
+
+    template <class T, class... Args>
+    constexpr explicit variant(in_place_type_t<T>, Args&&...);
+
+    template <class T, class U, class... Args>
+    constexpr explicit variant(
+        in_place_type_t<T>, initializer_list<U>, Args&&...);
+
+    template <size_t I, class... Args>
+    constexpr explicit variant(in_place_index_t<I>, Args&&...);
+
+    template <size_t I, class U, class... Args>
+    constexpr explicit variant(
+        in_place_index_t<I>, initializer_list<U>, Args&&...);
+
+    // 20.7.2.2, destructor
+    ~variant();
+
+    // 20.7.2.3, assignment
+    variant& operator=(const variant&);
+    variant& operator=(variant&&) noexcept(see below);
+
+    template <class T> variant& operator=(T&&) noexcept(see below);
+
+    // 20.7.2.4, modifiers
+    template <class T, class... Args>
+    void emplace(Args&&...);
+
+    template <class T, class U, class... Args>
+    void emplace(initializer_list<U>, Args&&...);
+
+    template <size_t I, class... Args>
+    void emplace(Args&&...);
+
+    template <size_t I, class U, class...  Args>
+    void emplace(initializer_list<U>, Args&&...);
+
+    // 20.7.2.5, value status
+    constexpr bool valueless_by_exception() const noexcept;
+    constexpr size_t index() const noexcept;
+
+    // 20.7.2.6, swap
+    void swap(variant&) noexcept(see below);
+  };
+
+  // 20.7.3, variant helper classes
+  template <class T> struct variant_size; // undefined
+
+  template <class T>
+  constexpr size_t variant_size_v = variant_size<T>::value;
+
+  template <class T> struct variant_size<const T>;
+  template <class T> struct variant_size<volatile T>;
+  template <class T> struct variant_size<const volatile T>;
+
+  template <class... Types>
+  struct variant_size<variant<Types...>>;
+
+  template <size_t I, class T> struct variant_alternative; // undefined
+
+  template <size_t I, class T>
+  using variant_alternative_t = typename variant_alternative<I, T>::type;
+
+  template <size_t I, class T> struct variant_alternative<I, const T>;
+  template <size_t I, class T> struct variant_alternative<I, volatile T>;
+  template <size_t I, class T> struct variant_alternative<I, const volatile T>;
+
+  template <size_t I, class... Types>
+  struct variant_alternative<I, variant<Types...>>;
+
+  constexpr size_t variant_npos = -1;
+
+  // 20.7.4, value access
+  template <class T, class... Types>
+  constexpr bool holds_alternative(const variant<Types...>&) noexcept;
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>>&
+  get(variant<Types...>&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>>&&
+  get(variant<Types...>&&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>> const&
+  get(const variant<Types...>&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>> const&&
+  get(const variant<Types...>&&);
+
+  template <class T, class...  Types>
+  constexpr T& get(variant<Types...>&);
+
+  template <class T, class... Types>
+  constexpr T&& get(variant<Types...>&&);
+
+  template <class T, class... Types>
+  constexpr const T& get(const variant<Types...>&);
+
+  template <class T, class... Types>
+  constexpr const T&& get(const variant<Types...>&&);
+
+  template <size_t I, class... Types>
+  constexpr add_pointer_t<variant_alternative_t<I, variant<Types...>>>
+  get_if(variant<Types...>*) noexcept;
+
+  template <size_t I, class... Types>
+  constexpr add_pointer_t<const variant_alternative_t<I, variant<Types...>>>
+  get_if(const variant<Types...>*) noexcept;
+
+  template <class T, class... Types>
+  constexpr add_pointer_t<T>
+  get_if(variant<Types...>*) noexcept;
+
+  template <class T, class... Types>
+  constexpr add_pointer_t<const T>
+  get_if(const variant<Types...>*) noexcept;
+
+  // 20.7.5, relational operators
+  template <class... Types>
+  constexpr bool operator==(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator!=(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator<(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator>(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator<=(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator>=(const variant<Types...>&, const variant<Types...>&);
+
+  // 20.7.6, visitation
+  template <class Visitor, class... Variants>
+  constexpr see below visit(Visitor&&, Variants&&...);
+
+  // 20.7.7, class monostate
+  struct monostate;
+
+  // 20.7.8, monostate relational operators
+  constexpr bool operator<(monostate, monostate) noexcept;
+  constexpr bool operator>(monostate, monostate) noexcept;
+  constexpr bool operator<=(monostate, monostate) noexcept;
+  constexpr bool operator>=(monostate, monostate) noexcept;
+  constexpr bool operator==(monostate, monostate) noexcept;
+  constexpr bool operator!=(monostate, monostate) noexcept;
+
+  // 20.7.9, specialized algorithms
+  template <class... Types>
+  void swap(variant<Types...>&, variant<Types...>&) noexcept(see below);
+
+  // 20.7.10, class bad_variant_access
+  class bad_variant_access;
+
+  // 20.7.11, hash support
+  template <class T> struct hash;
+  template <class... Types> struct hash<variant<Types...>>;
+  template <> struct hash<monostate>;
+
+} // namespace std
+
+*/
+
+#include <array>
+#include <cstddef>
+#include <exception>
+#include <functional>
+#include <initializer_list>
+#include <new>
+#include <type_traits>
+#include <utility>
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2016
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_IN_PLACE_HPP
+#define MPARK_IN_PLACE_HPP
+
+#include <cstddef>
+
+namespace mpark {
+
+  struct in_place_t {
+    explicit in_place_t() = default;
+  };
+
+  constexpr in_place_t in_place{};
+
+  template <std::size_t I>
+  struct in_place_index_t {
+    explicit in_place_index_t() = default;
+  };
+
+  template <std::size_t I>
+  constexpr in_place_index_t<I> in_place_index{};
+
+  template <typename T>
+  struct in_place_type_t {
+    explicit in_place_type_t() = default;
+  };
+
+  template <typename T>
+  constexpr in_place_type_t<T> in_place_type{};
+
+}  // namespace mpark
+
+#endif  // MPARK_IN_PLACE_HPP
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2016
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_LIB_HPP
+#define MPARK_LIB_HPP
+
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2016
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_CONFIG_HPP
+#define MPARK_CONFIG_HPP
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+#if __has_builtin(__builtin_addressof) || __GNUC__ >= 7
+#define MPARK_BUILTIN_ADDRESSOF
+#endif
+
+#if __has_builtin(__type_pack_element)
+#define MPARK_TYPE_PACK_ELEMENT
+#endif
+
+#endif  // MPARK_CONFIG_HPP
+
+
+namespace mpark {
+  namespace lib {
+
+    inline namespace cpp17 {
+
+      // <type_traits>
+      template <bool B>
+      using bool_constant = std::integral_constant<bool, B>;
+
+      template <typename...>
+      using void_t = void;
+
+      template <typename F, typename = void>
+      struct is_callable : std::false_type {};
+
+      template <typename F>
+      struct is_callable<F, void_t<std::result_of_t<F>>> : std::true_type {};
+
+      namespace detail {
+        namespace swappable {
+
+          using std::swap;
+
+          template <typename T>
+          struct is_swappable_impl {
+            private:
+            template <typename U,
+                      typename = decltype(swap(std::declval<U &>(),
+                                               std::declval<U &>()))>
+            inline static std::true_type test(int);
+
+            template <typename U>
+            inline static std::false_type test(...);
+
+            public:
+            using type = decltype(test<T>(0));
+          };
+
+          template <typename T>
+          using is_swappable = typename is_swappable_impl<T>::type;
+
+          template <typename T, bool = is_swappable<T>::value>
+          struct is_nothrow_swappable
+              : bool_constant<noexcept(
+                    swap(std::declval<T &>(), std::declval<T &>()))> {};
+
+          template <typename T>
+          struct is_nothrow_swappable<T, false> : std::false_type {};
+
+        }  // namespace swappable
+      }  // namespace detail
+
+      template <typename T>
+      using is_swappable = detail::swappable::is_swappable<T>;
+
+      template <typename T>
+      using is_nothrow_swappable = detail::swappable::is_nothrow_swappable<T>;
+
+      // <functional>
+#define RETURN(...) -> decltype(__VA_ARGS__) { return __VA_ARGS__; }
+
+      template <typename F, typename... As>
+      inline constexpr auto invoke(F &&f, As &&... as)
+          RETURN(std::forward<F>(f)(std::forward<As>(as)...))
+
+      template <typename B, typename T, typename D>
+      inline constexpr auto invoke(T B::*pmv, D &&d)
+          RETURN(std::forward<D>(d).*pmv)
+
+      template <typename Pmv, typename Ptr>
+      inline constexpr auto invoke(Pmv pmv, Ptr &&ptr)
+          RETURN((*std::forward<Ptr>(ptr)).*pmv)
+
+      template <typename B, typename T, typename D, typename... As>
+      inline constexpr auto invoke(T B::*pmf, D &&d, As &&... as)
+          RETURN((std::forward<D>(d).*pmf)(std::forward<As>(as)...))
+
+      template <typename Pmf, typename Ptr, typename... As>
+      inline constexpr auto invoke(Pmf pmf, Ptr &&ptr, As &&... as)
+          RETURN(((*std::forward<Ptr>(ptr)).*pmf)(std::forward<As>(as)...))
+
+#undef RETURN
+
+      // <memory>
+#ifdef MPARK_BUILTIN_ADDRESSOF
+      template <typename T>
+      inline constexpr T *addressof(T &arg) {
+        return __builtin_addressof(arg);
+      }
+#else
+      namespace detail {
+
+        namespace has_addressof_impl {
+
+          struct fail;
+
+          template <typename T>
+          inline fail operator&(T &&);
+
+          template <typename T>
+          inline static constexpr bool impl() {
+            return (std::is_class<T>::value || std::is_union<T>::value) &&
+                   !std::is_same<decltype(&std::declval<T &>()), fail>::value;
+          }
+
+        }  // namespace has_addressof_impl
+
+        template <typename T>
+        using has_addressof = bool_constant<has_addressof_impl::impl<T>()>;
+
+      }  // namespace detail
+
+      template <typename T,
+                std::enable_if_t<detail::has_addressof<T>::value, int> = 0>
+      inline T *addressof(T &arg) {
+        return std::addressof(arg);
+      }
+
+      template <typename T,
+                std::enable_if_t<!detail::has_addressof<T>::value, int> = 0>
+      inline constexpr T *addressof(T &arg) {
+        return &arg;
+      }
+#endif
+
+      template <typename T>
+      inline const T *addressof(const T &&) = delete;
+
+    }  // namespace cpp17
+
+    template <typename T>
+    struct identity {
+      using type = T;
+    };
+
+#ifdef MPARK_TYPE_PACK_ELEMENT
+    template <std::size_t I, typename... Ts>
+    using type_pack_element_t = __type_pack_element<I, Ts...>;
+#else
+    template <std::size_t I, typename... Ts>
+    struct type_pack_element_impl {
+      private:
+      template <std::size_t, typename T>
+      struct indexed_type {
+        using type = T;
+      };
+
+      template <std::size_t... Is>
+      inline static auto make_set(std::index_sequence<Is...>) {
+        struct set : indexed_type<Is, Ts>... {};
+        return set{};
+      }
+
+      template <typename T>
+      inline static std::enable_if<true, T> impl(indexed_type<I, T>);
+
+      inline static std::enable_if<false, void> impl(...);
+
+      public:
+      using type = decltype(impl(make_set(std::index_sequence_for<Ts...>{})));
+    };
+
+    template <std::size_t I, typename... Ts>
+    using type_pack_element = typename type_pack_element_impl<I, Ts...>::type;
+
+    template <std::size_t I, typename... Ts>
+    using type_pack_element_t = typename type_pack_element<I, Ts...>::type;
+#endif
+
+  }  // namespace lib
+}  // namespace mpark
+
+#endif  // MPARK_LIB_HPP
+
+
+namespace mpark {
+
+  class bad_variant_access : public std::exception {
+    public:
+    virtual const char *what() const noexcept { return "bad_variant_access"; }
+  };
+
+  template <typename... Ts>
+  class variant;
+
+  template <typename T>
+  struct variant_size;
+
+  template <typename T>
+  constexpr std::size_t variant_size_v = variant_size<T>::value;
+
+  template <typename T>
+  struct variant_size<const T> : variant_size<T> {};
+
+  template <typename T>
+  struct variant_size<volatile T> : variant_size<T> {};
+
+  template <typename T>
+  struct variant_size<const volatile T> : variant_size<T> {};
+
+  template <typename... Ts>
+  struct variant_size<variant<Ts...>>
+      : std::integral_constant<std::size_t, sizeof...(Ts)> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative;
+
+  template <std::size_t I, typename T>
+  using variant_alternative_t = typename variant_alternative<I, T>::type;
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, const T>
+      : std::add_const<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, volatile T>
+      : std::add_volatile<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, const volatile T>
+      : std::add_cv<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename... Ts>
+  struct variant_alternative<I, variant<Ts...>> {
+    static_assert(I < sizeof...(Ts),
+                  "`variant_alternative` index out of range.");
+    using type = lib::type_pack_element_t<I, Ts...>;
+  };
+
+  constexpr std::size_t variant_npos = static_cast<std::size_t>(-1);
+
+  namespace detail {
+
+    inline constexpr bool all(std::initializer_list<bool> bs) {
+      for (bool b : bs) {
+        if (!b) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+    constexpr std::size_t not_found = static_cast<std::size_t>(-1);
+    constexpr std::size_t ambiguous = static_cast<std::size_t>(-2);
+
+    template <typename T, typename... Ts>
+    inline constexpr std::size_t find_index() {
+      constexpr bool matches[] = {std::is_same<T, Ts>::value...};
+      std::size_t result = not_found;
+      for (std::size_t i = 0; i < sizeof...(Ts); ++i) {
+        if (matches[i]) {
+          if (result != not_found) {
+            return ambiguous;
+          }
+          result = i;
+        }
+      }
+      return result;
+    }
+
+    template <std::size_t I>
+    using find_index_sfinae_impl =
+        std::enable_if_t<I != not_found && I != ambiguous,
+                         std::integral_constant<std::size_t, I>>;
+
+    template <typename T, typename... Ts>
+    using find_index_sfinae = find_index_sfinae_impl<find_index<T, Ts...>()>;
+
+    template <std::size_t I>
+    struct find_index_checked_impl : std::integral_constant<std::size_t, I> {
+      static_assert(I != not_found, "the specified type is not found.");
+      static_assert(I != ambiguous, "the specified type is ambiguous.");
+    };
+
+    template <typename T, typename... Ts>
+    using find_index_checked = find_index_checked_impl<find_index<T, Ts...>()>;
+
+    struct valueless_t {};
+
+    enum class Trait { TriviallyAvailable, Available, Unavailable };
+
+    template <typename T,
+              template <typename> class IsTriviallyAvailable,
+              template <typename> class IsAvailable>
+    inline constexpr Trait trait() {
+      return IsTriviallyAvailable<T>::value
+                 ? Trait::TriviallyAvailable
+                 : IsAvailable<T>::value ? Trait::Available
+                                         : Trait::Unavailable;
+    }
+
+    inline constexpr Trait common_trait(std::initializer_list<Trait> traits) {
+      Trait result = Trait::TriviallyAvailable;
+      for (Trait t : traits) {
+        if (static_cast<int>(t) > static_cast<int>(result)) {
+          result = t;
+        }
+      }
+      return result;
+    }
+
+    template <typename... Ts>
+    struct traits {
+      static constexpr Trait copy_constructible_trait =
+          common_trait({trait<Ts,
+                              std::is_trivially_copy_constructible,
+                              std::is_copy_constructible>()...});
+
+      static constexpr Trait move_constructible_trait =
+          common_trait({trait<Ts,
+                              std::is_trivially_move_constructible,
+                              std::is_move_constructible>()...});
+
+      static constexpr Trait copy_assignable_trait =
+          common_trait({copy_constructible_trait,
+                        move_constructible_trait,
+                        trait<Ts,
+                              std::is_trivially_copy_assignable,
+                              std::is_copy_assignable>()...});
+
+      static constexpr Trait move_assignable_trait =
+          common_trait({move_constructible_trait,
+                        trait<Ts,
+                              std::is_trivially_move_assignable,
+                              std::is_move_assignable>()...});
+
+      static constexpr Trait destructible_trait =
+          common_trait({trait<Ts,
+                              std::is_trivially_destructible,
+                              std::is_destructible>()...});
+    };
+
+    namespace access {
+
+      struct recursive_union {
+        template <typename V>
+        inline static constexpr auto &&get_alt(V &&v, in_place_index_t<0>) {
+          return std::forward<V>(v).head_;
+        }
+
+        template <typename V, std::size_t I>
+        inline static constexpr auto &&get_alt(V &&v, in_place_index_t<I>) {
+          return get_alt(std::forward<V>(v).tail_, in_place_index<I - 1>);
+        }
+      };
+
+      struct base {
+        template <std::size_t I, typename V>
+        inline static constexpr auto &&get_alt(V &&v) {
+          return recursive_union::get_alt(std::forward<V>(v).data_,
+                                          in_place_index<I>);
+        }
+      };
+
+      struct variant {
+        template <std::size_t I, typename V>
+        inline static constexpr auto &&get_alt(V &&v) {
+          return base::get_alt<I>(std::forward<V>(v).impl_);
+        }
+      };
+
+    }  // namespace access
+
+    namespace visitation {
+
+      struct base {
+        template <typename Visitor, typename... Vs>
+        inline static constexpr decltype(auto) visit_alt_at(std::size_t index,
+                                                            Visitor &&visitor,
+                                                            Vs &&... vs) {
+          constexpr auto fdiagonal =
+              make_fdiagonal<Visitor &&,
+                             decltype(std::forward<Vs>(vs).as_base())...>();
+          return fdiagonal[index](std::forward<Visitor>(visitor),
+                                  std::forward<Vs>(vs).as_base()...);
+        }
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr decltype(auto) visit_alt(Visitor &&visitor,
+                                                         Vs &&... vs) {
+          constexpr auto fmatrix =
+              make_fmatrix<Visitor &&,
+                           decltype(std::forward<Vs>(vs).as_base())...>();
+          const std::size_t indices[] = {vs.index()...};
+          return at(fmatrix, indices)(std::forward<Visitor>(visitor),
+                                      std::forward<Vs>(vs).as_base()...);
+        }
+
+        private:
+        template <typename T>
+        inline static constexpr const T &at_impl(const T &elem,
+                                                 const std::size_t *) {
+          return elem;
+        }
+
+        template <typename T, std::size_t N>
+        inline static constexpr auto &&at_impl(const std::array<T, N> &elems,
+                                               const std::size_t *index) {
+          return at_impl(elems[*index], index + 1);
+        }
+
+        template <typename T, std::size_t N, std::size_t I>
+        inline static constexpr auto &&at(const std::array<T, N> &elems,
+                                          const std::size_t (&indices)[I]) {
+          return at_impl(elems, indices);
+        }
+
+        template <typename F, typename... Fs>
+        inline static constexpr void std_visit_visitor_return_type_check() {
+          static_assert(all({std::is_same<F, Fs>::value...}),
+                        "`std::visit` requires the visitor to have a single "
+                        "return type.");
+        }
+
+        template <typename... Fs>
+        inline static constexpr auto make_farray(Fs &&... fs) {
+          std_visit_visitor_return_type_check<std::decay_t<Fs>...>();
+          using result = std::array<std::common_type_t<std::decay_t<Fs>...>,
+                                    sizeof...(Fs)>;
+          return result{{std::forward<Fs>(fs)...}};
+        }
+
+        template <std::size_t... Is>
+        struct dispatcher {
+          template <typename F, typename... Vs>
+          inline static constexpr decltype(auto) dispatch(F f, Vs... vs) {
+            return lib::invoke(
+                static_cast<F>(f),
+                access::base::get_alt<Is>(static_cast<Vs>(vs))...);
+          }
+        };
+
+        template <typename F, typename... Vs, std::size_t... Is>
+        inline static constexpr auto make_dispatch(std::index_sequence<Is...>) {
+          return &dispatcher<Is...>::template dispatch<F, Vs...>;
+        }
+
+        template <std::size_t I, typename F, typename... Vs>
+        inline static constexpr auto make_fdiagonal_impl() {
+          return make_dispatch<F, Vs...>(
+              std::index_sequence<(lib::identity<Vs>{}, I)...>{});
+        }
+
+        template <typename F, typename... Vs, std::size_t... Is>
+        inline static constexpr auto make_fdiagonal_impl(
+            std::index_sequence<Is...>) {
+          return base::make_farray(make_fdiagonal_impl<Is, F, Vs...>()...);
+        }
+
+        template <typename F, typename V, typename... Vs>
+        inline static constexpr auto make_fdiagonal() {
+          constexpr std::size_t N = std::decay_t<V>::size();
+          static_assert(all({(N == std::decay_t<Vs>::size())...}),
+                        "all of the variants must be the same size.");
+          return make_fdiagonal_impl<F, V, Vs...>(
+              std::make_index_sequence<N>{});
+        }
+
+        template <typename F, typename... Vs, std::size_t... Is>
+        inline static constexpr auto make_fmatrix_impl(
+            std::index_sequence<Is...> is) {
+          return make_dispatch<F, Vs...>(is);
+        }
+
+        template <typename F,
+                  typename... Vs,
+                  std::size_t... Is,
+                  std::size_t... Js,
+                  typename... Ls>
+        inline static constexpr auto make_fmatrix_impl(
+            std::index_sequence<Is...>, std::index_sequence<Js...>, Ls... ls) {
+          return base::make_farray(make_fmatrix_impl<F, Vs...>(
+              std::index_sequence<Is..., Js>{}, ls...)...);
+        }
+
+        template <typename F, typename... Vs>
+        inline static constexpr auto make_fmatrix() {
+          return make_fmatrix_impl<F, Vs...>(
+              std::index_sequence<>{},
+              std::make_index_sequence<std::decay_t<Vs>::size()>{}...);
+        }
+      };
+
+      struct variant {
+        template <typename Visitor, typename... Vs>
+        inline static constexpr decltype(auto) visit_alt_at(std::size_t index,
+                                                            Visitor &&visitor,
+                                                            Vs &&... vs) {
+          return base::visit_alt_at(index,
+                                    std::forward<Visitor>(visitor),
+                                    std::forward<Vs>(vs).impl_...);
+        }
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr decltype(auto) visit_alt(Visitor &&visitor,
+                                                         Vs &&... vs) {
+          return base::visit_alt(std::forward<Visitor>(visitor),
+                                 std::forward<Vs>(vs).impl_...);
+        }
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr decltype(auto) visit_value_at(std::size_t index,
+                                                              Visitor &&visitor,
+                                                              Vs &&... vs) {
+          return visit_alt_at(
+              index,
+              make_value_visitor(std::forward<Visitor>(visitor)),
+              std::forward<Vs>(vs)...);
+        }
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr decltype(auto) visit_value(Visitor &&visitor,
+                                                           Vs &&... vs) {
+          return visit_alt(make_value_visitor(std::forward<Visitor>(visitor)),
+                           std::forward<Vs>(vs)...);
+        }
+
+        private:
+        template <typename Visitor, typename... Values>
+        inline static constexpr void std_visit_exhaustive_visitor_check() {
+          static_assert(lib::is_callable<Visitor(Values...)>::value,
+                        "`std::visit` requires the visitor to be exhaustive.");
+        }
+
+        template <typename Visitor>
+        struct value_visitor {
+          template <typename... Alts>
+          inline constexpr decltype(auto) operator()(Alts &&... alts) const {
+            std_visit_exhaustive_visitor_check<
+                Visitor,
+                decltype(std::forward<Alts>(alts).value_)...>();
+            return lib::invoke(std::forward<Visitor>(visitor_),
+                               std::forward<Alts>(alts).value_...);
+          }
+          Visitor &&visitor_;
+        };
+
+        template <typename Visitor>
+        inline static constexpr auto make_value_visitor(Visitor &&visitor) {
+          return value_visitor<Visitor>{std::forward<Visitor>(visitor)};
+        }
+      };
+
+    }  // namespace visitation
+
+    template <std::size_t Index, typename T>
+    struct alt {
+      using value_type = T;
+
+      template <typename... Args>
+      inline explicit constexpr alt(in_place_t, Args &&... args)
+          : value_(std::forward<Args>(args)...) {}
+
+      value_type value_;
+    };
+
+    template <Trait DestructibleTrait, std::size_t Index, typename... Ts>
+    union recursive_union;
+
+    template <Trait DestructibleTrait, std::size_t Index>
+    union recursive_union<DestructibleTrait, Index> {};
+
+#define MPARK_VARIANT_RECURSIVE_UNION(destructible_trait, destructor)  \
+  template <std::size_t Index, typename T, typename... Ts>             \
+  union recursive_union<destructible_trait, Index, T, Ts...> {         \
+    public:                                                            \
+    inline explicit constexpr recursive_union(valueless_t) noexcept    \
+        : dummy_{} {}                                                  \
+                                                                       \
+    template <typename... Args>                                        \
+    inline explicit constexpr recursive_union(in_place_index_t<0>,     \
+                                              Args &&... args)         \
+        : head_(in_place, std::forward<Args>(args)...) {}              \
+                                                                       \
+    template <std::size_t I, typename... Args>                         \
+    inline explicit constexpr recursive_union(in_place_index_t<I>,     \
+                                              Args &&... args)         \
+        : tail_(in_place_index<I - 1>, std::forward<Args>(args)...) {} \
+                                                                       \
+    recursive_union(const recursive_union &) = default;                \
+    recursive_union(recursive_union &&) = default;                     \
+                                                                       \
+    destructor                                                         \
+                                                                       \
+    recursive_union &operator=(const recursive_union &) = default;     \
+    recursive_union &operator=(recursive_union &&) = default;          \
+                                                                       \
+    private:                                                           \
+    char dummy_;                                                       \
+    alt<Index, T> head_;                                               \
+    recursive_union<destructible_trait, Index + 1, Ts...> tail_;       \
+                                                                       \
+    friend struct access::recursive_union;                             \
+  }
+
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::TriviallyAvailable,
+                                  ~recursive_union() = default;);
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::Available,
+                                  ~recursive_union() {});
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::Unavailable,
+                                  ~recursive_union() = delete;);
+
+#undef _MPARK_VARIANT_UNION
+
+    template <Trait DestructibleTrait, typename... Ts>
+    class base {
+      public:
+      inline explicit constexpr base(valueless_t tag) noexcept
+          : data_(tag), index_(-1) {}
+
+      template <std::size_t I, typename... Args>
+      inline explicit constexpr base(in_place_index_t<I>, Args &&... args)
+          : data_(in_place_index<I>, std::forward<Args>(args)...), index_(I) {}
+
+      inline constexpr bool valueless_by_exception() const noexcept {
+        return index() == variant_npos;
+      }
+
+      inline constexpr std::size_t index() const noexcept {
+        return index_ == static_cast<unsigned int>(-1) ? variant_npos : index_;
+      }
+
+      protected:
+      inline constexpr base &as_base() & { return *this; }
+      inline constexpr base &&as_base() && { return std::move(*this); }
+      inline constexpr const base &as_base() const & { return *this; }
+      inline constexpr const base &&as_base() const && { return std::move(*this); }
+
+      inline static constexpr std::size_t size() { return sizeof...(Ts); }
+
+      recursive_union<DestructibleTrait, 0, Ts...> data_;
+      unsigned int index_;
+
+      friend struct access::base;
+      friend struct visitation::base;
+    };
+
+    template <typename Traits, Trait = Traits::destructible_trait>
+    class destructor;
+
+#define MPARK_VARIANT_DESTRUCTOR(destructible_trait, definition, destroy) \
+  template <typename... Ts>                                               \
+  class destructor<traits<Ts...>, destructible_trait>                     \
+      : public base<destructible_trait, Ts...> {                          \
+    using super = base<destructible_trait, Ts...>;                        \
+                                                                          \
+    public:                                                               \
+    using super::super;                                                   \
+    using super::operator=;                                               \
+                                                                          \
+    destructor(const destructor &) = default;                             \
+    destructor(destructor &&) = default;                                  \
+    definition                                                            \
+    destructor &operator=(const destructor &) = default;                  \
+    destructor &operator=(destructor &&) = default;                       \
+                                                                          \
+    protected:                                                            \
+    inline destroy                                                        \
+  }
+
+    MPARK_VARIANT_DESTRUCTOR(Trait::TriviallyAvailable,
+                             ~destructor() = default;,
+                             void destroy() noexcept { this->index_ = -1; });
+
+    MPARK_VARIANT_DESTRUCTOR(Trait::Available,
+                             ~destructor() { destroy(); },
+                             void destroy() noexcept {
+                               if (!this->valueless_by_exception()) {
+                                 visitation::base::visit_alt(
+                                     [](auto &alt) noexcept {
+                                       using alt_type =
+                                           std::decay_t<decltype(alt)>;
+                                       alt.~alt_type();
+                                     },
+                                     *this);
+                               }
+                               this->index_ = -1;
+                             });
+
+    MPARK_VARIANT_DESTRUCTOR(Trait::Unavailable,
+                             ~destructor() = delete;,
+                             void destroy() noexcept = delete;);
+
+#undef MPARK_VARIANT_DESTRUCTOR
+
+    template <typename Traits>
+    class constructor : public destructor<Traits> {
+      using super = destructor<Traits>;
+
+      public:
+      using super::super;
+      using super::operator=;
+
+      protected:
+      template <std::size_t I, typename T, typename... Args>
+      inline static void construct_alt(alt<I, T> &a, Args &&... args) {
+        ::new (static_cast<void *>(lib::addressof(a)))
+            alt<I, T>(in_place, std::forward<Args>(args)...);
+      }
+
+      template <typename Rhs>
+      inline static void generic_construct(constructor &lhs, Rhs &&rhs) {
+        lhs.destroy();
+        if (!rhs.valueless_by_exception()) {
+          visitation::base::visit_alt_at(
+              rhs.index(),
+              [](auto &lhs_alt, auto &&rhs_alt) {
+                constructor::construct_alt(
+                    lhs_alt, std::forward<decltype(rhs_alt)>(rhs_alt).value_);
+              },
+              lhs,
+              std::forward<Rhs>(rhs));
+          lhs.index_ = rhs.index();
+        }
+      }
+    };
+
+    template <typename Traits, Trait = Traits::move_constructible_trait>
+    class move_constructor;
+
+#define MPARK_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, definition) \
+  template <typename... Ts>                                                  \
+  class move_constructor<traits<Ts...>, move_constructible_trait>            \
+      : public constructor<traits<Ts...>> {                                  \
+    using super = constructor<traits<Ts...>>;                                \
+                                                                             \
+    public:                                                                  \
+    using super::super;                                                      \
+    using super::operator=;                                                  \
+                                                                             \
+    move_constructor(const move_constructor &) = default;                    \
+    definition                                                               \
+    ~move_constructor() = default;                                           \
+    move_constructor &operator=(const move_constructor &) = default;         \
+    move_constructor &operator=(move_constructor &&) = default;              \
+  }
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::TriviallyAvailable,
+        move_constructor(move_constructor &&that) = default;);
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::Available,
+        move_constructor(move_constructor &&that) noexcept(
+            all({std::is_nothrow_move_constructible<Ts>::value...}))
+            : move_constructor(valueless_t{}) {
+          this->generic_construct(*this, std::move(that));
+        });
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::Unavailable,
+        move_constructor(move_constructor &&) = delete;);
+
+#undef MPARK_VARIANT_MOVE_CONSTRUCTOR
+
+    template <typename Traits, Trait = Traits::copy_constructible_trait>
+    class copy_constructor;
+
+#define MPARK_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, definition) \
+  template <typename... Ts>                                                  \
+  class copy_constructor<traits<Ts...>, copy_constructible_trait>            \
+      : public move_constructor<traits<Ts...>> {                             \
+    using super = move_constructor<traits<Ts...>>;                           \
+                                                                             \
+    public:                                                                  \
+    using super::super;                                                      \
+    using super::operator=;                                                  \
+                                                                             \
+    definition                                                               \
+    copy_constructor(copy_constructor &&) = default;                         \
+    ~copy_constructor() = default;                                           \
+    copy_constructor &operator=(const copy_constructor &) = default;         \
+    copy_constructor &operator=(copy_constructor &&) = default;              \
+  }
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::TriviallyAvailable,
+        copy_constructor(const copy_constructor &that) = default;);
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::Available,
+        copy_constructor(const copy_constructor &that)
+            : copy_constructor(valueless_t{}) {
+          this->generic_construct(*this, that);
+        });
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::Unavailable,
+        copy_constructor(const copy_constructor &) = delete;);
+
+#undef MPARK_VARIANT_COPY_CONSTRUCTOR
+
+    template <typename Traits>
+    class assignment : public copy_constructor<Traits> {
+      using super = copy_constructor<Traits>;
+
+      public:
+      using super::super;
+      using super::operator=;
+
+      template <std::size_t I, typename... Args>
+      inline void emplace(Args &&... args) {
+        this->destroy();
+        this->construct_alt(access::base::get_alt<I>(*this),
+                            std::forward<Args>(args)...);
+        this->index_ = I;
+      }
+
+      protected:
+      template <bool CopyAssign, std::size_t I, typename T, typename Arg>
+      inline void assign_alt(alt<I, T> &a,
+                             Arg &&arg,
+                             lib::bool_constant<CopyAssign> tag) {
+        if (this->index() == I) {
+          a.value_ = std::forward<Arg>(arg);
+        } else {
+          struct {
+            void operator()(std::true_type) const {
+              this_->emplace<I>(T(std::forward<Arg>(arg_)));
+            }
+            void operator()(std::false_type) const {
+              this_->emplace<I>(std::forward<Arg>(arg_));
+            }
+            assignment *this_;
+            Arg &&arg_;
+          } impl{this, std::forward<Arg>(arg)};
+          impl(tag);
+        }
+      }
+
+      template <typename That>
+      inline void generic_assign(That &&that) {
+        if (this->valueless_by_exception() && that.valueless_by_exception()) {
+          // do nothing.
+        } else if (that.valueless_by_exception()) {
+          this->destroy();
+        } else {
+          visitation::base::visit_alt_at(
+              that.index(),
+              [this](auto &this_alt, auto &&that_alt) {
+                this->assign_alt(
+                    this_alt,
+                    std::forward<decltype(that_alt)>(that_alt).value_,
+                    std::is_lvalue_reference<That>{});
+              },
+              *this,
+              std::forward<That>(that));
+        }
+      }
+    };
+
+    template <typename Traits, Trait = Traits::move_assignable_trait>
+    class move_assignment;
+
+#define MPARK_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, definition) \
+  template <typename... Ts>                                              \
+  class move_assignment<traits<Ts...>, move_assignable_trait>            \
+      : public assignment<traits<Ts...>> {                               \
+    using super = assignment<traits<Ts...>>;                             \
+                                                                         \
+    public:                                                              \
+    using super::super;                                                  \
+    using super::operator=;                                              \
+                                                                         \
+    move_assignment(const move_assignment &) = default;                  \
+    move_assignment(move_assignment &&) = default;                       \
+    ~move_assignment() = default;                                        \
+    move_assignment &operator=(const move_assignment &) = default;       \
+    definition                                                           \
+  }
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::TriviallyAvailable,
+        move_assignment &operator=(move_assignment &&that) = default;);
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::Available,
+        move_assignment &
+        operator=(move_assignment &&that) noexcept(
+            all({std::is_nothrow_move_constructible<Ts>::value &&
+                 std::is_nothrow_move_assignable<Ts>::value...})) {
+          this->generic_assign(std::move(that));
+          return *this;
+        });
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::Unavailable,
+        move_assignment &operator=(move_assignment &&) = delete;);
+
+#undef MPARK_VARIANT_MOVE_ASSIGNMENT
+
+    template <typename Traits, Trait = Traits::copy_assignable_trait>
+    class copy_assignment;
+
+#define MPARK_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, definition) \
+  template <typename... Ts>                                              \
+  class copy_assignment<traits<Ts...>, copy_assignable_trait>            \
+      : public move_assignment<traits<Ts...>> {                          \
+    using super = move_assignment<traits<Ts...>>;                        \
+                                                                         \
+    public:                                                              \
+    using super::super;                                                  \
+    using super::operator=;                                              \
+                                                                         \
+    copy_assignment(const copy_assignment &) = default;                  \
+    copy_assignment(copy_assignment &&) = default;                       \
+    ~copy_assignment() = default;                                        \
+    definition                                                           \
+    copy_assignment &operator=(copy_assignment &&) = default;            \
+  }
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::TriviallyAvailable,
+        copy_assignment &operator=(const copy_assignment &that) = default;);
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::Available,
+        copy_assignment &operator=(const copy_assignment &that) {
+          this->generic_assign(that);
+          return *this;
+        });
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::Unavailable,
+        copy_assignment &operator=(const copy_assignment &) = delete;);
+
+#undef MPARK_VARIANT_COPY_ASSIGNMENT
+
+    template <typename... Ts>
+    class impl : public copy_assignment<traits<Ts...>> {
+      using super = copy_assignment<traits<Ts...>>;
+
+      public:
+      using super::super;
+      using super::operator=;
+
+      template <std::size_t I, typename Arg>
+      inline void assign(Arg &&arg) {
+        this->assign_alt(access::base::get_alt<I>(*this),
+                         std::forward<Arg>(arg),
+                         std::false_type{});
+      }
+
+      inline void swap(impl &that) {
+        if (this->valueless_by_exception() && that.valueless_by_exception()) {
+          // do nothing.
+        } else if (this->index() == that.index()) {
+          visitation::base::visit_alt_at(this->index(),
+                                         [](auto &this_alt, auto &that_alt) {
+                                           using std::swap;
+                                           swap(this_alt.value_,
+                                                that_alt.value_);
+                                         },
+                                         *this,
+                                         that);
+        } else {
+          impl *lhs = this;
+          impl *rhs = lib::addressof(that);
+          if (lhs->move_nothrow() && !rhs->move_nothrow()) {
+            std::swap(lhs, rhs);
+          }
+          impl tmp(std::move(*rhs));
+          // EXTENSION: When the move construction of `lhs` into `rhs` throws
+          // and `tmp` is nothrow move constructible then we move `tmp` back
+          // into `rhs` and provide the strong exception safety guarentee.
+          try {
+            this->generic_construct(*rhs, std::move(*lhs));
+          } catch (...) {
+            if (tmp.move_nothrow()) {
+              this->generic_construct(*rhs, std::move(tmp));
+            }
+            throw;
+          }
+          this->generic_construct(*lhs, std::move(tmp));
+        }
+      }
+
+      private:
+      inline constexpr bool move_nothrow() const {
+        constexpr bool results[] = {
+            std::is_nothrow_move_constructible<Ts>::value...};
+        return this->valueless_by_exception() || results[this->index()];
+      }
+    };
+
+    template <typename... Ts>
+    struct overload;
+
+    template <>
+    struct overload<> {
+      void operator()() const;
+    };
+
+    template <typename T, typename... Ts>
+    struct overload<T, Ts...> : overload<Ts...> {
+      using overload<Ts...>::operator();
+      lib::identity<T> operator()(T) const;
+    };
+
+    template <typename T, typename... Ts>
+    using best_match_t =
+        typename std::result_of_t<overload<Ts...>(T &&)>::type;
+
+  }  // detail
+
+  template <typename... Ts>
+  class variant {
+    static_assert(0 < sizeof...(Ts),
+                  "variant must consist of at least one alternative.");
+
+    static_assert(detail::all({!std::is_array<Ts>::value...}),
+                  "variant can not have an array type as an alternative.");
+
+    static_assert(detail::all({!std::is_reference<Ts>::value...}),
+                  "variant can not have a reference type as an alternative.");
+
+    static_assert(detail::all({!std::is_void<Ts>::value...}),
+                  "variant can not have a void type as an alternative.");
+
+    public:
+    template <
+        typename Front = lib::type_pack_element_t<0, Ts...>,
+        std::enable_if_t<std::is_default_constructible<Front>::value, int> = 0>
+    inline constexpr variant() noexcept(
+        std::is_nothrow_default_constructible<Front>::value)
+        : impl_(in_place_index<0>) {}
+
+    variant(const variant &) = default;
+    variant(variant &&) = default;
+
+    template <typename Arg,
+              std::enable_if_t<!std::is_same<std::decay_t<Arg>, variant>::value,
+                               int> = 0,
+              typename T = detail::best_match_t<Arg, Ts...>,
+              std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+              std::enable_if_t<std::is_constructible<T, Arg>::value, int> = 0>
+    inline constexpr variant(Arg &&arg) noexcept(
+        std::is_nothrow_constructible<T, Arg>::value)
+        : impl_(in_place_index<I>, std::forward<Arg>(arg)) {}
+
+    template <
+        std::size_t I,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        std::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline explicit constexpr variant(
+        in_place_index_t<I>,
+        Args &&... args) noexcept(std::is_nothrow_constructible<T,
+                                                                Args...>::value)
+        : impl_(in_place_index<I>, std::forward<Args>(args)...) {}
+
+    template <
+        std::size_t I,
+        typename Up,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        std::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline explicit constexpr variant(
+        in_place_index_t<I>,
+        std::initializer_list<Up> il,
+        Args &&... args) noexcept(std::
+                                      is_nothrow_constructible<
+                                          T,
+                                          std::initializer_list<Up> &,
+                                          Args...>::value)
+        : impl_(in_place_index<I>, il, std::forward<Args>(args)...) {}
+
+    template <
+        typename T,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        std::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline explicit constexpr variant(
+        in_place_type_t<T>,
+        Args &&... args) noexcept(std::is_nothrow_constructible<T,
+                                                                Args...>::value)
+        : impl_(in_place_index<I>, std::forward<Args>(args)...) {}
+
+    template <
+        typename T,
+        typename Up,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        std::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline explicit constexpr variant(
+        in_place_type_t<T>,
+        std::initializer_list<Up> il,
+        Args &&... args) noexcept(std::
+                                      is_nothrow_constructible<
+                                          T,
+                                          std::initializer_list<Up> &,
+                                          Args...>::value)
+        : impl_(in_place_index<I>, il, std::forward<Args>(args)...) {}
+
+    ~variant() = default;
+
+    variant &operator=(const variant &) = default;
+    variant &operator=(variant &&) = default;
+
+    template <typename Arg,
+              std::enable_if_t<!std::is_same<std::decay_t<Arg>, variant>::value,
+                               int> = 0,
+              typename T = detail::best_match_t<Arg, Ts...>,
+              std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+              std::enable_if_t<std::is_assignable<T &, Arg>::value &&
+                                   std::is_constructible<T, Arg>::value,
+                               int> = 0>
+    inline variant &operator=(Arg &&arg) noexcept(
+        (std::is_nothrow_assignable<T &, Arg>::value &&
+         std::is_nothrow_constructible<T, Arg>::value)) {
+      impl_.template assign<I>(std::forward<Arg>(arg));
+      return *this;
+    }
+
+    template <
+        std::size_t I,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        std::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline void emplace(Args &&... args) {
+      impl_.template emplace<I>(std::forward<Args>(args)...);
+    }
+
+    template <
+        std::size_t I,
+        typename Up,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        std::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline void emplace(std::initializer_list<Up> il, Args &&... args) {
+      impl_.template emplace<I>(il, std::forward<Args>(args)...);
+    }
+
+    template <
+        typename T,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        std::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline void emplace(Args &&... args) {
+      impl_.template emplace<I>(std::forward<Args>(args)...);
+    }
+
+    template <
+        typename T,
+        typename Up,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        std::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline void emplace(std::initializer_list<Up> il, Args &&... args) {
+      impl_.template emplace<I>(il, std::forward<Args>(args)...);
+    }
+
+    inline constexpr bool valueless_by_exception() const noexcept {
+      return impl_.valueless_by_exception();
+    }
+
+    inline constexpr std::size_t index() const noexcept {
+      return impl_.index();
+    }
+
+    template <
+        bool Dummy = true,
+        std::enable_if_t<detail::all({Dummy,
+                                      (std::is_move_constructible<Ts>::value &&
+                                       lib::is_swappable<Ts>::value)...}),
+                         int> = 0>
+    inline void swap(variant &that) noexcept(
+        detail::all({(std::is_nothrow_move_constructible<Ts>::value &&
+                      lib::is_nothrow_swappable<Ts>::value)...})) {
+      impl_.swap(that.impl_);
+    }
+
+    private:
+    detail::impl<Ts...> impl_;
+
+    friend struct detail::access::variant;
+    friend struct detail::visitation::variant;
+  };
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept {
+    return v.index() == I;
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept {
+    return holds_alternative<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  namespace detail {
+
+    template <std::size_t I, typename V>
+    inline constexpr auto &&generic_get(V &&v) {
+      return (holds_alternative<I>(v) ? (void)0 : throw bad_variant_access{}),
+             access::variant::get_alt<I>(std::forward<V>(v)).value_;
+    }
+
+  }  // namespace detail
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr variant_alternative_t<I, variant<Ts...>> &get(
+      variant<Ts...> &v) {
+    return detail::generic_get<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr variant_alternative_t<I, variant<Ts...>> &&get(
+      variant<Ts...> &&v) {
+    return detail::generic_get<I>(std::move(v));
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr const variant_alternative_t<I, variant<Ts...>> &get(
+      const variant<Ts...> &v) {
+    return detail::generic_get<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr const variant_alternative_t<I, variant<Ts...>> &&get(
+      const variant<Ts...> &&v) {
+    return detail::generic_get<I>(std::move(v));
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr T &get(variant<Ts...> &v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr T &&get(variant<Ts...> &&v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(std::move(v));
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr const T &get(const variant<Ts...> &v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr const T &&get(const variant<Ts...> &&v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(std::move(v));
+  }
+
+  namespace detail {
+
+    template <std::size_t I, typename V>
+    inline constexpr auto *generic_get_if(V *v) noexcept {
+      return v && holds_alternative<I>(*v)
+                 ? lib::addressof(access::variant::get_alt<I>(*v).value_)
+                 : nullptr;
+    }
+
+  }  // namespace detail
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr std::add_pointer_t<variant_alternative_t<I, variant<Ts...>>>
+  get_if(variant<Ts...> *v) noexcept {
+    return detail::generic_get_if<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr std::add_pointer_t<
+      const variant_alternative_t<I, variant<Ts...>>>
+  get_if(const variant<Ts...> *v) noexcept {
+    return detail::generic_get_if<I>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr std::add_pointer_t<T>
+  get_if(variant<Ts...> *v) noexcept {
+    return get_if<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr std::add_pointer_t<const T>
+  get_if(const variant<Ts...> *v) noexcept {
+    return get_if<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator==(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    if (lhs.index() != rhs.index()) return false;
+    if (lhs.valueless_by_exception()) return true;
+    return variant::visit_value_at(lhs.index(), std::equal_to<>{}, lhs, rhs);
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator!=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    if (lhs.index() != rhs.index()) return true;
+    if (lhs.valueless_by_exception()) return false;
+    return variant::visit_value_at(
+        lhs.index(), std::not_equal_to<>{}, lhs, rhs);
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator<(const variant<Ts...> &lhs,
+                                  const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    if (rhs.valueless_by_exception()) return false;
+    if (lhs.valueless_by_exception()) return true;
+    if (lhs.index() < rhs.index()) return true;
+    if (lhs.index() > rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), std::less<>{}, lhs, rhs);
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator>(const variant<Ts...> &lhs,
+                                  const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    if (lhs.valueless_by_exception()) return false;
+    if (rhs.valueless_by_exception()) return true;
+    if (lhs.index() > rhs.index()) return true;
+    if (lhs.index() < rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), std::greater<>{}, lhs, rhs);
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator<=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    if (lhs.valueless_by_exception()) return true;
+    if (rhs.valueless_by_exception()) return false;
+    if (lhs.index() < rhs.index()) return true;
+    if (lhs.index() > rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), std::less_equal<>{}, lhs, rhs);
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator>=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    if (rhs.valueless_by_exception()) return true;
+    if (lhs.valueless_by_exception()) return false;
+    if (lhs.index() > rhs.index()) return true;
+    if (lhs.index() < rhs.index()) return false;
+    return variant::visit_value_at(
+        lhs.index(), std::greater_equal<>{}, lhs, rhs);
+  }
+
+  template <typename Visitor, typename... Vs>
+  inline constexpr decltype(auto) visit(Visitor &&visitor, Vs &&... vs) {
+    using detail::visitation::variant;
+    return (detail::all({!vs.valueless_by_exception()...})
+                ? (void)0
+                : throw bad_variant_access{}),
+           variant::visit_value(std::forward<Visitor>(visitor),
+                                std::forward<Vs>(vs)...);
+  }
+
+  struct monostate {};
+
+  inline constexpr bool operator<(monostate, monostate) noexcept {
+    return false;
+  }
+
+  inline constexpr bool operator>(monostate, monostate) noexcept {
+    return false;
+  }
+
+  inline constexpr bool operator<=(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator>=(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator==(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator!=(monostate, monostate) noexcept {
+    return false;
+  }
+
+  template <typename... Ts>
+  inline auto swap(variant<Ts...> &lhs,
+                   variant<Ts...> &rhs) noexcept(noexcept(lhs.swap(rhs)))
+      -> decltype(lhs.swap(rhs)) {
+    lhs.swap(rhs);
+  }
+
+}  // namespace mpark
+
+namespace std {
+
+  template <typename... Ts>
+  struct hash<mpark::variant<Ts...>> {
+    using argument_type = mpark::variant<Ts...>;
+    using result_type = std::size_t;
+
+    inline result_type operator()(const argument_type &v) const {
+      using mpark::detail::visitation::variant;
+      std::size_t result =
+          v.valueless_by_exception()
+              ? 299792458  // Random value chosen by the universe upon creation
+              : variant::visit_alt(
+                    [](const auto &alt) {
+                      using alt_type = decay_t<decltype(alt)>;
+                      using value_type = typename alt_type::value_type;
+                      return hash<value_type>{}(alt.value_);
+                    },
+                    v);
+      return hash_combine(result, hash<std::size_t>{}(v.index()));
+    }
+
+    private:
+    static std::size_t hash_combine(std::size_t lhs, std::size_t rhs) {
+      return lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
+    }
+  };
+
+  template <>
+  struct hash<mpark::monostate> {
+    using argument_type = mpark::monostate;
+    using result_type = std::size_t;
+
+    inline result_type operator()(const argument_type &) const {
+      return 66740831;  // return a fundamentally attractive random value.
+    }
+  };
+
+}  // namespace std
+
+#endif  // MPARK_VARIANT_HPP

+ 1798 - 0
src/external/mpark-variant/v1.0.1/variant.hpp

@@ -0,0 +1,1798 @@
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2016
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_VARIANT_HPP
+#define MPARK_VARIANT_HPP
+
+/*
+   variant synopsis
+
+namespace std {
+
+  // 20.7.2, class template variant
+  template <class... Types>
+  class variant {
+  public:
+
+    // 20.7.2.1, constructors
+    constexpr variant() noexcept(see below);
+    variant(const variant&);
+    variant(variant&&) noexcept(see below);
+
+    template <class T> constexpr variant(T&&) noexcept(see below);
+
+    template <class T, class... Args>
+    constexpr explicit variant(in_place_type_t<T>, Args&&...);
+
+    template <class T, class U, class... Args>
+    constexpr explicit variant(
+        in_place_type_t<T>, initializer_list<U>, Args&&...);
+
+    template <size_t I, class... Args>
+    constexpr explicit variant(in_place_index_t<I>, Args&&...);
+
+    template <size_t I, class U, class... Args>
+    constexpr explicit variant(
+        in_place_index_t<I>, initializer_list<U>, Args&&...);
+
+    // 20.7.2.2, destructor
+    ~variant();
+
+    // 20.7.2.3, assignment
+    variant& operator=(const variant&);
+    variant& operator=(variant&&) noexcept(see below);
+
+    template <class T> variant& operator=(T&&) noexcept(see below);
+
+    // 20.7.2.4, modifiers
+    template <class T, class... Args>
+    T& emplace(Args&&...);
+
+    template <class T, class U, class... Args>
+    T& emplace(initializer_list<U>, Args&&...);
+
+    template <size_t I, class... Args>
+    variant_alternative<I, variant>& emplace(Args&&...);
+
+    template <size_t I, class U, class...  Args>
+    variant_alternative<I, variant>& emplace(initializer_list<U>, Args&&...);
+
+    // 20.7.2.5, value status
+    constexpr bool valueless_by_exception() const noexcept;
+    constexpr size_t index() const noexcept;
+
+    // 20.7.2.6, swap
+    void swap(variant&) noexcept(see below);
+  };
+
+  // 20.7.3, variant helper classes
+  template <class T> struct variant_size; // undefined
+
+  template <class T>
+  constexpr size_t variant_size_v = variant_size<T>::value;
+
+  template <class T> struct variant_size<const T>;
+  template <class T> struct variant_size<volatile T>;
+  template <class T> struct variant_size<const volatile T>;
+
+  template <class... Types>
+  struct variant_size<variant<Types...>>;
+
+  template <size_t I, class T> struct variant_alternative; // undefined
+
+  template <size_t I, class T>
+  using variant_alternative_t = typename variant_alternative<I, T>::type;
+
+  template <size_t I, class T> struct variant_alternative<I, const T>;
+  template <size_t I, class T> struct variant_alternative<I, volatile T>;
+  template <size_t I, class T> struct variant_alternative<I, const volatile T>;
+
+  template <size_t I, class... Types>
+  struct variant_alternative<I, variant<Types...>>;
+
+  constexpr size_t variant_npos = -1;
+
+  // 20.7.4, value access
+  template <class T, class... Types>
+  constexpr bool holds_alternative(const variant<Types...>&) noexcept;
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>>&
+  get(variant<Types...>&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>>&&
+  get(variant<Types...>&&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>> const&
+  get(const variant<Types...>&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>> const&&
+  get(const variant<Types...>&&);
+
+  template <class T, class...  Types>
+  constexpr T& get(variant<Types...>&);
+
+  template <class T, class... Types>
+  constexpr T&& get(variant<Types...>&&);
+
+  template <class T, class... Types>
+  constexpr const T& get(const variant<Types...>&);
+
+  template <class T, class... Types>
+  constexpr const T&& get(const variant<Types...>&&);
+
+  template <size_t I, class... Types>
+  constexpr add_pointer_t<variant_alternative_t<I, variant<Types...>>>
+  get_if(variant<Types...>*) noexcept;
+
+  template <size_t I, class... Types>
+  constexpr add_pointer_t<const variant_alternative_t<I, variant<Types...>>>
+  get_if(const variant<Types...>*) noexcept;
+
+  template <class T, class... Types>
+  constexpr add_pointer_t<T>
+  get_if(variant<Types...>*) noexcept;
+
+  template <class T, class... Types>
+  constexpr add_pointer_t<const T>
+  get_if(const variant<Types...>*) noexcept;
+
+  // 20.7.5, relational operators
+  template <class... Types>
+  constexpr bool operator==(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator!=(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator<(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator>(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator<=(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator>=(const variant<Types...>&, const variant<Types...>&);
+
+  // 20.7.6, visitation
+  template <class Visitor, class... Variants>
+  constexpr see below visit(Visitor&&, Variants&&...);
+
+  // 20.7.7, class monostate
+  struct monostate;
+
+  // 20.7.8, monostate relational operators
+  constexpr bool operator<(monostate, monostate) noexcept;
+  constexpr bool operator>(monostate, monostate) noexcept;
+  constexpr bool operator<=(monostate, monostate) noexcept;
+  constexpr bool operator>=(monostate, monostate) noexcept;
+  constexpr bool operator==(monostate, monostate) noexcept;
+  constexpr bool operator!=(monostate, monostate) noexcept;
+
+  // 20.7.9, specialized algorithms
+  template <class... Types>
+  void swap(variant<Types...>&, variant<Types...>&) noexcept(see below);
+
+  // 20.7.10, class bad_variant_access
+  class bad_variant_access;
+
+  // 20.7.11, hash support
+  template <class T> struct hash;
+  template <class... Types> struct hash<variant<Types...>>;
+  template <> struct hash<monostate>;
+
+} // namespace std
+
+*/
+
+#include <array>
+#include <cstddef>
+#include <exception>
+#include <functional>
+#include <initializer_list>
+#include <new>
+#include <type_traits>
+#include <utility>
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2016
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_IN_PLACE_HPP
+#define MPARK_IN_PLACE_HPP
+
+#include <cstddef>
+
+namespace mpark {
+
+  struct in_place_t {
+    explicit in_place_t() = default;
+  };
+
+  constexpr in_place_t in_place{};
+
+  template <std::size_t I>
+  struct in_place_index_t {
+    explicit in_place_index_t() = default;
+  };
+
+  template <std::size_t I>
+  constexpr in_place_index_t<I> in_place_index{};
+
+  template <typename T>
+  struct in_place_type_t {
+    explicit in_place_type_t() = default;
+  };
+
+  template <typename T>
+  constexpr in_place_type_t<T> in_place_type{};
+
+}  // namespace mpark
+
+#endif  // MPARK_IN_PLACE_HPP
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2016
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_LIB_HPP
+#define MPARK_LIB_HPP
+
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2016
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_CONFIG_HPP
+#define MPARK_CONFIG_HPP
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+#if __has_builtin(__builtin_addressof) || __GNUC__ >= 7
+#define MPARK_BUILTIN_ADDRESSOF
+#endif
+
+#if __has_builtin(__type_pack_element)
+#define MPARK_TYPE_PACK_ELEMENT
+#endif
+
+#endif  // MPARK_CONFIG_HPP
+
+
+namespace mpark {
+  namespace lib {
+
+    template <typename T>
+    struct identity { using type = T; };
+
+#ifdef MPARK_TYPE_PACK_ELEMENT
+    template <std::size_t I, typename... Ts>
+    using type_pack_element_t = __type_pack_element<I, Ts...>;
+#else
+    template <std::size_t I, typename... Ts>
+    struct type_pack_element_impl {
+      private:
+      template <std::size_t, typename T>
+      struct indexed_type : identity<T> {};
+
+      template <std::size_t... Is>
+      inline static auto make_set(std::index_sequence<Is...>) {
+        struct set : indexed_type<Is, Ts>... {};
+        return set{};
+      }
+
+      template <typename T>
+      inline static std::enable_if<true, T> impl(indexed_type<I, T>);
+
+      inline static std::enable_if<false> impl(...);
+
+      public:
+      using type = decltype(impl(make_set(std::index_sequence_for<Ts...>{})));
+    };
+
+    template <std::size_t I, typename... Ts>
+    using type_pack_element = typename type_pack_element_impl<I, Ts...>::type;
+
+    template <std::size_t I, typename... Ts>
+    using type_pack_element_t = typename type_pack_element<I, Ts...>::type;
+#endif
+
+    inline namespace cpp17 {
+
+      // <type_traits>
+      template <bool B>
+      using bool_constant = std::integral_constant<bool, B>;
+
+      template <typename...>
+      using void_t = void;
+
+      namespace detail {
+        namespace swappable {
+
+          using std::swap;
+
+          template <typename T>
+          struct is_swappable_impl {
+            private:
+            template <typename U,
+                      typename = decltype(swap(std::declval<U &>(),
+                                               std::declval<U &>()))>
+            inline static std::true_type test(int);
+
+            template <typename U>
+            inline static std::false_type test(...);
+
+            public:
+            using type = decltype(test<T>(0));
+          };
+
+          template <typename T>
+          using is_swappable = typename is_swappable_impl<T>::type;
+
+          template <typename T, bool = is_swappable<T>::value>
+          struct is_nothrow_swappable
+              : bool_constant<noexcept(
+                    swap(std::declval<T &>(), std::declval<T &>()))> {};
+
+          template <typename T>
+          struct is_nothrow_swappable<T, false> : std::false_type {};
+
+        }  // namespace swappable
+      }  // namespace detail
+
+      template <typename T>
+      using is_swappable = detail::swappable::is_swappable<T>;
+
+      template <typename T>
+      using is_nothrow_swappable = detail::swappable::is_nothrow_swappable<T>;
+
+      // <functional>
+#define RETURN(...) -> decltype(__VA_ARGS__) { return __VA_ARGS__; }
+
+      template <typename F, typename... As>
+      inline constexpr auto invoke(F &&f, As &&... as)
+          RETURN(std::forward<F>(f)(std::forward<As>(as)...))
+
+      template <typename B, typename T, typename D>
+      inline constexpr auto invoke(T B::*pmv, D &&d)
+          RETURN(std::forward<D>(d).*pmv)
+
+      template <typename Pmv, typename Ptr>
+      inline constexpr auto invoke(Pmv pmv, Ptr &&ptr)
+          RETURN((*std::forward<Ptr>(ptr)).*pmv)
+
+      template <typename B, typename T, typename D, typename... As>
+      inline constexpr auto invoke(T B::*pmf, D &&d, As &&... as)
+          RETURN((std::forward<D>(d).*pmf)(std::forward<As>(as)...))
+
+      template <typename Pmf, typename Ptr, typename... As>
+      inline constexpr auto invoke(Pmf pmf, Ptr &&ptr, As &&... as)
+          RETURN(((*std::forward<Ptr>(ptr)).*pmf)(std::forward<As>(as)...))
+
+#undef RETURN
+
+      namespace detail {
+
+        template <typename Void, typename, typename...>
+        struct invoke_result {};
+
+        template <typename F, typename... Args>
+        struct invoke_result<void_t<decltype(lib::invoke(
+                                 std::declval<F>(), std::declval<Args>()...))>,
+                             F,
+                             Args...>
+            : identity<decltype(
+                  lib::invoke(std::declval<F>(), std::declval<Args>()...))> {};
+
+      }  // namespace detail
+
+      template <typename F, typename... Args>
+      using invoke_result = detail::invoke_result<void, F, Args...>;
+
+      template <typename F, typename... Args>
+      using invoke_result_t = typename invoke_result<F, Args...>::type;
+
+      namespace detail {
+
+        template <typename Void, typename, typename...>
+        struct is_invocable : std::false_type {};
+
+        template <typename F, typename... Args>
+        struct is_invocable<void_t<invoke_result_t<F, Args...>>, F, Args...>
+            : std::true_type {};
+
+        template <typename Void, typename, typename, typename...>
+        struct is_invocable_r : std::false_type {};
+
+        template <typename R, typename F, typename... Args>
+        struct is_invocable_r<void_t<invoke_result_t<F, Args...>>,
+                              R,
+                              F,
+                              Args...>
+            : std::is_convertible<invoke_result_t<F, Args...>, R> {};
+
+      }  // namespace detail
+
+      template <typename F, typename... Args>
+      using is_invocable = detail::is_invocable<void, F, Args...>;
+
+      template <typename R, typename F, typename... Args>
+      using is_invocable_r = detail::is_invocable_r<void, R, F, Args...>;
+
+      // <memory>
+#ifdef MPARK_BUILTIN_ADDRESSOF
+      template <typename T>
+      inline constexpr T *addressof(T &arg) {
+        return __builtin_addressof(arg);
+      }
+#else
+      namespace detail {
+
+        namespace has_addressof_impl {
+
+          struct fail;
+
+          template <typename T>
+          inline fail operator&(T &&);
+
+          template <typename T>
+          inline static constexpr bool impl() {
+            return (std::is_class<T>::value || std::is_union<T>::value) &&
+                   !std::is_same<decltype(&std::declval<T &>()), fail>::value;
+          }
+
+        }  // namespace has_addressof_impl
+
+        template <typename T>
+        using has_addressof = bool_constant<has_addressof_impl::impl<T>()>;
+
+      }  // namespace detail
+
+      template <typename T,
+                std::enable_if_t<detail::has_addressof<T>::value, int> = 0>
+      inline T *addressof(T &arg) {
+        return std::addressof(arg);
+      }
+
+      template <typename T,
+                std::enable_if_t<!detail::has_addressof<T>::value, int> = 0>
+      inline constexpr T *addressof(T &arg) {
+        return &arg;
+      }
+#endif
+
+      template <typename T>
+      inline const T *addressof(const T &&) = delete;
+
+    }  // namespace cpp17
+
+  }  // namespace lib
+}  // namespace mpark
+
+#endif  // MPARK_LIB_HPP
+
+
+namespace mpark {
+
+  class bad_variant_access : public std::exception {
+    public:
+    virtual const char *what() const noexcept { return "bad_variant_access"; }
+  };
+
+  template <typename... Ts>
+  class variant;
+
+  template <typename T>
+  struct variant_size;
+
+  template <typename T>
+  constexpr std::size_t variant_size_v = variant_size<T>::value;
+
+  template <typename T>
+  struct variant_size<const T> : variant_size<T> {};
+
+  template <typename T>
+  struct variant_size<volatile T> : variant_size<T> {};
+
+  template <typename T>
+  struct variant_size<const volatile T> : variant_size<T> {};
+
+  template <typename... Ts>
+  struct variant_size<variant<Ts...>>
+      : std::integral_constant<std::size_t, sizeof...(Ts)> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative;
+
+  template <std::size_t I, typename T>
+  using variant_alternative_t = typename variant_alternative<I, T>::type;
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, const T>
+      : std::add_const<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, volatile T>
+      : std::add_volatile<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, const volatile T>
+      : std::add_cv<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename... Ts>
+  struct variant_alternative<I, variant<Ts...>>
+      : lib::identity<lib::type_pack_element_t<I, Ts...>> {
+    static_assert(I < sizeof...(Ts),
+                  "`variant_alternative` index out of range.");
+  };
+
+  constexpr std::size_t variant_npos = static_cast<std::size_t>(-1);
+
+  namespace detail {
+
+    inline constexpr bool all(std::initializer_list<bool> bs) {
+      for (bool b : bs) {
+        if (!b) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+    constexpr std::size_t not_found = static_cast<std::size_t>(-1);
+    constexpr std::size_t ambiguous = static_cast<std::size_t>(-2);
+
+    template <typename T, typename... Ts>
+    inline constexpr std::size_t find_index() {
+      constexpr bool matches[] = {std::is_same<T, Ts>::value...};
+      std::size_t result = not_found;
+      for (std::size_t i = 0; i < sizeof...(Ts); ++i) {
+        if (matches[i]) {
+          if (result != not_found) {
+            return ambiguous;
+          }
+          result = i;
+        }
+      }
+      return result;
+    }
+
+    template <std::size_t I>
+    using find_index_sfinae_impl =
+        std::enable_if_t<I != not_found && I != ambiguous,
+                         std::integral_constant<std::size_t, I>>;
+
+    template <typename T, typename... Ts>
+    using find_index_sfinae = find_index_sfinae_impl<find_index<T, Ts...>()>;
+
+    template <std::size_t I>
+    struct find_index_checked_impl : std::integral_constant<std::size_t, I> {
+      static_assert(I != not_found, "the specified type is not found.");
+      static_assert(I != ambiguous, "the specified type is ambiguous.");
+    };
+
+    template <typename T, typename... Ts>
+    using find_index_checked = find_index_checked_impl<find_index<T, Ts...>()>;
+
+    struct valueless_t {};
+
+    enum class Trait { TriviallyAvailable, Available, Unavailable };
+
+    template <typename T,
+              template <typename> class IsTriviallyAvailable,
+              template <typename> class IsAvailable>
+    inline constexpr Trait trait() {
+      return IsTriviallyAvailable<T>::value
+                 ? Trait::TriviallyAvailable
+                 : IsAvailable<T>::value ? Trait::Available
+                                         : Trait::Unavailable;
+    }
+
+    inline constexpr Trait common_trait(std::initializer_list<Trait> traits) {
+      Trait result = Trait::TriviallyAvailable;
+      for (Trait t : traits) {
+        if (static_cast<int>(t) > static_cast<int>(result)) {
+          result = t;
+        }
+      }
+      return result;
+    }
+
+    template <typename... Ts>
+    struct traits {
+      static constexpr Trait copy_constructible_trait =
+          common_trait({trait<Ts,
+                              std::is_trivially_copy_constructible,
+                              std::is_copy_constructible>()...});
+
+      static constexpr Trait move_constructible_trait =
+          common_trait({trait<Ts,
+                              std::is_trivially_move_constructible,
+                              std::is_move_constructible>()...});
+
+      static constexpr Trait copy_assignable_trait =
+          common_trait({copy_constructible_trait,
+                        move_constructible_trait,
+                        trait<Ts,
+                              std::is_trivially_copy_assignable,
+                              std::is_copy_assignable>()...});
+
+      static constexpr Trait move_assignable_trait =
+          common_trait({move_constructible_trait,
+                        trait<Ts,
+                              std::is_trivially_move_assignable,
+                              std::is_move_assignable>()...});
+
+      static constexpr Trait destructible_trait =
+          common_trait({trait<Ts,
+                              std::is_trivially_destructible,
+                              std::is_destructible>()...});
+    };
+
+    namespace access {
+
+      struct recursive_union {
+        template <typename V>
+        inline static constexpr auto &&get_alt(V &&v, in_place_index_t<0>) {
+          return std::forward<V>(v).head_;
+        }
+
+        template <typename V, std::size_t I>
+        inline static constexpr auto &&get_alt(V &&v, in_place_index_t<I>) {
+          return get_alt(std::forward<V>(v).tail_, in_place_index<I - 1>);
+        }
+      };
+
+      struct base {
+        template <std::size_t I, typename V>
+        inline static constexpr auto &&get_alt(V &&v) {
+          return recursive_union::get_alt(std::forward<V>(v).data_,
+                                          in_place_index<I>);
+        }
+      };
+
+      struct variant {
+        template <std::size_t I, typename V>
+        inline static constexpr auto &&get_alt(V &&v) {
+          return base::get_alt<I>(std::forward<V>(v).impl_);
+        }
+      };
+
+    }  // namespace access
+
+    namespace visitation {
+
+      struct base {
+        template <typename Visitor, typename... Vs>
+        inline static constexpr decltype(auto) visit_alt_at(std::size_t index,
+                                                            Visitor &&visitor,
+                                                            Vs &&... vs) {
+          constexpr auto fdiagonal =
+              make_fdiagonal<Visitor &&,
+                             decltype(std::forward<Vs>(vs).as_base())...>();
+          return fdiagonal[index](std::forward<Visitor>(visitor),
+                                  std::forward<Vs>(vs).as_base()...);
+        }
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr decltype(auto) visit_alt(Visitor &&visitor,
+                                                         Vs &&... vs) {
+          constexpr auto fmatrix =
+              make_fmatrix<Visitor &&,
+                           decltype(std::forward<Vs>(vs).as_base())...>();
+          return at(fmatrix, vs.index()...)(std::forward<Visitor>(visitor),
+                                            std::forward<Vs>(vs).as_base()...);
+        }
+
+        private:
+        template <typename T>
+        inline static constexpr const T &at(const T &elem) {
+          return elem;
+        }
+
+        template <typename T, std::size_t N, typename... Is>
+        inline static constexpr auto &&at(const std::array<T, N> &elems,
+                                          std::size_t i,
+                                          Is... is) {
+          return at(elems[i], is...);
+        }
+
+        template <typename F, typename... Fs>
+        inline static constexpr void visit_visitor_return_type_check() {
+          static_assert(all({std::is_same<F, Fs>::value...}),
+                        "`mpark::visit` requires the visitor to have a single "
+                        "return type.");
+        }
+
+        template <typename... Fs>
+        inline static constexpr auto make_farray(Fs &&... fs) {
+          visit_visitor_return_type_check<std::decay_t<Fs>...>();
+          using result = std::array<std::common_type_t<std::decay_t<Fs>...>,
+                                    sizeof...(Fs)>;
+          return result{{std::forward<Fs>(fs)...}};
+        }
+
+        template <std::size_t... Is>
+        struct dispatcher {
+          template <typename F, typename... Vs>
+          inline static constexpr decltype(auto) dispatch(F f, Vs... vs) {
+            return lib::invoke(
+                static_cast<F>(f),
+                access::base::get_alt<Is>(static_cast<Vs>(vs))...);
+          }
+        };
+
+        template <typename F, typename... Vs, std::size_t... Is>
+        inline static constexpr auto make_dispatch(std::index_sequence<Is...>) {
+          return &dispatcher<Is...>::template dispatch<F, Vs...>;
+        }
+
+        template <std::size_t I, typename F, typename... Vs>
+        inline static constexpr auto make_fdiagonal_impl() {
+          return make_dispatch<F, Vs...>(
+              std::index_sequence<(lib::identity<Vs>{}, I)...>{});
+        }
+
+        template <typename F, typename... Vs, std::size_t... Is>
+        inline static constexpr auto make_fdiagonal_impl(
+            std::index_sequence<Is...>) {
+          return base::make_farray(make_fdiagonal_impl<Is, F, Vs...>()...);
+        }
+
+        template <typename F, typename V, typename... Vs>
+        inline static constexpr auto make_fdiagonal() {
+          constexpr std::size_t N = std::decay_t<V>::size();
+          static_assert(all({(N == std::decay_t<Vs>::size())...}),
+                        "all of the variants must be the same size.");
+          return make_fdiagonal_impl<F, V, Vs...>(
+              std::make_index_sequence<N>{});
+        }
+
+        template <typename F, typename... Vs, std::size_t... Is>
+        inline static constexpr auto make_fmatrix_impl(
+            std::index_sequence<Is...> is) {
+          return make_dispatch<F, Vs...>(is);
+        }
+
+        template <typename F,
+                  typename... Vs,
+                  std::size_t... Is,
+                  std::size_t... Js,
+                  typename... Ls>
+        inline static constexpr auto make_fmatrix_impl(
+            std::index_sequence<Is...>, std::index_sequence<Js...>, Ls... ls) {
+          return base::make_farray(make_fmatrix_impl<F, Vs...>(
+              std::index_sequence<Is..., Js>{}, ls...)...);
+        }
+
+        template <typename F, typename... Vs>
+        inline static constexpr auto make_fmatrix() {
+          return make_fmatrix_impl<F, Vs...>(
+              std::index_sequence<>{},
+              std::make_index_sequence<std::decay_t<Vs>::size()>{}...);
+        }
+      };
+
+      struct variant {
+        template <typename Visitor, typename... Vs>
+        inline static constexpr decltype(auto) visit_alt_at(std::size_t index,
+                                                            Visitor &&visitor,
+                                                            Vs &&... vs) {
+          return base::visit_alt_at(index,
+                                    std::forward<Visitor>(visitor),
+                                    std::forward<Vs>(vs).impl_...);
+        }
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr decltype(auto) visit_alt(Visitor &&visitor,
+                                                         Vs &&... vs) {
+          return base::visit_alt(std::forward<Visitor>(visitor),
+                                 std::forward<Vs>(vs).impl_...);
+        }
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr decltype(auto) visit_value_at(std::size_t index,
+                                                              Visitor &&visitor,
+                                                              Vs &&... vs) {
+          return visit_alt_at(
+              index,
+              make_value_visitor(std::forward<Visitor>(visitor)),
+              std::forward<Vs>(vs)...);
+        }
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr decltype(auto) visit_value(Visitor &&visitor,
+                                                           Vs &&... vs) {
+          return visit_alt(make_value_visitor(std::forward<Visitor>(visitor)),
+                           std::forward<Vs>(vs)...);
+        }
+
+        private:
+        template <typename Visitor, typename... Values>
+        inline static constexpr void visit_exhaustive_visitor_check() {
+          static_assert(
+              lib::is_invocable<Visitor, Values...>::value,
+              "`mpark::visit` requires the visitor to be exhaustive.");
+        }
+
+        template <typename Visitor>
+        struct value_visitor {
+          template <typename... Alts>
+          inline constexpr decltype(auto) operator()(Alts &&... alts) const {
+            visit_exhaustive_visitor_check<
+                Visitor,
+                decltype((std::forward<Alts>(alts).value_))...>();
+            return lib::invoke(std::forward<Visitor>(visitor_),
+                               std::forward<Alts>(alts).value_...);
+          }
+          Visitor &&visitor_;
+        };
+
+        template <typename Visitor>
+        inline static constexpr auto make_value_visitor(Visitor &&visitor) {
+          return value_visitor<Visitor>{std::forward<Visitor>(visitor)};
+        }
+      };
+
+    }  // namespace visitation
+
+    template <std::size_t Index, typename T>
+    struct alt {
+      using value_type = T;
+
+      template <typename... Args>
+      inline explicit constexpr alt(in_place_t, Args &&... args)
+          : value_(std::forward<Args>(args)...) {}
+
+      value_type value_;
+    };
+
+    template <Trait DestructibleTrait, std::size_t Index, typename... Ts>
+    union recursive_union;
+
+    template <Trait DestructibleTrait, std::size_t Index>
+    union recursive_union<DestructibleTrait, Index> {};
+
+#define MPARK_VARIANT_RECURSIVE_UNION(destructible_trait, destructor)  \
+  template <std::size_t Index, typename T, typename... Ts>             \
+  union recursive_union<destructible_trait, Index, T, Ts...> {         \
+    public:                                                            \
+    inline explicit constexpr recursive_union(valueless_t) noexcept    \
+        : dummy_{} {}                                                  \
+                                                                       \
+    template <typename... Args>                                        \
+    inline explicit constexpr recursive_union(in_place_index_t<0>,     \
+                                              Args &&... args)         \
+        : head_(in_place, std::forward<Args>(args)...) {}              \
+                                                                       \
+    template <std::size_t I, typename... Args>                         \
+    inline explicit constexpr recursive_union(in_place_index_t<I>,     \
+                                              Args &&... args)         \
+        : tail_(in_place_index<I - 1>, std::forward<Args>(args)...) {} \
+                                                                       \
+    recursive_union(const recursive_union &) = default;                \
+    recursive_union(recursive_union &&) = default;                     \
+                                                                       \
+    destructor                                                         \
+                                                                       \
+    recursive_union &operator=(const recursive_union &) = default;     \
+    recursive_union &operator=(recursive_union &&) = default;          \
+                                                                       \
+    private:                                                           \
+    char dummy_;                                                       \
+    alt<Index, T> head_;                                               \
+    recursive_union<destructible_trait, Index + 1, Ts...> tail_;       \
+                                                                       \
+    friend struct access::recursive_union;                             \
+  }
+
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::TriviallyAvailable,
+                                  ~recursive_union() = default;);
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::Available,
+                                  ~recursive_union() {});
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::Unavailable,
+                                  ~recursive_union() = delete;);
+
+#undef _MPARK_VARIANT_UNION
+
+    using index_t = unsigned int;
+
+    template <Trait DestructibleTrait, typename... Ts>
+    class base {
+      public:
+      inline explicit constexpr base(valueless_t tag) noexcept
+          : data_(tag), index_(static_cast<index_t>(-1)) {}
+
+      template <std::size_t I, typename... Args>
+      inline explicit constexpr base(in_place_index_t<I>, Args &&... args)
+          : data_(in_place_index<I>, std::forward<Args>(args)...), index_(I) {}
+
+      inline constexpr bool valueless_by_exception() const noexcept {
+        return index_ == static_cast<index_t>(-1);
+      }
+
+      inline constexpr std::size_t index() const noexcept {
+        return valueless_by_exception() ? variant_npos : index_;
+      }
+
+      protected:
+      inline constexpr base &as_base() & { return *this; }
+      inline constexpr base &&as_base() && { return std::move(*this); }
+      inline constexpr const base &as_base() const & { return *this; }
+      inline constexpr const base &&as_base() const && { return std::move(*this); }
+
+      inline static constexpr std::size_t size() { return sizeof...(Ts); }
+
+      recursive_union<DestructibleTrait, 0, Ts...> data_;
+      index_t index_;
+
+      friend struct access::base;
+      friend struct visitation::base;
+    };
+
+    template <typename Traits, Trait = Traits::destructible_trait>
+    class destructor;
+
+#define MPARK_VARIANT_DESTRUCTOR(destructible_trait, definition, destroy) \
+  template <typename... Ts>                                               \
+  class destructor<traits<Ts...>, destructible_trait>                     \
+      : public base<destructible_trait, Ts...> {                          \
+    using super = base<destructible_trait, Ts...>;                        \
+                                                                          \
+    public:                                                               \
+    using super::super;                                                   \
+    using super::operator=;                                               \
+                                                                          \
+    destructor(const destructor &) = default;                             \
+    destructor(destructor &&) = default;                                  \
+    definition                                                            \
+    destructor &operator=(const destructor &) = default;                  \
+    destructor &operator=(destructor &&) = default;                       \
+                                                                          \
+    protected:                                                            \
+    inline destroy                                                        \
+  }
+
+    MPARK_VARIANT_DESTRUCTOR(Trait::TriviallyAvailable,
+                             ~destructor() = default;,
+                             void destroy() noexcept {
+                               this->index_ = static_cast<index_t>(-1);
+                             });
+
+    MPARK_VARIANT_DESTRUCTOR(Trait::Available,
+                             ~destructor() { destroy(); },
+                             void destroy() noexcept {
+                               if (!this->valueless_by_exception()) {
+                                 visitation::base::visit_alt(
+                                     [](auto &alt) noexcept {
+                                       using alt_type =
+                                           std::decay_t<decltype(alt)>;
+                                       alt.~alt_type();
+                                     },
+                                     *this);
+                               }
+                               this->index_ = static_cast<index_t>(-1);
+                             });
+
+    MPARK_VARIANT_DESTRUCTOR(Trait::Unavailable,
+                             ~destructor() = delete;,
+                             void destroy() noexcept = delete;);
+
+#undef MPARK_VARIANT_DESTRUCTOR
+
+    template <typename Traits>
+    class constructor : public destructor<Traits> {
+      using super = destructor<Traits>;
+
+      public:
+      using super::super;
+      using super::operator=;
+
+      protected:
+      template <std::size_t I, typename T, typename... Args>
+      inline static T &construct_alt(alt<I, T> &a, Args &&... args) {
+        ::new (static_cast<void *>(lib::addressof(a)))
+            alt<I, T>(in_place, std::forward<Args>(args)...);
+        return a.value_;
+      }
+
+      template <typename Rhs>
+      inline static void generic_construct(constructor &lhs, Rhs &&rhs) {
+        lhs.destroy();
+        if (!rhs.valueless_by_exception()) {
+          visitation::base::visit_alt_at(
+              rhs.index(),
+              [](auto &lhs_alt, auto &&rhs_alt) {
+                constructor::construct_alt(
+                    lhs_alt, std::forward<decltype(rhs_alt)>(rhs_alt).value_);
+              },
+              lhs,
+              std::forward<Rhs>(rhs));
+          lhs.index_ = rhs.index_;
+        }
+      }
+    };
+
+    template <typename Traits, Trait = Traits::move_constructible_trait>
+    class move_constructor;
+
+#define MPARK_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, definition) \
+  template <typename... Ts>                                                  \
+  class move_constructor<traits<Ts...>, move_constructible_trait>            \
+      : public constructor<traits<Ts...>> {                                  \
+    using super = constructor<traits<Ts...>>;                                \
+                                                                             \
+    public:                                                                  \
+    using super::super;                                                      \
+    using super::operator=;                                                  \
+                                                                             \
+    move_constructor(const move_constructor &) = default;                    \
+    definition                                                               \
+    ~move_constructor() = default;                                           \
+    move_constructor &operator=(const move_constructor &) = default;         \
+    move_constructor &operator=(move_constructor &&) = default;              \
+  }
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::TriviallyAvailable,
+        move_constructor(move_constructor &&that) = default;);
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::Available,
+        move_constructor(move_constructor &&that) noexcept(
+            all({std::is_nothrow_move_constructible<Ts>::value...}))
+            : move_constructor(valueless_t{}) {
+          this->generic_construct(*this, std::move(that));
+        });
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::Unavailable,
+        move_constructor(move_constructor &&) = delete;);
+
+#undef MPARK_VARIANT_MOVE_CONSTRUCTOR
+
+    template <typename Traits, Trait = Traits::copy_constructible_trait>
+    class copy_constructor;
+
+#define MPARK_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, definition) \
+  template <typename... Ts>                                                  \
+  class copy_constructor<traits<Ts...>, copy_constructible_trait>            \
+      : public move_constructor<traits<Ts...>> {                             \
+    using super = move_constructor<traits<Ts...>>;                           \
+                                                                             \
+    public:                                                                  \
+    using super::super;                                                      \
+    using super::operator=;                                                  \
+                                                                             \
+    definition                                                               \
+    copy_constructor(copy_constructor &&) = default;                         \
+    ~copy_constructor() = default;                                           \
+    copy_constructor &operator=(const copy_constructor &) = default;         \
+    copy_constructor &operator=(copy_constructor &&) = default;              \
+  }
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::TriviallyAvailable,
+        copy_constructor(const copy_constructor &that) = default;);
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::Available,
+        copy_constructor(const copy_constructor &that)
+            : copy_constructor(valueless_t{}) {
+          this->generic_construct(*this, that);
+        });
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::Unavailable,
+        copy_constructor(const copy_constructor &) = delete;);
+
+#undef MPARK_VARIANT_COPY_CONSTRUCTOR
+
+    template <typename Traits>
+    class assignment : public copy_constructor<Traits> {
+      using super = copy_constructor<Traits>;
+
+      public:
+      using super::super;
+      using super::operator=;
+
+      template <std::size_t I, typename... Args>
+      inline auto &emplace(Args &&... args) {
+        this->destroy();
+        auto &result = this->construct_alt(access::base::get_alt<I>(*this),
+                                           std::forward<Args>(args)...);
+        this->index_ = I;
+        return result;
+      }
+
+      protected:
+      template <bool CopyAssign, std::size_t I, typename T, typename Arg>
+      inline void assign_alt(alt<I, T> &a,
+                             Arg &&arg,
+                             lib::bool_constant<CopyAssign> tag) {
+        if (this->index() == I) {
+          a.value_ = std::forward<Arg>(arg);
+        } else {
+          struct {
+            void operator()(std::true_type) const {
+              this_->emplace<I>(T(std::forward<Arg>(arg_)));
+            }
+            void operator()(std::false_type) const {
+              this_->emplace<I>(std::forward<Arg>(arg_));
+            }
+            assignment *this_;
+            Arg &&arg_;
+          } impl{this, std::forward<Arg>(arg)};
+          impl(tag);
+        }
+      }
+
+      template <typename That>
+      inline void generic_assign(That &&that) {
+        if (this->valueless_by_exception() && that.valueless_by_exception()) {
+          // do nothing.
+        } else if (that.valueless_by_exception()) {
+          this->destroy();
+        } else {
+          visitation::base::visit_alt_at(
+              that.index(),
+              [this](auto &this_alt, auto &&that_alt) {
+                this->assign_alt(
+                    this_alt,
+                    std::forward<decltype(that_alt)>(that_alt).value_,
+                    std::is_lvalue_reference<That>{});
+              },
+              *this,
+              std::forward<That>(that));
+        }
+      }
+    };
+
+    template <typename Traits, Trait = Traits::move_assignable_trait>
+    class move_assignment;
+
+#define MPARK_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, definition) \
+  template <typename... Ts>                                              \
+  class move_assignment<traits<Ts...>, move_assignable_trait>            \
+      : public assignment<traits<Ts...>> {                               \
+    using super = assignment<traits<Ts...>>;                             \
+                                                                         \
+    public:                                                              \
+    using super::super;                                                  \
+    using super::operator=;                                              \
+                                                                         \
+    move_assignment(const move_assignment &) = default;                  \
+    move_assignment(move_assignment &&) = default;                       \
+    ~move_assignment() = default;                                        \
+    move_assignment &operator=(const move_assignment &) = default;       \
+    definition                                                           \
+  }
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::TriviallyAvailable,
+        move_assignment &operator=(move_assignment &&that) = default;);
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::Available,
+        move_assignment &
+        operator=(move_assignment &&that) noexcept(
+            all({std::is_nothrow_move_constructible<Ts>::value &&
+                 std::is_nothrow_move_assignable<Ts>::value...})) {
+          this->generic_assign(std::move(that));
+          return *this;
+        });
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::Unavailable,
+        move_assignment &operator=(move_assignment &&) = delete;);
+
+#undef MPARK_VARIANT_MOVE_ASSIGNMENT
+
+    template <typename Traits, Trait = Traits::copy_assignable_trait>
+    class copy_assignment;
+
+#define MPARK_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, definition) \
+  template <typename... Ts>                                              \
+  class copy_assignment<traits<Ts...>, copy_assignable_trait>            \
+      : public move_assignment<traits<Ts...>> {                          \
+    using super = move_assignment<traits<Ts...>>;                        \
+                                                                         \
+    public:                                                              \
+    using super::super;                                                  \
+    using super::operator=;                                              \
+                                                                         \
+    copy_assignment(const copy_assignment &) = default;                  \
+    copy_assignment(copy_assignment &&) = default;                       \
+    ~copy_assignment() = default;                                        \
+    definition                                                           \
+    copy_assignment &operator=(copy_assignment &&) = default;            \
+  }
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::TriviallyAvailable,
+        copy_assignment &operator=(const copy_assignment &that) = default;);
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::Available,
+        copy_assignment &operator=(const copy_assignment &that) {
+          this->generic_assign(that);
+          return *this;
+        });
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::Unavailable,
+        copy_assignment &operator=(const copy_assignment &) = delete;);
+
+#undef MPARK_VARIANT_COPY_ASSIGNMENT
+
+    template <typename... Ts>
+    class impl : public copy_assignment<traits<Ts...>> {
+      using super = copy_assignment<traits<Ts...>>;
+
+      public:
+      using super::super;
+      using super::operator=;
+
+      template <std::size_t I, typename Arg>
+      inline void assign(Arg &&arg) {
+        this->assign_alt(access::base::get_alt<I>(*this),
+                         std::forward<Arg>(arg),
+                         std::false_type{});
+      }
+
+      inline void swap(impl &that) {
+        if (this->valueless_by_exception() && that.valueless_by_exception()) {
+          // do nothing.
+        } else if (this->index() == that.index()) {
+          visitation::base::visit_alt_at(this->index(),
+                                         [](auto &this_alt, auto &that_alt) {
+                                           using std::swap;
+                                           swap(this_alt.value_,
+                                                that_alt.value_);
+                                         },
+                                         *this,
+                                         that);
+        } else {
+          impl *lhs = this;
+          impl *rhs = lib::addressof(that);
+          if (lhs->move_nothrow() && !rhs->move_nothrow()) {
+            std::swap(lhs, rhs);
+          }
+          impl tmp(std::move(*rhs));
+          // EXTENSION: When the move construction of `lhs` into `rhs` throws
+          // and `tmp` is nothrow move constructible then we move `tmp` back
+          // into `rhs` and provide the strong exception safety guarentee.
+          try {
+            this->generic_construct(*rhs, std::move(*lhs));
+          } catch (...) {
+            if (tmp.move_nothrow()) {
+              this->generic_construct(*rhs, std::move(tmp));
+            }
+            throw;
+          }
+          this->generic_construct(*lhs, std::move(tmp));
+        }
+      }
+
+      private:
+      inline constexpr bool move_nothrow() const {
+        constexpr bool results[] = {
+            std::is_nothrow_move_constructible<Ts>::value...};
+        return this->valueless_by_exception() || results[this->index()];
+      }
+    };
+
+    template <typename... Ts>
+    struct overload;
+
+    template <>
+    struct overload<> {
+      void operator()() const;
+    };
+
+    template <typename T, typename... Ts>
+    struct overload<T, Ts...> : overload<Ts...> {
+      using overload<Ts...>::operator();
+      lib::identity<T> operator()(T) const;
+    };
+
+    template <typename T, typename... Ts>
+    using best_match_t =
+        typename lib::invoke_result_t<overload<Ts...>, T &&>::type;
+
+  }  // detail
+
+  template <typename... Ts>
+  class variant {
+    static_assert(0 < sizeof...(Ts),
+                  "variant must consist of at least one alternative.");
+
+    static_assert(detail::all({!std::is_array<Ts>::value...}),
+                  "variant can not have an array type as an alternative.");
+
+    static_assert(detail::all({!std::is_reference<Ts>::value...}),
+                  "variant can not have a reference type as an alternative.");
+
+    static_assert(detail::all({!std::is_void<Ts>::value...}),
+                  "variant can not have a void type as an alternative.");
+
+    public:
+    template <
+        typename Front = lib::type_pack_element_t<0, Ts...>,
+        std::enable_if_t<std::is_default_constructible<Front>::value, int> = 0>
+    inline constexpr variant() noexcept(
+        std::is_nothrow_default_constructible<Front>::value)
+        : impl_(in_place_index<0>) {}
+
+    variant(const variant &) = default;
+    variant(variant &&) = default;
+
+    template <typename Arg,
+              std::enable_if_t<!std::is_same<std::decay_t<Arg>, variant>::value,
+                               int> = 0,
+              typename T = detail::best_match_t<Arg, Ts...>,
+              std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+              std::enable_if_t<std::is_constructible<T, Arg>::value, int> = 0>
+    inline constexpr variant(Arg &&arg) noexcept(
+        std::is_nothrow_constructible<T, Arg>::value)
+        : impl_(in_place_index<I>, std::forward<Arg>(arg)) {}
+
+    template <
+        std::size_t I,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        std::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline explicit constexpr variant(
+        in_place_index_t<I>,
+        Args &&... args) noexcept(std::is_nothrow_constructible<T,
+                                                                Args...>::value)
+        : impl_(in_place_index<I>, std::forward<Args>(args)...) {}
+
+    template <
+        std::size_t I,
+        typename Up,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        std::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline explicit constexpr variant(
+        in_place_index_t<I>,
+        std::initializer_list<Up> il,
+        Args &&... args) noexcept(std::
+                                      is_nothrow_constructible<
+                                          T,
+                                          std::initializer_list<Up> &,
+                                          Args...>::value)
+        : impl_(in_place_index<I>, il, std::forward<Args>(args)...) {}
+
+    template <
+        typename T,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        std::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline explicit constexpr variant(
+        in_place_type_t<T>,
+        Args &&... args) noexcept(std::is_nothrow_constructible<T,
+                                                                Args...>::value)
+        : impl_(in_place_index<I>, std::forward<Args>(args)...) {}
+
+    template <
+        typename T,
+        typename Up,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        std::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline explicit constexpr variant(
+        in_place_type_t<T>,
+        std::initializer_list<Up> il,
+        Args &&... args) noexcept(std::
+                                      is_nothrow_constructible<
+                                          T,
+                                          std::initializer_list<Up> &,
+                                          Args...>::value)
+        : impl_(in_place_index<I>, il, std::forward<Args>(args)...) {}
+
+    ~variant() = default;
+
+    variant &operator=(const variant &) = default;
+    variant &operator=(variant &&) = default;
+
+    template <typename Arg,
+              std::enable_if_t<!std::is_same<std::decay_t<Arg>, variant>::value,
+                               int> = 0,
+              typename T = detail::best_match_t<Arg, Ts...>,
+              std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+              std::enable_if_t<std::is_assignable<T &, Arg>::value &&
+                                   std::is_constructible<T, Arg>::value,
+                               int> = 0>
+    inline variant &operator=(Arg &&arg) noexcept(
+        (std::is_nothrow_assignable<T &, Arg>::value &&
+         std::is_nothrow_constructible<T, Arg>::value)) {
+      impl_.template assign<I>(std::forward<Arg>(arg));
+      return *this;
+    }
+
+    template <
+        std::size_t I,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        std::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline T &emplace(Args &&... args) {
+      return impl_.template emplace<I>(std::forward<Args>(args)...);
+    }
+
+    template <
+        std::size_t I,
+        typename Up,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        std::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline T &emplace(std::initializer_list<Up> il, Args &&... args) {
+      return impl_.template emplace<I>(il, std::forward<Args>(args)...);
+    }
+
+    template <
+        typename T,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        std::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline T &emplace(Args &&... args) {
+      return impl_.template emplace<I>(std::forward<Args>(args)...);
+    }
+
+    template <
+        typename T,
+        typename Up,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        std::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline T &emplace(std::initializer_list<Up> il, Args &&... args) {
+      return impl_.template emplace<I>(il, std::forward<Args>(args)...);
+    }
+
+    inline constexpr bool valueless_by_exception() const noexcept {
+      return impl_.valueless_by_exception();
+    }
+
+    inline constexpr std::size_t index() const noexcept {
+      return impl_.index();
+    }
+
+    template <
+        bool Dummy = true,
+        std::enable_if_t<detail::all({Dummy,
+                                      (std::is_move_constructible<Ts>::value &&
+                                       lib::is_swappable<Ts>::value)...}),
+                         int> = 0>
+    inline void swap(variant &that) noexcept(
+        detail::all({(std::is_nothrow_move_constructible<Ts>::value &&
+                      lib::is_nothrow_swappable<Ts>::value)...})) {
+      impl_.swap(that.impl_);
+    }
+
+    private:
+    detail::impl<Ts...> impl_;
+
+    friend struct detail::access::variant;
+    friend struct detail::visitation::variant;
+  };
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept {
+    return v.index() == I;
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept {
+    return holds_alternative<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  namespace detail {
+
+    template <std::size_t I, typename V>
+    inline constexpr auto &&generic_get(V &&v) {
+      return (holds_alternative<I>(v) ? (void)0 : throw bad_variant_access{}),
+             access::variant::get_alt<I>(std::forward<V>(v)).value_;
+    }
+
+  }  // namespace detail
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr variant_alternative_t<I, variant<Ts...>> &get(
+      variant<Ts...> &v) {
+    return detail::generic_get<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr variant_alternative_t<I, variant<Ts...>> &&get(
+      variant<Ts...> &&v) {
+    return detail::generic_get<I>(std::move(v));
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr const variant_alternative_t<I, variant<Ts...>> &get(
+      const variant<Ts...> &v) {
+    return detail::generic_get<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr const variant_alternative_t<I, variant<Ts...>> &&get(
+      const variant<Ts...> &&v) {
+    return detail::generic_get<I>(std::move(v));
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr T &get(variant<Ts...> &v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr T &&get(variant<Ts...> &&v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(std::move(v));
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr const T &get(const variant<Ts...> &v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr const T &&get(const variant<Ts...> &&v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(std::move(v));
+  }
+
+  namespace detail {
+
+    template <std::size_t I, typename V>
+    inline constexpr auto *generic_get_if(V *v) noexcept {
+      return v && holds_alternative<I>(*v)
+                 ? lib::addressof(access::variant::get_alt<I>(*v).value_)
+                 : nullptr;
+    }
+
+  }  // namespace detail
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr std::add_pointer_t<variant_alternative_t<I, variant<Ts...>>>
+  get_if(variant<Ts...> *v) noexcept {
+    return detail::generic_get_if<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr std::add_pointer_t<
+      const variant_alternative_t<I, variant<Ts...>>>
+  get_if(const variant<Ts...> *v) noexcept {
+    return detail::generic_get_if<I>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr std::add_pointer_t<T>
+  get_if(variant<Ts...> *v) noexcept {
+    return get_if<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr std::add_pointer_t<const T>
+  get_if(const variant<Ts...> *v) noexcept {
+    return get_if<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator==(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    if (lhs.index() != rhs.index()) return false;
+    if (lhs.valueless_by_exception()) return true;
+    return variant::visit_value_at(lhs.index(), std::equal_to<>{}, lhs, rhs);
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator!=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    if (lhs.index() != rhs.index()) return true;
+    if (lhs.valueless_by_exception()) return false;
+    return variant::visit_value_at(
+        lhs.index(), std::not_equal_to<>{}, lhs, rhs);
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator<(const variant<Ts...> &lhs,
+                                  const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    if (rhs.valueless_by_exception()) return false;
+    if (lhs.valueless_by_exception()) return true;
+    if (lhs.index() < rhs.index()) return true;
+    if (lhs.index() > rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), std::less<>{}, lhs, rhs);
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator>(const variant<Ts...> &lhs,
+                                  const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    if (lhs.valueless_by_exception()) return false;
+    if (rhs.valueless_by_exception()) return true;
+    if (lhs.index() > rhs.index()) return true;
+    if (lhs.index() < rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), std::greater<>{}, lhs, rhs);
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator<=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    if (lhs.valueless_by_exception()) return true;
+    if (rhs.valueless_by_exception()) return false;
+    if (lhs.index() < rhs.index()) return true;
+    if (lhs.index() > rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), std::less_equal<>{}, lhs, rhs);
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator>=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    if (rhs.valueless_by_exception()) return true;
+    if (lhs.valueless_by_exception()) return false;
+    if (lhs.index() > rhs.index()) return true;
+    if (lhs.index() < rhs.index()) return false;
+    return variant::visit_value_at(
+        lhs.index(), std::greater_equal<>{}, lhs, rhs);
+  }
+
+  template <typename Visitor, typename... Vs>
+  inline constexpr decltype(auto) visit(Visitor &&visitor, Vs &&... vs) {
+    using detail::visitation::variant;
+    return (detail::all({!vs.valueless_by_exception()...})
+                ? (void)0
+                : throw bad_variant_access{}),
+           variant::visit_value(std::forward<Visitor>(visitor),
+                                std::forward<Vs>(vs)...);
+  }
+
+  struct monostate {};
+
+  inline constexpr bool operator<(monostate, monostate) noexcept {
+    return false;
+  }
+
+  inline constexpr bool operator>(monostate, monostate) noexcept {
+    return false;
+  }
+
+  inline constexpr bool operator<=(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator>=(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator==(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator!=(monostate, monostate) noexcept {
+    return false;
+  }
+
+  template <typename... Ts>
+  inline auto swap(variant<Ts...> &lhs,
+                   variant<Ts...> &rhs) noexcept(noexcept(lhs.swap(rhs)))
+      -> decltype(lhs.swap(rhs)) {
+    lhs.swap(rhs);
+  }
+
+  namespace detail {
+
+    template <typename T, typename...>
+    using enabled_type = T;
+
+    namespace hash {
+
+      template <typename Hash, typename Key>
+      constexpr bool meets_requirements() {
+        return std::is_copy_constructible<Hash>::value &&
+               std::is_move_constructible<Hash>::value &&
+               lib::is_invocable_r<std::size_t, Hash, const Key &>::value;
+      }
+
+      template <typename Key>
+      constexpr bool is_enabled() {
+        using Hash = std::hash<Key>;
+        return meets_requirements<Hash, Key>() &&
+               std::is_default_constructible<Hash>::value &&
+               std::is_copy_assignable<Hash>::value &&
+               std::is_move_assignable<Hash>::value;
+      }
+
+    }  // namespace hash
+
+  }  // namespace detail
+
+}  // namespace mpark
+
+namespace std {
+
+  template <typename... Ts>
+  struct hash<mpark::detail::enabled_type<
+      mpark::variant<Ts...>,
+      std::enable_if_t<mpark::detail::all(
+          {mpark::detail::hash::is_enabled<std::remove_const_t<Ts>>()...})>>> {
+    using argument_type = mpark::variant<Ts...>;
+    using result_type = std::size_t;
+
+    inline result_type operator()(const argument_type &v) const {
+      using mpark::detail::visitation::variant;
+      std::size_t result =
+          v.valueless_by_exception()
+              ? 299792458  // Random value chosen by the universe upon creation
+              : variant::visit_alt(
+                    [](const auto &alt) {
+                      using alt_type = decay_t<decltype(alt)>;
+                      using value_type =
+                          std::remove_const_t<typename alt_type::value_type>;
+                      return hash<value_type>{}(alt.value_);
+                    },
+                    v);
+      return hash_combine(result, hash<std::size_t>{}(v.index()));
+    }
+
+    private:
+    static std::size_t hash_combine(std::size_t lhs, std::size_t rhs) {
+      return lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
+    }
+  };
+
+  template <>
+  struct hash<mpark::monostate> {
+    using argument_type = mpark::monostate;
+    using result_type = std::size_t;
+
+    inline result_type operator()(const argument_type &) const noexcept {
+      return 66740831;  // return a fundamentally attractive random value.
+    }
+  };
+
+}  // namespace std
+
+#endif  // MPARK_VARIANT_HPP

+ 2360 - 0
src/external/mpark-variant/v1.1.0/variant.hpp

@@ -0,0 +1,2360 @@
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_VARIANT_HPP
+#define MPARK_VARIANT_HPP
+
+/*
+   variant synopsis
+
+namespace std {
+
+  // 20.7.2, class template variant
+  template <class... Types>
+  class variant {
+  public:
+
+    // 20.7.2.1, constructors
+    constexpr variant() noexcept(see below);
+    variant(const variant&);
+    variant(variant&&) noexcept(see below);
+
+    template <class T> constexpr variant(T&&) noexcept(see below);
+
+    template <class T, class... Args>
+    constexpr explicit variant(in_place_type_t<T>, Args&&...);
+
+    template <class T, class U, class... Args>
+    constexpr explicit variant(
+        in_place_type_t<T>, initializer_list<U>, Args&&...);
+
+    template <size_t I, class... Args>
+    constexpr explicit variant(in_place_index_t<I>, Args&&...);
+
+    template <size_t I, class U, class... Args>
+    constexpr explicit variant(
+        in_place_index_t<I>, initializer_list<U>, Args&&...);
+
+    // 20.7.2.2, destructor
+    ~variant();
+
+    // 20.7.2.3, assignment
+    variant& operator=(const variant&);
+    variant& operator=(variant&&) noexcept(see below);
+
+    template <class T> variant& operator=(T&&) noexcept(see below);
+
+    // 20.7.2.4, modifiers
+    template <class T, class... Args>
+    T& emplace(Args&&...);
+
+    template <class T, class U, class... Args>
+    T& emplace(initializer_list<U>, Args&&...);
+
+    template <size_t I, class... Args>
+    variant_alternative<I, variant>& emplace(Args&&...);
+
+    template <size_t I, class U, class...  Args>
+    variant_alternative<I, variant>& emplace(initializer_list<U>, Args&&...);
+
+    // 20.7.2.5, value status
+    constexpr bool valueless_by_exception() const noexcept;
+    constexpr size_t index() const noexcept;
+
+    // 20.7.2.6, swap
+    void swap(variant&) noexcept(see below);
+  };
+
+  // 20.7.3, variant helper classes
+  template <class T> struct variant_size; // undefined
+
+  template <class T>
+  constexpr size_t variant_size_v = variant_size<T>::value;
+
+  template <class T> struct variant_size<const T>;
+  template <class T> struct variant_size<volatile T>;
+  template <class T> struct variant_size<const volatile T>;
+
+  template <class... Types>
+  struct variant_size<variant<Types...>>;
+
+  template <size_t I, class T> struct variant_alternative; // undefined
+
+  template <size_t I, class T>
+  using variant_alternative_t = typename variant_alternative<I, T>::type;
+
+  template <size_t I, class T> struct variant_alternative<I, const T>;
+  template <size_t I, class T> struct variant_alternative<I, volatile T>;
+  template <size_t I, class T> struct variant_alternative<I, const volatile T>;
+
+  template <size_t I, class... Types>
+  struct variant_alternative<I, variant<Types...>>;
+
+  constexpr size_t variant_npos = -1;
+
+  // 20.7.4, value access
+  template <class T, class... Types>
+  constexpr bool holds_alternative(const variant<Types...>&) noexcept;
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>>&
+  get(variant<Types...>&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>>&&
+  get(variant<Types...>&&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>> const&
+  get(const variant<Types...>&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>> const&&
+  get(const variant<Types...>&&);
+
+  template <class T, class...  Types>
+  constexpr T& get(variant<Types...>&);
+
+  template <class T, class... Types>
+  constexpr T&& get(variant<Types...>&&);
+
+  template <class T, class... Types>
+  constexpr const T& get(const variant<Types...>&);
+
+  template <class T, class... Types>
+  constexpr const T&& get(const variant<Types...>&&);
+
+  template <size_t I, class... Types>
+  constexpr add_pointer_t<variant_alternative_t<I, variant<Types...>>>
+  get_if(variant<Types...>*) noexcept;
+
+  template <size_t I, class... Types>
+  constexpr add_pointer_t<const variant_alternative_t<I, variant<Types...>>>
+  get_if(const variant<Types...>*) noexcept;
+
+  template <class T, class... Types>
+  constexpr add_pointer_t<T>
+  get_if(variant<Types...>*) noexcept;
+
+  template <class T, class... Types>
+  constexpr add_pointer_t<const T>
+  get_if(const variant<Types...>*) noexcept;
+
+  // 20.7.5, relational operators
+  template <class... Types>
+  constexpr bool operator==(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator!=(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator<(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator>(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator<=(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator>=(const variant<Types...>&, const variant<Types...>&);
+
+  // 20.7.6, visitation
+  template <class Visitor, class... Variants>
+  constexpr see below visit(Visitor&&, Variants&&...);
+
+  // 20.7.7, class monostate
+  struct monostate;
+
+  // 20.7.8, monostate relational operators
+  constexpr bool operator<(monostate, monostate) noexcept;
+  constexpr bool operator>(monostate, monostate) noexcept;
+  constexpr bool operator<=(monostate, monostate) noexcept;
+  constexpr bool operator>=(monostate, monostate) noexcept;
+  constexpr bool operator==(monostate, monostate) noexcept;
+  constexpr bool operator!=(monostate, monostate) noexcept;
+
+  // 20.7.9, specialized algorithms
+  template <class... Types>
+  void swap(variant<Types...>&, variant<Types...>&) noexcept(see below);
+
+  // 20.7.10, class bad_variant_access
+  class bad_variant_access;
+
+  // 20.7.11, hash support
+  template <class T> struct hash;
+  template <class... Types> struct hash<variant<Types...>>;
+  template <> struct hash<monostate>;
+
+} // namespace std
+
+*/
+
+#include <cstddef>
+#include <exception>
+#include <functional>
+#include <initializer_list>
+#include <new>
+#include <type_traits>
+#include <utility>
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_IN_PLACE_HPP
+#define MPARK_IN_PLACE_HPP
+
+#include <cstddef>
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_VARIANTS_CONFIG_HPP
+#define MPARK_VARIANTS_CONFIG_HPP
+
+// MSVC 2015 Update 3.
+#if __cplusplus < 201103L && (!defined(_MSC_VER) || _MSC_FULL_VER < 190024215)
+#error "MPark.Variant requires C++11 support."
+#endif
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+#if __has_builtin(__builtin_addressof) || __GNUC__ >= 7 || defined(_MSC_VER)
+#define MPARK_BUILTIN_ADDRESSOF
+#endif
+
+#if __has_builtin(__type_pack_element)
+#define MPARK_TYPE_PACK_ELEMENT
+#endif
+
+#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304
+#define MPARK_CPP14_CONSTEXPR
+#endif
+
+#if defined(__cpp_generic_lambdas) || defined(_MSC_VER)
+#define MPARK_GENERIC_LAMBDAS
+#endif
+
+#if defined(__cpp_lib_integer_sequence)
+#define MPARK_INTEGER_SEQUENCE
+#endif
+
+#if defined(__cpp_return_type_deduction) || defined(_MSC_VER)
+#define MPARK_RETURN_TYPE_DEDUCTION
+#endif
+
+#if defined(__cpp_lib_transparent_operators) || defined(_MSC_VER)
+#define MPARK_TRANSPARENT_OPERATORS
+#endif
+
+#if defined(__cpp_variable_templates) || defined(_MSC_VER)
+#define MPARK_VARIABLE_TEMPLATES
+#endif
+
+#endif  // MPARK_VARIANTS_CONFIG_HPP
+
+
+namespace mpark {
+
+  struct in_place_t { explicit in_place_t() = default; };
+
+  template <std::size_t I>
+  struct in_place_index_t { explicit in_place_index_t() = default; };
+
+  template <typename T>
+  struct in_place_type_t { explicit in_place_type_t() = default; };
+
+#ifdef MPARK_VARIABLE_TEMPLATES
+  constexpr in_place_t in_place{};
+
+  template <std::size_t I> constexpr in_place_index_t<I> in_place_index{};
+
+  template <typename T> constexpr in_place_type_t<T> in_place_type{};
+#endif
+
+}  // namespace mpark
+
+#endif  // MPARK_IN_PLACE_HPP
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_VARIANTS_LIB_HPP
+#define MPARK_VARIANTS_LIB_HPP
+
+#include <memory>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+
+#define RETURN(...)                                          \
+  noexcept(noexcept(__VA_ARGS__)) -> decltype(__VA_ARGS__) { \
+    return __VA_ARGS__;                                      \
+  }
+
+namespace mpark {
+  namespace variants {
+    namespace lib {
+
+      template <typename T>
+      struct identity { using type = T; };
+
+      inline namespace cpp14 {
+        template <typename T, std::size_t N>
+        struct array {
+          constexpr const T &operator[](std::size_t index) const {
+            return data[index];
+          }
+
+          T data[N == 0 ? 1 : N];
+        };
+
+        template <typename T>
+        using add_pointer_t = typename std::add_pointer<T>::type;
+
+        template <typename... Ts>
+        using common_type_t = typename std::common_type<Ts...>::type;
+
+        template <typename T>
+        using decay_t = typename std::decay<T>::type;
+
+        template <bool B, typename T = void>
+        using enable_if_t = typename std::enable_if<B, T>::type;
+
+        template <typename T>
+        using remove_const_t = typename std::remove_const<T>::type;
+
+        template <typename T>
+        using remove_reference_t = typename std::remove_reference<T>::type;
+
+        template <typename T>
+        inline constexpr T &&forward(remove_reference_t<T> &t) noexcept {
+          return static_cast<T &&>(t);
+        }
+
+        template <typename T>
+        inline constexpr T &&forward(remove_reference_t<T> &&t) noexcept {
+          static_assert(!std::is_lvalue_reference<T>::value,
+                        "can not forward an rvalue as an lvalue");
+          return static_cast<T &&>(t);
+        }
+
+        template <typename T>
+        constexpr remove_reference_t<T> &&move(T &&t) noexcept {
+          return static_cast<remove_reference_t<T> &&>(t);
+        }
+
+#ifdef MPARK_INTEGER_SEQUENCE
+        template <typename T, T... Is>
+        using integer_sequence = std::integer_sequence<T, Is...>;
+
+        template <std::size_t... Is>
+        using index_sequence = std::index_sequence<Is...>;
+
+        template <std::size_t N>
+        using make_index_sequence = std::make_index_sequence<N>;
+
+        template <typename... Ts>
+        using index_sequence_for = std::index_sequence_for<Ts...>;
+#else
+        template <typename T, T... Is>
+        struct integer_sequence {
+          using value_type = T;
+          static constexpr std::size_t size() noexcept { return sizeof...(Is); }
+        };
+
+        template <std::size_t... Is>
+        using index_sequence = integer_sequence<std::size_t, Is...>;
+
+        template <typename Lhs, typename Rhs>
+        struct make_index_sequence_concat;
+
+        template <std::size_t... Lhs, std::size_t... Rhs>
+        struct make_index_sequence_concat<index_sequence<Lhs...>,
+                                          index_sequence<Rhs...>>
+            : identity<index_sequence<Lhs..., (sizeof...(Lhs) + Rhs)...>> {};
+
+        template <std::size_t N>
+        struct make_index_sequence_impl;
+
+        template <std::size_t N>
+        using make_index_sequence = typename make_index_sequence_impl<N>::type;
+
+        template <std::size_t N>
+        struct make_index_sequence_impl
+            : make_index_sequence_concat<make_index_sequence<N / 2>,
+                                         make_index_sequence<N - (N / 2)>> {};
+
+        template <>
+        struct make_index_sequence_impl<0> : identity<index_sequence<>> {};
+
+        template <>
+        struct make_index_sequence_impl<1> : identity<index_sequence<0>> {};
+
+        template <typename... Ts>
+        using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
+#endif
+
+        // <functional>
+#ifdef MPARK_TRANSPARENT_OPERATORS
+        using equal_to = std::equal_to<>;
+#else
+        struct equal_to {
+          template <typename Lhs, typename Rhs>
+          inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+            RETURN(lib::forward<Lhs>(lhs) == lib::forward<Rhs>(rhs))
+        };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+        using not_equal_to = std::not_equal_to<>;
+#else
+        struct not_equal_to {
+          template <typename Lhs, typename Rhs>
+          inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+            RETURN(lib::forward<Lhs>(lhs) != lib::forward<Rhs>(rhs))
+        };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+        using less = std::less<>;
+#else
+        struct less {
+          template <typename Lhs, typename Rhs>
+          inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+            RETURN(lib::forward<Lhs>(lhs) < lib::forward<Rhs>(rhs))
+        };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+        using greater = std::greater<>;
+#else
+        struct greater {
+          template <typename Lhs, typename Rhs>
+          inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+            RETURN(lib::forward<Lhs>(lhs) > lib::forward<Rhs>(rhs))
+        };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+        using less_equal = std::less_equal<>;
+#else
+        struct less_equal {
+          template <typename Lhs, typename Rhs>
+          inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+            RETURN(lib::forward<Lhs>(lhs) <= lib::forward<Rhs>(rhs))
+        };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+        using greater_equal = std::greater_equal<>;
+#else
+        struct greater_equal {
+          template <typename Lhs, typename Rhs>
+          inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+            RETURN(lib::forward<Lhs>(lhs) >= lib::forward<Rhs>(rhs))
+        };
+#endif
+      }  // namespace cpp14
+
+      inline namespace cpp17 {
+
+        // <type_traits>
+        template <bool B>
+        using bool_constant = std::integral_constant<bool, B>;
+
+        template <typename...>
+        using void_t = void;
+
+        namespace detail {
+          namespace swappable {
+
+            using std::swap;
+
+            template <typename T>
+            struct is_swappable_impl {
+              private:
+              template <typename U,
+                        typename = decltype(swap(std::declval<U &>(),
+                                                 std::declval<U &>()))>
+              inline static std::true_type test(int);
+
+              template <typename U>
+              inline static std::false_type test(...);
+
+              public:
+              using type = decltype(test<T>(0));
+            };
+
+            template <typename T>
+            using is_swappable = typename is_swappable_impl<T>::type;
+
+            template <typename T, bool = is_swappable<T>::value>
+            struct is_nothrow_swappable {
+              static constexpr bool value =
+                  noexcept(swap(std::declval<T &>(), std::declval<T &>()));
+            };
+
+            template <typename T>
+            struct is_nothrow_swappable<T, false> : std::false_type {};
+
+          }  // namespace swappable
+        }  // namespace detail
+
+        template <typename T>
+        using is_swappable = detail::swappable::is_swappable<T>;
+
+        template <typename T>
+        using is_nothrow_swappable = detail::swappable::is_nothrow_swappable<T>;
+
+        // <functional>
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+        template <typename F, typename... As>
+        inline constexpr auto invoke(F &&f, As &&... as)
+            RETURN(lib::forward<F>(f)(lib::forward<As>(as)...))
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+        template <typename B, typename T, typename D>
+        inline constexpr auto invoke(T B::*pmv, D &&d)
+            RETURN(lib::forward<D>(d).*pmv)
+
+        template <typename Pmv, typename Ptr>
+        inline constexpr auto invoke(Pmv pmv, Ptr &&ptr)
+            RETURN((*lib::forward<Ptr>(ptr)).*pmv)
+
+        template <typename B, typename T, typename D, typename... As>
+        inline constexpr auto invoke(T B::*pmf, D &&d, As &&... as)
+            RETURN((lib::forward<D>(d).*pmf)(lib::forward<As>(as)...))
+
+        template <typename Pmf, typename Ptr, typename... As>
+        inline constexpr auto invoke(Pmf pmf, Ptr &&ptr, As &&... as)
+            RETURN(((*lib::forward<Ptr>(ptr)).*pmf)(lib::forward<As>(as)...))
+
+        namespace detail {
+
+          template <typename Void, typename, typename...>
+          struct invoke_result {};
+
+          template <typename F, typename... Args>
+          struct invoke_result<void_t<decltype(lib::invoke(
+                                   std::declval<F>(), std::declval<Args>()...))>,
+                               F,
+                               Args...>
+              : identity<decltype(
+                    lib::invoke(std::declval<F>(), std::declval<Args>()...))> {};
+
+        }  // namespace detail
+
+        template <typename F, typename... Args>
+        using invoke_result = detail::invoke_result<void, F, Args...>;
+
+        template <typename F, typename... Args>
+        using invoke_result_t = typename invoke_result<F, Args...>::type;
+
+        namespace detail {
+
+          template <typename Void, typename, typename...>
+          struct is_invocable : std::false_type {};
+
+          template <typename F, typename... Args>
+          struct is_invocable<void_t<invoke_result_t<F, Args...>>, F, Args...>
+              : std::true_type {};
+
+          template <typename Void, typename, typename, typename...>
+          struct is_invocable_r : std::false_type {};
+
+          template <typename R, typename F, typename... Args>
+          struct is_invocable_r<void_t<invoke_result_t<F, Args...>>,
+                                R,
+                                F,
+                                Args...>
+              : std::is_convertible<invoke_result_t<F, Args...>, R> {};
+
+        }  // namespace detail
+
+        template <typename F, typename... Args>
+        using is_invocable = detail::is_invocable<void, F, Args...>;
+
+        template <typename R, typename F, typename... Args>
+        using is_invocable_r = detail::is_invocable_r<void, R, F, Args...>;
+
+        // <memory>
+#ifdef MPARK_BUILTIN_ADDRESSOF
+        template <typename T>
+        inline constexpr T *addressof(T &arg) {
+          return __builtin_addressof(arg);
+        }
+#else
+        namespace detail {
+
+          namespace has_addressof_impl {
+
+            struct fail;
+
+            template <typename T>
+            inline fail operator&(T &&);
+
+            template <typename T>
+            inline static constexpr bool impl() {
+              return (std::is_class<T>::value || std::is_union<T>::value) &&
+                     !std::is_same<decltype(&std::declval<T &>()), fail>::value;
+            }
+
+          }  // namespace has_addressof_impl
+
+          template <typename T>
+          using has_addressof = bool_constant<has_addressof_impl::impl<T>()>;
+
+          template <typename T>
+          inline constexpr T *addressof(T &arg, std::true_type) {
+            return std::addressof(arg);
+          }
+
+          template <typename T>
+          inline constexpr T *addressof(T &arg, std::false_type) {
+            return &arg;
+          }
+
+        }  // namespace detail
+
+        template <typename T>
+        inline constexpr T *addressof(T &arg) {
+          return detail::addressof(arg, detail::has_addressof<T>{});
+        }
+#endif
+
+        template <typename T>
+        inline constexpr T *addressof(const T &&) = delete;
+
+      }  // namespace cpp17
+
+      template <typename T>
+      struct remove_all_extents : identity<T> {};
+
+      template <typename T, std::size_t N>
+      struct remove_all_extents<array<T, N>> : remove_all_extents<T> {};
+
+      template <typename T>
+      using remove_all_extents_t = typename remove_all_extents<T>::type;
+
+      template <std::size_t N>
+      using size_constant = std::integral_constant<std::size_t, N>;
+
+      template <bool... Bs>
+      using bool_sequence = integer_sequence<bool, Bs...>;
+
+      template <std::size_t I, typename T>
+      struct indexed_type : size_constant<I>, identity<T> {};
+
+      template <bool... Bs>
+      using all =
+          std::is_same<bool_sequence<true, Bs...>, bool_sequence<Bs..., true>>;
+
+#ifdef MPARK_TYPE_PACK_ELEMENT
+      template <std::size_t I, typename... Ts>
+      using type_pack_element_t = __type_pack_element<I, Ts...>;
+#else
+      template <std::size_t I, typename... Ts>
+      struct type_pack_element_impl {
+        private:
+        template <typename>
+        struct set;
+
+        template <std::size_t... Is>
+        struct set<index_sequence<Is...>> : indexed_type<Is, Ts>... {};
+
+        template <typename T>
+        inline static std::enable_if<true, T> impl(indexed_type<I, T>);
+
+        inline static std::enable_if<false> impl(...);
+
+        public:
+        using type = decltype(impl(set<index_sequence_for<Ts...>>{}));
+      };
+
+      template <std::size_t I, typename... Ts>
+      using type_pack_element = typename type_pack_element_impl<I, Ts...>::type;
+
+      template <std::size_t I, typename... Ts>
+      using type_pack_element_t = typename type_pack_element<I, Ts...>::type;
+#endif
+
+    }  // namespace lib
+  }  // namespace variants
+}  // namespace mpark
+
+#undef RETURN
+
+#endif  // MPARK_VARIANTS_LIB_HPP
+
+
+namespace mpark {
+
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+
+#define AUTO auto
+#define AUTO_RETURN(...) { return __VA_ARGS__; }
+
+#define AUTO_REFREF auto &&
+#define AUTO_REFREF_RETURN(...) { return __VA_ARGS__; }
+
+#define DECLTYPE_AUTO decltype(auto)
+#define DECLTYPE_AUTO_RETURN(...) { return __VA_ARGS__; }
+
+#else
+
+#define AUTO auto
+#define AUTO_RETURN(...)                                    \
+  -> mpark::variants::lib::decay_t<decltype(__VA_ARGS__)> { \
+    return __VA_ARGS__;                                     \
+  }
+
+#define AUTO_REFREF auto
+#define AUTO_REFREF_RETURN(...)                                           \
+  -> decltype((__VA_ARGS__)) {                                            \
+    static_assert(std::is_reference<decltype((__VA_ARGS__))>::value, ""); \
+    return __VA_ARGS__;                                                   \
+  }
+
+#define DECLTYPE_AUTO auto
+#define DECLTYPE_AUTO_RETURN(...) \
+  -> decltype(__VA_ARGS__) { return __VA_ARGS__; }
+
+#endif
+
+  class bad_variant_access : public std::exception {
+    public:
+    virtual const char *what() const noexcept { return "bad_variant_access"; }
+  };
+
+  template <typename... Ts>
+  class variant;
+
+  template <typename T>
+  struct variant_size;
+
+#ifdef MPARK_VARIABLE_TEMPLATES
+  template <typename T>
+  constexpr std::size_t variant_size_v = variant_size<T>::value;
+#endif
+
+  template <typename T>
+  struct variant_size<const T> : variant_size<T> {};
+
+  template <typename T>
+  struct variant_size<volatile T> : variant_size<T> {};
+
+  template <typename T>
+  struct variant_size<const volatile T> : variant_size<T> {};
+
+  template <typename... Ts>
+  struct variant_size<variant<Ts...>>
+      : variants::lib::size_constant<sizeof...(Ts)> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative;
+
+  template <std::size_t I, typename T>
+  using variant_alternative_t = typename variant_alternative<I, T>::type;
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, const T>
+      : std::add_const<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, volatile T>
+      : std::add_volatile<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, const volatile T>
+      : std::add_cv<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename... Ts>
+  struct variant_alternative<I, variant<Ts...>>
+      : variants::lib::identity<variants::lib::type_pack_element_t<I, Ts...>> {
+    static_assert(I < sizeof...(Ts),
+                  "`variant_alternative` index out of range.");
+  };
+
+  constexpr std::size_t variant_npos = static_cast<std::size_t>(-1);
+
+  namespace detail {
+
+    inline constexpr bool all() { return true; }
+
+    template <typename... Bs>
+    inline constexpr bool all(bool b, Bs... bs) {
+      return b && all(bs...);
+    }
+
+    constexpr std::size_t not_found = static_cast<std::size_t>(-1);
+    constexpr std::size_t ambiguous = static_cast<std::size_t>(-2);
+
+#ifdef MPARK_CPP14_CONSTEXPR
+    template <typename T, typename... Ts>
+    inline constexpr std::size_t find_index() {
+      constexpr variants::lib::array<bool, sizeof...(Ts)> matches = {
+          {std::is_same<T, Ts>::value...}
+      };
+      std::size_t result = not_found;
+      for (std::size_t i = 0; i < sizeof...(Ts); ++i) {
+        if (matches[i]) {
+          if (result != not_found) {
+            return ambiguous;
+          }
+          result = i;
+        }
+      }
+      return result;
+    }
+#else
+    inline constexpr std::size_t find_index_impl(std::size_t result,
+                                                 std::size_t) {
+      return result;
+    }
+
+    template <typename... Bs>
+    inline constexpr std::size_t find_index_impl(std::size_t result,
+                                                 std::size_t idx,
+                                                 bool b,
+                                                 Bs... bs) {
+      return b ? (result != not_found ? ambiguous
+                                      : find_index_impl(idx, idx + 1, bs...))
+               : find_index_impl(result, idx + 1, bs...);
+    }
+
+    template <typename T, typename... Ts>
+    inline constexpr std::size_t find_index() {
+      return find_index_impl(not_found, 0, std::is_same<T, Ts>::value...);
+    }
+#endif
+
+    template <std::size_t I>
+    using find_index_sfinae_impl =
+        variants::lib::enable_if_t<I != not_found && I != ambiguous,
+                                   variants::lib::size_constant<I>>;
+
+    template <typename T, typename... Ts>
+    using find_index_sfinae = find_index_sfinae_impl<find_index<T, Ts...>()>;
+
+    template <std::size_t I>
+    struct find_index_checked_impl : variants::lib::size_constant<I> {
+      static_assert(I != not_found, "the specified type is not found.");
+      static_assert(I != ambiguous, "the specified type is ambiguous.");
+    };
+
+    template <typename T, typename... Ts>
+    using find_index_checked = find_index_checked_impl<find_index<T, Ts...>()>;
+
+    struct valueless_t {};
+
+    enum class Trait { TriviallyAvailable, Available, Unavailable };
+
+    template <typename T,
+              template <typename> class IsTriviallyAvailable,
+              template <typename> class IsAvailable>
+    inline constexpr Trait trait() {
+      return IsTriviallyAvailable<T>::value
+                 ? Trait::TriviallyAvailable
+                 : IsAvailable<T>::value ? Trait::Available
+                                         : Trait::Unavailable;
+    }
+
+#ifdef MPARK_CPP14_CONSTEXPR
+    template <typename... Traits>
+    inline constexpr Trait common_trait(Traits... traits) {
+      Trait result = Trait::TriviallyAvailable;
+      for (Trait t : {traits...}) {
+        if (static_cast<int>(t) > static_cast<int>(result)) {
+          result = t;
+        }
+      }
+      return result;
+    }
+#else
+    inline constexpr Trait common_trait_impl(Trait result) { return result; }
+
+    template <typename... Traits>
+    inline constexpr Trait common_trait_impl(Trait result,
+                                             Trait t,
+                                             Traits... ts) {
+      return static_cast<int>(t) > static_cast<int>(result)
+                 ? common_trait_impl(t, ts...)
+                 : common_trait_impl(result, ts...);
+    }
+
+    template <typename... Traits>
+    inline constexpr Trait common_trait(Traits... ts) {
+      return common_trait_impl(Trait::TriviallyAvailable, ts...);
+    }
+#endif
+
+    template <typename... Ts>
+    struct traits {
+      static constexpr Trait copy_constructible_trait =
+          common_trait(trait<Ts,
+                             std::is_trivially_copy_constructible,
+                             std::is_copy_constructible>()...);
+
+      static constexpr Trait move_constructible_trait =
+          common_trait(trait<Ts,
+                             std::is_trivially_move_constructible,
+                             std::is_move_constructible>()...);
+
+      static constexpr Trait copy_assignable_trait =
+          common_trait(copy_constructible_trait,
+                       move_constructible_trait,
+                       trait<Ts,
+                             std::is_trivially_copy_assignable,
+                             std::is_copy_assignable>()...);
+
+      static constexpr Trait move_assignable_trait =
+          common_trait(move_constructible_trait,
+                       trait<Ts,
+                             std::is_trivially_move_assignable,
+                             std::is_move_assignable>()...);
+
+      static constexpr Trait destructible_trait =
+          common_trait(trait<Ts,
+                             std::is_trivially_destructible,
+                             std::is_destructible>()...);
+    };
+
+    namespace access {
+
+      struct recursive_union {
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+        template <typename V>
+        inline static constexpr auto &&get_alt(V &&v, in_place_index_t<0>) {
+          return variants::lib::forward<V>(v).head_;
+        }
+
+        template <typename V, std::size_t I>
+        inline static constexpr auto &&get_alt(V &&v, in_place_index_t<I>) {
+          return get_alt(variants::lib::forward<V>(v).tail_,
+                         in_place_index_t<I - 1>{});
+        }
+#else
+        template <std::size_t I, bool Dummy = true>
+        struct get_alt_impl {
+          template <typename V>
+          inline constexpr AUTO_REFREF operator()(V &&v) const
+            AUTO_REFREF_RETURN(
+                get_alt_impl<I - 1>{}(variants::lib::forward<V>(v).tail_))
+        };
+
+        template <bool Dummy>
+        struct get_alt_impl<0, Dummy> {
+          template <typename V>
+          inline constexpr AUTO_REFREF operator()(V &&v) const
+            AUTO_REFREF_RETURN(variants::lib::forward<V>(v).head_)
+        };
+
+        template <typename V, std::size_t I>
+        inline static constexpr AUTO_REFREF get_alt(V &&v,
+                                                    in_place_index_t<I>)
+          AUTO_REFREF_RETURN(get_alt_impl<I>{}(variants::lib::forward<V>(v)))
+#endif
+      };
+
+      struct base {
+        template <std::size_t I, typename V>
+        inline static constexpr AUTO_REFREF get_alt(V &&v)
+          AUTO_REFREF_RETURN(recursive_union::get_alt(
+              variants::lib::forward<V>(v).data_, in_place_index_t<I>{}))
+      };
+
+      struct variant {
+        template <std::size_t I, typename V>
+        inline static constexpr AUTO_REFREF get_alt(V &&v)
+          AUTO_REFREF_RETURN(
+              base::get_alt<I>(variants::lib::forward<V>(v).impl_))
+      };
+
+    }  // namespace access
+
+    namespace visitation {
+
+      struct base {
+        private:
+        template <typename T>
+        inline static constexpr const T &at(const T &elem) {
+          return elem;
+        }
+
+        template <typename T, std::size_t N, typename... Is>
+        inline static constexpr const variants::lib::remove_all_extents_t<T> &
+        at(const variants::lib::array<T, N> &elems, std::size_t i, Is... is) {
+          return at(elems[i], is...);
+        }
+
+        template <typename F, typename... Fs>
+        inline static constexpr int visit_visitor_return_type_check() {
+          static_assert(all(std::is_same<F, Fs>::value...),
+                        "`mpark::visit` requires the visitor to have a single "
+                        "return type.");
+          return 0;
+        }
+
+        template <typename... Fs>
+        inline static constexpr variants::lib::array<
+            variants::lib::common_type_t<variants::lib::decay_t<Fs>...>,
+            sizeof...(Fs)>
+        make_farray(Fs &&... fs) {
+          using result = variants::lib::array<
+              variants::lib::common_type_t<variants::lib::decay_t<Fs>...>,
+              sizeof...(Fs)>;
+          return visit_visitor_return_type_check<
+                     variants::lib::decay_t<Fs>...>(),
+                 result{{variants::lib::forward<Fs>(fs)...}};
+        }
+
+        template <std::size_t... Is>
+        struct dispatcher {
+          template <typename F, typename... Vs>
+          struct impl {
+            inline static constexpr DECLTYPE_AUTO dispatch(F f, Vs... vs)
+              DECLTYPE_AUTO_RETURN(variants::lib::invoke(
+                  static_cast<F>(f),
+                  access::base::get_alt<Is>(static_cast<Vs>(vs))...))
+          };
+        };
+
+        template <typename F, typename... Vs, std::size_t... Is>
+        inline static constexpr AUTO make_dispatch(
+            variants::lib::index_sequence<Is...>)
+          AUTO_RETURN(&dispatcher<Is...>::template impl<F, Vs...>::dispatch)
+
+        template <std::size_t I, typename F, typename... Vs>
+        inline static constexpr AUTO make_fdiagonal_impl()
+          AUTO_RETURN(make_dispatch<F, Vs...>(
+              variants::lib::index_sequence<
+                  variants::lib::indexed_type<I, Vs>::value...>{}))
+
+        template <typename F, typename... Vs, std::size_t... Is>
+        inline static constexpr AUTO make_fdiagonal_impl(
+            variants::lib::index_sequence<Is...>)
+          AUTO_RETURN(make_farray(make_fdiagonal_impl<Is, F, Vs...>()...))
+
+        template <typename F, typename V, typename... Vs>
+        inline static constexpr /* auto * */ auto make_fdiagonal()
+            -> decltype(make_fdiagonal_impl<F, V, Vs...>(
+                variants::lib::make_index_sequence<
+                    variants::lib::decay_t<V>::size()>{})) {
+          static_assert(all((variants::lib::decay_t<V>::size() ==
+                             variants::lib::decay_t<Vs>::size())...),
+                        "all of the variants must be the same size.");
+          return make_fdiagonal_impl<F, V, Vs...>(
+              variants::lib::make_index_sequence<
+                  variants::lib::decay_t<V>::size()>{});
+        }
+
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+        template <typename F, typename... Vs, std::size_t... Is>
+        inline static constexpr auto make_fmatrix_impl(
+            variants::lib::index_sequence<Is...> is) {
+          return make_dispatch<F, Vs...>(is);
+        }
+
+        template <typename F,
+                  typename... Vs,
+                  std::size_t... Is,
+                  std::size_t... Js,
+                  typename... Ls>
+        inline static constexpr auto make_fmatrix_impl(
+            variants::lib::index_sequence<Is...>,
+            variants::lib::index_sequence<Js...>,
+            Ls... ls) {
+          return make_farray(make_fmatrix_impl<F, Vs...>(
+              variants::lib::index_sequence<Is..., Js>{}, ls...)...);
+        }
+
+        template <typename F, typename... Vs>
+        inline static constexpr auto make_fmatrix() {
+          return make_fmatrix_impl<F, Vs...>(
+              variants::lib::index_sequence<>{},
+              variants::lib::make_index_sequence<
+                  variants::lib::decay_t<Vs>::size()>{}...);
+        }
+#else
+        template <typename F, typename... Vs>
+        struct make_fmatrix_impl {
+          template <typename...>
+          struct impl;
+
+          template <std::size_t... Is>
+          struct impl<variants::lib::index_sequence<Is...>> {
+            inline constexpr AUTO operator()() const
+              AUTO_RETURN(make_dispatch<F, Vs...>(
+                  variants::lib::index_sequence<Is...>{}))
+          };
+
+          template <std::size_t... Is, std::size_t... Js, typename... Ls>
+          struct impl<variants::lib::index_sequence<Is...>,
+                      variants::lib::index_sequence<Js...>,
+                      Ls...> {
+            inline constexpr AUTO operator()() const
+              AUTO_RETURN(
+                  make_farray(impl<variants::lib::index_sequence<Is..., Js>,
+                                   Ls...>{}()...))
+          };
+        };
+
+        template <typename F, typename... Vs>
+        inline static constexpr AUTO make_fmatrix()
+          AUTO_RETURN(typename make_fmatrix_impl<F, Vs...>::template impl<
+                      variants::lib::index_sequence<>,
+                      variants::lib::make_index_sequence<
+                          variants::lib::decay_t<Vs>::size()>...>{}())
+#endif
+
+        public:
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index,
+                                                           Visitor &&visitor,
+                                                           Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(at(
+              make_fdiagonal<Visitor &&,
+                             decltype(
+                                 as_base(variants::lib::forward<Vs>(vs)))...>(),
+              index)(variants::lib::forward<Visitor>(visitor),
+                     as_base(variants::lib::forward<Vs>(vs))...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor,
+                                                        Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(at(
+              make_fmatrix<Visitor &&,
+                           decltype(
+                               as_base(variants::lib::forward<Vs>(vs)))...>(),
+              vs.index()...)(variants::lib::forward<Visitor>(visitor),
+                             as_base(variants::lib::forward<Vs>(vs))...))
+      };
+
+      struct variant {
+        private:
+        template <typename Visitor, typename... Values>
+        struct visit_exhaustive_visitor_check {
+          static_assert(
+              variants::lib::is_invocable<Visitor, Values...>::value,
+              "`mpark::visit` requires the visitor to be exhaustive.");
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+          inline constexpr DECLTYPE_AUTO operator()(Visitor &&visitor,
+                                                    Values &&... values) const
+            DECLTYPE_AUTO_RETURN(variants::lib::invoke(
+                variants::lib::forward<Visitor>(visitor),
+                variants::lib::forward<Values>(values)...))
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+        };
+
+        template <typename Visitor>
+        struct value_visitor {
+          Visitor &&visitor_;
+
+          template <typename... Alts>
+          inline constexpr DECLTYPE_AUTO operator()(Alts &&... alts) const
+            DECLTYPE_AUTO_RETURN(
+                visit_exhaustive_visitor_check<
+                    Visitor,
+                    decltype((variants::lib::forward<Alts>(alts).value))...>{}(
+                    variants::lib::forward<Visitor>(visitor_),
+                    variants::lib::forward<Alts>(alts).value...))
+        };
+
+        template <typename Visitor>
+        inline static constexpr AUTO make_value_visitor(Visitor &&visitor)
+          AUTO_RETURN(
+              value_visitor<Visitor>{variants::lib::forward<Visitor>(visitor)})
+
+        public:
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index,
+                                                           Visitor &&visitor,
+                                                           Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              base::visit_alt_at(index,
+                                 variants::lib::forward<Visitor>(visitor),
+                                 variants::lib::forward<Vs>(vs).impl_...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor,
+                                                        Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              base::visit_alt(variants::lib::forward<Visitor>(visitor),
+                              variants::lib::forward<Vs>(vs).impl_...));
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_value_at(std::size_t index,
+                                                             Visitor &&visitor,
+                                                             Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(visit_alt_at(
+              index,
+              make_value_visitor(variants::lib::forward<Visitor>(visitor)),
+              variants::lib::forward<Vs>(vs)...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_value(Visitor &&visitor,
+                                                          Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(visit_alt(
+              make_value_visitor(variants::lib::forward<Visitor>(visitor)),
+              variants::lib::forward<Vs>(vs)...))
+      };
+
+    }  // namespace visitation
+
+    template <std::size_t Index, typename T>
+    struct alt {
+      using value_type = T;
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4244)
+#endif
+      template <typename... Args>
+      inline explicit constexpr alt(in_place_t, Args &&... args)
+          : value(variants::lib::forward<Args>(args)...) {}
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+      T value;
+    };
+
+    template <Trait DestructibleTrait, std::size_t Index, typename... Ts>
+    union recursive_union;
+
+    template <Trait DestructibleTrait, std::size_t Index>
+    union recursive_union<DestructibleTrait, Index> {};
+
+#define MPARK_VARIANT_RECURSIVE_UNION(destructible_trait, destructor)   \
+  template <std::size_t Index, typename T, typename... Ts>              \
+  union recursive_union<destructible_trait, Index, T, Ts...> {          \
+    public:                                                             \
+    inline explicit constexpr recursive_union(valueless_t) noexcept     \
+        : dummy_{} {}                                                   \
+                                                                        \
+    template <typename... Args>                                         \
+    inline explicit constexpr recursive_union(in_place_index_t<0>,      \
+                                              Args &&... args)          \
+        : head_(in_place_t{}, variants::lib::forward<Args>(args)...) {} \
+                                                                        \
+    template <std::size_t I, typename... Args>                          \
+    inline explicit constexpr recursive_union(in_place_index_t<I>,      \
+                                              Args &&... args)          \
+        : tail_(in_place_index_t<I - 1>{},                              \
+                variants::lib::forward<Args>(args)...) {}               \
+                                                                        \
+    recursive_union(const recursive_union &) = default;                 \
+    recursive_union(recursive_union &&) = default;                      \
+                                                                        \
+    destructor                                                          \
+                                                                        \
+    recursive_union &operator=(const recursive_union &) = default;      \
+    recursive_union &operator=(recursive_union &&) = default;           \
+                                                                        \
+    private:                                                            \
+    char dummy_;                                                        \
+    alt<Index, T> head_;                                                \
+    recursive_union<destructible_trait, Index + 1, Ts...> tail_;        \
+                                                                        \
+    friend struct access::recursive_union;                              \
+  }
+
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::TriviallyAvailable,
+                                  ~recursive_union() = default;);
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::Available,
+                                  ~recursive_union() {});
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::Unavailable,
+                                  ~recursive_union() = delete;);
+
+#undef MPARK_VARIANT_RECURSIVE_UNION
+
+    using index_t = unsigned int;
+
+    template <Trait DestructibleTrait, typename... Ts>
+    class base {
+      public:
+      inline explicit constexpr base(valueless_t tag) noexcept
+          : data_(tag), index_(static_cast<index_t>(-1)) {}
+
+      template <std::size_t I, typename... Args>
+      inline explicit constexpr base(in_place_index_t<I>, Args &&... args)
+          : data_(in_place_index_t<I>{}, variants::lib::forward<Args>(args)...),
+            index_(I) {}
+
+      inline constexpr bool valueless_by_exception() const noexcept {
+        return index_ == static_cast<index_t>(-1);
+      }
+
+      inline constexpr std::size_t index() const noexcept {
+        return valueless_by_exception() ? variant_npos : index_;
+      }
+
+      protected:
+      friend inline constexpr base &as_base(base &b) { return b; }
+      friend inline constexpr const base &as_base(const base &b) { return b; }
+      friend inline constexpr base &&as_base(base &&b) {
+        return variants::lib::move(b);
+      }
+      friend inline constexpr const base &&as_base(const base &&b) {
+        return variants::lib::move(b);
+      }
+
+      inline static constexpr std::size_t size() { return sizeof...(Ts); }
+
+      recursive_union<DestructibleTrait, 0, Ts...> data_;
+      index_t index_;
+
+      friend struct access::base;
+      friend struct visitation::base;
+    };
+
+    struct dtor {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+      template <typename Alt>
+      inline void operator()(Alt &alt) const noexcept { alt.~Alt(); }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+    };
+
+#if defined(_MSC_VER) && _MSC_VER < 1910
+#define INHERITING_CTOR(type, base)               \
+  template <typename... Args>                     \
+  inline explicit constexpr type(Args &&... args) \
+      : base(variants::lib::forward<Args>(args)...) {}
+#else
+#define INHERITING_CTOR(type, base) using base::base;
+#endif
+
+    template <typename Traits, Trait = Traits::destructible_trait>
+    class destructor;
+
+#define MPARK_VARIANT_DESTRUCTOR(destructible_trait, definition, destroy) \
+  template <typename... Ts>                                               \
+  class destructor<traits<Ts...>, destructible_trait>                     \
+      : public base<destructible_trait, Ts...> {                          \
+    using super = base<destructible_trait, Ts...>;                        \
+                                                                          \
+    public:                                                               \
+    INHERITING_CTOR(destructor, super)                                    \
+    using super::operator=;                                               \
+                                                                          \
+    destructor(const destructor &) = default;                             \
+    destructor(destructor &&) = default;                                  \
+    definition                                                            \
+    destructor &operator=(const destructor &) = default;                  \
+    destructor &operator=(destructor &&) = default;                       \
+                                                                          \
+    protected:                                                            \
+    destroy                                                               \
+  }
+
+    MPARK_VARIANT_DESTRUCTOR(
+        Trait::TriviallyAvailable,
+        ~destructor() = default;,
+        inline void destroy() noexcept {
+          this->index_ = static_cast<index_t>(-1);
+        });
+
+    MPARK_VARIANT_DESTRUCTOR(
+        Trait::Available,
+        ~destructor() { destroy(); },
+        inline void destroy() noexcept {
+          if (!this->valueless_by_exception()) {
+            visitation::base::visit_alt(dtor{}, *this);
+          }
+          this->index_ = static_cast<index_t>(-1);
+        });
+
+    MPARK_VARIANT_DESTRUCTOR(
+        Trait::Unavailable,
+        ~destructor() = delete;,
+        inline void destroy() noexcept = delete;);
+
+#undef MPARK_VARIANT_DESTRUCTOR
+
+    template <typename Traits>
+    class constructor : public destructor<Traits> {
+      using super = destructor<Traits>;
+
+      public:
+      INHERITING_CTOR(constructor, super)
+      using super::operator=;
+
+      protected:
+#ifndef MPARK_GENERIC_LAMBDAS
+      struct ctor {
+        template <typename LhsAlt, typename RhsAlt>
+        inline void operator()(LhsAlt &lhs_alt, RhsAlt &&rhs_alt) const {
+          constructor::construct_alt(
+              lhs_alt, variants::lib::forward<RhsAlt>(rhs_alt).value);
+        }
+      };
+#endif
+
+      template <std::size_t I, typename T, typename... Args>
+      inline static T &construct_alt(alt<I, T> &a, Args &&... args) {
+        ::new (static_cast<void *>(variants::lib::addressof(a)))
+            alt<I, T>(in_place_t{}, variants::lib::forward<Args>(args)...);
+        return a.value;
+      }
+
+      template <typename Rhs>
+      inline static void generic_construct(constructor &lhs, Rhs &&rhs) {
+        lhs.destroy();
+        if (!rhs.valueless_by_exception()) {
+          visitation::base::visit_alt_at(
+              rhs.index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+              [](auto &lhs_alt, auto &&rhs_alt) {
+                constructor::construct_alt(
+                    lhs_alt,
+                    variants::lib::forward<decltype(rhs_alt)>(rhs_alt).value);
+              }
+#else
+              ctor{}
+#endif
+              ,
+              lhs,
+              variants::lib::forward<Rhs>(rhs));
+          lhs.index_ = rhs.index_;
+        }
+      }
+    };
+
+    template <typename Traits, Trait = Traits::move_constructible_trait>
+    class move_constructor;
+
+#define MPARK_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, definition) \
+  template <typename... Ts>                                                  \
+  class move_constructor<traits<Ts...>, move_constructible_trait>            \
+      : public constructor<traits<Ts...>> {                                  \
+    using super = constructor<traits<Ts...>>;                                \
+                                                                             \
+    public:                                                                  \
+    INHERITING_CTOR(move_constructor, super)                                 \
+    using super::operator=;                                                  \
+                                                                             \
+    move_constructor(const move_constructor &) = default;                    \
+    definition                                                               \
+    ~move_constructor() = default;                                           \
+    move_constructor &operator=(const move_constructor &) = default;         \
+    move_constructor &operator=(move_constructor &&) = default;              \
+  }
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::TriviallyAvailable,
+        move_constructor(move_constructor &&that) = default;);
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::Available,
+        move_constructor(move_constructor &&that) noexcept(
+            all(std::is_nothrow_move_constructible<Ts>::value...))
+            : move_constructor(valueless_t{}) {
+          this->generic_construct(*this, variants::lib::move(that));
+        });
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::Unavailable,
+        move_constructor(move_constructor &&) = delete;);
+
+#undef MPARK_VARIANT_MOVE_CONSTRUCTOR
+
+    template <typename Traits, Trait = Traits::copy_constructible_trait>
+    class copy_constructor;
+
+#define MPARK_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, definition) \
+  template <typename... Ts>                                                  \
+  class copy_constructor<traits<Ts...>, copy_constructible_trait>            \
+      : public move_constructor<traits<Ts...>> {                             \
+    using super = move_constructor<traits<Ts...>>;                           \
+                                                                             \
+    public:                                                                  \
+    INHERITING_CTOR(copy_constructor, super)                                 \
+    using super::operator=;                                                  \
+                                                                             \
+    definition                                                               \
+    copy_constructor(copy_constructor &&) = default;                         \
+    ~copy_constructor() = default;                                           \
+    copy_constructor &operator=(const copy_constructor &) = default;         \
+    copy_constructor &operator=(copy_constructor &&) = default;              \
+  }
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::TriviallyAvailable,
+        copy_constructor(const copy_constructor &that) = default;);
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::Available,
+        copy_constructor(const copy_constructor &that)
+            : copy_constructor(valueless_t{}) {
+          this->generic_construct(*this, that);
+        });
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::Unavailable,
+        copy_constructor(const copy_constructor &) = delete;);
+
+#undef MPARK_VARIANT_COPY_CONSTRUCTOR
+
+    template <typename Traits>
+    class assignment : public copy_constructor<Traits> {
+      using super = copy_constructor<Traits>;
+
+      public:
+      INHERITING_CTOR(assignment, super)
+      using super::operator=;
+
+      template <std::size_t I, typename... Args>
+      inline /* auto & */ auto emplace(Args &&... args) -> decltype(
+          this->construct_alt(access::base::get_alt<I>(*this),
+                              variants::lib::forward<Args>(args)...)) {
+        this->destroy();
+        auto &result =
+            this->construct_alt(access::base::get_alt<I>(*this),
+                                variants::lib::forward<Args>(args)...);
+        this->index_ = I;
+        return result;
+      }
+
+      protected:
+#ifndef MPARK_GENERIC_LAMBDAS
+      template <typename That>
+      struct assigner {
+        template <typename ThisAlt, typename ThatAlt>
+        inline void operator()(ThisAlt &this_alt, ThatAlt &&that_alt) const {
+          self->assign_alt(this_alt,
+                           variants::lib::forward<ThatAlt>(that_alt).value,
+                           std::is_lvalue_reference<That>{});
+        }
+        assignment *self;
+      };
+#endif
+
+      template <bool CopyAssign, std::size_t I, typename T, typename Arg>
+      inline void assign_alt(alt<I, T> &a,
+                             Arg &&arg,
+                             variants::lib::bool_constant<CopyAssign> tag) {
+        if (this->index() == I) {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4244)
+#endif
+          a.value = variants::lib::forward<Arg>(arg);
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+        } else {
+          struct {
+            void operator()(std::true_type) const {
+              this_->emplace<I>(T(variants::lib::forward<Arg>(arg_)));
+            }
+            void operator()(std::false_type) const {
+              this_->emplace<I>(variants::lib::forward<Arg>(arg_));
+            }
+            assignment *this_;
+            Arg &&arg_;
+          } impl{this, variants::lib::forward<Arg>(arg)};
+          impl(tag);
+        }
+      }
+
+      template <typename That>
+      inline void generic_assign(That &&that) {
+        if (this->valueless_by_exception() && that.valueless_by_exception()) {
+          // do nothing.
+        } else if (that.valueless_by_exception()) {
+          this->destroy();
+        } else {
+          visitation::base::visit_alt_at(
+              that.index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+              [this](auto &this_alt, auto &&that_alt) {
+                this->assign_alt(
+                    this_alt,
+                    variants::lib::forward<decltype(that_alt)>(that_alt).value,
+                    std::is_lvalue_reference<That>{});
+              }
+#else
+              assigner<That>{this}
+#endif
+              ,
+              *this,
+              variants::lib::forward<That>(that));
+        }
+      }
+    };
+
+    template <typename Traits, Trait = Traits::move_assignable_trait>
+    class move_assignment;
+
+#define MPARK_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, definition) \
+  template <typename... Ts>                                              \
+  class move_assignment<traits<Ts...>, move_assignable_trait>            \
+      : public assignment<traits<Ts...>> {                               \
+    using super = assignment<traits<Ts...>>;                             \
+                                                                         \
+    public:                                                              \
+    INHERITING_CTOR(move_assignment, super)                              \
+    using super::operator=;                                              \
+                                                                         \
+    move_assignment(const move_assignment &) = default;                  \
+    move_assignment(move_assignment &&) = default;                       \
+    ~move_assignment() = default;                                        \
+    move_assignment &operator=(const move_assignment &) = default;       \
+    definition                                                           \
+  }
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::TriviallyAvailable,
+        move_assignment &operator=(move_assignment &&that) = default;);
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::Available,
+        move_assignment &
+        operator=(move_assignment &&that) noexcept(
+            all((std::is_nothrow_move_constructible<Ts>::value &&
+                 std::is_nothrow_move_assignable<Ts>::value)...)) {
+          this->generic_assign(variants::lib::move(that));
+          return *this;
+        });
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::Unavailable,
+        move_assignment &operator=(move_assignment &&) = delete;);
+
+#undef MPARK_VARIANT_MOVE_ASSIGNMENT
+
+    template <typename Traits, Trait = Traits::copy_assignable_trait>
+    class copy_assignment;
+
+#define MPARK_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, definition) \
+  template <typename... Ts>                                              \
+  class copy_assignment<traits<Ts...>, copy_assignable_trait>            \
+      : public move_assignment<traits<Ts...>> {                          \
+    using super = move_assignment<traits<Ts...>>;                        \
+                                                                         \
+    public:                                                              \
+    INHERITING_CTOR(copy_assignment, super)                              \
+    using super::operator=;                                              \
+                                                                         \
+    copy_assignment(const copy_assignment &) = default;                  \
+    copy_assignment(copy_assignment &&) = default;                       \
+    ~copy_assignment() = default;                                        \
+    definition                                                           \
+    copy_assignment &operator=(copy_assignment &&) = default;            \
+  }
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::TriviallyAvailable,
+        copy_assignment &operator=(const copy_assignment &that) = default;);
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::Available,
+        copy_assignment &operator=(const copy_assignment &that) {
+          this->generic_assign(that);
+          return *this;
+        });
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::Unavailable,
+        copy_assignment &operator=(const copy_assignment &) = delete;);
+
+#undef MPARK_VARIANT_COPY_ASSIGNMENT
+
+    template <typename... Ts>
+    class impl : public copy_assignment<traits<Ts...>> {
+      using super = copy_assignment<traits<Ts...>>;
+
+      public:
+      INHERITING_CTOR(impl, super)
+      using super::operator=;
+
+      template <std::size_t I, typename Arg>
+      inline void assign(Arg &&arg) {
+        this->assign_alt(access::base::get_alt<I>(*this),
+                         variants::lib::forward<Arg>(arg),
+                         std::false_type{});
+      }
+
+      inline void swap(impl &that) {
+        if (this->valueless_by_exception() && that.valueless_by_exception()) {
+          // do nothing.
+        } else if (this->index() == that.index()) {
+          visitation::base::visit_alt_at(this->index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+                                         [](auto &this_alt, auto &that_alt) {
+                                           using std::swap;
+                                           swap(this_alt.value,
+                                                that_alt.value);
+                                         }
+#else
+                                         swapper{}
+#endif
+                                         ,
+                                         *this,
+                                         that);
+        } else {
+          impl *lhs = this;
+          impl *rhs = variants::lib::addressof(that);
+          if (lhs->move_nothrow() && !rhs->move_nothrow()) {
+            std::swap(lhs, rhs);
+          }
+          impl tmp(variants::lib::move(*rhs));
+          // EXTENSION: When the move construction of `lhs` into `rhs` throws
+          // and `tmp` is nothrow move constructible then we move `tmp` back
+          // into `rhs` and provide the strong exception safety guarentee.
+          try {
+            this->generic_construct(*rhs, variants::lib::move(*lhs));
+          } catch (...) {
+            if (tmp.move_nothrow()) {
+              this->generic_construct(*rhs, variants::lib::move(tmp));
+            }
+            throw;
+          }
+          this->generic_construct(*lhs, variants::lib::move(tmp));
+        }
+      }
+
+      private:
+#ifndef MPARK_GENERIC_LAMBDAS
+      struct swapper {
+        template <typename ThisAlt, typename ThatAlt>
+        inline void operator()(ThisAlt &this_alt, ThatAlt &that_alt) const {
+          using std::swap;
+          swap(this_alt.value, that_alt.value);
+        }
+      };
+#endif
+
+      inline constexpr bool move_nothrow() const {
+        return this->valueless_by_exception() ||
+               variants::lib::array<bool, sizeof...(Ts)>{
+                   {std::is_nothrow_move_constructible<Ts>::value...}
+               }[this->index()];
+      }
+    };
+
+    template <typename... Ts>
+    struct overload;
+
+    template <>
+    struct overload<> { void operator()() const {} };
+
+    template <typename T, typename... Ts>
+    struct overload<T, Ts...> : overload<Ts...> {
+      using overload<Ts...>::operator();
+      variants::lib::identity<T> operator()(T) const { return {}; }
+    };
+
+    template <typename T, typename... Ts>
+    using best_match_t =
+        typename variants::lib::invoke_result_t<overload<Ts...>, T &&>::type;
+
+  }  // detail
+
+  template <typename... Ts>
+  class variant {
+    static_assert(0 < sizeof...(Ts),
+                  "variant must consist of at least one alternative.");
+
+    static_assert(variants::lib::all<!std::is_array<Ts>::value...>::value,
+                  "variant can not have an array type as an alternative.");
+
+    static_assert(variants::lib::all<!std::is_reference<Ts>::value...>::value,
+                  "variant can not have a reference type as an alternative.");
+
+    static_assert(variants::lib::all<!std::is_void<Ts>::value...>::value,
+                  "variant can not have a void type as an alternative.");
+
+    public:
+    template <
+        typename Front = variants::lib::type_pack_element_t<0, Ts...>,
+        variants::lib::enable_if_t<std::is_default_constructible<Front>::value,
+                                   int> = 0>
+    inline constexpr variant() noexcept(
+        std::is_nothrow_default_constructible<Front>::value)
+        : impl_(in_place_index_t<0>{}) {}
+
+    variant(const variant &) = default;
+    variant(variant &&) = default;
+
+    template <typename Arg,
+              variants::lib::enable_if_t<
+                  !std::is_same<variants::lib::decay_t<Arg>, variant>::value,
+                  int> = 0,
+              typename T = detail::best_match_t<Arg, Ts...>,
+              std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+              variants::lib::enable_if_t<std::is_constructible<T, Arg>::value,
+                                         int> = 0>
+    inline constexpr variant(Arg &&arg) noexcept(
+        std::is_nothrow_constructible<T, Arg>::value)
+        : impl_(in_place_index_t<I>{}, variants::lib::forward<Arg>(arg)) {}
+
+    template <
+        std::size_t I,
+        typename... Args,
+        typename T = variants::lib::type_pack_element_t<I, Ts...>,
+        variants::lib::enable_if_t<std::is_constructible<T, Args...>::value,
+                                   int> = 0>
+    inline explicit constexpr variant(
+        in_place_index_t<I>,
+        Args &&... args) noexcept(std::is_nothrow_constructible<T,
+                                                                Args...>::value)
+        : impl_(in_place_index_t<I>{}, variants::lib::forward<Args>(args)...) {}
+
+    template <
+        std::size_t I,
+        typename Up,
+        typename... Args,
+        typename T = variants::lib::type_pack_element_t<I, Ts...>,
+        variants::lib::enable_if_t<
+            std::is_constructible<T, std::initializer_list<Up> &, Args...>::
+                value,
+            int> = 0>
+    inline explicit constexpr variant(
+        in_place_index_t<I>,
+        std::initializer_list<Up> il,
+        Args &&... args) noexcept(std::
+                                      is_nothrow_constructible<
+                                          T,
+                                          std::initializer_list<Up> &,
+                                          Args...>::value)
+        : impl_(in_place_index_t<I>{},
+                il,
+                variants::lib::forward<Args>(args)...) {}
+
+    template <
+        typename T,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        variants::lib::enable_if_t<std::is_constructible<T, Args...>::value,
+                                   int> = 0>
+    inline explicit constexpr variant(
+        in_place_type_t<T>,
+        Args &&... args) noexcept(std::is_nothrow_constructible<T,
+                                                                Args...>::value)
+        : impl_(in_place_index_t<I>{}, variants::lib::forward<Args>(args)...) {}
+
+    template <
+        typename T,
+        typename Up,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        variants::lib::enable_if_t<
+            std::is_constructible<T, std::initializer_list<Up> &, Args...>::
+                value,
+            int> = 0>
+    inline explicit constexpr variant(
+        in_place_type_t<T>,
+        std::initializer_list<Up> il,
+        Args &&... args) noexcept(std::
+                                      is_nothrow_constructible<
+                                          T,
+                                          std::initializer_list<Up> &,
+                                          Args...>::value)
+        : impl_(in_place_index_t<I>{},
+                il,
+                variants::lib::forward<Args>(args)...) {}
+
+    ~variant() = default;
+
+    variant &operator=(const variant &) = default;
+    variant &operator=(variant &&) = default;
+
+    template <typename Arg,
+              variants::lib::enable_if_t<
+                  !std::is_same<variants::lib::decay_t<Arg>, variant>::value,
+                  int> = 0,
+              typename T = detail::best_match_t<Arg, Ts...>,
+              std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+              variants::lib::enable_if_t<(std::is_assignable<T &, Arg>::value &&
+                                          std::is_constructible<T, Arg>::value),
+                                         int> = 0>
+    inline variant &operator=(Arg &&arg) noexcept(
+        (std::is_nothrow_assignable<T &, Arg>::value &&
+         std::is_nothrow_constructible<T, Arg>::value)) {
+      impl_.template assign<I>(variants::lib::forward<Arg>(arg));
+      return *this;
+    }
+
+    template <
+        std::size_t I,
+        typename... Args,
+        typename T = variants::lib::type_pack_element_t<I, Ts...>,
+        variants::lib::enable_if_t<std::is_constructible<T, Args...>::value,
+                                   int> = 0>
+    inline T &emplace(Args &&... args) {
+      return impl_.template emplace<I>(variants::lib::forward<Args>(args)...);
+    }
+
+    template <
+        std::size_t I,
+        typename Up,
+        typename... Args,
+        typename T = variants::lib::type_pack_element_t<I, Ts...>,
+        variants::lib::enable_if_t<
+            std::is_constructible<T, std::initializer_list<Up> &, Args...>::
+                value,
+            int> = 0>
+    inline T &emplace(std::initializer_list<Up> il, Args &&... args) {
+      return impl_.template emplace<I>(il,
+                                       variants::lib::forward<Args>(args)...);
+    }
+
+    template <
+        typename T,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        variants::lib::enable_if_t<std::is_constructible<T, Args...>::value,
+                                   int> = 0>
+    inline T &emplace(Args &&... args) {
+      return impl_.template emplace<I>(variants::lib::forward<Args>(args)...);
+    }
+
+    template <
+        typename T,
+        typename Up,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        variants::lib::enable_if_t<
+            std::is_constructible<T, std::initializer_list<Up> &, Args...>::
+                value,
+            int> = 0>
+    inline T &emplace(std::initializer_list<Up> il, Args &&... args) {
+      return impl_.template emplace<I>(il,
+                                       variants::lib::forward<Args>(args)...);
+    }
+
+    inline constexpr bool valueless_by_exception() const noexcept {
+      return impl_.valueless_by_exception();
+    }
+
+    inline constexpr std::size_t index() const noexcept {
+      return impl_.index();
+    }
+
+    template <bool Dummy = true,
+              variants::lib::enable_if_t<
+                  variants::lib::all<
+                      Dummy,
+                      (std::is_move_constructible<Ts>::value &&
+                       variants::lib::is_swappable<Ts>::value)...>::value,
+                  int> = 0>
+    inline void swap(variant &that) noexcept(
+        variants::lib::all<
+            (std::is_nothrow_move_constructible<Ts>::value &&
+             variants::lib::is_nothrow_swappable<Ts>::value)...>::value) {
+      impl_.swap(that.impl_);
+    }
+
+    private:
+    detail::impl<Ts...> impl_;
+
+    friend struct detail::access::variant;
+    friend struct detail::visitation::variant;
+  };
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept {
+    return v.index() == I;
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept {
+    return holds_alternative<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  namespace detail {
+    template <std::size_t I, typename V>
+    struct generic_get_impl {
+      constexpr generic_get_impl(int) {}
+
+      constexpr AUTO_REFREF operator()(V &&v) const
+        AUTO_REFREF_RETURN(
+            access::variant::get_alt<I>(variants::lib::forward<V>(v)).value)
+    };
+
+    template <std::size_t I, typename V>
+    inline constexpr AUTO_REFREF generic_get(V &&v)
+      AUTO_REFREF_RETURN(generic_get_impl<I, V>(
+          holds_alternative<I>(v) ? 0 : throw bad_variant_access{})(
+          variants::lib::forward<V>(v)))
+  }  // namespace detail
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr variant_alternative_t<I, variant<Ts...>> &get(
+      variant<Ts...> &v) {
+    return detail::generic_get<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr variant_alternative_t<I, variant<Ts...>> &&get(
+      variant<Ts...> &&v) {
+    return detail::generic_get<I>(variants::lib::move(v));
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr const variant_alternative_t<I, variant<Ts...>> &get(
+      const variant<Ts...> &v) {
+    return detail::generic_get<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr const variant_alternative_t<I, variant<Ts...>> &&get(
+      const variant<Ts...> &&v) {
+    return detail::generic_get<I>(variants::lib::move(v));
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr T &get(variant<Ts...> &v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr T &&get(variant<Ts...> &&v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(
+        variants::lib::move(v));
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr const T &get(const variant<Ts...> &v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr const T &&get(const variant<Ts...> &&v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(
+        variants::lib::move(v));
+  }
+
+  namespace detail {
+
+    template <std::size_t I, typename V>
+    inline constexpr /* auto * */ AUTO generic_get_if(V *v) noexcept
+      AUTO_RETURN(
+          v && holds_alternative<I>(*v)
+              ? variants::lib::addressof(access::variant::get_alt<I>(*v).value)
+              : nullptr)
+
+  }  // namespace detail
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr variants::lib::add_pointer_t<
+      variant_alternative_t<I, variant<Ts...>>>
+  get_if(variant<Ts...> *v) noexcept {
+    return detail::generic_get_if<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr variants::lib::add_pointer_t<
+      const variant_alternative_t<I, variant<Ts...>>>
+  get_if(const variant<Ts...> *v) noexcept {
+    return detail::generic_get_if<I>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr variants::lib::add_pointer_t<T>
+  get_if(variant<Ts...> *v) noexcept {
+    return get_if<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr variants::lib::add_pointer_t<const T>
+  get_if(const variant<Ts...> *v) noexcept {
+    return get_if<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator==(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using variants::lib::equal_to;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.index() != rhs.index()) return false;
+    if (lhs.valueless_by_exception()) return true;
+    return variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs);
+#else
+    return lhs.index() == rhs.index() &&
+           (lhs.valueless_by_exception() ||
+            variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator!=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using variants::lib::not_equal_to;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.index() != rhs.index()) return true;
+    if (lhs.valueless_by_exception()) return false;
+    return variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs);
+#else
+    return lhs.index() != rhs.index() ||
+           (!lhs.valueless_by_exception() &&
+            variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator<(const variant<Ts...> &lhs,
+                                  const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using variants::lib::less;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (rhs.valueless_by_exception()) return false;
+    if (lhs.valueless_by_exception()) return true;
+    if (lhs.index() < rhs.index()) return true;
+    if (lhs.index() > rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), less{}, lhs, rhs);
+#else
+    return !rhs.valueless_by_exception() &&
+           (lhs.valueless_by_exception() || lhs.index() < rhs.index() ||
+            (lhs.index() == rhs.index() &&
+             variant::visit_value_at(lhs.index(), less{}, lhs, rhs)));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator>(const variant<Ts...> &lhs,
+                                  const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using variants::lib::greater;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.valueless_by_exception()) return false;
+    if (rhs.valueless_by_exception()) return true;
+    if (lhs.index() > rhs.index()) return true;
+    if (lhs.index() < rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), greater{}, lhs, rhs);
+#else
+    return !lhs.valueless_by_exception() &&
+           (rhs.valueless_by_exception() || lhs.index() > rhs.index() ||
+            (lhs.index() == rhs.index() &&
+             variant::visit_value_at(lhs.index(), greater{}, lhs, rhs)));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator<=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using variants::lib::less_equal;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.valueless_by_exception()) return true;
+    if (rhs.valueless_by_exception()) return false;
+    if (lhs.index() < rhs.index()) return true;
+    if (lhs.index() > rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs);
+#else
+    return lhs.valueless_by_exception() ||
+           (!rhs.valueless_by_exception() &&
+            (lhs.index() < rhs.index() ||
+             (lhs.index() == rhs.index() &&
+              variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs))));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator>=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using variants::lib::greater_equal;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (rhs.valueless_by_exception()) return true;
+    if (lhs.valueless_by_exception()) return false;
+    if (lhs.index() > rhs.index()) return true;
+    if (lhs.index() < rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), greater_equal{}, lhs, rhs);
+#else
+    return rhs.valueless_by_exception() ||
+           (!lhs.valueless_by_exception() &&
+            (lhs.index() > rhs.index() ||
+             (lhs.index() == rhs.index() &&
+              variant::visit_value_at(
+                  lhs.index(), greater_equal{}, lhs, rhs))));
+#endif
+  }
+
+  template <typename Visitor, typename... Vs>
+  inline constexpr DECLTYPE_AUTO visit(Visitor &&visitor, Vs &&... vs)
+    DECLTYPE_AUTO_RETURN((detail::all(!vs.valueless_by_exception()...)
+                              ? (void)0
+                              : throw bad_variant_access{}),
+                         detail::visitation::variant::visit_value(
+                             variants::lib::forward<Visitor>(visitor),
+                             variants::lib::forward<Vs>(vs)...))
+
+  struct monostate {};
+
+  inline constexpr bool operator<(monostate, monostate) noexcept {
+    return false;
+  }
+
+  inline constexpr bool operator>(monostate, monostate) noexcept {
+    return false;
+  }
+
+  inline constexpr bool operator<=(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator>=(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator==(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator!=(monostate, monostate) noexcept {
+    return false;
+  }
+
+  template <typename... Ts>
+  inline auto swap(variant<Ts...> &lhs,
+                   variant<Ts...> &rhs) noexcept(noexcept(lhs.swap(rhs)))
+      -> decltype(lhs.swap(rhs)) {
+    lhs.swap(rhs);
+  }
+
+  namespace detail {
+
+    template <typename T, typename...>
+    using enabled_type = T;
+
+    namespace hash {
+
+      template <typename H, typename K>
+      constexpr bool meets_requirements() {
+        return std::is_copy_constructible<H>::value &&
+               std::is_move_constructible<H>::value &&
+               variants::lib::is_invocable_r<std::size_t, H, const K &>::value;
+      }
+
+      template <typename K>
+      constexpr bool is_enabled() {
+        using H = std::hash<K>;
+        return meets_requirements<H, K>() &&
+               std::is_default_constructible<H>::value &&
+               std::is_copy_assignable<H>::value &&
+               std::is_move_assignable<H>::value;
+      }
+
+    }  // namespace hash
+
+  }  // namespace detail
+
+#undef AUTO
+#undef AUTO_RETURN
+
+#undef AUTO_REFREF
+#undef AUTO_REFREF_RETURN
+
+#undef DECLTYPE_AUTO
+#undef DECLTYPE_AUTO_RETURN
+
+}  // namespace mpark
+
+namespace std {
+
+  template <typename... Ts>
+  struct hash<mpark::detail::enabled_type<
+      mpark::variant<Ts...>,
+      mpark::variants::lib::enable_if_t<
+          mpark::variants::lib::all<mpark::detail::hash::is_enabled<
+              mpark::variants::lib::remove_const_t<Ts>>()...>::value>>> {
+    using argument_type = mpark::variant<Ts...>;
+    using result_type = std::size_t;
+
+    inline result_type operator()(const argument_type &v) const {
+      using mpark::detail::visitation::variant;
+      std::size_t result =
+          v.valueless_by_exception()
+              ? 299792458  // Random value chosen by the universe upon creation
+              : variant::visit_alt(
+#ifdef MPARK_GENERIC_LAMBDAS
+                    [](const auto &alt) {
+                      using alt_type =
+                          mpark::variants::lib::decay_t<decltype(alt)>;
+                      using value_type = mpark::variants::lib::remove_const_t<
+                          typename alt_type::value_type>;
+                      return hash<value_type>{}(alt.value);
+                    }
+#else
+                    hasher{}
+#endif
+                    ,
+                    v);
+      return hash_combine(result, hash<std::size_t>{}(v.index()));
+    }
+
+    private:
+#ifndef MPARK_GENERIC_LAMBDAS
+    struct hasher {
+      template <typename Alt>
+      inline std::size_t operator()(const Alt &alt) const {
+        using alt_type = mpark::variants::lib::decay_t<Alt>;
+        using value_type =
+            mpark::variants::lib::remove_const_t<typename alt_type::value_type>;
+        return hash<value_type>{}(alt.value);
+      }
+    };
+#endif
+
+    static std::size_t hash_combine(std::size_t lhs, std::size_t rhs) {
+      return lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
+    }
+  };
+
+  template <>
+  struct hash<mpark::monostate> {
+    using argument_type = mpark::monostate;
+    using result_type = std::size_t;
+
+    inline result_type operator()(const argument_type &) const noexcept {
+      return 66740831;  // return a fundamentally attractive random value.
+    }
+  };
+
+}  // namespace std
+
+#endif  // MPARK_VARIANT_HPP

+ 2365 - 0
src/external/mpark-variant/v1.2.0/variant.hpp

@@ -0,0 +1,2365 @@
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_VARIANT_HPP
+#define MPARK_VARIANT_HPP
+
+/*
+   variant synopsis
+
+namespace std {
+
+  // 20.7.2, class template variant
+  template <class... Types>
+  class variant {
+  public:
+
+    // 20.7.2.1, constructors
+    constexpr variant() noexcept(see below);
+    variant(const variant&);
+    variant(variant&&) noexcept(see below);
+
+    template <class T> constexpr variant(T&&) noexcept(see below);
+
+    template <class T, class... Args>
+    constexpr explicit variant(in_place_type_t<T>, Args&&...);
+
+    template <class T, class U, class... Args>
+    constexpr explicit variant(
+        in_place_type_t<T>, initializer_list<U>, Args&&...);
+
+    template <size_t I, class... Args>
+    constexpr explicit variant(in_place_index_t<I>, Args&&...);
+
+    template <size_t I, class U, class... Args>
+    constexpr explicit variant(
+        in_place_index_t<I>, initializer_list<U>, Args&&...);
+
+    // 20.7.2.2, destructor
+    ~variant();
+
+    // 20.7.2.3, assignment
+    variant& operator=(const variant&);
+    variant& operator=(variant&&) noexcept(see below);
+
+    template <class T> variant& operator=(T&&) noexcept(see below);
+
+    // 20.7.2.4, modifiers
+    template <class T, class... Args>
+    T& emplace(Args&&...);
+
+    template <class T, class U, class... Args>
+    T& emplace(initializer_list<U>, Args&&...);
+
+    template <size_t I, class... Args>
+    variant_alternative<I, variant>& emplace(Args&&...);
+
+    template <size_t I, class U, class...  Args>
+    variant_alternative<I, variant>& emplace(initializer_list<U>, Args&&...);
+
+    // 20.7.2.5, value status
+    constexpr bool valueless_by_exception() const noexcept;
+    constexpr size_t index() const noexcept;
+
+    // 20.7.2.6, swap
+    void swap(variant&) noexcept(see below);
+  };
+
+  // 20.7.3, variant helper classes
+  template <class T> struct variant_size; // undefined
+
+  template <class T>
+  constexpr size_t variant_size_v = variant_size<T>::value;
+
+  template <class T> struct variant_size<const T>;
+  template <class T> struct variant_size<volatile T>;
+  template <class T> struct variant_size<const volatile T>;
+
+  template <class... Types>
+  struct variant_size<variant<Types...>>;
+
+  template <size_t I, class T> struct variant_alternative; // undefined
+
+  template <size_t I, class T>
+  using variant_alternative_t = typename variant_alternative<I, T>::type;
+
+  template <size_t I, class T> struct variant_alternative<I, const T>;
+  template <size_t I, class T> struct variant_alternative<I, volatile T>;
+  template <size_t I, class T> struct variant_alternative<I, const volatile T>;
+
+  template <size_t I, class... Types>
+  struct variant_alternative<I, variant<Types...>>;
+
+  constexpr size_t variant_npos = -1;
+
+  // 20.7.4, value access
+  template <class T, class... Types>
+  constexpr bool holds_alternative(const variant<Types...>&) noexcept;
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>>&
+  get(variant<Types...>&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>>&&
+  get(variant<Types...>&&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>> const&
+  get(const variant<Types...>&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>> const&&
+  get(const variant<Types...>&&);
+
+  template <class T, class...  Types>
+  constexpr T& get(variant<Types...>&);
+
+  template <class T, class... Types>
+  constexpr T&& get(variant<Types...>&&);
+
+  template <class T, class... Types>
+  constexpr const T& get(const variant<Types...>&);
+
+  template <class T, class... Types>
+  constexpr const T&& get(const variant<Types...>&&);
+
+  template <size_t I, class... Types>
+  constexpr add_pointer_t<variant_alternative_t<I, variant<Types...>>>
+  get_if(variant<Types...>*) noexcept;
+
+  template <size_t I, class... Types>
+  constexpr add_pointer_t<const variant_alternative_t<I, variant<Types...>>>
+  get_if(const variant<Types...>*) noexcept;
+
+  template <class T, class... Types>
+  constexpr add_pointer_t<T>
+  get_if(variant<Types...>*) noexcept;
+
+  template <class T, class... Types>
+  constexpr add_pointer_t<const T>
+  get_if(const variant<Types...>*) noexcept;
+
+  // 20.7.5, relational operators
+  template <class... Types>
+  constexpr bool operator==(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator!=(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator<(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator>(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator<=(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator>=(const variant<Types...>&, const variant<Types...>&);
+
+  // 20.7.6, visitation
+  template <class Visitor, class... Variants>
+  constexpr see below visit(Visitor&&, Variants&&...);
+
+  // 20.7.7, class monostate
+  struct monostate;
+
+  // 20.7.8, monostate relational operators
+  constexpr bool operator<(monostate, monostate) noexcept;
+  constexpr bool operator>(monostate, monostate) noexcept;
+  constexpr bool operator<=(monostate, monostate) noexcept;
+  constexpr bool operator>=(monostate, monostate) noexcept;
+  constexpr bool operator==(monostate, monostate) noexcept;
+  constexpr bool operator!=(monostate, monostate) noexcept;
+
+  // 20.7.9, specialized algorithms
+  template <class... Types>
+  void swap(variant<Types...>&, variant<Types...>&) noexcept(see below);
+
+  // 20.7.10, class bad_variant_access
+  class bad_variant_access;
+
+  // 20.7.11, hash support
+  template <class T> struct hash;
+  template <class... Types> struct hash<variant<Types...>>;
+  template <> struct hash<monostate>;
+
+} // namespace std
+
+*/
+
+#include <cstddef>
+#include <exception>
+#include <functional>
+#include <initializer_list>
+#include <new>
+#include <type_traits>
+#include <utility>
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_CONFIG_HPP
+#define MPARK_CONFIG_HPP
+
+// MSVC 2015 Update 3.
+#if __cplusplus < 201103L && (!defined(_MSC_VER) || _MSC_FULL_VER < 190024215)
+#error "MPark.Variant requires C++11 support."
+#endif
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+#ifndef __has_feature
+#define __has_feature(x) 0
+#endif
+
+#if __has_builtin(__builtin_addressof) || \
+    (defined(__GNUC__) && __GNUC__ >= 7) || defined(_MSC_VER)
+#define MPARK_BUILTIN_ADDRESSOF
+#endif
+
+#if __has_builtin(__type_pack_element)
+#define MPARK_TYPE_PACK_ELEMENT
+#endif
+
+#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304
+#define MPARK_CPP14_CONSTEXPR
+#endif
+
+#if __has_feature(cxx_exceptions) || defined(__cpp_exceptions)
+#define MPARK_EXCEPTIONS
+#endif
+
+#if defined(__cpp_generic_lambdas) || defined(_MSC_VER)
+#define MPARK_GENERIC_LAMBDAS
+#endif
+
+#if defined(__cpp_lib_integer_sequence)
+#define MPARK_INTEGER_SEQUENCE
+#endif
+
+#if defined(__cpp_return_type_deduction) || defined(_MSC_VER)
+#define MPARK_RETURN_TYPE_DEDUCTION
+#endif
+
+#if defined(__cpp_lib_transparent_operators) || defined(_MSC_VER)
+#define MPARK_TRANSPARENT_OPERATORS
+#endif
+
+#if defined(__cpp_variable_templates) || defined(_MSC_VER)
+#define MPARK_VARIABLE_TEMPLATES
+#endif
+
+#if !defined(__GLIBCXX__)
+#define MPARK_TRIVIALITY_TYPE_TRAITS
+#elif defined(__has_include) && __has_include(<codecvt>)  // >= libstdc++-5
+#define MPARK_TRIVIALITY_TYPE_TRAITS
+#endif
+
+#endif  // MPARK_CONFIG_HPP
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_IN_PLACE_HPP
+#define MPARK_IN_PLACE_HPP
+
+#include <cstddef>
+
+
+namespace mpark {
+
+  struct in_place_t { explicit in_place_t() = default; };
+
+  template <std::size_t I>
+  struct in_place_index_t { explicit in_place_index_t() = default; };
+
+  template <typename T>
+  struct in_place_type_t { explicit in_place_type_t() = default; };
+
+#ifdef MPARK_VARIABLE_TEMPLATES
+  constexpr in_place_t in_place{};
+
+  template <std::size_t I> constexpr in_place_index_t<I> in_place_index{};
+
+  template <typename T> constexpr in_place_type_t<T> in_place_type{};
+#endif
+
+}  // namespace mpark
+
+#endif  // MPARK_IN_PLACE_HPP
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_LIB_HPP
+#define MPARK_LIB_HPP
+
+#include <memory>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+
+#define RETURN(...)                                          \
+  noexcept(noexcept(__VA_ARGS__)) -> decltype(__VA_ARGS__) { \
+    return __VA_ARGS__;                                      \
+  }
+
+namespace mpark {
+  namespace lib {
+    template <typename T>
+    struct identity { using type = T; };
+
+    inline namespace cpp14 {
+      template <typename T, std::size_t N>
+      struct array {
+        constexpr const T &operator[](std::size_t index) const {
+          return data[index];
+        }
+
+        T data[N == 0 ? 1 : N];
+      };
+
+      template <typename T>
+      using add_pointer_t = typename std::add_pointer<T>::type;
+
+      template <typename... Ts>
+      using common_type_t = typename std::common_type<Ts...>::type;
+
+      template <typename T>
+      using decay_t = typename std::decay<T>::type;
+
+      template <bool B, typename T = void>
+      using enable_if_t = typename std::enable_if<B, T>::type;
+
+      template <typename T>
+      using remove_const_t = typename std::remove_const<T>::type;
+
+      template <typename T>
+      using remove_reference_t = typename std::remove_reference<T>::type;
+
+      template <typename T>
+      inline constexpr T &&forward(remove_reference_t<T> &t) noexcept {
+        return static_cast<T &&>(t);
+      }
+
+      template <typename T>
+      inline constexpr T &&forward(remove_reference_t<T> &&t) noexcept {
+        static_assert(!std::is_lvalue_reference<T>::value,
+                      "can not forward an rvalue as an lvalue");
+        return static_cast<T &&>(t);
+      }
+
+      template <typename T>
+      inline constexpr remove_reference_t<T> &&move(T &&t) noexcept {
+        return static_cast<remove_reference_t<T> &&>(t);
+      }
+
+#ifdef MPARK_INTEGER_SEQUENCE
+      template <typename T, T... Is>
+      using integer_sequence = std::integer_sequence<T, Is...>;
+
+      template <std::size_t... Is>
+      using index_sequence = std::index_sequence<Is...>;
+
+      template <std::size_t N>
+      using make_index_sequence = std::make_index_sequence<N>;
+
+      template <typename... Ts>
+      using index_sequence_for = std::index_sequence_for<Ts...>;
+#else
+      template <typename T, T... Is>
+      struct integer_sequence {
+        using value_type = T;
+        static constexpr std::size_t size() noexcept { return sizeof...(Is); }
+      };
+
+      template <std::size_t... Is>
+      using index_sequence = integer_sequence<std::size_t, Is...>;
+
+      template <typename Lhs, typename Rhs>
+      struct make_index_sequence_concat;
+
+      template <std::size_t... Lhs, std::size_t... Rhs>
+      struct make_index_sequence_concat<index_sequence<Lhs...>,
+                                        index_sequence<Rhs...>>
+          : identity<index_sequence<Lhs..., (sizeof...(Lhs) + Rhs)...>> {};
+
+      template <std::size_t N>
+      struct make_index_sequence_impl;
+
+      template <std::size_t N>
+      using make_index_sequence = typename make_index_sequence_impl<N>::type;
+
+      template <std::size_t N>
+      struct make_index_sequence_impl
+          : make_index_sequence_concat<make_index_sequence<N / 2>,
+                                       make_index_sequence<N - (N / 2)>> {};
+
+      template <>
+      struct make_index_sequence_impl<0> : identity<index_sequence<>> {};
+
+      template <>
+      struct make_index_sequence_impl<1> : identity<index_sequence<0>> {};
+
+      template <typename... Ts>
+      using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
+#endif
+
+      // <functional>
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using equal_to = std::equal_to<>;
+#else
+      struct equal_to {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          RETURN(lib::forward<Lhs>(lhs) == lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using not_equal_to = std::not_equal_to<>;
+#else
+      struct not_equal_to {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          RETURN(lib::forward<Lhs>(lhs) != lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using less = std::less<>;
+#else
+      struct less {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          RETURN(lib::forward<Lhs>(lhs) < lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using greater = std::greater<>;
+#else
+      struct greater {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          RETURN(lib::forward<Lhs>(lhs) > lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using less_equal = std::less_equal<>;
+#else
+      struct less_equal {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          RETURN(lib::forward<Lhs>(lhs) <= lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using greater_equal = std::greater_equal<>;
+#else
+      struct greater_equal {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          RETURN(lib::forward<Lhs>(lhs) >= lib::forward<Rhs>(rhs))
+      };
+#endif
+    }  // namespace cpp14
+
+    inline namespace cpp17 {
+
+      // <type_traits>
+      template <bool B>
+      using bool_constant = std::integral_constant<bool, B>;
+
+      template <typename...>
+      struct voider : identity<void> {};
+
+      template <typename... Ts>
+      using void_t = typename voider<Ts...>::type;
+
+      namespace detail {
+        namespace swappable {
+
+          using std::swap;
+
+          template <typename T>
+          struct is_swappable_impl {
+            private:
+            template <typename U,
+                      typename = decltype(swap(std::declval<U &>(),
+                                               std::declval<U &>()))>
+            inline static std::true_type test(int);
+
+            template <typename U>
+            inline static std::false_type test(...);
+
+            public:
+            using type = decltype(test<T>(0));
+          };
+
+          template <typename T>
+          using is_swappable = typename is_swappable_impl<T>::type;
+
+          template <typename T, bool = is_swappable<T>::value>
+          struct is_nothrow_swappable {
+            static constexpr bool value =
+                noexcept(swap(std::declval<T &>(), std::declval<T &>()));
+          };
+
+          template <typename T>
+          struct is_nothrow_swappable<T, false> : std::false_type {};
+
+        }  // namespace swappable
+      }  // namespace detail
+
+      template <typename T>
+      using is_swappable = detail::swappable::is_swappable<T>;
+
+      template <typename T>
+      using is_nothrow_swappable = detail::swappable::is_nothrow_swappable<T>;
+
+      // <functional>
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+      template <typename F, typename... As>
+      inline constexpr auto invoke(F &&f, As &&... as)
+          RETURN(lib::forward<F>(f)(lib::forward<As>(as)...))
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+      template <typename B, typename T, typename D>
+      inline constexpr auto invoke(T B::*pmv, D &&d)
+          RETURN(lib::forward<D>(d).*pmv)
+
+      template <typename Pmv, typename Ptr>
+      inline constexpr auto invoke(Pmv pmv, Ptr &&ptr)
+          RETURN((*lib::forward<Ptr>(ptr)).*pmv)
+
+      template <typename B, typename T, typename D, typename... As>
+      inline constexpr auto invoke(T B::*pmf, D &&d, As &&... as)
+          RETURN((lib::forward<D>(d).*pmf)(lib::forward<As>(as)...))
+
+      template <typename Pmf, typename Ptr, typename... As>
+      inline constexpr auto invoke(Pmf pmf, Ptr &&ptr, As &&... as)
+          RETURN(((*lib::forward<Ptr>(ptr)).*pmf)(lib::forward<As>(as)...))
+
+      namespace detail {
+
+        template <typename Void, typename, typename...>
+        struct invoke_result {};
+
+        template <typename F, typename... Args>
+        struct invoke_result<void_t<decltype(lib::invoke(
+                                 std::declval<F>(), std::declval<Args>()...))>,
+                             F,
+                             Args...>
+            : identity<decltype(
+                  lib::invoke(std::declval<F>(), std::declval<Args>()...))> {};
+
+      }  // namespace detail
+
+      template <typename F, typename... Args>
+      using invoke_result = detail::invoke_result<void, F, Args...>;
+
+      template <typename F, typename... Args>
+      using invoke_result_t = typename invoke_result<F, Args...>::type;
+
+      namespace detail {
+
+        template <typename Void, typename, typename...>
+        struct is_invocable : std::false_type {};
+
+        template <typename F, typename... Args>
+        struct is_invocable<void_t<invoke_result_t<F, Args...>>, F, Args...>
+            : std::true_type {};
+
+        template <typename Void, typename, typename, typename...>
+        struct is_invocable_r : std::false_type {};
+
+        template <typename R, typename F, typename... Args>
+        struct is_invocable_r<void_t<invoke_result_t<F, Args...>>,
+                              R,
+                              F,
+                              Args...>
+            : std::is_convertible<invoke_result_t<F, Args...>, R> {};
+
+      }  // namespace detail
+
+      template <typename F, typename... Args>
+      using is_invocable = detail::is_invocable<void, F, Args...>;
+
+      template <typename R, typename F, typename... Args>
+      using is_invocable_r = detail::is_invocable_r<void, R, F, Args...>;
+
+      // <memory>
+#ifdef MPARK_BUILTIN_ADDRESSOF
+      template <typename T>
+      inline constexpr T *addressof(T &arg) {
+        return __builtin_addressof(arg);
+      }
+#else
+      namespace detail {
+
+        namespace has_addressof_impl {
+
+          struct fail;
+
+          template <typename T>
+          inline fail operator&(T &&);
+
+          template <typename T>
+          inline static constexpr bool impl() {
+            return (std::is_class<T>::value || std::is_union<T>::value) &&
+                   !std::is_same<decltype(&std::declval<T &>()), fail>::value;
+          }
+
+        }  // namespace has_addressof_impl
+
+        template <typename T>
+        using has_addressof = bool_constant<has_addressof_impl::impl<T>()>;
+
+        template <typename T>
+        inline constexpr T *addressof(T &arg, std::true_type) {
+          return std::addressof(arg);
+        }
+
+        template <typename T>
+        inline constexpr T *addressof(T &arg, std::false_type) {
+          return &arg;
+        }
+
+      }  // namespace detail
+
+      template <typename T>
+      inline constexpr T *addressof(T &arg) {
+        return detail::addressof(arg, detail::has_addressof<T>{});
+      }
+#endif
+
+      template <typename T>
+      inline constexpr T *addressof(const T &&) = delete;
+
+    }  // namespace cpp17
+
+    template <typename T>
+    struct remove_all_extents : identity<T> {};
+
+    template <typename T, std::size_t N>
+    struct remove_all_extents<array<T, N>> : remove_all_extents<T> {};
+
+    template <typename T>
+    using remove_all_extents_t = typename remove_all_extents<T>::type;
+
+    template <std::size_t N>
+    using size_constant = std::integral_constant<std::size_t, N>;
+
+    template <bool... Bs>
+    using bool_sequence = integer_sequence<bool, Bs...>;
+
+    template <std::size_t I, typename T>
+    struct indexed_type : size_constant<I>, identity<T> {};
+
+    template <bool... Bs>
+    using all =
+        std::is_same<bool_sequence<true, Bs...>, bool_sequence<Bs..., true>>;
+
+#ifdef MPARK_TYPE_PACK_ELEMENT
+    template <std::size_t I, typename... Ts>
+    using type_pack_element_t = __type_pack_element<I, Ts...>;
+#else
+    template <std::size_t I, typename... Ts>
+    struct type_pack_element_impl {
+      private:
+      template <typename>
+      struct set;
+
+      template <std::size_t... Is>
+      struct set<index_sequence<Is...>> : indexed_type<Is, Ts>... {};
+
+      template <typename T>
+      inline static std::enable_if<true, T> impl(indexed_type<I, T>);
+
+      inline static std::enable_if<false> impl(...);
+
+      public:
+      using type = decltype(impl(set<index_sequence_for<Ts...>>{}));
+    };
+
+    template <std::size_t I, typename... Ts>
+    using type_pack_element = typename type_pack_element_impl<I, Ts...>::type;
+
+    template <std::size_t I, typename... Ts>
+    using type_pack_element_t = typename type_pack_element<I, Ts...>::type;
+#endif
+
+#ifdef MPARK_TRIVIALITY_TYPE_TRAITS
+    template <typename T>
+    using is_trivially_copy_constructible =
+        std::is_trivially_copy_constructible<T>;
+
+    template <typename T>
+    using is_trivially_move_constructible =
+        std::is_trivially_move_constructible<T>;
+
+    template <typename T>
+    using is_trivially_copy_assignable = std::is_trivially_copy_assignable<T>;
+
+    template <typename T>
+    using is_trivially_move_assignable = std::is_trivially_move_assignable<T>;
+#else
+    template <typename T>
+    struct is_trivially_copy_constructible
+        : bool_constant<
+              std::is_copy_constructible<T>::value && __has_trivial_copy(T)> {};
+
+    template <typename T>
+    struct is_trivially_move_constructible : bool_constant<__is_trivial(T)> {};
+
+    template <typename T>
+    struct is_trivially_copy_assignable
+        : bool_constant<
+              std::is_copy_assignable<T>::value && __has_trivial_assign(T)> {};
+
+    template <typename T>
+    struct is_trivially_move_assignable : bool_constant<__is_trivial(T)> {};
+#endif
+
+  }  // namespace lib
+}  // namespace mpark
+
+#undef RETURN
+
+#endif  // MPARK_LIB_HPP
+
+
+namespace mpark {
+
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+
+#define AUTO auto
+#define AUTO_RETURN(...) { return __VA_ARGS__; }
+
+#define AUTO_REFREF auto &&
+#define AUTO_REFREF_RETURN(...) { return __VA_ARGS__; }
+
+#define DECLTYPE_AUTO decltype(auto)
+#define DECLTYPE_AUTO_RETURN(...) { return __VA_ARGS__; }
+
+#else
+
+#define AUTO auto
+#define AUTO_RETURN(...) \
+  -> lib::decay_t<decltype(__VA_ARGS__)> { return __VA_ARGS__; }
+
+#define AUTO_REFREF auto
+#define AUTO_REFREF_RETURN(...)                                           \
+  -> decltype((__VA_ARGS__)) {                                            \
+    static_assert(std::is_reference<decltype((__VA_ARGS__))>::value, ""); \
+    return __VA_ARGS__;                                                   \
+  }
+
+#define DECLTYPE_AUTO auto
+#define DECLTYPE_AUTO_RETURN(...) \
+  -> decltype(__VA_ARGS__) { return __VA_ARGS__; }
+
+#endif
+
+  class bad_variant_access : public std::exception {
+    public:
+    virtual const char *what() const noexcept { return "bad_variant_access"; }
+  };
+
+  [[noreturn]] inline void throw_bad_variant_access() {
+#ifdef MPARK_EXCEPTIONS
+    throw bad_variant_access{};
+#else
+    std::terminate();
+#endif
+  }
+
+  template <typename... Ts>
+  class variant;
+
+  template <typename T>
+  struct variant_size;
+
+#ifdef MPARK_VARIABLE_TEMPLATES
+  template <typename T>
+  constexpr std::size_t variant_size_v = variant_size<T>::value;
+#endif
+
+  template <typename T>
+  struct variant_size<const T> : variant_size<T> {};
+
+  template <typename T>
+  struct variant_size<volatile T> : variant_size<T> {};
+
+  template <typename T>
+  struct variant_size<const volatile T> : variant_size<T> {};
+
+  template <typename... Ts>
+  struct variant_size<variant<Ts...>> : lib::size_constant<sizeof...(Ts)> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative;
+
+  template <std::size_t I, typename T>
+  using variant_alternative_t = typename variant_alternative<I, T>::type;
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, const T>
+      : std::add_const<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, volatile T>
+      : std::add_volatile<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, const volatile T>
+      : std::add_cv<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename... Ts>
+  struct variant_alternative<I, variant<Ts...>>
+      : lib::identity<lib::type_pack_element_t<I, Ts...>> {
+    static_assert(I < sizeof...(Ts),
+                  "`variant_alternative` index out of range.");
+  };
+
+  constexpr std::size_t variant_npos = static_cast<std::size_t>(-1);
+
+  namespace detail {
+
+    inline constexpr bool all() { return true; }
+
+    template <typename... Bs>
+    inline constexpr bool all(bool b, Bs... bs) {
+      return b && all(bs...);
+    }
+
+    constexpr std::size_t not_found = static_cast<std::size_t>(-1);
+    constexpr std::size_t ambiguous = static_cast<std::size_t>(-2);
+
+#ifdef MPARK_CPP14_CONSTEXPR
+    template <typename T, typename... Ts>
+    inline constexpr std::size_t find_index() {
+      constexpr lib::array<bool, sizeof...(Ts)> matches = {
+          {std::is_same<T, Ts>::value...}
+      };
+      std::size_t result = not_found;
+      for (std::size_t i = 0; i < sizeof...(Ts); ++i) {
+        if (matches[i]) {
+          if (result != not_found) {
+            return ambiguous;
+          }
+          result = i;
+        }
+      }
+      return result;
+    }
+#else
+    inline constexpr std::size_t find_index_impl(std::size_t result,
+                                                 std::size_t) {
+      return result;
+    }
+
+    template <typename... Bs>
+    inline constexpr std::size_t find_index_impl(std::size_t result,
+                                                 std::size_t idx,
+                                                 bool b,
+                                                 Bs... bs) {
+      return b ? (result != not_found ? ambiguous
+                                      : find_index_impl(idx, idx + 1, bs...))
+               : find_index_impl(result, idx + 1, bs...);
+    }
+
+    template <typename T, typename... Ts>
+    inline constexpr std::size_t find_index() {
+      return find_index_impl(not_found, 0, std::is_same<T, Ts>::value...);
+    }
+#endif
+
+    template <std::size_t I>
+    using find_index_sfinae_impl =
+        lib::enable_if_t<I != not_found && I != ambiguous,
+                         lib::size_constant<I>>;
+
+    template <typename T, typename... Ts>
+    using find_index_sfinae = find_index_sfinae_impl<find_index<T, Ts...>()>;
+
+    template <std::size_t I>
+    struct find_index_checked_impl : lib::size_constant<I> {
+      static_assert(I != not_found, "the specified type is not found.");
+      static_assert(I != ambiguous, "the specified type is ambiguous.");
+    };
+
+    template <typename T, typename... Ts>
+    using find_index_checked = find_index_checked_impl<find_index<T, Ts...>()>;
+
+    struct valueless_t {};
+
+    enum class Trait { TriviallyAvailable, Available, Unavailable };
+
+    template <typename T,
+              template <typename> class IsTriviallyAvailable,
+              template <typename> class IsAvailable>
+    inline constexpr Trait trait() {
+      return IsTriviallyAvailable<T>::value
+                 ? Trait::TriviallyAvailable
+                 : IsAvailable<T>::value ? Trait::Available
+                                         : Trait::Unavailable;
+    }
+
+#ifdef MPARK_CPP14_CONSTEXPR
+    template <typename... Traits>
+    inline constexpr Trait common_trait(Traits... traits) {
+      Trait result = Trait::TriviallyAvailable;
+      for (Trait t : {traits...}) {
+        if (static_cast<int>(t) > static_cast<int>(result)) {
+          result = t;
+        }
+      }
+      return result;
+    }
+#else
+    inline constexpr Trait common_trait_impl(Trait result) { return result; }
+
+    template <typename... Traits>
+    inline constexpr Trait common_trait_impl(Trait result,
+                                             Trait t,
+                                             Traits... ts) {
+      return static_cast<int>(t) > static_cast<int>(result)
+                 ? common_trait_impl(t, ts...)
+                 : common_trait_impl(result, ts...);
+    }
+
+    template <typename... Traits>
+    inline constexpr Trait common_trait(Traits... ts) {
+      return common_trait_impl(Trait::TriviallyAvailable, ts...);
+    }
+#endif
+
+    template <typename... Ts>
+    struct traits {
+      static constexpr Trait copy_constructible_trait =
+          common_trait(trait<Ts,
+                             lib::is_trivially_copy_constructible,
+                             std::is_copy_constructible>()...);
+
+      static constexpr Trait move_constructible_trait =
+          common_trait(trait<Ts,
+                             lib::is_trivially_move_constructible,
+                             std::is_move_constructible>()...);
+
+      static constexpr Trait copy_assignable_trait =
+          common_trait(copy_constructible_trait,
+                       trait<Ts,
+                             lib::is_trivially_copy_assignable,
+                             std::is_copy_assignable>()...);
+
+      static constexpr Trait move_assignable_trait =
+          common_trait(move_constructible_trait,
+                       trait<Ts,
+                             lib::is_trivially_move_assignable,
+                             std::is_move_assignable>()...);
+
+      static constexpr Trait destructible_trait =
+          common_trait(trait<Ts,
+                             std::is_trivially_destructible,
+                             std::is_destructible>()...);
+    };
+
+    namespace access {
+
+      struct recursive_union {
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+        template <typename V>
+        inline static constexpr auto &&get_alt(V &&v, in_place_index_t<0>) {
+          return lib::forward<V>(v).head_;
+        }
+
+        template <typename V, std::size_t I>
+        inline static constexpr auto &&get_alt(V &&v, in_place_index_t<I>) {
+          return get_alt(lib::forward<V>(v).tail_, in_place_index_t<I - 1>{});
+        }
+#else
+        template <std::size_t I, bool Dummy = true>
+        struct get_alt_impl {
+          template <typename V>
+          inline constexpr AUTO_REFREF operator()(V &&v) const
+            AUTO_REFREF_RETURN(get_alt_impl<I - 1>{}(lib::forward<V>(v).tail_))
+        };
+
+        template <bool Dummy>
+        struct get_alt_impl<0, Dummy> {
+          template <typename V>
+          inline constexpr AUTO_REFREF operator()(V &&v) const
+            AUTO_REFREF_RETURN(lib::forward<V>(v).head_)
+        };
+
+        template <typename V, std::size_t I>
+        inline static constexpr AUTO_REFREF get_alt(V &&v, in_place_index_t<I>)
+          AUTO_REFREF_RETURN(get_alt_impl<I>{}(lib::forward<V>(v)))
+#endif
+      };
+
+      struct base {
+        template <std::size_t I, typename V>
+        inline static constexpr AUTO_REFREF get_alt(V &&v)
+          AUTO_REFREF_RETURN(recursive_union::get_alt(
+              data(lib::forward<V>(v)), in_place_index_t<I>{}))
+      };
+
+      struct variant {
+        template <std::size_t I, typename V>
+        inline static constexpr AUTO_REFREF get_alt(V &&v)
+          AUTO_REFREF_RETURN(base::get_alt<I>(lib::forward<V>(v).impl_))
+      };
+
+    }  // namespace access
+
+    namespace visitation {
+
+      struct base {
+        private:
+        template <typename T>
+        inline static constexpr const T &at(const T &elem) {
+          return elem;
+        }
+
+        template <typename T, std::size_t N, typename... Is>
+        inline static constexpr const lib::remove_all_extents_t<T> &at(
+            const lib::array<T, N> &elems, std::size_t i, Is... is) {
+          return at(elems[i], is...);
+        }
+
+        template <typename F, typename... Fs>
+        inline static constexpr int visit_visitor_return_type_check() {
+          static_assert(all(std::is_same<F, Fs>::value...),
+                        "`mpark::visit` requires the visitor to have a single "
+                        "return type.");
+          return 0;
+        }
+
+        template <typename... Fs>
+        inline static constexpr lib::array<
+            lib::common_type_t<lib::decay_t<Fs>...>,
+            sizeof...(Fs)>
+        make_farray(Fs &&... fs) {
+          using result = lib::array<lib::common_type_t<lib::decay_t<Fs>...>,
+                                    sizeof...(Fs)>;
+          return visit_visitor_return_type_check<lib::decay_t<Fs>...>(),
+                 result{{lib::forward<Fs>(fs)...}};
+        }
+
+        template <std::size_t... Is>
+        struct dispatcher {
+          template <typename F, typename... Vs>
+          struct impl {
+            inline static constexpr DECLTYPE_AUTO dispatch(F f, Vs... vs)
+              DECLTYPE_AUTO_RETURN(lib::invoke(
+                  static_cast<F>(f),
+                  access::base::get_alt<Is>(static_cast<Vs>(vs))...))
+          };
+        };
+
+        template <typename F, typename... Vs, std::size_t... Is>
+        inline static constexpr AUTO make_dispatch(lib::index_sequence<Is...>)
+          AUTO_RETURN(&dispatcher<Is...>::template impl<F, Vs...>::dispatch)
+
+        template <std::size_t I, typename F, typename... Vs>
+        inline static constexpr AUTO make_fdiagonal_impl()
+          AUTO_RETURN(make_dispatch<F, Vs...>(
+              lib::index_sequence<lib::indexed_type<I, Vs>::value...>{}))
+
+        template <typename F, typename... Vs, std::size_t... Is>
+        inline static constexpr AUTO make_fdiagonal_impl(
+            lib::index_sequence<Is...>)
+          AUTO_RETURN(make_farray(make_fdiagonal_impl<Is, F, Vs...>()...))
+
+        template <typename F, typename V, typename... Vs>
+        inline static constexpr /* auto * */ auto make_fdiagonal()
+            -> decltype(make_fdiagonal_impl<F, V, Vs...>(
+                lib::make_index_sequence<lib::decay_t<V>::size()>{})) {
+          static_assert(
+              all((lib::decay_t<V>::size() == lib::decay_t<Vs>::size())...),
+              "all of the variants must be the same size.");
+          return make_fdiagonal_impl<F, V, Vs...>(
+              lib::make_index_sequence<lib::decay_t<V>::size()>{});
+        }
+
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+        template <typename F, typename... Vs, std::size_t... Is>
+        inline static constexpr auto make_fmatrix_impl(
+            lib::index_sequence<Is...> is) {
+          return make_dispatch<F, Vs...>(is);
+        }
+
+        template <typename F,
+                  typename... Vs,
+                  std::size_t... Is,
+                  std::size_t... Js,
+                  typename... Ls>
+        inline static constexpr auto make_fmatrix_impl(
+            lib::index_sequence<Is...>, lib::index_sequence<Js...>, Ls... ls) {
+          return make_farray(make_fmatrix_impl<F, Vs...>(
+              lib::index_sequence<Is..., Js>{}, ls...)...);
+        }
+
+        template <typename F, typename... Vs>
+        inline static constexpr auto make_fmatrix() {
+          return make_fmatrix_impl<F, Vs...>(
+              lib::index_sequence<>{},
+              lib::make_index_sequence<lib::decay_t<Vs>::size()>{}...);
+        }
+#else
+        template <typename F, typename... Vs>
+        struct make_fmatrix_impl {
+          template <typename...>
+          struct impl;
+
+          template <std::size_t... Is>
+          struct impl<lib::index_sequence<Is...>> {
+            inline constexpr AUTO operator()() const
+              AUTO_RETURN(
+                  make_dispatch<F, Vs...>(lib::index_sequence<Is...>{}))
+          };
+
+          template <std::size_t... Is, std::size_t... Js, typename... Ls>
+          struct impl<lib::index_sequence<Is...>,
+                      lib::index_sequence<Js...>,
+                      Ls...> {
+            inline constexpr AUTO operator()() const
+              AUTO_RETURN(make_farray(
+                  impl<lib::index_sequence<Is..., Js>, Ls...>{}()...))
+          };
+        };
+
+        template <typename F, typename... Vs>
+        inline static constexpr AUTO make_fmatrix()
+          AUTO_RETURN(
+              typename make_fmatrix_impl<F, Vs...>::template impl<
+                  lib::index_sequence<>,
+                  lib::make_index_sequence<lib::decay_t<Vs>::size()>...>{}())
+#endif
+
+        public:
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index,
+                                                           Visitor &&visitor,
+                                                           Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              at(make_fdiagonal<Visitor &&,
+                                decltype(as_base(lib::forward<Vs>(vs)))...>(),
+                 index)(lib::forward<Visitor>(visitor),
+                        as_base(lib::forward<Vs>(vs))...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor,
+                                                        Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              at(make_fmatrix<Visitor &&,
+                              decltype(as_base(lib::forward<Vs>(vs)))...>(),
+                 vs.index()...)(lib::forward<Visitor>(visitor),
+                                as_base(lib::forward<Vs>(vs))...))
+      };
+
+      struct variant {
+        private:
+        template <typename Visitor, typename... Values>
+        struct visit_exhaustive_visitor_check {
+          static_assert(
+              lib::is_invocable<Visitor, Values...>::value,
+              "`mpark::visit` requires the visitor to be exhaustive.");
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+          inline constexpr DECLTYPE_AUTO operator()(Visitor &&visitor,
+                                                    Values &&... values) const
+            DECLTYPE_AUTO_RETURN(lib::invoke(lib::forward<Visitor>(visitor),
+                                             lib::forward<Values>(values)...))
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+        };
+
+        template <typename Visitor>
+        struct value_visitor {
+          Visitor &&visitor_;
+
+          template <typename... Alts>
+          inline constexpr DECLTYPE_AUTO operator()(Alts &&... alts) const
+            DECLTYPE_AUTO_RETURN(
+                visit_exhaustive_visitor_check<
+                    Visitor,
+                    decltype((lib::forward<Alts>(alts).value))...>{}(
+                    lib::forward<Visitor>(visitor_),
+                    lib::forward<Alts>(alts).value...))
+        };
+
+        template <typename Visitor>
+        inline static constexpr AUTO make_value_visitor(Visitor &&visitor)
+          AUTO_RETURN(value_visitor<Visitor>{lib::forward<Visitor>(visitor)})
+
+        public:
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index,
+                                                           Visitor &&visitor,
+                                                           Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              base::visit_alt_at(index,
+                                 lib::forward<Visitor>(visitor),
+                                 lib::forward<Vs>(vs).impl_...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor,
+                                                        Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(base::visit_alt(lib::forward<Visitor>(visitor),
+                                               lib::forward<Vs>(vs).impl_...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_value_at(std::size_t index,
+                                                             Visitor &&visitor,
+                                                             Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              visit_alt_at(index,
+                           make_value_visitor(lib::forward<Visitor>(visitor)),
+                           lib::forward<Vs>(vs)...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_value(Visitor &&visitor,
+                                                          Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              visit_alt(make_value_visitor(lib::forward<Visitor>(visitor)),
+                        lib::forward<Vs>(vs)...))
+      };
+
+    }  // namespace visitation
+
+    template <std::size_t Index, typename T>
+    struct alt {
+      using value_type = T;
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4244)
+#endif
+      template <typename... Args>
+      inline explicit constexpr alt(in_place_t, Args &&... args)
+          : value(lib::forward<Args>(args)...) {}
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+      T value;
+    };
+
+    template <Trait DestructibleTrait, std::size_t Index, typename... Ts>
+    union recursive_union;
+
+    template <Trait DestructibleTrait, std::size_t Index>
+    union recursive_union<DestructibleTrait, Index> {};
+
+#define MPARK_VARIANT_RECURSIVE_UNION(destructible_trait, destructor)      \
+  template <std::size_t Index, typename T, typename... Ts>                 \
+  union recursive_union<destructible_trait, Index, T, Ts...> {             \
+    public:                                                                \
+    inline explicit constexpr recursive_union(valueless_t) noexcept        \
+        : dummy_{} {}                                                      \
+                                                                           \
+    template <typename... Args>                                            \
+    inline explicit constexpr recursive_union(in_place_index_t<0>,         \
+                                              Args &&... args)             \
+        : head_(in_place_t{}, lib::forward<Args>(args)...) {}              \
+                                                                           \
+    template <std::size_t I, typename... Args>                             \
+    inline explicit constexpr recursive_union(in_place_index_t<I>,         \
+                                              Args &&... args)             \
+        : tail_(in_place_index_t<I - 1>{}, lib::forward<Args>(args)...) {} \
+                                                                           \
+    recursive_union(const recursive_union &) = default;                    \
+    recursive_union(recursive_union &&) = default;                         \
+                                                                           \
+    destructor                                                             \
+                                                                           \
+    recursive_union &operator=(const recursive_union &) = default;         \
+    recursive_union &operator=(recursive_union &&) = default;              \
+                                                                           \
+    private:                                                               \
+    char dummy_;                                                           \
+    alt<Index, T> head_;                                                   \
+    recursive_union<destructible_trait, Index + 1, Ts...> tail_;           \
+                                                                           \
+    friend struct access::recursive_union;                                 \
+  }
+
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::TriviallyAvailable,
+                                  ~recursive_union() = default;);
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::Available,
+                                  ~recursive_union() {});
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::Unavailable,
+                                  ~recursive_union() = delete;);
+
+#undef MPARK_VARIANT_RECURSIVE_UNION
+
+    using index_t = unsigned int;
+
+    template <Trait DestructibleTrait, typename... Ts>
+    class base {
+      public:
+      inline explicit constexpr base(valueless_t tag) noexcept
+          : data_(tag), index_(static_cast<index_t>(-1)) {}
+
+      template <std::size_t I, typename... Args>
+      inline explicit constexpr base(in_place_index_t<I>, Args &&... args)
+          : data_(in_place_index_t<I>{}, lib::forward<Args>(args)...),
+            index_(I) {}
+
+      inline constexpr bool valueless_by_exception() const noexcept {
+        return index_ == static_cast<index_t>(-1);
+      }
+
+      inline constexpr std::size_t index() const noexcept {
+        return valueless_by_exception() ? variant_npos : index_;
+      }
+
+      protected:
+      using data_t = recursive_union<DestructibleTrait, 0, Ts...>;
+
+      friend inline constexpr base &as_base(base &b) { return b; }
+      friend inline constexpr const base &as_base(const base &b) { return b; }
+      friend inline constexpr base &&as_base(base &&b) { return lib::move(b); }
+      friend inline constexpr const base &&as_base(const base &&b) { return lib::move(b); }
+
+      friend inline constexpr data_t &data(base &b) { return b.data_; }
+      friend inline constexpr const data_t &data(const base &b) { return b.data_; }
+      friend inline constexpr data_t &&data(base &&b) { return lib::move(b).data_; }
+      friend inline constexpr const data_t &&data(const base &&b) { return lib::move(b).data_; }
+
+      inline static constexpr std::size_t size() { return sizeof...(Ts); }
+
+      data_t data_;
+      index_t index_;
+
+      friend struct access::base;
+      friend struct visitation::base;
+    };
+
+    struct dtor {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+      template <typename Alt>
+      inline void operator()(Alt &alt) const noexcept { alt.~Alt(); }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+    };
+
+#if defined(_MSC_VER) && _MSC_VER < 1910
+#define INHERITING_CTOR(type, base)               \
+  template <typename... Args>                     \
+  inline explicit constexpr type(Args &&... args) \
+      : base(lib::forward<Args>(args)...) {}
+#else
+#define INHERITING_CTOR(type, base) using base::base;
+#endif
+
+    template <typename Traits, Trait = Traits::destructible_trait>
+    class destructor;
+
+#define MPARK_VARIANT_DESTRUCTOR(destructible_trait, definition, destroy) \
+  template <typename... Ts>                                               \
+  class destructor<traits<Ts...>, destructible_trait>                     \
+      : public base<destructible_trait, Ts...> {                          \
+    using super = base<destructible_trait, Ts...>;                        \
+                                                                          \
+    public:                                                               \
+    INHERITING_CTOR(destructor, super)                                    \
+    using super::operator=;                                               \
+                                                                          \
+    destructor(const destructor &) = default;                             \
+    destructor(destructor &&) = default;                                  \
+    definition                                                            \
+    destructor &operator=(const destructor &) = default;                  \
+    destructor &operator=(destructor &&) = default;                       \
+                                                                          \
+    protected:                                                            \
+    destroy                                                               \
+  }
+
+    MPARK_VARIANT_DESTRUCTOR(
+        Trait::TriviallyAvailable,
+        ~destructor() = default;,
+        inline void destroy() noexcept {
+          this->index_ = static_cast<index_t>(-1);
+        });
+
+    MPARK_VARIANT_DESTRUCTOR(
+        Trait::Available,
+        ~destructor() { destroy(); },
+        inline void destroy() noexcept {
+          if (!this->valueless_by_exception()) {
+            visitation::base::visit_alt(dtor{}, *this);
+          }
+          this->index_ = static_cast<index_t>(-1);
+        });
+
+    MPARK_VARIANT_DESTRUCTOR(
+        Trait::Unavailable,
+        ~destructor() = delete;,
+        inline void destroy() noexcept = delete;);
+
+#undef MPARK_VARIANT_DESTRUCTOR
+
+    template <typename Traits>
+    class constructor : public destructor<Traits> {
+      using super = destructor<Traits>;
+
+      public:
+      INHERITING_CTOR(constructor, super)
+      using super::operator=;
+
+      protected:
+#ifndef MPARK_GENERIC_LAMBDAS
+      struct ctor {
+        template <typename LhsAlt, typename RhsAlt>
+        inline void operator()(LhsAlt &lhs_alt, RhsAlt &&rhs_alt) const {
+          constructor::construct_alt(lhs_alt,
+                                     lib::forward<RhsAlt>(rhs_alt).value);
+        }
+      };
+#endif
+
+      template <std::size_t I, typename T, typename... Args>
+      inline static T &construct_alt(alt<I, T> &a, Args &&... args) {
+        ::new (static_cast<void *>(lib::addressof(a)))
+            alt<I, T>(in_place_t{}, lib::forward<Args>(args)...);
+        return a.value;
+      }
+
+      template <typename Rhs>
+      inline static void generic_construct(constructor &lhs, Rhs &&rhs) {
+        lhs.destroy();
+        if (!rhs.valueless_by_exception()) {
+          visitation::base::visit_alt_at(
+              rhs.index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+              [](auto &lhs_alt, auto &&rhs_alt) {
+                constructor::construct_alt(
+                    lhs_alt, lib::forward<decltype(rhs_alt)>(rhs_alt).value);
+              }
+#else
+              ctor{}
+#endif
+              ,
+              lhs,
+              lib::forward<Rhs>(rhs));
+          lhs.index_ = rhs.index_;
+        }
+      }
+    };
+
+    template <typename Traits, Trait = Traits::move_constructible_trait>
+    class move_constructor;
+
+#define MPARK_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, definition) \
+  template <typename... Ts>                                                  \
+  class move_constructor<traits<Ts...>, move_constructible_trait>            \
+      : public constructor<traits<Ts...>> {                                  \
+    using super = constructor<traits<Ts...>>;                                \
+                                                                             \
+    public:                                                                  \
+    INHERITING_CTOR(move_constructor, super)                                 \
+    using super::operator=;                                                  \
+                                                                             \
+    move_constructor(const move_constructor &) = default;                    \
+    definition                                                               \
+    ~move_constructor() = default;                                           \
+    move_constructor &operator=(const move_constructor &) = default;         \
+    move_constructor &operator=(move_constructor &&) = default;              \
+  }
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::TriviallyAvailable,
+        move_constructor(move_constructor &&that) = default;);
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::Available,
+        move_constructor(move_constructor &&that) noexcept(
+            all(std::is_nothrow_move_constructible<Ts>::value...))
+            : move_constructor(valueless_t{}) {
+          this->generic_construct(*this, lib::move(that));
+        });
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::Unavailable,
+        move_constructor(move_constructor &&) = delete;);
+
+#undef MPARK_VARIANT_MOVE_CONSTRUCTOR
+
+    template <typename Traits, Trait = Traits::copy_constructible_trait>
+    class copy_constructor;
+
+#define MPARK_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, definition) \
+  template <typename... Ts>                                                  \
+  class copy_constructor<traits<Ts...>, copy_constructible_trait>            \
+      : public move_constructor<traits<Ts...>> {                             \
+    using super = move_constructor<traits<Ts...>>;                           \
+                                                                             \
+    public:                                                                  \
+    INHERITING_CTOR(copy_constructor, super)                                 \
+    using super::operator=;                                                  \
+                                                                             \
+    definition                                                               \
+    copy_constructor(copy_constructor &&) = default;                         \
+    ~copy_constructor() = default;                                           \
+    copy_constructor &operator=(const copy_constructor &) = default;         \
+    copy_constructor &operator=(copy_constructor &&) = default;              \
+  }
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::TriviallyAvailable,
+        copy_constructor(const copy_constructor &that) = default;);
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::Available,
+        copy_constructor(const copy_constructor &that)
+            : copy_constructor(valueless_t{}) {
+          this->generic_construct(*this, that);
+        });
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::Unavailable,
+        copy_constructor(const copy_constructor &) = delete;);
+
+#undef MPARK_VARIANT_COPY_CONSTRUCTOR
+
+    template <typename Traits>
+    class assignment : public copy_constructor<Traits> {
+      using super = copy_constructor<Traits>;
+
+      public:
+      INHERITING_CTOR(assignment, super)
+      using super::operator=;
+
+      template <std::size_t I, typename... Args>
+      inline /* auto & */ auto emplace(Args &&... args)
+          -> decltype(this->construct_alt(access::base::get_alt<I>(*this),
+                                          lib::forward<Args>(args)...)) {
+        this->destroy();
+        auto &result = this->construct_alt(access::base::get_alt<I>(*this),
+                                           lib::forward<Args>(args)...);
+        this->index_ = I;
+        return result;
+      }
+
+      protected:
+#ifndef MPARK_GENERIC_LAMBDAS
+      template <typename That>
+      struct assigner {
+        template <typename ThisAlt, typename ThatAlt>
+        inline void operator()(ThisAlt &this_alt, ThatAlt &&that_alt) const {
+          self->assign_alt(this_alt, lib::forward<ThatAlt>(that_alt).value);
+        }
+        assignment *self;
+      };
+#endif
+
+      template <std::size_t I, typename T, typename Arg>
+      inline void assign_alt(alt<I, T> &a, Arg &&arg) {
+        if (this->index() == I) {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4244)
+#endif
+          a.value = lib::forward<Arg>(arg);
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+        } else {
+          struct {
+            void operator()(std::true_type) const {
+              this_->emplace<I>(lib::forward<Arg>(arg_));
+            }
+            void operator()(std::false_type) const {
+              this_->emplace<I>(T(lib::forward<Arg>(arg_)));
+            }
+            assignment *this_;
+            Arg &&arg_;
+          } impl{this, lib::forward<Arg>(arg)};
+          impl(lib::bool_constant<
+                   std::is_nothrow_constructible<T, Arg>::value ||
+                   !std::is_nothrow_move_constructible<T>::value>{});
+        }
+      }
+
+      template <typename That>
+      inline void generic_assign(That &&that) {
+        if (this->valueless_by_exception() && that.valueless_by_exception()) {
+          // do nothing.
+        } else if (that.valueless_by_exception()) {
+          this->destroy();
+        } else {
+          visitation::base::visit_alt_at(
+              that.index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+              [this](auto &this_alt, auto &&that_alt) {
+                this->assign_alt(
+                    this_alt, lib::forward<decltype(that_alt)>(that_alt).value);
+              }
+#else
+              assigner<That>{this}
+#endif
+              ,
+              *this,
+              lib::forward<That>(that));
+        }
+      }
+    };
+
+    template <typename Traits, Trait = Traits::move_assignable_trait>
+    class move_assignment;
+
+#define MPARK_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, definition) \
+  template <typename... Ts>                                              \
+  class move_assignment<traits<Ts...>, move_assignable_trait>            \
+      : public assignment<traits<Ts...>> {                               \
+    using super = assignment<traits<Ts...>>;                             \
+                                                                         \
+    public:                                                              \
+    INHERITING_CTOR(move_assignment, super)                              \
+    using super::operator=;                                              \
+                                                                         \
+    move_assignment(const move_assignment &) = default;                  \
+    move_assignment(move_assignment &&) = default;                       \
+    ~move_assignment() = default;                                        \
+    move_assignment &operator=(const move_assignment &) = default;       \
+    definition                                                           \
+  }
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::TriviallyAvailable,
+        move_assignment &operator=(move_assignment &&that) = default;);
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::Available,
+        move_assignment &
+        operator=(move_assignment &&that) noexcept(
+            all((std::is_nothrow_move_constructible<Ts>::value &&
+                 std::is_nothrow_move_assignable<Ts>::value)...)) {
+          this->generic_assign(lib::move(that));
+          return *this;
+        });
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::Unavailable,
+        move_assignment &operator=(move_assignment &&) = delete;);
+
+#undef MPARK_VARIANT_MOVE_ASSIGNMENT
+
+    template <typename Traits, Trait = Traits::copy_assignable_trait>
+    class copy_assignment;
+
+#define MPARK_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, definition) \
+  template <typename... Ts>                                              \
+  class copy_assignment<traits<Ts...>, copy_assignable_trait>            \
+      : public move_assignment<traits<Ts...>> {                          \
+    using super = move_assignment<traits<Ts...>>;                        \
+                                                                         \
+    public:                                                              \
+    INHERITING_CTOR(copy_assignment, super)                              \
+    using super::operator=;                                              \
+                                                                         \
+    copy_assignment(const copy_assignment &) = default;                  \
+    copy_assignment(copy_assignment &&) = default;                       \
+    ~copy_assignment() = default;                                        \
+    definition                                                           \
+    copy_assignment &operator=(copy_assignment &&) = default;            \
+  }
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::TriviallyAvailable,
+        copy_assignment &operator=(const copy_assignment &that) = default;);
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::Available,
+        copy_assignment &operator=(const copy_assignment &that) {
+          this->generic_assign(that);
+          return *this;
+        });
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::Unavailable,
+        copy_assignment &operator=(const copy_assignment &) = delete;);
+
+#undef MPARK_VARIANT_COPY_ASSIGNMENT
+
+    template <typename... Ts>
+    class impl : public copy_assignment<traits<Ts...>> {
+      using super = copy_assignment<traits<Ts...>>;
+
+      public:
+      INHERITING_CTOR(impl, super)
+      using super::operator=;
+
+      template <std::size_t I, typename Arg>
+      inline void assign(Arg &&arg) {
+        this->assign_alt(access::base::get_alt<I>(*this),
+                         lib::forward<Arg>(arg));
+      }
+
+      inline void swap(impl &that) {
+        if (this->valueless_by_exception() && that.valueless_by_exception()) {
+          // do nothing.
+        } else if (this->index() == that.index()) {
+          visitation::base::visit_alt_at(this->index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+                                         [](auto &this_alt, auto &that_alt) {
+                                           using std::swap;
+                                           swap(this_alt.value,
+                                                that_alt.value);
+                                         }
+#else
+                                         swapper{}
+#endif
+                                         ,
+                                         *this,
+                                         that);
+        } else {
+          impl *lhs = this;
+          impl *rhs = lib::addressof(that);
+          if (lhs->move_nothrow() && !rhs->move_nothrow()) {
+            std::swap(lhs, rhs);
+          }
+          impl tmp(lib::move(*rhs));
+#ifdef MPARK_EXCEPTIONS
+          // EXTENSION: When the move construction of `lhs` into `rhs` throws
+          // and `tmp` is nothrow move constructible then we move `tmp` back
+          // into `rhs` and provide the strong exception safety guarantee.
+          try {
+            this->generic_construct(*rhs, lib::move(*lhs));
+          } catch (...) {
+            if (tmp.move_nothrow()) {
+              this->generic_construct(*rhs, lib::move(tmp));
+            }
+            throw;
+          }
+#else
+          this->generic_construct(*rhs, lib::move(*lhs));
+#endif
+          this->generic_construct(*lhs, lib::move(tmp));
+        }
+      }
+
+      private:
+#ifndef MPARK_GENERIC_LAMBDAS
+      struct swapper {
+        template <typename ThisAlt, typename ThatAlt>
+        inline void operator()(ThisAlt &this_alt, ThatAlt &that_alt) const {
+          using std::swap;
+          swap(this_alt.value, that_alt.value);
+        }
+      };
+#endif
+
+      inline constexpr bool move_nothrow() const {
+        return this->valueless_by_exception() ||
+               lib::array<bool, sizeof...(Ts)>{
+                   {std::is_nothrow_move_constructible<Ts>::value...}
+               }[this->index()];
+      }
+    };
+
+    template <typename... Ts>
+    struct overload;
+
+    template <>
+    struct overload<> { void operator()() const {} };
+
+    template <typename T, typename... Ts>
+    struct overload<T, Ts...> : overload<Ts...> {
+      using overload<Ts...>::operator();
+      lib::identity<T> operator()(T) const { return {}; }
+    };
+
+    template <typename T, typename... Ts>
+    using best_match_t =
+        typename lib::invoke_result_t<overload<Ts...>, T &&>::type;
+
+  }  // detail
+
+  template <typename... Ts>
+  class variant {
+    static_assert(0 < sizeof...(Ts),
+                  "variant must consist of at least one alternative.");
+
+    static_assert(lib::all<!std::is_array<Ts>::value...>::value,
+                  "variant can not have an array type as an alternative.");
+
+    static_assert(lib::all<!std::is_reference<Ts>::value...>::value,
+                  "variant can not have a reference type as an alternative.");
+
+    static_assert(lib::all<!std::is_void<Ts>::value...>::value,
+                  "variant can not have a void type as an alternative.");
+
+    public:
+    template <
+        typename Front = lib::type_pack_element_t<0, Ts...>,
+        lib::enable_if_t<std::is_default_constructible<Front>::value, int> = 0>
+    inline constexpr variant() noexcept(
+        std::is_nothrow_default_constructible<Front>::value)
+        : impl_(in_place_index_t<0>{}) {}
+
+    variant(const variant &) = default;
+    variant(variant &&) = default;
+
+    template <typename Arg,
+              lib::enable_if_t<!std::is_same<lib::decay_t<Arg>, variant>::value,
+                               int> = 0,
+              typename T = detail::best_match_t<Arg, Ts...>,
+              std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+              lib::enable_if_t<std::is_constructible<T, Arg>::value, int> = 0>
+    inline constexpr variant(Arg &&arg) noexcept(
+        std::is_nothrow_constructible<T, Arg>::value)
+        : impl_(in_place_index_t<I>{}, lib::forward<Arg>(arg)) {}
+
+    template <
+        std::size_t I,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline explicit constexpr variant(
+        in_place_index_t<I>,
+        Args &&... args) noexcept(std::is_nothrow_constructible<T,
+                                                                Args...>::value)
+        : impl_(in_place_index_t<I>{}, lib::forward<Args>(args)...) {}
+
+    template <
+        std::size_t I,
+        typename Up,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline explicit constexpr variant(
+        in_place_index_t<I>,
+        std::initializer_list<Up> il,
+        Args &&... args) noexcept(std::
+                                      is_nothrow_constructible<
+                                          T,
+                                          std::initializer_list<Up> &,
+                                          Args...>::value)
+        : impl_(in_place_index_t<I>{}, il, lib::forward<Args>(args)...) {}
+
+    template <
+        typename T,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline explicit constexpr variant(
+        in_place_type_t<T>,
+        Args &&... args) noexcept(std::is_nothrow_constructible<T,
+                                                                Args...>::value)
+        : impl_(in_place_index_t<I>{}, lib::forward<Args>(args)...) {}
+
+    template <
+        typename T,
+        typename Up,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline explicit constexpr variant(
+        in_place_type_t<T>,
+        std::initializer_list<Up> il,
+        Args &&... args) noexcept(std::
+                                      is_nothrow_constructible<
+                                          T,
+                                          std::initializer_list<Up> &,
+                                          Args...>::value)
+        : impl_(in_place_index_t<I>{}, il, lib::forward<Args>(args)...) {}
+
+    ~variant() = default;
+
+    variant &operator=(const variant &) = default;
+    variant &operator=(variant &&) = default;
+
+    template <typename Arg,
+              lib::enable_if_t<!std::is_same<lib::decay_t<Arg>, variant>::value,
+                               int> = 0,
+              typename T = detail::best_match_t<Arg, Ts...>,
+              std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+              lib::enable_if_t<(std::is_assignable<T &, Arg>::value &&
+                                std::is_constructible<T, Arg>::value),
+                               int> = 0>
+    inline variant &operator=(Arg &&arg) noexcept(
+        (std::is_nothrow_assignable<T &, Arg>::value &&
+         std::is_nothrow_constructible<T, Arg>::value)) {
+      impl_.template assign<I>(lib::forward<Arg>(arg));
+      return *this;
+    }
+
+    template <
+        std::size_t I,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline T &emplace(Args &&... args) {
+      return impl_.template emplace<I>(lib::forward<Args>(args)...);
+    }
+
+    template <
+        std::size_t I,
+        typename Up,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline T &emplace(std::initializer_list<Up> il, Args &&... args) {
+      return impl_.template emplace<I>(il, lib::forward<Args>(args)...);
+    }
+
+    template <
+        typename T,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline T &emplace(Args &&... args) {
+      return impl_.template emplace<I>(lib::forward<Args>(args)...);
+    }
+
+    template <
+        typename T,
+        typename Up,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline T &emplace(std::initializer_list<Up> il, Args &&... args) {
+      return impl_.template emplace<I>(il, lib::forward<Args>(args)...);
+    }
+
+    inline constexpr bool valueless_by_exception() const noexcept {
+      return impl_.valueless_by_exception();
+    }
+
+    inline constexpr std::size_t index() const noexcept {
+      return impl_.index();
+    }
+
+    template <
+        bool Dummy = true,
+        lib::enable_if_t<lib::all<Dummy,
+                                  (std::is_move_constructible<Ts>::value &&
+                                   lib::is_swappable<Ts>::value)...>::value,
+                         int> = 0>
+    inline void swap(variant &that) noexcept(
+        lib::all<(std::is_nothrow_move_constructible<Ts>::value &&
+                  lib::is_nothrow_swappable<Ts>::value)...>::value) {
+      impl_.swap(that.impl_);
+    }
+
+    private:
+    detail::impl<Ts...> impl_;
+
+    friend struct detail::access::variant;
+    friend struct detail::visitation::variant;
+  };
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept {
+    return v.index() == I;
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept {
+    return holds_alternative<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  namespace detail {
+    template <std::size_t I, typename V>
+    struct generic_get_impl {
+      constexpr generic_get_impl(int) {}
+
+      constexpr AUTO_REFREF operator()(V &&v) const
+        AUTO_REFREF_RETURN(
+            access::variant::get_alt<I>(lib::forward<V>(v)).value)
+    };
+
+    template <std::size_t I, typename V>
+    inline constexpr AUTO_REFREF generic_get(V &&v)
+      AUTO_REFREF_RETURN(generic_get_impl<I, V>(
+          holds_alternative<I>(v) ? 0 : (throw_bad_variant_access(), 0))(
+          lib::forward<V>(v)))
+  }  // namespace detail
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr variant_alternative_t<I, variant<Ts...>> &get(
+      variant<Ts...> &v) {
+    return detail::generic_get<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr variant_alternative_t<I, variant<Ts...>> &&get(
+      variant<Ts...> &&v) {
+    return detail::generic_get<I>(lib::move(v));
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr const variant_alternative_t<I, variant<Ts...>> &get(
+      const variant<Ts...> &v) {
+    return detail::generic_get<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr const variant_alternative_t<I, variant<Ts...>> &&get(
+      const variant<Ts...> &&v) {
+    return detail::generic_get<I>(lib::move(v));
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr T &get(variant<Ts...> &v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr T &&get(variant<Ts...> &&v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(lib::move(v));
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr const T &get(const variant<Ts...> &v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr const T &&get(const variant<Ts...> &&v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(lib::move(v));
+  }
+
+  namespace detail {
+
+    template <std::size_t I, typename V>
+    inline constexpr /* auto * */ AUTO generic_get_if(V *v) noexcept
+      AUTO_RETURN(v && holds_alternative<I>(*v)
+                      ? lib::addressof(access::variant::get_alt<I>(*v).value)
+                      : nullptr)
+
+  }  // namespace detail
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr lib::add_pointer_t<variant_alternative_t<I, variant<Ts...>>>
+  get_if(variant<Ts...> *v) noexcept {
+    return detail::generic_get_if<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr lib::add_pointer_t<
+      const variant_alternative_t<I, variant<Ts...>>>
+  get_if(const variant<Ts...> *v) noexcept {
+    return detail::generic_get_if<I>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr lib::add_pointer_t<T>
+  get_if(variant<Ts...> *v) noexcept {
+    return get_if<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr lib::add_pointer_t<const T>
+  get_if(const variant<Ts...> *v) noexcept {
+    return get_if<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator==(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using lib::equal_to;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.index() != rhs.index()) return false;
+    if (lhs.valueless_by_exception()) return true;
+    return variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs);
+#else
+    return lhs.index() == rhs.index() &&
+           (lhs.valueless_by_exception() ||
+            variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator!=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using lib::not_equal_to;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.index() != rhs.index()) return true;
+    if (lhs.valueless_by_exception()) return false;
+    return variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs);
+#else
+    return lhs.index() != rhs.index() ||
+           (!lhs.valueless_by_exception() &&
+            variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator<(const variant<Ts...> &lhs,
+                                  const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using lib::less;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (rhs.valueless_by_exception()) return false;
+    if (lhs.valueless_by_exception()) return true;
+    if (lhs.index() < rhs.index()) return true;
+    if (lhs.index() > rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), less{}, lhs, rhs);
+#else
+    return !rhs.valueless_by_exception() &&
+           (lhs.valueless_by_exception() || lhs.index() < rhs.index() ||
+            (lhs.index() == rhs.index() &&
+             variant::visit_value_at(lhs.index(), less{}, lhs, rhs)));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator>(const variant<Ts...> &lhs,
+                                  const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using lib::greater;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.valueless_by_exception()) return false;
+    if (rhs.valueless_by_exception()) return true;
+    if (lhs.index() > rhs.index()) return true;
+    if (lhs.index() < rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), greater{}, lhs, rhs);
+#else
+    return !lhs.valueless_by_exception() &&
+           (rhs.valueless_by_exception() || lhs.index() > rhs.index() ||
+            (lhs.index() == rhs.index() &&
+             variant::visit_value_at(lhs.index(), greater{}, lhs, rhs)));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator<=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using lib::less_equal;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.valueless_by_exception()) return true;
+    if (rhs.valueless_by_exception()) return false;
+    if (lhs.index() < rhs.index()) return true;
+    if (lhs.index() > rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs);
+#else
+    return lhs.valueless_by_exception() ||
+           (!rhs.valueless_by_exception() &&
+            (lhs.index() < rhs.index() ||
+             (lhs.index() == rhs.index() &&
+              variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs))));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator>=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using lib::greater_equal;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (rhs.valueless_by_exception()) return true;
+    if (lhs.valueless_by_exception()) return false;
+    if (lhs.index() > rhs.index()) return true;
+    if (lhs.index() < rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), greater_equal{}, lhs, rhs);
+#else
+    return rhs.valueless_by_exception() ||
+           (!lhs.valueless_by_exception() &&
+            (lhs.index() > rhs.index() ||
+             (lhs.index() == rhs.index() &&
+              variant::visit_value_at(
+                  lhs.index(), greater_equal{}, lhs, rhs))));
+#endif
+  }
+
+  template <typename Visitor, typename... Vs>
+  inline constexpr DECLTYPE_AUTO visit(Visitor &&visitor, Vs &&... vs)
+    DECLTYPE_AUTO_RETURN(
+        (detail::all(!vs.valueless_by_exception()...)
+             ? (void)0
+             : throw_bad_variant_access()),
+        detail::visitation::variant::visit_value(lib::forward<Visitor>(visitor),
+                                                 lib::forward<Vs>(vs)...))
+
+  struct monostate {};
+
+  inline constexpr bool operator<(monostate, monostate) noexcept {
+    return false;
+  }
+
+  inline constexpr bool operator>(monostate, monostate) noexcept {
+    return false;
+  }
+
+  inline constexpr bool operator<=(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator>=(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator==(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator!=(monostate, monostate) noexcept {
+    return false;
+  }
+
+  template <typename... Ts>
+  inline auto swap(variant<Ts...> &lhs,
+                   variant<Ts...> &rhs) noexcept(noexcept(lhs.swap(rhs)))
+      -> decltype(lhs.swap(rhs)) {
+    lhs.swap(rhs);
+  }
+
+  namespace detail {
+
+    template <typename T, typename...>
+    using enabled_type = T;
+
+    namespace hash {
+
+      template <typename H, typename K>
+      constexpr bool meets_requirements() {
+        return std::is_copy_constructible<H>::value &&
+               std::is_move_constructible<H>::value &&
+               lib::is_invocable_r<std::size_t, H, const K &>::value;
+      }
+
+      template <typename K>
+      constexpr bool is_enabled() {
+        using H = std::hash<K>;
+        return meets_requirements<H, K>() &&
+               std::is_default_constructible<H>::value &&
+               std::is_copy_assignable<H>::value &&
+               std::is_move_assignable<H>::value;
+      }
+
+    }  // namespace hash
+
+  }  // namespace detail
+
+#undef AUTO
+#undef AUTO_RETURN
+
+#undef AUTO_REFREF
+#undef AUTO_REFREF_RETURN
+
+#undef DECLTYPE_AUTO
+#undef DECLTYPE_AUTO_RETURN
+
+}  // namespace mpark
+
+namespace std {
+
+  template <typename... Ts>
+  struct hash<mpark::detail::enabled_type<
+      mpark::variant<Ts...>,
+      mpark::lib::enable_if_t<mpark::lib::all<mpark::detail::hash::is_enabled<
+          mpark::lib::remove_const_t<Ts>>()...>::value>>> {
+    using argument_type = mpark::variant<Ts...>;
+    using result_type = std::size_t;
+
+    inline result_type operator()(const argument_type &v) const {
+      using mpark::detail::visitation::variant;
+      std::size_t result =
+          v.valueless_by_exception()
+              ? 299792458  // Random value chosen by the universe upon creation
+              : variant::visit_alt(
+#ifdef MPARK_GENERIC_LAMBDAS
+                    [](const auto &alt) {
+                      using alt_type = mpark::lib::decay_t<decltype(alt)>;
+                      using value_type = mpark::lib::remove_const_t<
+                          typename alt_type::value_type>;
+                      return hash<value_type>{}(alt.value);
+                    }
+#else
+                    hasher{}
+#endif
+                    ,
+                    v);
+      return hash_combine(result, hash<std::size_t>{}(v.index()));
+    }
+
+    private:
+#ifndef MPARK_GENERIC_LAMBDAS
+    struct hasher {
+      template <typename Alt>
+      inline std::size_t operator()(const Alt &alt) const {
+        using alt_type = mpark::lib::decay_t<Alt>;
+        using value_type =
+            mpark::lib::remove_const_t<typename alt_type::value_type>;
+        return hash<value_type>{}(alt.value);
+      }
+    };
+#endif
+
+    static std::size_t hash_combine(std::size_t lhs, std::size_t rhs) {
+      return lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
+    }
+  };
+
+  template <>
+  struct hash<mpark::monostate> {
+    using argument_type = mpark::monostate;
+    using result_type = std::size_t;
+
+    inline result_type operator()(const argument_type &) const noexcept {
+      return 66740831;  // return a fundamentally attractive random value.
+    }
+  };
+
+}  // namespace std
+
+#endif  // MPARK_VARIANT_HPP

+ 2424 - 0
src/external/mpark-variant/v1.2.1/variant.hpp

@@ -0,0 +1,2424 @@
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_VARIANT_HPP
+#define MPARK_VARIANT_HPP
+
+/*
+   variant synopsis
+
+namespace std {
+
+  // 20.7.2, class template variant
+  template <class... Types>
+  class variant {
+  public:
+
+    // 20.7.2.1, constructors
+    constexpr variant() noexcept(see below);
+    variant(const variant&);
+    variant(variant&&) noexcept(see below);
+
+    template <class T> constexpr variant(T&&) noexcept(see below);
+
+    template <class T, class... Args>
+    constexpr explicit variant(in_place_type_t<T>, Args&&...);
+
+    template <class T, class U, class... Args>
+    constexpr explicit variant(
+        in_place_type_t<T>, initializer_list<U>, Args&&...);
+
+    template <size_t I, class... Args>
+    constexpr explicit variant(in_place_index_t<I>, Args&&...);
+
+    template <size_t I, class U, class... Args>
+    constexpr explicit variant(
+        in_place_index_t<I>, initializer_list<U>, Args&&...);
+
+    // 20.7.2.2, destructor
+    ~variant();
+
+    // 20.7.2.3, assignment
+    variant& operator=(const variant&);
+    variant& operator=(variant&&) noexcept(see below);
+
+    template <class T> variant& operator=(T&&) noexcept(see below);
+
+    // 20.7.2.4, modifiers
+    template <class T, class... Args>
+    T& emplace(Args&&...);
+
+    template <class T, class U, class... Args>
+    T& emplace(initializer_list<U>, Args&&...);
+
+    template <size_t I, class... Args>
+    variant_alternative<I, variant>& emplace(Args&&...);
+
+    template <size_t I, class U, class...  Args>
+    variant_alternative<I, variant>& emplace(initializer_list<U>, Args&&...);
+
+    // 20.7.2.5, value status
+    constexpr bool valueless_by_exception() const noexcept;
+    constexpr size_t index() const noexcept;
+
+    // 20.7.2.6, swap
+    void swap(variant&) noexcept(see below);
+  };
+
+  // 20.7.3, variant helper classes
+  template <class T> struct variant_size; // undefined
+
+  template <class T>
+  constexpr size_t variant_size_v = variant_size<T>::value;
+
+  template <class T> struct variant_size<const T>;
+  template <class T> struct variant_size<volatile T>;
+  template <class T> struct variant_size<const volatile T>;
+
+  template <class... Types>
+  struct variant_size<variant<Types...>>;
+
+  template <size_t I, class T> struct variant_alternative; // undefined
+
+  template <size_t I, class T>
+  using variant_alternative_t = typename variant_alternative<I, T>::type;
+
+  template <size_t I, class T> struct variant_alternative<I, const T>;
+  template <size_t I, class T> struct variant_alternative<I, volatile T>;
+  template <size_t I, class T> struct variant_alternative<I, const volatile T>;
+
+  template <size_t I, class... Types>
+  struct variant_alternative<I, variant<Types...>>;
+
+  constexpr size_t variant_npos = -1;
+
+  // 20.7.4, value access
+  template <class T, class... Types>
+  constexpr bool holds_alternative(const variant<Types...>&) noexcept;
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>>&
+  get(variant<Types...>&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>>&&
+  get(variant<Types...>&&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>> const&
+  get(const variant<Types...>&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>> const&&
+  get(const variant<Types...>&&);
+
+  template <class T, class...  Types>
+  constexpr T& get(variant<Types...>&);
+
+  template <class T, class... Types>
+  constexpr T&& get(variant<Types...>&&);
+
+  template <class T, class... Types>
+  constexpr const T& get(const variant<Types...>&);
+
+  template <class T, class... Types>
+  constexpr const T&& get(const variant<Types...>&&);
+
+  template <size_t I, class... Types>
+  constexpr add_pointer_t<variant_alternative_t<I, variant<Types...>>>
+  get_if(variant<Types...>*) noexcept;
+
+  template <size_t I, class... Types>
+  constexpr add_pointer_t<const variant_alternative_t<I, variant<Types...>>>
+  get_if(const variant<Types...>*) noexcept;
+
+  template <class T, class... Types>
+  constexpr add_pointer_t<T>
+  get_if(variant<Types...>*) noexcept;
+
+  template <class T, class... Types>
+  constexpr add_pointer_t<const T>
+  get_if(const variant<Types...>*) noexcept;
+
+  // 20.7.5, relational operators
+  template <class... Types>
+  constexpr bool operator==(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator!=(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator<(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator>(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator<=(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator>=(const variant<Types...>&, const variant<Types...>&);
+
+  // 20.7.6, visitation
+  template <class Visitor, class... Variants>
+  constexpr see below visit(Visitor&&, Variants&&...);
+
+  // 20.7.7, class monostate
+  struct monostate;
+
+  // 20.7.8, monostate relational operators
+  constexpr bool operator<(monostate, monostate) noexcept;
+  constexpr bool operator>(monostate, monostate) noexcept;
+  constexpr bool operator<=(monostate, monostate) noexcept;
+  constexpr bool operator>=(monostate, monostate) noexcept;
+  constexpr bool operator==(monostate, monostate) noexcept;
+  constexpr bool operator!=(monostate, monostate) noexcept;
+
+  // 20.7.9, specialized algorithms
+  template <class... Types>
+  void swap(variant<Types...>&, variant<Types...>&) noexcept(see below);
+
+  // 20.7.10, class bad_variant_access
+  class bad_variant_access;
+
+  // 20.7.11, hash support
+  template <class T> struct hash;
+  template <class... Types> struct hash<variant<Types...>>;
+  template <> struct hash<monostate>;
+
+} // namespace std
+
+*/
+
+#include <cstddef>
+#include <exception>
+#include <functional>
+#include <initializer_list>
+#include <new>
+#include <type_traits>
+#include <utility>
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_CONFIG_HPP
+#define MPARK_CONFIG_HPP
+
+// MSVC 2015 Update 3.
+#if __cplusplus < 201103L && (!defined(_MSC_VER) || _MSC_FULL_VER < 190024215)
+#error "MPark.Variant requires C++11 support."
+#endif
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+#ifndef __has_include
+#define __has_include(x) 0
+#endif
+
+#ifndef __has_feature
+#define __has_feature(x) 0
+#endif
+
+#if __has_builtin(__builtin_addressof) || \
+    (defined(__GNUC__) && __GNUC__ >= 7) || defined(_MSC_VER)
+#define MPARK_BUILTIN_ADDRESSOF
+#endif
+
+#if __has_builtin(__type_pack_element)
+#define MPARK_TYPE_PACK_ELEMENT
+#endif
+
+#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304
+#define MPARK_CPP14_CONSTEXPR
+#endif
+
+#if __has_feature(cxx_exceptions) || defined(__cpp_exceptions)
+#define MPARK_EXCEPTIONS
+#endif
+
+#if defined(__cpp_generic_lambdas) || defined(_MSC_VER)
+#define MPARK_GENERIC_LAMBDAS
+#endif
+
+#if defined(__cpp_lib_integer_sequence)
+#define MPARK_INTEGER_SEQUENCE
+#endif
+
+#if defined(__cpp_return_type_deduction) || defined(_MSC_VER)
+#define MPARK_RETURN_TYPE_DEDUCTION
+#endif
+
+#if defined(__cpp_lib_transparent_operators) || defined(_MSC_VER)
+#define MPARK_TRANSPARENT_OPERATORS
+#endif
+
+#if defined(__cpp_variable_templates) || defined(_MSC_VER)
+#define MPARK_VARIABLE_TEMPLATES
+#endif
+
+#if !defined(__GLIBCXX__) || __has_include(<codecvt>)  // >= libstdc++-5
+#define MPARK_TRIVIALITY_TYPE_TRAITS
+#endif
+
+#endif  // MPARK_CONFIG_HPP
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_IN_PLACE_HPP
+#define MPARK_IN_PLACE_HPP
+
+#include <cstddef>
+
+
+namespace mpark {
+
+  struct in_place_t { explicit in_place_t() = default; };
+
+  template <std::size_t I>
+  struct in_place_index_t { explicit in_place_index_t() = default; };
+
+  template <typename T>
+  struct in_place_type_t { explicit in_place_type_t() = default; };
+
+#ifdef MPARK_VARIABLE_TEMPLATES
+  constexpr in_place_t in_place{};
+
+  template <std::size_t I> constexpr in_place_index_t<I> in_place_index{};
+
+  template <typename T> constexpr in_place_type_t<T> in_place_type{};
+#endif
+
+}  // namespace mpark
+
+#endif  // MPARK_IN_PLACE_HPP
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_LIB_HPP
+#define MPARK_LIB_HPP
+
+#include <memory>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+
+#define RETURN(...)                                          \
+  noexcept(noexcept(__VA_ARGS__)) -> decltype(__VA_ARGS__) { \
+    return __VA_ARGS__;                                      \
+  }
+
+namespace mpark {
+  namespace lib {
+    template <typename T>
+    struct identity { using type = T; };
+
+    inline namespace cpp14 {
+      template <typename T, std::size_t N>
+      struct array {
+        constexpr const T &operator[](std::size_t index) const {
+          return data[index];
+        }
+
+        T data[N == 0 ? 1 : N];
+      };
+
+      template <typename T>
+      using add_pointer_t = typename std::add_pointer<T>::type;
+
+      template <typename... Ts>
+      using common_type_t = typename std::common_type<Ts...>::type;
+
+      template <typename T>
+      using decay_t = typename std::decay<T>::type;
+
+      template <bool B, typename T = void>
+      using enable_if_t = typename std::enable_if<B, T>::type;
+
+      template <typename T>
+      using remove_const_t = typename std::remove_const<T>::type;
+
+      template <typename T>
+      using remove_reference_t = typename std::remove_reference<T>::type;
+
+      template <typename T>
+      inline constexpr T &&forward(remove_reference_t<T> &t) noexcept {
+        return static_cast<T &&>(t);
+      }
+
+      template <typename T>
+      inline constexpr T &&forward(remove_reference_t<T> &&t) noexcept {
+        static_assert(!std::is_lvalue_reference<T>::value,
+                      "can not forward an rvalue as an lvalue");
+        return static_cast<T &&>(t);
+      }
+
+      template <typename T>
+      inline constexpr remove_reference_t<T> &&move(T &&t) noexcept {
+        return static_cast<remove_reference_t<T> &&>(t);
+      }
+
+#ifdef MPARK_INTEGER_SEQUENCE
+      template <typename T, T... Is>
+      using integer_sequence = std::integer_sequence<T, Is...>;
+
+      template <std::size_t... Is>
+      using index_sequence = std::index_sequence<Is...>;
+
+      template <std::size_t N>
+      using make_index_sequence = std::make_index_sequence<N>;
+
+      template <typename... Ts>
+      using index_sequence_for = std::index_sequence_for<Ts...>;
+#else
+      template <typename T, T... Is>
+      struct integer_sequence {
+        using value_type = T;
+        static constexpr std::size_t size() noexcept { return sizeof...(Is); }
+      };
+
+      template <std::size_t... Is>
+      using index_sequence = integer_sequence<std::size_t, Is...>;
+
+      template <typename Lhs, typename Rhs>
+      struct make_index_sequence_concat;
+
+      template <std::size_t... Lhs, std::size_t... Rhs>
+      struct make_index_sequence_concat<index_sequence<Lhs...>,
+                                        index_sequence<Rhs...>>
+          : identity<index_sequence<Lhs..., (sizeof...(Lhs) + Rhs)...>> {};
+
+      template <std::size_t N>
+      struct make_index_sequence_impl;
+
+      template <std::size_t N>
+      using make_index_sequence = typename make_index_sequence_impl<N>::type;
+
+      template <std::size_t N>
+      struct make_index_sequence_impl
+          : make_index_sequence_concat<make_index_sequence<N / 2>,
+                                       make_index_sequence<N - (N / 2)>> {};
+
+      template <>
+      struct make_index_sequence_impl<0> : identity<index_sequence<>> {};
+
+      template <>
+      struct make_index_sequence_impl<1> : identity<index_sequence<0>> {};
+
+      template <typename... Ts>
+      using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
+#endif
+
+      // <functional>
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using equal_to = std::equal_to<>;
+#else
+      struct equal_to {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          RETURN(lib::forward<Lhs>(lhs) == lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using not_equal_to = std::not_equal_to<>;
+#else
+      struct not_equal_to {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          RETURN(lib::forward<Lhs>(lhs) != lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using less = std::less<>;
+#else
+      struct less {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          RETURN(lib::forward<Lhs>(lhs) < lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using greater = std::greater<>;
+#else
+      struct greater {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          RETURN(lib::forward<Lhs>(lhs) > lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using less_equal = std::less_equal<>;
+#else
+      struct less_equal {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          RETURN(lib::forward<Lhs>(lhs) <= lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using greater_equal = std::greater_equal<>;
+#else
+      struct greater_equal {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          RETURN(lib::forward<Lhs>(lhs) >= lib::forward<Rhs>(rhs))
+      };
+#endif
+    }  // namespace cpp14
+
+    inline namespace cpp17 {
+
+      // <type_traits>
+      template <bool B>
+      using bool_constant = std::integral_constant<bool, B>;
+
+      template <typename...>
+      struct voider : identity<void> {};
+
+      template <typename... Ts>
+      using void_t = typename voider<Ts...>::type;
+
+      namespace detail {
+        namespace swappable {
+
+          using std::swap;
+
+          template <typename T>
+          struct is_swappable_impl {
+            private:
+            template <typename U,
+                      typename = decltype(swap(std::declval<U &>(),
+                                               std::declval<U &>()))>
+            inline static std::true_type test(int);
+
+            template <typename U>
+            inline static std::false_type test(...);
+
+            public:
+            using type = decltype(test<T>(0));
+          };
+
+          template <typename T>
+          using is_swappable = typename is_swappable_impl<T>::type;
+
+          template <typename T, bool = is_swappable<T>::value>
+          struct is_nothrow_swappable {
+            static constexpr bool value =
+                noexcept(swap(std::declval<T &>(), std::declval<T &>()));
+          };
+
+          template <typename T>
+          struct is_nothrow_swappable<T, false> : std::false_type {};
+
+        }  // namespace swappable
+      }  // namespace detail
+
+      template <typename T>
+      using is_swappable = detail::swappable::is_swappable<T>;
+
+      template <typename T>
+      using is_nothrow_swappable = detail::swappable::is_nothrow_swappable<T>;
+
+      // <functional>
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+      template <typename F, typename... As>
+      inline constexpr auto invoke(F &&f, As &&... as)
+          RETURN(lib::forward<F>(f)(lib::forward<As>(as)...))
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+      template <typename B, typename T, typename D>
+      inline constexpr auto invoke(T B::*pmv, D &&d)
+          RETURN(lib::forward<D>(d).*pmv)
+
+      template <typename Pmv, typename Ptr>
+      inline constexpr auto invoke(Pmv pmv, Ptr &&ptr)
+          RETURN((*lib::forward<Ptr>(ptr)).*pmv)
+
+      template <typename B, typename T, typename D, typename... As>
+      inline constexpr auto invoke(T B::*pmf, D &&d, As &&... as)
+          RETURN((lib::forward<D>(d).*pmf)(lib::forward<As>(as)...))
+
+      template <typename Pmf, typename Ptr, typename... As>
+      inline constexpr auto invoke(Pmf pmf, Ptr &&ptr, As &&... as)
+          RETURN(((*lib::forward<Ptr>(ptr)).*pmf)(lib::forward<As>(as)...))
+
+      namespace detail {
+
+        template <typename Void, typename, typename...>
+        struct invoke_result {};
+
+        template <typename F, typename... Args>
+        struct invoke_result<void_t<decltype(lib::invoke(
+                                 std::declval<F>(), std::declval<Args>()...))>,
+                             F,
+                             Args...>
+            : identity<decltype(
+                  lib::invoke(std::declval<F>(), std::declval<Args>()...))> {};
+
+      }  // namespace detail
+
+      template <typename F, typename... Args>
+      using invoke_result = detail::invoke_result<void, F, Args...>;
+
+      template <typename F, typename... Args>
+      using invoke_result_t = typename invoke_result<F, Args...>::type;
+
+      namespace detail {
+
+        template <typename Void, typename, typename...>
+        struct is_invocable : std::false_type {};
+
+        template <typename F, typename... Args>
+        struct is_invocable<void_t<invoke_result_t<F, Args...>>, F, Args...>
+            : std::true_type {};
+
+        template <typename Void, typename, typename, typename...>
+        struct is_invocable_r : std::false_type {};
+
+        template <typename R, typename F, typename... Args>
+        struct is_invocable_r<void_t<invoke_result_t<F, Args...>>,
+                              R,
+                              F,
+                              Args...>
+            : std::is_convertible<invoke_result_t<F, Args...>, R> {};
+
+      }  // namespace detail
+
+      template <typename F, typename... Args>
+      using is_invocable = detail::is_invocable<void, F, Args...>;
+
+      template <typename R, typename F, typename... Args>
+      using is_invocable_r = detail::is_invocable_r<void, R, F, Args...>;
+
+      // <memory>
+#ifdef MPARK_BUILTIN_ADDRESSOF
+      template <typename T>
+      inline constexpr T *addressof(T &arg) {
+        return __builtin_addressof(arg);
+      }
+#else
+      namespace detail {
+
+        namespace has_addressof_impl {
+
+          struct fail;
+
+          template <typename T>
+          inline fail operator&(T &&);
+
+          template <typename T>
+          inline static constexpr bool impl() {
+            return (std::is_class<T>::value || std::is_union<T>::value) &&
+                   !std::is_same<decltype(&std::declval<T &>()), fail>::value;
+          }
+
+        }  // namespace has_addressof_impl
+
+        template <typename T>
+        using has_addressof = bool_constant<has_addressof_impl::impl<T>()>;
+
+        template <typename T>
+        inline constexpr T *addressof(T &arg, std::true_type) {
+          return std::addressof(arg);
+        }
+
+        template <typename T>
+        inline constexpr T *addressof(T &arg, std::false_type) {
+          return &arg;
+        }
+
+      }  // namespace detail
+
+      template <typename T>
+      inline constexpr T *addressof(T &arg) {
+        return detail::addressof(arg, detail::has_addressof<T>{});
+      }
+#endif
+
+      template <typename T>
+      inline constexpr T *addressof(const T &&) = delete;
+
+    }  // namespace cpp17
+
+    template <typename T>
+    struct remove_all_extents : identity<T> {};
+
+    template <typename T, std::size_t N>
+    struct remove_all_extents<array<T, N>> : remove_all_extents<T> {};
+
+    template <typename T>
+    using remove_all_extents_t = typename remove_all_extents<T>::type;
+
+    template <std::size_t N>
+    using size_constant = std::integral_constant<std::size_t, N>;
+
+    template <bool... Bs>
+    using bool_sequence = integer_sequence<bool, Bs...>;
+
+    template <std::size_t I, typename T>
+    struct indexed_type : size_constant<I>, identity<T> {};
+
+    template <bool... Bs>
+    using all =
+        std::is_same<bool_sequence<true, Bs...>, bool_sequence<Bs..., true>>;
+
+#ifdef MPARK_TYPE_PACK_ELEMENT
+    template <std::size_t I, typename... Ts>
+    using type_pack_element_t = __type_pack_element<I, Ts...>;
+#else
+    template <std::size_t I, typename... Ts>
+    struct type_pack_element_impl {
+      private:
+      template <typename>
+      struct set;
+
+      template <std::size_t... Is>
+      struct set<index_sequence<Is...>> : indexed_type<Is, Ts>... {};
+
+      template <typename T>
+      inline static std::enable_if<true, T> impl(indexed_type<I, T>);
+
+      inline static std::enable_if<false> impl(...);
+
+      public:
+      using type = decltype(impl(set<index_sequence_for<Ts...>>{}));
+    };
+
+    template <std::size_t I, typename... Ts>
+    using type_pack_element = typename type_pack_element_impl<I, Ts...>::type;
+
+    template <std::size_t I, typename... Ts>
+    using type_pack_element_t = typename type_pack_element<I, Ts...>::type;
+#endif
+
+#ifdef MPARK_TRIVIALITY_TYPE_TRAITS
+    template <typename T>
+    using is_trivially_copy_constructible =
+        std::is_trivially_copy_constructible<T>;
+
+    template <typename T>
+    using is_trivially_move_constructible =
+        std::is_trivially_move_constructible<T>;
+
+    template <typename T>
+    using is_trivially_copy_assignable = std::is_trivially_copy_assignable<T>;
+
+    template <typename T>
+    using is_trivially_move_assignable = std::is_trivially_move_assignable<T>;
+#else
+    template <typename T>
+    struct is_trivially_copy_constructible
+        : bool_constant<
+              std::is_copy_constructible<T>::value && __has_trivial_copy(T)> {};
+
+    template <typename T>
+    struct is_trivially_move_constructible : bool_constant<__is_trivial(T)> {};
+
+    template <typename T>
+    struct is_trivially_copy_assignable
+        : bool_constant<
+              std::is_copy_assignable<T>::value && __has_trivial_assign(T)> {};
+
+    template <typename T>
+    struct is_trivially_move_assignable : bool_constant<__is_trivial(T)> {};
+#endif
+
+  }  // namespace lib
+}  // namespace mpark
+
+#undef RETURN
+
+#endif  // MPARK_LIB_HPP
+
+
+namespace mpark {
+
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+
+#define AUTO auto
+#define AUTO_RETURN(...) { return __VA_ARGS__; }
+
+#define AUTO_REFREF auto &&
+#define AUTO_REFREF_RETURN(...) { return __VA_ARGS__; }
+
+#define DECLTYPE_AUTO decltype(auto)
+#define DECLTYPE_AUTO_RETURN(...) { return __VA_ARGS__; }
+
+#else
+
+#define AUTO auto
+#define AUTO_RETURN(...) \
+  -> lib::decay_t<decltype(__VA_ARGS__)> { return __VA_ARGS__; }
+
+#define AUTO_REFREF auto
+#define AUTO_REFREF_RETURN(...)                                           \
+  -> decltype((__VA_ARGS__)) {                                            \
+    static_assert(std::is_reference<decltype((__VA_ARGS__))>::value, ""); \
+    return __VA_ARGS__;                                                   \
+  }
+
+#define DECLTYPE_AUTO auto
+#define DECLTYPE_AUTO_RETURN(...) \
+  -> decltype(__VA_ARGS__) { return __VA_ARGS__; }
+
+#endif
+
+  class bad_variant_access : public std::exception {
+    public:
+    virtual const char *what() const noexcept { return "bad_variant_access"; }
+  };
+
+  [[noreturn]] inline void throw_bad_variant_access() {
+#ifdef MPARK_EXCEPTIONS
+    throw bad_variant_access{};
+#else
+    std::terminate();
+#endif
+  }
+
+  template <typename... Ts>
+  class variant;
+
+  template <typename T>
+  struct variant_size;
+
+#ifdef MPARK_VARIABLE_TEMPLATES
+  template <typename T>
+  constexpr std::size_t variant_size_v = variant_size<T>::value;
+#endif
+
+  template <typename T>
+  struct variant_size<const T> : variant_size<T> {};
+
+  template <typename T>
+  struct variant_size<volatile T> : variant_size<T> {};
+
+  template <typename T>
+  struct variant_size<const volatile T> : variant_size<T> {};
+
+  template <typename... Ts>
+  struct variant_size<variant<Ts...>> : lib::size_constant<sizeof...(Ts)> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative;
+
+  template <std::size_t I, typename T>
+  using variant_alternative_t = typename variant_alternative<I, T>::type;
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, const T>
+      : std::add_const<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, volatile T>
+      : std::add_volatile<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, const volatile T>
+      : std::add_cv<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename... Ts>
+  struct variant_alternative<I, variant<Ts...>> {
+    static_assert(I < sizeof...(Ts),
+                  "Index out of bounds in std::variant_alternative<>");
+    using type = lib::type_pack_element_t<I, Ts...>;
+  };
+
+  constexpr std::size_t variant_npos = static_cast<std::size_t>(-1);
+
+  namespace detail {
+
+    constexpr std::size_t not_found = static_cast<std::size_t>(-1);
+    constexpr std::size_t ambiguous = static_cast<std::size_t>(-2);
+
+#ifdef MPARK_CPP14_CONSTEXPR
+    template <typename T, typename... Ts>
+    inline constexpr std::size_t find_index() {
+      constexpr lib::array<bool, sizeof...(Ts)> matches = {
+          {std::is_same<T, Ts>::value...}
+      };
+      std::size_t result = not_found;
+      for (std::size_t i = 0; i < sizeof...(Ts); ++i) {
+        if (matches[i]) {
+          if (result != not_found) {
+            return ambiguous;
+          }
+          result = i;
+        }
+      }
+      return result;
+    }
+#else
+    inline constexpr std::size_t find_index_impl(std::size_t result,
+                                                 std::size_t) {
+      return result;
+    }
+
+    template <typename... Bs>
+    inline constexpr std::size_t find_index_impl(std::size_t result,
+                                                 std::size_t idx,
+                                                 bool b,
+                                                 Bs... bs) {
+      return b ? (result != not_found ? ambiguous
+                                      : find_index_impl(idx, idx + 1, bs...))
+               : find_index_impl(result, idx + 1, bs...);
+    }
+
+    template <typename T, typename... Ts>
+    inline constexpr std::size_t find_index() {
+      return find_index_impl(not_found, 0, std::is_same<T, Ts>::value...);
+    }
+#endif
+
+    template <std::size_t I>
+    using find_index_sfinae_impl =
+        lib::enable_if_t<I != not_found && I != ambiguous,
+                         lib::size_constant<I>>;
+
+    template <typename T, typename... Ts>
+    using find_index_sfinae = find_index_sfinae_impl<find_index<T, Ts...>()>;
+
+    template <std::size_t I>
+    struct find_index_checked_impl : lib::size_constant<I> {
+      static_assert(I != not_found, "the specified type is not found.");
+      static_assert(I != ambiguous, "the specified type is ambiguous.");
+    };
+
+    template <typename T, typename... Ts>
+    using find_index_checked = find_index_checked_impl<find_index<T, Ts...>()>;
+
+    struct valueless_t {};
+
+    enum class Trait { TriviallyAvailable, Available, Unavailable };
+
+    template <typename T,
+              template <typename> class IsTriviallyAvailable,
+              template <typename> class IsAvailable>
+    inline constexpr Trait trait() {
+      return IsTriviallyAvailable<T>::value
+                 ? Trait::TriviallyAvailable
+                 : IsAvailable<T>::value ? Trait::Available
+                                         : Trait::Unavailable;
+    }
+
+#ifdef MPARK_CPP14_CONSTEXPR
+    template <typename... Traits>
+    inline constexpr Trait common_trait(Traits... traits) {
+      Trait result = Trait::TriviallyAvailable;
+      for (Trait t : {traits...}) {
+        if (static_cast<int>(t) > static_cast<int>(result)) {
+          result = t;
+        }
+      }
+      return result;
+    }
+#else
+    inline constexpr Trait common_trait_impl(Trait result) { return result; }
+
+    template <typename... Traits>
+    inline constexpr Trait common_trait_impl(Trait result,
+                                             Trait t,
+                                             Traits... ts) {
+      return static_cast<int>(t) > static_cast<int>(result)
+                 ? common_trait_impl(t, ts...)
+                 : common_trait_impl(result, ts...);
+    }
+
+    template <typename... Traits>
+    inline constexpr Trait common_trait(Traits... ts) {
+      return common_trait_impl(Trait::TriviallyAvailable, ts...);
+    }
+#endif
+
+    template <typename... Ts>
+    struct traits {
+      static constexpr Trait copy_constructible_trait =
+          common_trait(trait<Ts,
+                             lib::is_trivially_copy_constructible,
+                             std::is_copy_constructible>()...);
+
+      static constexpr Trait move_constructible_trait =
+          common_trait(trait<Ts,
+                             lib::is_trivially_move_constructible,
+                             std::is_move_constructible>()...);
+
+      static constexpr Trait copy_assignable_trait =
+          common_trait(copy_constructible_trait,
+                       trait<Ts,
+                             lib::is_trivially_copy_assignable,
+                             std::is_copy_assignable>()...);
+
+      static constexpr Trait move_assignable_trait =
+          common_trait(move_constructible_trait,
+                       trait<Ts,
+                             lib::is_trivially_move_assignable,
+                             std::is_move_assignable>()...);
+
+      static constexpr Trait destructible_trait =
+          common_trait(trait<Ts,
+                             std::is_trivially_destructible,
+                             std::is_destructible>()...);
+    };
+
+    namespace access {
+
+      struct recursive_union {
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+        template <typename V>
+        inline static constexpr auto &&get_alt(V &&v, in_place_index_t<0>) {
+          return lib::forward<V>(v).head_;
+        }
+
+        template <typename V, std::size_t I>
+        inline static constexpr auto &&get_alt(V &&v, in_place_index_t<I>) {
+          return get_alt(lib::forward<V>(v).tail_, in_place_index_t<I - 1>{});
+        }
+#else
+        template <std::size_t I, bool Dummy = true>
+        struct get_alt_impl {
+          template <typename V>
+          inline constexpr AUTO_REFREF operator()(V &&v) const
+            AUTO_REFREF_RETURN(get_alt_impl<I - 1>{}(lib::forward<V>(v).tail_))
+        };
+
+        template <bool Dummy>
+        struct get_alt_impl<0, Dummy> {
+          template <typename V>
+          inline constexpr AUTO_REFREF operator()(V &&v) const
+            AUTO_REFREF_RETURN(lib::forward<V>(v).head_)
+        };
+
+        template <typename V, std::size_t I>
+        inline static constexpr AUTO_REFREF get_alt(V &&v, in_place_index_t<I>)
+          AUTO_REFREF_RETURN(get_alt_impl<I>{}(lib::forward<V>(v)))
+#endif
+      };
+
+      struct base {
+        template <std::size_t I, typename V>
+        inline static constexpr AUTO_REFREF get_alt(V &&v)
+          AUTO_REFREF_RETURN(recursive_union::get_alt(
+              data(lib::forward<V>(v)), in_place_index_t<I>{}))
+      };
+
+      struct variant {
+        template <std::size_t I, typename V>
+        inline static constexpr AUTO_REFREF get_alt(V &&v)
+          AUTO_REFREF_RETURN(base::get_alt<I>(lib::forward<V>(v).impl_))
+      };
+
+    }  // namespace access
+
+    namespace visitation {
+
+      struct base {
+        private:
+        template <typename T>
+        inline static constexpr const T &at(const T &elem) {
+          return elem;
+        }
+
+        template <typename T, std::size_t N, typename... Is>
+        inline static constexpr const lib::remove_all_extents_t<T> &at(
+            const lib::array<T, N> &elems, std::size_t i, Is... is) {
+          return at(elems[i], is...);
+        }
+
+        template <typename F, typename... Fs>
+        inline static constexpr int visit_visitor_return_type_check() {
+          static_assert(lib::all<std::is_same<F, Fs>::value...>::value,
+                        "`mpark::visit` requires the visitor to have a single "
+                        "return type.");
+          return 0;
+        }
+
+        template <typename... Fs>
+        inline static constexpr lib::array<
+            lib::common_type_t<lib::decay_t<Fs>...>,
+            sizeof...(Fs)>
+        make_farray(Fs &&... fs) {
+          using result = lib::array<lib::common_type_t<lib::decay_t<Fs>...>,
+                                    sizeof...(Fs)>;
+          return visit_visitor_return_type_check<lib::decay_t<Fs>...>(),
+                 result{{lib::forward<Fs>(fs)...}};
+        }
+
+        template <std::size_t... Is>
+        struct dispatcher {
+          template <typename F, typename... Vs>
+          struct impl {
+            inline static constexpr DECLTYPE_AUTO dispatch(F f, Vs... vs)
+              DECLTYPE_AUTO_RETURN(lib::invoke(
+                  static_cast<F>(f),
+                  access::base::get_alt<Is>(static_cast<Vs>(vs))...))
+          };
+        };
+
+        template <typename F, typename... Vs, std::size_t... Is>
+        inline static constexpr AUTO make_dispatch(lib::index_sequence<Is...>)
+          AUTO_RETURN(&dispatcher<Is...>::template impl<F, Vs...>::dispatch)
+
+        template <std::size_t I, typename F, typename... Vs>
+        inline static constexpr AUTO make_fdiagonal_impl()
+          AUTO_RETURN(make_dispatch<F, Vs...>(
+              lib::index_sequence<lib::indexed_type<I, Vs>::value...>{}))
+
+        template <typename F, typename... Vs, std::size_t... Is>
+        inline static constexpr AUTO make_fdiagonal_impl(
+            lib::index_sequence<Is...>)
+          AUTO_RETURN(make_farray(make_fdiagonal_impl<Is, F, Vs...>()...))
+
+        template <typename F, typename V, typename... Vs>
+        inline static constexpr /* auto * */ auto make_fdiagonal()
+            -> decltype(make_fdiagonal_impl<F, V, Vs...>(
+                lib::make_index_sequence<lib::decay_t<V>::size()>{})) {
+          static_assert(lib::all<(lib::decay_t<V>::size() ==
+                                  lib::decay_t<Vs>::size())...>::value,
+                        "all of the variants must be the same size.");
+          return make_fdiagonal_impl<F, V, Vs...>(
+              lib::make_index_sequence<lib::decay_t<V>::size()>{});
+        }
+
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+        template <typename F, typename... Vs, std::size_t... Is>
+        inline static constexpr auto make_fmatrix_impl(
+            lib::index_sequence<Is...> is) {
+          return make_dispatch<F, Vs...>(is);
+        }
+
+        template <typename F,
+                  typename... Vs,
+                  std::size_t... Is,
+                  std::size_t... Js,
+                  typename... Ls>
+        inline static constexpr auto make_fmatrix_impl(
+            lib::index_sequence<Is...>, lib::index_sequence<Js...>, Ls... ls) {
+          return make_farray(make_fmatrix_impl<F, Vs...>(
+              lib::index_sequence<Is..., Js>{}, ls...)...);
+        }
+
+        template <typename F, typename... Vs>
+        inline static constexpr auto make_fmatrix() {
+          return make_fmatrix_impl<F, Vs...>(
+              lib::index_sequence<>{},
+              lib::make_index_sequence<lib::decay_t<Vs>::size()>{}...);
+        }
+#else
+        template <typename F, typename... Vs>
+        struct make_fmatrix_impl {
+          template <typename...>
+          struct impl;
+
+          template <std::size_t... Is>
+          struct impl<lib::index_sequence<Is...>> {
+            inline constexpr AUTO operator()() const
+              AUTO_RETURN(
+                  make_dispatch<F, Vs...>(lib::index_sequence<Is...>{}))
+          };
+
+          template <std::size_t... Is, std::size_t... Js, typename... Ls>
+          struct impl<lib::index_sequence<Is...>,
+                      lib::index_sequence<Js...>,
+                      Ls...> {
+            inline constexpr AUTO operator()() const
+              AUTO_RETURN(make_farray(
+                  impl<lib::index_sequence<Is..., Js>, Ls...>{}()...))
+          };
+        };
+
+        template <typename F, typename... Vs>
+        inline static constexpr AUTO make_fmatrix()
+          AUTO_RETURN(
+              typename make_fmatrix_impl<F, Vs...>::template impl<
+                  lib::index_sequence<>,
+                  lib::make_index_sequence<lib::decay_t<Vs>::size()>...>{}())
+#endif
+
+        public:
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index,
+                                                           Visitor &&visitor,
+                                                           Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              at(make_fdiagonal<Visitor &&,
+                                decltype(as_base(lib::forward<Vs>(vs)))...>(),
+                 index)(lib::forward<Visitor>(visitor),
+                        as_base(lib::forward<Vs>(vs))...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor,
+                                                        Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              at(make_fmatrix<Visitor &&,
+                              decltype(as_base(lib::forward<Vs>(vs)))...>(),
+                 vs.index()...)(lib::forward<Visitor>(visitor),
+                                as_base(lib::forward<Vs>(vs))...))
+      };
+
+      struct variant {
+        private:
+        template <typename Visitor, typename... Values>
+        struct visit_exhaustive_visitor_check {
+          static_assert(
+              lib::is_invocable<Visitor, Values...>::value,
+              "`mpark::visit` requires the visitor to be exhaustive.");
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+          inline constexpr DECLTYPE_AUTO operator()(Visitor &&visitor,
+                                                    Values &&... values) const
+            DECLTYPE_AUTO_RETURN(lib::invoke(lib::forward<Visitor>(visitor),
+                                             lib::forward<Values>(values)...))
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+        };
+
+        template <typename Visitor>
+        struct value_visitor {
+          Visitor &&visitor_;
+
+          template <typename... Alts>
+          inline constexpr DECLTYPE_AUTO operator()(Alts &&... alts) const
+            DECLTYPE_AUTO_RETURN(
+                visit_exhaustive_visitor_check<
+                    Visitor,
+                    decltype((lib::forward<Alts>(alts).value))...>{}(
+                    lib::forward<Visitor>(visitor_),
+                    lib::forward<Alts>(alts).value...))
+        };
+
+        template <typename Visitor>
+        inline static constexpr AUTO make_value_visitor(Visitor &&visitor)
+          AUTO_RETURN(value_visitor<Visitor>{lib::forward<Visitor>(visitor)})
+
+        public:
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index,
+                                                           Visitor &&visitor,
+                                                           Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              base::visit_alt_at(index,
+                                 lib::forward<Visitor>(visitor),
+                                 lib::forward<Vs>(vs).impl_...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor,
+                                                        Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(base::visit_alt(lib::forward<Visitor>(visitor),
+                                               lib::forward<Vs>(vs).impl_...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_value_at(std::size_t index,
+                                                             Visitor &&visitor,
+                                                             Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              visit_alt_at(index,
+                           make_value_visitor(lib::forward<Visitor>(visitor)),
+                           lib::forward<Vs>(vs)...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_value(Visitor &&visitor,
+                                                          Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              visit_alt(make_value_visitor(lib::forward<Visitor>(visitor)),
+                        lib::forward<Vs>(vs)...))
+      };
+
+    }  // namespace visitation
+
+    template <std::size_t Index, typename T>
+    struct alt {
+      using value_type = T;
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4244)
+#endif
+      template <typename... Args>
+      inline explicit constexpr alt(in_place_t, Args &&... args)
+          : value(lib::forward<Args>(args)...) {}
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+      T value;
+    };
+
+    template <Trait DestructibleTrait, std::size_t Index, typename... Ts>
+    union recursive_union;
+
+    template <Trait DestructibleTrait, std::size_t Index>
+    union recursive_union<DestructibleTrait, Index> {};
+
+#define MPARK_VARIANT_RECURSIVE_UNION(destructible_trait, destructor)      \
+  template <std::size_t Index, typename T, typename... Ts>                 \
+  union recursive_union<destructible_trait, Index, T, Ts...> {             \
+    public:                                                                \
+    inline explicit constexpr recursive_union(valueless_t) noexcept        \
+        : dummy_{} {}                                                      \
+                                                                           \
+    template <typename... Args>                                            \
+    inline explicit constexpr recursive_union(in_place_index_t<0>,         \
+                                              Args &&... args)             \
+        : head_(in_place_t{}, lib::forward<Args>(args)...) {}              \
+                                                                           \
+    template <std::size_t I, typename... Args>                             \
+    inline explicit constexpr recursive_union(in_place_index_t<I>,         \
+                                              Args &&... args)             \
+        : tail_(in_place_index_t<I - 1>{}, lib::forward<Args>(args)...) {} \
+                                                                           \
+    recursive_union(const recursive_union &) = default;                    \
+    recursive_union(recursive_union &&) = default;                         \
+                                                                           \
+    destructor                                                             \
+                                                                           \
+    recursive_union &operator=(const recursive_union &) = default;         \
+    recursive_union &operator=(recursive_union &&) = default;              \
+                                                                           \
+    private:                                                               \
+    char dummy_;                                                           \
+    alt<Index, T> head_;                                                   \
+    recursive_union<destructible_trait, Index + 1, Ts...> tail_;           \
+                                                                           \
+    friend struct access::recursive_union;                                 \
+  }
+
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::TriviallyAvailable,
+                                  ~recursive_union() = default;);
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::Available,
+                                  ~recursive_union() {});
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::Unavailable,
+                                  ~recursive_union() = delete;);
+
+#undef MPARK_VARIANT_RECURSIVE_UNION
+
+    using index_t = unsigned int;
+
+    template <Trait DestructibleTrait, typename... Ts>
+    class base {
+      public:
+      inline explicit constexpr base(valueless_t tag) noexcept
+          : data_(tag), index_(static_cast<index_t>(-1)) {}
+
+      template <std::size_t I, typename... Args>
+      inline explicit constexpr base(in_place_index_t<I>, Args &&... args)
+          : data_(in_place_index_t<I>{}, lib::forward<Args>(args)...),
+            index_(I) {}
+
+      inline constexpr bool valueless_by_exception() const noexcept {
+        return index_ == static_cast<index_t>(-1);
+      }
+
+      inline constexpr std::size_t index() const noexcept {
+        return valueless_by_exception() ? variant_npos : index_;
+      }
+
+      protected:
+      using data_t = recursive_union<DestructibleTrait, 0, Ts...>;
+
+      friend inline constexpr base &as_base(base &b) { return b; }
+      friend inline constexpr const base &as_base(const base &b) { return b; }
+      friend inline constexpr base &&as_base(base &&b) { return lib::move(b); }
+      friend inline constexpr const base &&as_base(const base &&b) { return lib::move(b); }
+
+      friend inline constexpr data_t &data(base &b) { return b.data_; }
+      friend inline constexpr const data_t &data(const base &b) { return b.data_; }
+      friend inline constexpr data_t &&data(base &&b) { return lib::move(b).data_; }
+      friend inline constexpr const data_t &&data(const base &&b) { return lib::move(b).data_; }
+
+      inline static constexpr std::size_t size() { return sizeof...(Ts); }
+
+      data_t data_;
+      index_t index_;
+
+      friend struct access::base;
+      friend struct visitation::base;
+    };
+
+    struct dtor {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+      template <typename Alt>
+      inline void operator()(Alt &alt) const noexcept { alt.~Alt(); }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+    };
+
+#if defined(_MSC_VER) && _MSC_VER < 1910
+#define INHERITING_CTOR(type, base)               \
+  template <typename... Args>                     \
+  inline explicit constexpr type(Args &&... args) \
+      : base(lib::forward<Args>(args)...) {}
+#else
+#define INHERITING_CTOR(type, base) using base::base;
+#endif
+
+    template <typename Traits, Trait = Traits::destructible_trait>
+    class destructor;
+
+#define MPARK_VARIANT_DESTRUCTOR(destructible_trait, definition, destroy) \
+  template <typename... Ts>                                               \
+  class destructor<traits<Ts...>, destructible_trait>                     \
+      : public base<destructible_trait, Ts...> {                          \
+    using super = base<destructible_trait, Ts...>;                        \
+                                                                          \
+    public:                                                               \
+    INHERITING_CTOR(destructor, super)                                    \
+    using super::operator=;                                               \
+                                                                          \
+    destructor(const destructor &) = default;                             \
+    destructor(destructor &&) = default;                                  \
+    definition                                                            \
+    destructor &operator=(const destructor &) = default;                  \
+    destructor &operator=(destructor &&) = default;                       \
+                                                                          \
+    protected:                                                            \
+    destroy                                                               \
+  }
+
+    MPARK_VARIANT_DESTRUCTOR(
+        Trait::TriviallyAvailable,
+        ~destructor() = default;,
+        inline void destroy() noexcept {
+          this->index_ = static_cast<index_t>(-1);
+        });
+
+    MPARK_VARIANT_DESTRUCTOR(
+        Trait::Available,
+        ~destructor() { destroy(); },
+        inline void destroy() noexcept {
+          if (!this->valueless_by_exception()) {
+            visitation::base::visit_alt(dtor{}, *this);
+          }
+          this->index_ = static_cast<index_t>(-1);
+        });
+
+    MPARK_VARIANT_DESTRUCTOR(
+        Trait::Unavailable,
+        ~destructor() = delete;,
+        inline void destroy() noexcept = delete;);
+
+#undef MPARK_VARIANT_DESTRUCTOR
+
+    template <typename Traits>
+    class constructor : public destructor<Traits> {
+      using super = destructor<Traits>;
+
+      public:
+      INHERITING_CTOR(constructor, super)
+      using super::operator=;
+
+      protected:
+#ifndef MPARK_GENERIC_LAMBDAS
+      struct ctor {
+        template <typename LhsAlt, typename RhsAlt>
+        inline void operator()(LhsAlt &lhs_alt, RhsAlt &&rhs_alt) const {
+          constructor::construct_alt(lhs_alt,
+                                     lib::forward<RhsAlt>(rhs_alt).value);
+        }
+      };
+#endif
+
+      template <std::size_t I, typename T, typename... Args>
+      inline static T &construct_alt(alt<I, T> &a, Args &&... args) {
+        ::new (static_cast<void *>(lib::addressof(a)))
+            alt<I, T>(in_place_t{}, lib::forward<Args>(args)...);
+        return a.value;
+      }
+
+      template <typename Rhs>
+      inline static void generic_construct(constructor &lhs, Rhs &&rhs) {
+        lhs.destroy();
+        if (!rhs.valueless_by_exception()) {
+          visitation::base::visit_alt_at(
+              rhs.index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+              [](auto &lhs_alt, auto &&rhs_alt) {
+                constructor::construct_alt(
+                    lhs_alt, lib::forward<decltype(rhs_alt)>(rhs_alt).value);
+              }
+#else
+              ctor{}
+#endif
+              ,
+              lhs,
+              lib::forward<Rhs>(rhs));
+          lhs.index_ = rhs.index_;
+        }
+      }
+    };
+
+    template <typename Traits, Trait = Traits::move_constructible_trait>
+    class move_constructor;
+
+#define MPARK_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, definition) \
+  template <typename... Ts>                                                  \
+  class move_constructor<traits<Ts...>, move_constructible_trait>            \
+      : public constructor<traits<Ts...>> {                                  \
+    using super = constructor<traits<Ts...>>;                                \
+                                                                             \
+    public:                                                                  \
+    INHERITING_CTOR(move_constructor, super)                                 \
+    using super::operator=;                                                  \
+                                                                             \
+    move_constructor(const move_constructor &) = default;                    \
+    definition                                                               \
+    ~move_constructor() = default;                                           \
+    move_constructor &operator=(const move_constructor &) = default;         \
+    move_constructor &operator=(move_constructor &&) = default;              \
+  }
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::TriviallyAvailable,
+        move_constructor(move_constructor &&that) = default;);
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::Available,
+        move_constructor(move_constructor &&that) noexcept(
+            lib::all<std::is_nothrow_move_constructible<Ts>::value...>::value)
+            : move_constructor(valueless_t{}) {
+          this->generic_construct(*this, lib::move(that));
+        });
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::Unavailable,
+        move_constructor(move_constructor &&) = delete;);
+
+#undef MPARK_VARIANT_MOVE_CONSTRUCTOR
+
+    template <typename Traits, Trait = Traits::copy_constructible_trait>
+    class copy_constructor;
+
+#define MPARK_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, definition) \
+  template <typename... Ts>                                                  \
+  class copy_constructor<traits<Ts...>, copy_constructible_trait>            \
+      : public move_constructor<traits<Ts...>> {                             \
+    using super = move_constructor<traits<Ts...>>;                           \
+                                                                             \
+    public:                                                                  \
+    INHERITING_CTOR(copy_constructor, super)                                 \
+    using super::operator=;                                                  \
+                                                                             \
+    definition                                                               \
+    copy_constructor(copy_constructor &&) = default;                         \
+    ~copy_constructor() = default;                                           \
+    copy_constructor &operator=(const copy_constructor &) = default;         \
+    copy_constructor &operator=(copy_constructor &&) = default;              \
+  }
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::TriviallyAvailable,
+        copy_constructor(const copy_constructor &that) = default;);
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::Available,
+        copy_constructor(const copy_constructor &that)
+            : copy_constructor(valueless_t{}) {
+          this->generic_construct(*this, that);
+        });
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::Unavailable,
+        copy_constructor(const copy_constructor &) = delete;);
+
+#undef MPARK_VARIANT_COPY_CONSTRUCTOR
+
+    template <typename Traits>
+    class assignment : public copy_constructor<Traits> {
+      using super = copy_constructor<Traits>;
+
+      public:
+      INHERITING_CTOR(assignment, super)
+      using super::operator=;
+
+      template <std::size_t I, typename... Args>
+      inline /* auto & */ auto emplace(Args &&... args)
+          -> decltype(this->construct_alt(access::base::get_alt<I>(*this),
+                                          lib::forward<Args>(args)...)) {
+        this->destroy();
+        auto &result = this->construct_alt(access::base::get_alt<I>(*this),
+                                           lib::forward<Args>(args)...);
+        this->index_ = I;
+        return result;
+      }
+
+      protected:
+#ifndef MPARK_GENERIC_LAMBDAS
+      template <typename That>
+      struct assigner {
+        template <typename ThisAlt, typename ThatAlt>
+        inline void operator()(ThisAlt &this_alt, ThatAlt &&that_alt) const {
+          self->assign_alt(this_alt, lib::forward<ThatAlt>(that_alt).value);
+        }
+        assignment *self;
+      };
+#endif
+
+      template <std::size_t I, typename T, typename Arg>
+      inline void assign_alt(alt<I, T> &a, Arg &&arg) {
+        if (this->index() == I) {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4244)
+#endif
+          a.value = lib::forward<Arg>(arg);
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+        } else {
+          struct {
+            void operator()(std::true_type) const {
+              this_->emplace<I>(lib::forward<Arg>(arg_));
+            }
+            void operator()(std::false_type) const {
+              this_->emplace<I>(T(lib::forward<Arg>(arg_)));
+            }
+            assignment *this_;
+            Arg &&arg_;
+          } impl{this, lib::forward<Arg>(arg)};
+          impl(lib::bool_constant<
+                   std::is_nothrow_constructible<T, Arg>::value ||
+                   !std::is_nothrow_move_constructible<T>::value>{});
+        }
+      }
+
+      template <typename That>
+      inline void generic_assign(That &&that) {
+        if (this->valueless_by_exception() && that.valueless_by_exception()) {
+          // do nothing.
+        } else if (that.valueless_by_exception()) {
+          this->destroy();
+        } else {
+          visitation::base::visit_alt_at(
+              that.index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+              [this](auto &this_alt, auto &&that_alt) {
+                this->assign_alt(
+                    this_alt, lib::forward<decltype(that_alt)>(that_alt).value);
+              }
+#else
+              assigner<That>{this}
+#endif
+              ,
+              *this,
+              lib::forward<That>(that));
+        }
+      }
+    };
+
+    template <typename Traits, Trait = Traits::move_assignable_trait>
+    class move_assignment;
+
+#define MPARK_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, definition) \
+  template <typename... Ts>                                              \
+  class move_assignment<traits<Ts...>, move_assignable_trait>            \
+      : public assignment<traits<Ts...>> {                               \
+    using super = assignment<traits<Ts...>>;                             \
+                                                                         \
+    public:                                                              \
+    INHERITING_CTOR(move_assignment, super)                              \
+    using super::operator=;                                              \
+                                                                         \
+    move_assignment(const move_assignment &) = default;                  \
+    move_assignment(move_assignment &&) = default;                       \
+    ~move_assignment() = default;                                        \
+    move_assignment &operator=(const move_assignment &) = default;       \
+    definition                                                           \
+  }
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::TriviallyAvailable,
+        move_assignment &operator=(move_assignment &&that) = default;);
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::Available,
+        move_assignment &
+        operator=(move_assignment &&that) noexcept(
+            lib::all<(std::is_nothrow_move_constructible<Ts>::value &&
+                      std::is_nothrow_move_assignable<Ts>::value)...>::value) {
+          this->generic_assign(lib::move(that));
+          return *this;
+        });
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::Unavailable,
+        move_assignment &operator=(move_assignment &&) = delete;);
+
+#undef MPARK_VARIANT_MOVE_ASSIGNMENT
+
+    template <typename Traits, Trait = Traits::copy_assignable_trait>
+    class copy_assignment;
+
+#define MPARK_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, definition) \
+  template <typename... Ts>                                              \
+  class copy_assignment<traits<Ts...>, copy_assignable_trait>            \
+      : public move_assignment<traits<Ts...>> {                          \
+    using super = move_assignment<traits<Ts...>>;                        \
+                                                                         \
+    public:                                                              \
+    INHERITING_CTOR(copy_assignment, super)                              \
+    using super::operator=;                                              \
+                                                                         \
+    copy_assignment(const copy_assignment &) = default;                  \
+    copy_assignment(copy_assignment &&) = default;                       \
+    ~copy_assignment() = default;                                        \
+    definition                                                           \
+    copy_assignment &operator=(copy_assignment &&) = default;            \
+  }
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::TriviallyAvailable,
+        copy_assignment &operator=(const copy_assignment &that) = default;);
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::Available,
+        copy_assignment &operator=(const copy_assignment &that) {
+          this->generic_assign(that);
+          return *this;
+        });
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::Unavailable,
+        copy_assignment &operator=(const copy_assignment &) = delete;);
+
+#undef MPARK_VARIANT_COPY_ASSIGNMENT
+
+    template <typename... Ts>
+    class impl : public copy_assignment<traits<Ts...>> {
+      using super = copy_assignment<traits<Ts...>>;
+
+      public:
+      INHERITING_CTOR(impl, super)
+      using super::operator=;
+
+      template <std::size_t I, typename Arg>
+      inline void assign(Arg &&arg) {
+        this->assign_alt(access::base::get_alt<I>(*this),
+                         lib::forward<Arg>(arg));
+      }
+
+      inline void swap(impl &that) {
+        if (this->valueless_by_exception() && that.valueless_by_exception()) {
+          // do nothing.
+        } else if (this->index() == that.index()) {
+          visitation::base::visit_alt_at(this->index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+                                         [](auto &this_alt, auto &that_alt) {
+                                           using std::swap;
+                                           swap(this_alt.value,
+                                                that_alt.value);
+                                         }
+#else
+                                         swapper{}
+#endif
+                                         ,
+                                         *this,
+                                         that);
+        } else {
+          impl *lhs = this;
+          impl *rhs = lib::addressof(that);
+          if (lhs->move_nothrow() && !rhs->move_nothrow()) {
+            std::swap(lhs, rhs);
+          }
+          impl tmp(lib::move(*rhs));
+#ifdef MPARK_EXCEPTIONS
+          // EXTENSION: When the move construction of `lhs` into `rhs` throws
+          // and `tmp` is nothrow move constructible then we move `tmp` back
+          // into `rhs` and provide the strong exception safety guarantee.
+          try {
+            this->generic_construct(*rhs, lib::move(*lhs));
+          } catch (...) {
+            if (tmp.move_nothrow()) {
+              this->generic_construct(*rhs, lib::move(tmp));
+            }
+            throw;
+          }
+#else
+          this->generic_construct(*rhs, lib::move(*lhs));
+#endif
+          this->generic_construct(*lhs, lib::move(tmp));
+        }
+      }
+
+      private:
+#ifndef MPARK_GENERIC_LAMBDAS
+      struct swapper {
+        template <typename ThisAlt, typename ThatAlt>
+        inline void operator()(ThisAlt &this_alt, ThatAlt &that_alt) const {
+          using std::swap;
+          swap(this_alt.value, that_alt.value);
+        }
+      };
+#endif
+
+      inline constexpr bool move_nothrow() const {
+        return this->valueless_by_exception() ||
+               lib::array<bool, sizeof...(Ts)>{
+                   {std::is_nothrow_move_constructible<Ts>::value...}
+               }[this->index()];
+      }
+    };
+
+    template <std::size_t I, typename T>
+    struct overload_leaf {
+      using F = lib::size_constant<I> (*)(T);
+      operator F() const { return nullptr; }
+    };
+
+    template <typename... Ts>
+    struct overload_impl {
+      private:
+      template <typename>
+      struct impl;
+
+      template <std::size_t... Is>
+      struct impl<lib::index_sequence<Is...>> : overload_leaf<Is, Ts>... {};
+
+      public:
+      using type = impl<lib::index_sequence_for<Ts...>>;
+    };
+
+    template <typename... Ts>
+    using overload = typename overload_impl<Ts...>::type;
+
+    template <typename T, typename... Ts>
+    using best_match = lib::invoke_result_t<overload<Ts...>, T &&>;
+
+    template <typename T>
+    struct is_in_place_index : std::false_type {};
+
+    template <std::size_t I>
+    struct is_in_place_index<in_place_index_t<I>> : std::true_type {};
+
+    template <typename T>
+    struct is_in_place_type : std::false_type {};
+
+    template <typename T>
+    struct is_in_place_type<in_place_type_t<T>> : std::true_type {};
+
+  }  // detail
+
+  template <typename... Ts>
+  class variant {
+    static_assert(0 < sizeof...(Ts),
+                  "variant must consist of at least one alternative.");
+
+    static_assert(lib::all<!std::is_array<Ts>::value...>::value,
+                  "variant can not have an array type as an alternative.");
+
+    static_assert(lib::all<!std::is_reference<Ts>::value...>::value,
+                  "variant can not have a reference type as an alternative.");
+
+    static_assert(lib::all<!std::is_void<Ts>::value...>::value,
+                  "variant can not have a void type as an alternative.");
+
+    public:
+    template <
+        typename Front = lib::type_pack_element_t<0, Ts...>,
+        lib::enable_if_t<std::is_default_constructible<Front>::value, int> = 0>
+    inline constexpr variant() noexcept(
+        std::is_nothrow_default_constructible<Front>::value)
+        : impl_(in_place_index_t<0>{}) {}
+
+    variant(const variant &) = default;
+    variant(variant &&) = default;
+
+    template <
+        typename Arg,
+        typename Decayed = lib::decay_t<Arg>,
+        lib::enable_if_t<!std::is_same<Decayed, variant>::value, int> = 0,
+        lib::enable_if_t<!detail::is_in_place_index<Decayed>::value, int> = 0,
+        lib::enable_if_t<!detail::is_in_place_type<Decayed>::value, int> = 0,
+        std::size_t I = detail::best_match<Arg, Ts...>::value,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T, Arg>::value, int> = 0>
+    inline constexpr variant(Arg &&arg) noexcept(
+        std::is_nothrow_constructible<T, Arg>::value)
+        : impl_(in_place_index_t<I>{}, lib::forward<Arg>(arg)) {}
+
+    template <
+        std::size_t I,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline explicit constexpr variant(
+        in_place_index_t<I>,
+        Args &&... args) noexcept(std::is_nothrow_constructible<T,
+                                                                Args...>::value)
+        : impl_(in_place_index_t<I>{}, lib::forward<Args>(args)...) {}
+
+    template <
+        std::size_t I,
+        typename Up,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline explicit constexpr variant(
+        in_place_index_t<I>,
+        std::initializer_list<Up> il,
+        Args &&... args) noexcept(std::
+                                      is_nothrow_constructible<
+                                          T,
+                                          std::initializer_list<Up> &,
+                                          Args...>::value)
+        : impl_(in_place_index_t<I>{}, il, lib::forward<Args>(args)...) {}
+
+    template <
+        typename T,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline explicit constexpr variant(
+        in_place_type_t<T>,
+        Args &&... args) noexcept(std::is_nothrow_constructible<T,
+                                                                Args...>::value)
+        : impl_(in_place_index_t<I>{}, lib::forward<Args>(args)...) {}
+
+    template <
+        typename T,
+        typename Up,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline explicit constexpr variant(
+        in_place_type_t<T>,
+        std::initializer_list<Up> il,
+        Args &&... args) noexcept(std::
+                                      is_nothrow_constructible<
+                                          T,
+                                          std::initializer_list<Up> &,
+                                          Args...>::value)
+        : impl_(in_place_index_t<I>{}, il, lib::forward<Args>(args)...) {}
+
+    ~variant() = default;
+
+    variant &operator=(const variant &) = default;
+    variant &operator=(variant &&) = default;
+
+    template <typename Arg,
+              lib::enable_if_t<!std::is_same<lib::decay_t<Arg>, variant>::value,
+                               int> = 0,
+              std::size_t I = detail::best_match<Arg, Ts...>::value,
+              typename T = lib::type_pack_element_t<I, Ts...>,
+              lib::enable_if_t<(std::is_assignable<T &, Arg>::value &&
+                                std::is_constructible<T, Arg>::value),
+                               int> = 0>
+    inline variant &operator=(Arg &&arg) noexcept(
+        (std::is_nothrow_assignable<T &, Arg>::value &&
+         std::is_nothrow_constructible<T, Arg>::value)) {
+      impl_.template assign<I>(lib::forward<Arg>(arg));
+      return *this;
+    }
+
+    template <
+        std::size_t I,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline T &emplace(Args &&... args) {
+      return impl_.template emplace<I>(lib::forward<Args>(args)...);
+    }
+
+    template <
+        std::size_t I,
+        typename Up,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline T &emplace(std::initializer_list<Up> il, Args &&... args) {
+      return impl_.template emplace<I>(il, lib::forward<Args>(args)...);
+    }
+
+    template <
+        typename T,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline T &emplace(Args &&... args) {
+      return impl_.template emplace<I>(lib::forward<Args>(args)...);
+    }
+
+    template <
+        typename T,
+        typename Up,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline T &emplace(std::initializer_list<Up> il, Args &&... args) {
+      return impl_.template emplace<I>(il, lib::forward<Args>(args)...);
+    }
+
+    inline constexpr bool valueless_by_exception() const noexcept {
+      return impl_.valueless_by_exception();
+    }
+
+    inline constexpr std::size_t index() const noexcept {
+      return impl_.index();
+    }
+
+    template <
+        bool Dummy = true,
+        lib::enable_if_t<lib::all<Dummy,
+                                  (std::is_move_constructible<Ts>::value &&
+                                   lib::is_swappable<Ts>::value)...>::value,
+                         int> = 0>
+    inline void swap(variant &that) noexcept(
+        lib::all<(std::is_nothrow_move_constructible<Ts>::value &&
+                  lib::is_nothrow_swappable<Ts>::value)...>::value) {
+      impl_.swap(that.impl_);
+    }
+
+    private:
+    detail::impl<Ts...> impl_;
+
+    friend struct detail::access::variant;
+    friend struct detail::visitation::variant;
+  };
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept {
+    return v.index() == I;
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept {
+    return holds_alternative<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  namespace detail {
+    template <std::size_t I, typename V>
+    struct generic_get_impl {
+      constexpr generic_get_impl(int) {}
+
+      constexpr AUTO_REFREF operator()(V &&v) const
+        AUTO_REFREF_RETURN(
+            access::variant::get_alt<I>(lib::forward<V>(v)).value)
+    };
+
+    template <std::size_t I, typename V>
+    inline constexpr AUTO_REFREF generic_get(V &&v)
+      AUTO_REFREF_RETURN(generic_get_impl<I, V>(
+          holds_alternative<I>(v) ? 0 : (throw_bad_variant_access(), 0))(
+          lib::forward<V>(v)))
+  }  // namespace detail
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr variant_alternative_t<I, variant<Ts...>> &get(
+      variant<Ts...> &v) {
+    return detail::generic_get<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr variant_alternative_t<I, variant<Ts...>> &&get(
+      variant<Ts...> &&v) {
+    return detail::generic_get<I>(lib::move(v));
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr const variant_alternative_t<I, variant<Ts...>> &get(
+      const variant<Ts...> &v) {
+    return detail::generic_get<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr const variant_alternative_t<I, variant<Ts...>> &&get(
+      const variant<Ts...> &&v) {
+    return detail::generic_get<I>(lib::move(v));
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr T &get(variant<Ts...> &v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr T &&get(variant<Ts...> &&v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(lib::move(v));
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr const T &get(const variant<Ts...> &v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr const T &&get(const variant<Ts...> &&v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(lib::move(v));
+  }
+
+  namespace detail {
+
+    template <std::size_t I, typename V>
+    inline constexpr /* auto * */ AUTO generic_get_if(V *v) noexcept
+      AUTO_RETURN(v && holds_alternative<I>(*v)
+                      ? lib::addressof(access::variant::get_alt<I>(*v).value)
+                      : nullptr)
+
+  }  // namespace detail
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr lib::add_pointer_t<variant_alternative_t<I, variant<Ts...>>>
+  get_if(variant<Ts...> *v) noexcept {
+    return detail::generic_get_if<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr lib::add_pointer_t<
+      const variant_alternative_t<I, variant<Ts...>>>
+  get_if(const variant<Ts...> *v) noexcept {
+    return detail::generic_get_if<I>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr lib::add_pointer_t<T>
+  get_if(variant<Ts...> *v) noexcept {
+    return get_if<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr lib::add_pointer_t<const T>
+  get_if(const variant<Ts...> *v) noexcept {
+    return get_if<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator==(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using lib::equal_to;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.index() != rhs.index()) return false;
+    if (lhs.valueless_by_exception()) return true;
+    return variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs);
+#else
+    return lhs.index() == rhs.index() &&
+           (lhs.valueless_by_exception() ||
+            variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator!=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using lib::not_equal_to;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.index() != rhs.index()) return true;
+    if (lhs.valueless_by_exception()) return false;
+    return variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs);
+#else
+    return lhs.index() != rhs.index() ||
+           (!lhs.valueless_by_exception() &&
+            variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator<(const variant<Ts...> &lhs,
+                                  const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using lib::less;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (rhs.valueless_by_exception()) return false;
+    if (lhs.valueless_by_exception()) return true;
+    if (lhs.index() < rhs.index()) return true;
+    if (lhs.index() > rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), less{}, lhs, rhs);
+#else
+    return !rhs.valueless_by_exception() &&
+           (lhs.valueless_by_exception() || lhs.index() < rhs.index() ||
+            (lhs.index() == rhs.index() &&
+             variant::visit_value_at(lhs.index(), less{}, lhs, rhs)));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator>(const variant<Ts...> &lhs,
+                                  const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using lib::greater;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.valueless_by_exception()) return false;
+    if (rhs.valueless_by_exception()) return true;
+    if (lhs.index() > rhs.index()) return true;
+    if (lhs.index() < rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), greater{}, lhs, rhs);
+#else
+    return !lhs.valueless_by_exception() &&
+           (rhs.valueless_by_exception() || lhs.index() > rhs.index() ||
+            (lhs.index() == rhs.index() &&
+             variant::visit_value_at(lhs.index(), greater{}, lhs, rhs)));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator<=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using lib::less_equal;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.valueless_by_exception()) return true;
+    if (rhs.valueless_by_exception()) return false;
+    if (lhs.index() < rhs.index()) return true;
+    if (lhs.index() > rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs);
+#else
+    return lhs.valueless_by_exception() ||
+           (!rhs.valueless_by_exception() &&
+            (lhs.index() < rhs.index() ||
+             (lhs.index() == rhs.index() &&
+              variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs))));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator>=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using lib::greater_equal;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (rhs.valueless_by_exception()) return true;
+    if (lhs.valueless_by_exception()) return false;
+    if (lhs.index() > rhs.index()) return true;
+    if (lhs.index() < rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), greater_equal{}, lhs, rhs);
+#else
+    return rhs.valueless_by_exception() ||
+           (!lhs.valueless_by_exception() &&
+            (lhs.index() > rhs.index() ||
+             (lhs.index() == rhs.index() &&
+              variant::visit_value_at(
+                  lhs.index(), greater_equal{}, lhs, rhs))));
+#endif
+  }
+
+  struct monostate {};
+
+  inline constexpr bool operator<(monostate, monostate) noexcept {
+    return false;
+  }
+
+  inline constexpr bool operator>(monostate, monostate) noexcept {
+    return false;
+  }
+
+  inline constexpr bool operator<=(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator>=(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator==(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator!=(monostate, monostate) noexcept {
+    return false;
+  }
+
+#ifdef MPARK_CPP14_CONSTEXPR
+  namespace detail {
+
+    inline constexpr bool all(std::initializer_list<bool> bs) {
+      for (bool b : bs) {
+        if (!b) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+  }  // namespace detail
+
+  template <typename Visitor, typename... Vs>
+  inline constexpr decltype(auto) visit(Visitor &&visitor, Vs &&... vs) {
+    return (detail::all({!vs.valueless_by_exception()...})
+                ? (void)0
+                : throw_bad_variant_access()),
+           detail::visitation::variant::visit_value(
+               lib::forward<Visitor>(visitor), lib::forward<Vs>(vs)...);
+  }
+#else
+  namespace detail {
+
+    template <std::size_t N>
+    inline constexpr bool all_impl(const lib::array<bool, N> &bs,
+                                   std::size_t idx) {
+      return idx >= N || (bs[idx] && all_impl(bs, idx + 1));
+    }
+
+    template <std::size_t N>
+    inline constexpr bool all(const lib::array<bool, N> &bs) {
+      return all_impl(bs, 0);
+    }
+
+  }  // namespace detail
+
+  template <typename Visitor, typename... Vs>
+  inline constexpr DECLTYPE_AUTO visit(Visitor &&visitor, Vs &&... vs)
+    DECLTYPE_AUTO_RETURN(
+        (detail::all(
+             lib::array<bool, sizeof...(Vs)>{{!vs.valueless_by_exception()...}})
+             ? (void)0
+             : throw_bad_variant_access()),
+        detail::visitation::variant::visit_value(lib::forward<Visitor>(visitor),
+                                                 lib::forward<Vs>(vs)...))
+#endif
+
+  template <typename... Ts>
+  inline auto swap(variant<Ts...> &lhs,
+                   variant<Ts...> &rhs) noexcept(noexcept(lhs.swap(rhs)))
+      -> decltype(lhs.swap(rhs)) {
+    lhs.swap(rhs);
+  }
+
+  namespace detail {
+
+    template <typename T, typename...>
+    using enabled_type = T;
+
+    namespace hash {
+
+      template <typename H, typename K>
+      constexpr bool meets_requirements() {
+        return std::is_copy_constructible<H>::value &&
+               std::is_move_constructible<H>::value &&
+               lib::is_invocable_r<std::size_t, H, const K &>::value;
+      }
+
+      template <typename K>
+      constexpr bool is_enabled() {
+        using H = std::hash<K>;
+        return meets_requirements<H, K>() &&
+               std::is_default_constructible<H>::value &&
+               std::is_copy_assignable<H>::value &&
+               std::is_move_assignable<H>::value;
+      }
+
+    }  // namespace hash
+
+  }  // namespace detail
+
+#undef AUTO
+#undef AUTO_RETURN
+
+#undef AUTO_REFREF
+#undef AUTO_REFREF_RETURN
+
+#undef DECLTYPE_AUTO
+#undef DECLTYPE_AUTO_RETURN
+
+}  // namespace mpark
+
+namespace std {
+
+  template <typename... Ts>
+  struct hash<mpark::detail::enabled_type<
+      mpark::variant<Ts...>,
+      mpark::lib::enable_if_t<mpark::lib::all<mpark::detail::hash::is_enabled<
+          mpark::lib::remove_const_t<Ts>>()...>::value>>> {
+    using argument_type = mpark::variant<Ts...>;
+    using result_type = std::size_t;
+
+    inline result_type operator()(const argument_type &v) const {
+      using mpark::detail::visitation::variant;
+      std::size_t result =
+          v.valueless_by_exception()
+              ? 299792458  // Random value chosen by the universe upon creation
+              : variant::visit_alt(
+#ifdef MPARK_GENERIC_LAMBDAS
+                    [](const auto &alt) {
+                      using alt_type = mpark::lib::decay_t<decltype(alt)>;
+                      using value_type = mpark::lib::remove_const_t<
+                          typename alt_type::value_type>;
+                      return hash<value_type>{}(alt.value);
+                    }
+#else
+                    hasher{}
+#endif
+                    ,
+                    v);
+      return hash_combine(result, hash<std::size_t>{}(v.index()));
+    }
+
+    private:
+#ifndef MPARK_GENERIC_LAMBDAS
+    struct hasher {
+      template <typename Alt>
+      inline std::size_t operator()(const Alt &alt) const {
+        using alt_type = mpark::lib::decay_t<Alt>;
+        using value_type =
+            mpark::lib::remove_const_t<typename alt_type::value_type>;
+        return hash<value_type>{}(alt.value);
+      }
+    };
+#endif
+
+    static std::size_t hash_combine(std::size_t lhs, std::size_t rhs) {
+      return lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
+    }
+  };
+
+  template <>
+  struct hash<mpark::monostate> {
+    using argument_type = mpark::monostate;
+    using result_type = std::size_t;
+
+    inline result_type operator()(const argument_type &) const noexcept {
+      return 66740831;  // return a fundamentally attractive random value.
+    }
+  };
+
+}  // namespace std
+
+#endif  // MPARK_VARIANT_HPP

+ 2416 - 0
src/external/mpark-variant/v1.2.2/variant.hpp

@@ -0,0 +1,2416 @@
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_VARIANT_HPP
+#define MPARK_VARIANT_HPP
+
+/*
+   variant synopsis
+
+namespace std {
+
+  // 20.7.2, class template variant
+  template <class... Types>
+  class variant {
+  public:
+
+    // 20.7.2.1, constructors
+    constexpr variant() noexcept(see below);
+    variant(const variant&);
+    variant(variant&&) noexcept(see below);
+
+    template <class T> constexpr variant(T&&) noexcept(see below);
+
+    template <class T, class... Args>
+    constexpr explicit variant(in_place_type_t<T>, Args&&...);
+
+    template <class T, class U, class... Args>
+    constexpr explicit variant(
+        in_place_type_t<T>, initializer_list<U>, Args&&...);
+
+    template <size_t I, class... Args>
+    constexpr explicit variant(in_place_index_t<I>, Args&&...);
+
+    template <size_t I, class U, class... Args>
+    constexpr explicit variant(
+        in_place_index_t<I>, initializer_list<U>, Args&&...);
+
+    // 20.7.2.2, destructor
+    ~variant();
+
+    // 20.7.2.3, assignment
+    variant& operator=(const variant&);
+    variant& operator=(variant&&) noexcept(see below);
+
+    template <class T> variant& operator=(T&&) noexcept(see below);
+
+    // 20.7.2.4, modifiers
+    template <class T, class... Args>
+    T& emplace(Args&&...);
+
+    template <class T, class U, class... Args>
+    T& emplace(initializer_list<U>, Args&&...);
+
+    template <size_t I, class... Args>
+    variant_alternative<I, variant>& emplace(Args&&...);
+
+    template <size_t I, class U, class...  Args>
+    variant_alternative<I, variant>& emplace(initializer_list<U>, Args&&...);
+
+    // 20.7.2.5, value status
+    constexpr bool valueless_by_exception() const noexcept;
+    constexpr size_t index() const noexcept;
+
+    // 20.7.2.6, swap
+    void swap(variant&) noexcept(see below);
+  };
+
+  // 20.7.3, variant helper classes
+  template <class T> struct variant_size; // undefined
+
+  template <class T>
+  constexpr size_t variant_size_v = variant_size<T>::value;
+
+  template <class T> struct variant_size<const T>;
+  template <class T> struct variant_size<volatile T>;
+  template <class T> struct variant_size<const volatile T>;
+
+  template <class... Types>
+  struct variant_size<variant<Types...>>;
+
+  template <size_t I, class T> struct variant_alternative; // undefined
+
+  template <size_t I, class T>
+  using variant_alternative_t = typename variant_alternative<I, T>::type;
+
+  template <size_t I, class T> struct variant_alternative<I, const T>;
+  template <size_t I, class T> struct variant_alternative<I, volatile T>;
+  template <size_t I, class T> struct variant_alternative<I, const volatile T>;
+
+  template <size_t I, class... Types>
+  struct variant_alternative<I, variant<Types...>>;
+
+  constexpr size_t variant_npos = -1;
+
+  // 20.7.4, value access
+  template <class T, class... Types>
+  constexpr bool holds_alternative(const variant<Types...>&) noexcept;
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>>&
+  get(variant<Types...>&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>>&&
+  get(variant<Types...>&&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>> const&
+  get(const variant<Types...>&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>> const&&
+  get(const variant<Types...>&&);
+
+  template <class T, class...  Types>
+  constexpr T& get(variant<Types...>&);
+
+  template <class T, class... Types>
+  constexpr T&& get(variant<Types...>&&);
+
+  template <class T, class... Types>
+  constexpr const T& get(const variant<Types...>&);
+
+  template <class T, class... Types>
+  constexpr const T&& get(const variant<Types...>&&);
+
+  template <size_t I, class... Types>
+  constexpr add_pointer_t<variant_alternative_t<I, variant<Types...>>>
+  get_if(variant<Types...>*) noexcept;
+
+  template <size_t I, class... Types>
+  constexpr add_pointer_t<const variant_alternative_t<I, variant<Types...>>>
+  get_if(const variant<Types...>*) noexcept;
+
+  template <class T, class... Types>
+  constexpr add_pointer_t<T>
+  get_if(variant<Types...>*) noexcept;
+
+  template <class T, class... Types>
+  constexpr add_pointer_t<const T>
+  get_if(const variant<Types...>*) noexcept;
+
+  // 20.7.5, relational operators
+  template <class... Types>
+  constexpr bool operator==(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator!=(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator<(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator>(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator<=(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator>=(const variant<Types...>&, const variant<Types...>&);
+
+  // 20.7.6, visitation
+  template <class Visitor, class... Variants>
+  constexpr see below visit(Visitor&&, Variants&&...);
+
+  // 20.7.7, class monostate
+  struct monostate;
+
+  // 20.7.8, monostate relational operators
+  constexpr bool operator<(monostate, monostate) noexcept;
+  constexpr bool operator>(monostate, monostate) noexcept;
+  constexpr bool operator<=(monostate, monostate) noexcept;
+  constexpr bool operator>=(monostate, monostate) noexcept;
+  constexpr bool operator==(monostate, monostate) noexcept;
+  constexpr bool operator!=(monostate, monostate) noexcept;
+
+  // 20.7.9, specialized algorithms
+  template <class... Types>
+  void swap(variant<Types...>&, variant<Types...>&) noexcept(see below);
+
+  // 20.7.10, class bad_variant_access
+  class bad_variant_access;
+
+  // 20.7.11, hash support
+  template <class T> struct hash;
+  template <class... Types> struct hash<variant<Types...>>;
+  template <> struct hash<monostate>;
+
+} // namespace std
+
+*/
+
+#include <cstddef>
+#include <exception>
+#include <functional>
+#include <initializer_list>
+#include <new>
+#include <type_traits>
+#include <utility>
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_CONFIG_HPP
+#define MPARK_CONFIG_HPP
+
+// MSVC 2015 Update 3.
+#if __cplusplus < 201103L && (!defined(_MSC_VER) || _MSC_FULL_VER < 190024215)
+#error "MPark.Variant requires C++11 support."
+#endif
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+#ifndef __has_include
+#define __has_include(x) 0
+#endif
+
+#ifndef __has_feature
+#define __has_feature(x) 0
+#endif
+
+#if __has_builtin(__builtin_addressof) || \
+    (defined(__GNUC__) && __GNUC__ >= 7) || defined(_MSC_VER)
+#define MPARK_BUILTIN_ADDRESSOF
+#endif
+
+#if __has_builtin(__builtin_unreachable)
+#define MPARK_BUILTIN_UNREACHABLE
+#endif
+
+#if __has_builtin(__type_pack_element)
+#define MPARK_TYPE_PACK_ELEMENT
+#endif
+
+#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304
+#define MPARK_CPP14_CONSTEXPR
+#endif
+
+#if __has_feature(cxx_exceptions) || defined(__cpp_exceptions) || \
+    (defined(_MSC_VER) && defined(_CPPUNWIND))
+#define MPARK_EXCEPTIONS
+#endif
+
+#if defined(__cpp_generic_lambdas) || defined(_MSC_VER)
+#define MPARK_GENERIC_LAMBDAS
+#endif
+
+#if defined(__cpp_lib_integer_sequence)
+#define MPARK_INTEGER_SEQUENCE
+#endif
+
+#if defined(__cpp_return_type_deduction) || defined(_MSC_VER)
+#define MPARK_RETURN_TYPE_DEDUCTION
+#endif
+
+#if defined(__cpp_lib_transparent_operators) || defined(_MSC_VER)
+#define MPARK_TRANSPARENT_OPERATORS
+#endif
+
+#if defined(__cpp_variable_templates) || defined(_MSC_VER)
+#define MPARK_VARIABLE_TEMPLATES
+#endif
+
+#if !defined(__GLIBCXX__) || __has_include(<codecvt>)  // >= libstdc++-5
+#define MPARK_TRIVIALITY_TYPE_TRAITS
+#endif
+
+#endif  // MPARK_CONFIG_HPP
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_IN_PLACE_HPP
+#define MPARK_IN_PLACE_HPP
+
+#include <cstddef>
+
+
+namespace mpark {
+
+  struct in_place_t { explicit in_place_t() = default; };
+
+  template <std::size_t I>
+  struct in_place_index_t { explicit in_place_index_t() = default; };
+
+  template <typename T>
+  struct in_place_type_t { explicit in_place_type_t() = default; };
+
+#ifdef MPARK_VARIABLE_TEMPLATES
+  constexpr in_place_t in_place{};
+
+  template <std::size_t I> constexpr in_place_index_t<I> in_place_index{};
+
+  template <typename T> constexpr in_place_type_t<T> in_place_type{};
+#endif
+
+}  // namespace mpark
+
+#endif  // MPARK_IN_PLACE_HPP
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_LIB_HPP
+#define MPARK_LIB_HPP
+
+#include <memory>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+
+#define RETURN(...)                                          \
+  noexcept(noexcept(__VA_ARGS__)) -> decltype(__VA_ARGS__) { \
+    return __VA_ARGS__;                                      \
+  }
+
+namespace mpark {
+  namespace lib {
+    template <typename T>
+    struct identity { using type = T; };
+
+    inline namespace cpp14 {
+      template <typename T, std::size_t N>
+      struct array {
+        constexpr const T &operator[](std::size_t index) const {
+          return data[index];
+        }
+
+        T data[N == 0 ? 1 : N];
+      };
+
+      template <typename T>
+      using add_pointer_t = typename std::add_pointer<T>::type;
+
+      template <typename... Ts>
+      using common_type_t = typename std::common_type<Ts...>::type;
+
+      template <typename T>
+      using decay_t = typename std::decay<T>::type;
+
+      template <bool B, typename T = void>
+      using enable_if_t = typename std::enable_if<B, T>::type;
+
+      template <typename T>
+      using remove_const_t = typename std::remove_const<T>::type;
+
+      template <typename T>
+      using remove_reference_t = typename std::remove_reference<T>::type;
+
+      template <typename T>
+      inline constexpr T &&forward(remove_reference_t<T> &t) noexcept {
+        return static_cast<T &&>(t);
+      }
+
+      template <typename T>
+      inline constexpr T &&forward(remove_reference_t<T> &&t) noexcept {
+        static_assert(!std::is_lvalue_reference<T>::value,
+                      "can not forward an rvalue as an lvalue");
+        return static_cast<T &&>(t);
+      }
+
+      template <typename T>
+      inline constexpr remove_reference_t<T> &&move(T &&t) noexcept {
+        return static_cast<remove_reference_t<T> &&>(t);
+      }
+
+#ifdef MPARK_INTEGER_SEQUENCE
+      using std::integer_sequence;
+      using std::index_sequence;
+      using std::make_index_sequence;
+      using std::index_sequence_for;
+#else
+      template <typename T, T... Is>
+      struct integer_sequence {
+        using value_type = T;
+        static constexpr std::size_t size() noexcept { return sizeof...(Is); }
+      };
+
+      template <std::size_t... Is>
+      using index_sequence = integer_sequence<std::size_t, Is...>;
+
+      template <typename Lhs, typename Rhs>
+      struct make_index_sequence_concat;
+
+      template <std::size_t... Lhs, std::size_t... Rhs>
+      struct make_index_sequence_concat<index_sequence<Lhs...>,
+                                        index_sequence<Rhs...>>
+          : identity<index_sequence<Lhs..., (sizeof...(Lhs) + Rhs)...>> {};
+
+      template <std::size_t N>
+      struct make_index_sequence_impl;
+
+      template <std::size_t N>
+      using make_index_sequence = typename make_index_sequence_impl<N>::type;
+
+      template <std::size_t N>
+      struct make_index_sequence_impl
+          : make_index_sequence_concat<make_index_sequence<N / 2>,
+                                       make_index_sequence<N - (N / 2)>> {};
+
+      template <>
+      struct make_index_sequence_impl<0> : identity<index_sequence<>> {};
+
+      template <>
+      struct make_index_sequence_impl<1> : identity<index_sequence<0>> {};
+
+      template <typename... Ts>
+      using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
+#endif
+
+      // <functional>
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using equal_to = std::equal_to<>;
+#else
+      struct equal_to {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          RETURN(lib::forward<Lhs>(lhs) == lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using not_equal_to = std::not_equal_to<>;
+#else
+      struct not_equal_to {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          RETURN(lib::forward<Lhs>(lhs) != lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using less = std::less<>;
+#else
+      struct less {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          RETURN(lib::forward<Lhs>(lhs) < lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using greater = std::greater<>;
+#else
+      struct greater {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          RETURN(lib::forward<Lhs>(lhs) > lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using less_equal = std::less_equal<>;
+#else
+      struct less_equal {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          RETURN(lib::forward<Lhs>(lhs) <= lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using greater_equal = std::greater_equal<>;
+#else
+      struct greater_equal {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          RETURN(lib::forward<Lhs>(lhs) >= lib::forward<Rhs>(rhs))
+      };
+#endif
+    }  // namespace cpp14
+
+    inline namespace cpp17 {
+
+      // <type_traits>
+      template <bool B>
+      using bool_constant = std::integral_constant<bool, B>;
+
+      template <typename...>
+      struct voider : identity<void> {};
+
+      template <typename... Ts>
+      using void_t = typename voider<Ts...>::type;
+
+      namespace detail {
+        namespace swappable {
+
+          using std::swap;
+
+          template <typename T>
+          struct is_swappable_impl {
+            private:
+            template <typename U,
+                      typename = decltype(swap(std::declval<U &>(),
+                                               std::declval<U &>()))>
+            inline static std::true_type test(int);
+
+            template <typename U>
+            inline static std::false_type test(...);
+
+            public:
+            using type = decltype(test<T>(0));
+          };
+
+          template <typename T>
+          using is_swappable = typename is_swappable_impl<T>::type;
+
+          template <typename T, bool = is_swappable<T>::value>
+          struct is_nothrow_swappable {
+            static constexpr bool value =
+                noexcept(swap(std::declval<T &>(), std::declval<T &>()));
+          };
+
+          template <typename T>
+          struct is_nothrow_swappable<T, false> : std::false_type {};
+
+        }  // namespace swappable
+      }  // namespace detail
+
+      template <typename T>
+      using is_swappable = detail::swappable::is_swappable<T>;
+
+      template <typename T>
+      using is_nothrow_swappable = detail::swappable::is_nothrow_swappable<T>;
+
+      // <functional>
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+      template <typename F, typename... As>
+      inline constexpr auto invoke(F &&f, As &&... as)
+          RETURN(lib::forward<F>(f)(lib::forward<As>(as)...))
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+      template <typename B, typename T, typename D>
+      inline constexpr auto invoke(T B::*pmv, D &&d)
+          RETURN(lib::forward<D>(d).*pmv)
+
+      template <typename Pmv, typename Ptr>
+      inline constexpr auto invoke(Pmv pmv, Ptr &&ptr)
+          RETURN((*lib::forward<Ptr>(ptr)).*pmv)
+
+      template <typename B, typename T, typename D, typename... As>
+      inline constexpr auto invoke(T B::*pmf, D &&d, As &&... as)
+          RETURN((lib::forward<D>(d).*pmf)(lib::forward<As>(as)...))
+
+      template <typename Pmf, typename Ptr, typename... As>
+      inline constexpr auto invoke(Pmf pmf, Ptr &&ptr, As &&... as)
+          RETURN(((*lib::forward<Ptr>(ptr)).*pmf)(lib::forward<As>(as)...))
+
+      namespace detail {
+
+        template <typename Void, typename, typename...>
+        struct invoke_result {};
+
+        template <typename F, typename... Args>
+        struct invoke_result<void_t<decltype(lib::invoke(
+                                 std::declval<F>(), std::declval<Args>()...))>,
+                             F,
+                             Args...>
+            : identity<decltype(
+                  lib::invoke(std::declval<F>(), std::declval<Args>()...))> {};
+
+      }  // namespace detail
+
+      template <typename F, typename... Args>
+      using invoke_result = detail::invoke_result<void, F, Args...>;
+
+      template <typename F, typename... Args>
+      using invoke_result_t = typename invoke_result<F, Args...>::type;
+
+      namespace detail {
+
+        template <typename Void, typename, typename...>
+        struct is_invocable : std::false_type {};
+
+        template <typename F, typename... Args>
+        struct is_invocable<void_t<invoke_result_t<F, Args...>>, F, Args...>
+            : std::true_type {};
+
+        template <typename Void, typename, typename, typename...>
+        struct is_invocable_r : std::false_type {};
+
+        template <typename R, typename F, typename... Args>
+        struct is_invocable_r<void_t<invoke_result_t<F, Args...>>,
+                              R,
+                              F,
+                              Args...>
+            : std::is_convertible<invoke_result_t<F, Args...>, R> {};
+
+      }  // namespace detail
+
+      template <typename F, typename... Args>
+      using is_invocable = detail::is_invocable<void, F, Args...>;
+
+      template <typename R, typename F, typename... Args>
+      using is_invocable_r = detail::is_invocable_r<void, R, F, Args...>;
+
+      // <memory>
+#ifdef MPARK_BUILTIN_ADDRESSOF
+      template <typename T>
+      inline constexpr T *addressof(T &arg) {
+        return __builtin_addressof(arg);
+      }
+#else
+      namespace detail {
+
+        namespace has_addressof_impl {
+
+          struct fail;
+
+          template <typename T>
+          inline fail operator&(T &&);
+
+          template <typename T>
+          inline static constexpr bool impl() {
+            return (std::is_class<T>::value || std::is_union<T>::value) &&
+                   !std::is_same<decltype(&std::declval<T &>()), fail>::value;
+          }
+
+        }  // namespace has_addressof_impl
+
+        template <typename T>
+        using has_addressof = bool_constant<has_addressof_impl::impl<T>()>;
+
+        template <typename T>
+        inline constexpr T *addressof(T &arg, std::true_type) {
+          return std::addressof(arg);
+        }
+
+        template <typename T>
+        inline constexpr T *addressof(T &arg, std::false_type) {
+          return &arg;
+        }
+
+      }  // namespace detail
+
+      template <typename T>
+      inline constexpr T *addressof(T &arg) {
+        return detail::addressof(arg, detail::has_addressof<T>{});
+      }
+#endif
+
+      template <typename T>
+      inline constexpr T *addressof(const T &&) = delete;
+
+    }  // namespace cpp17
+
+    template <typename T>
+    struct remove_all_extents : identity<T> {};
+
+    template <typename T, std::size_t N>
+    struct remove_all_extents<array<T, N>> : remove_all_extents<T> {};
+
+    template <typename T>
+    using remove_all_extents_t = typename remove_all_extents<T>::type;
+
+    template <std::size_t N>
+    using size_constant = std::integral_constant<std::size_t, N>;
+
+    template <bool... Bs>
+    using bool_sequence = integer_sequence<bool, Bs...>;
+
+    template <std::size_t I, typename T>
+    struct indexed_type : size_constant<I>, identity<T> {};
+
+    template <bool... Bs>
+    using all =
+        std::is_same<bool_sequence<true, Bs...>, bool_sequence<Bs..., true>>;
+
+#ifdef MPARK_TYPE_PACK_ELEMENT
+    template <std::size_t I, typename... Ts>
+    using type_pack_element_t = __type_pack_element<I, Ts...>;
+#else
+    template <std::size_t I, typename... Ts>
+    struct type_pack_element_impl {
+      private:
+      template <typename>
+      struct set;
+
+      template <std::size_t... Is>
+      struct set<index_sequence<Is...>> : indexed_type<Is, Ts>... {};
+
+      template <typename T>
+      inline static std::enable_if<true, T> impl(indexed_type<I, T>);
+
+      inline static std::enable_if<false> impl(...);
+
+      public:
+      using type = decltype(impl(set<index_sequence_for<Ts...>>{}));
+    };
+
+    template <std::size_t I, typename... Ts>
+    using type_pack_element = typename type_pack_element_impl<I, Ts...>::type;
+
+    template <std::size_t I, typename... Ts>
+    using type_pack_element_t = typename type_pack_element<I, Ts...>::type;
+#endif
+
+#ifdef MPARK_TRIVIALITY_TYPE_TRAITS
+    using std::is_trivially_copy_constructible;
+    using std::is_trivially_move_constructible;
+    using std::is_trivially_copy_assignable;
+    using std::is_trivially_move_assignable;
+#else
+    template <typename T>
+    struct is_trivially_copy_constructible
+        : bool_constant<
+              std::is_copy_constructible<T>::value && __has_trivial_copy(T)> {};
+
+    template <typename T>
+    struct is_trivially_move_constructible : bool_constant<__is_trivial(T)> {};
+
+    template <typename T>
+    struct is_trivially_copy_assignable
+        : bool_constant<
+              std::is_copy_assignable<T>::value && __has_trivial_assign(T)> {};
+
+    template <typename T>
+    struct is_trivially_move_assignable : bool_constant<__is_trivial(T)> {};
+#endif
+
+  }  // namespace lib
+}  // namespace mpark
+
+#undef RETURN
+
+#endif  // MPARK_LIB_HPP
+
+
+namespace mpark {
+
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+
+#define AUTO auto
+#define AUTO_RETURN(...) { return __VA_ARGS__; }
+
+#define AUTO_REFREF auto &&
+#define AUTO_REFREF_RETURN(...) { return __VA_ARGS__; }
+
+#define DECLTYPE_AUTO decltype(auto)
+#define DECLTYPE_AUTO_RETURN(...) { return __VA_ARGS__; }
+
+#else
+
+#define AUTO auto
+#define AUTO_RETURN(...) \
+  -> lib::decay_t<decltype(__VA_ARGS__)> { return __VA_ARGS__; }
+
+#define AUTO_REFREF auto
+#define AUTO_REFREF_RETURN(...)                                           \
+  -> decltype((__VA_ARGS__)) {                                            \
+    static_assert(std::is_reference<decltype((__VA_ARGS__))>::value, ""); \
+    return __VA_ARGS__;                                                   \
+  }
+
+#define DECLTYPE_AUTO auto
+#define DECLTYPE_AUTO_RETURN(...) \
+  -> decltype(__VA_ARGS__) { return __VA_ARGS__; }
+
+#endif
+
+  class bad_variant_access : public std::exception {
+    public:
+    virtual const char *what() const noexcept { return "bad_variant_access"; }
+  };
+
+  [[noreturn]] inline void throw_bad_variant_access() {
+#ifdef MPARK_EXCEPTIONS
+    throw bad_variant_access{};
+#else
+    std::terminate();
+#ifdef MPARK_BUILTIN_UNREACHABLE
+    __builtin_unreachable();
+#endif
+#endif
+  }
+
+  template <typename... Ts>
+  class variant;
+
+  template <typename T>
+  struct variant_size;
+
+#ifdef MPARK_VARIABLE_TEMPLATES
+  template <typename T>
+  constexpr std::size_t variant_size_v = variant_size<T>::value;
+#endif
+
+  template <typename T>
+  struct variant_size<const T> : variant_size<T> {};
+
+  template <typename T>
+  struct variant_size<volatile T> : variant_size<T> {};
+
+  template <typename T>
+  struct variant_size<const volatile T> : variant_size<T> {};
+
+  template <typename... Ts>
+  struct variant_size<variant<Ts...>> : lib::size_constant<sizeof...(Ts)> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative;
+
+  template <std::size_t I, typename T>
+  using variant_alternative_t = typename variant_alternative<I, T>::type;
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, const T>
+      : std::add_const<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, volatile T>
+      : std::add_volatile<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, const volatile T>
+      : std::add_cv<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename... Ts>
+  struct variant_alternative<I, variant<Ts...>> {
+    static_assert(I < sizeof...(Ts),
+                  "Index out of bounds in std::variant_alternative<>");
+    using type = lib::type_pack_element_t<I, Ts...>;
+  };
+
+  constexpr std::size_t variant_npos = static_cast<std::size_t>(-1);
+
+  namespace detail {
+
+    constexpr std::size_t not_found = static_cast<std::size_t>(-1);
+    constexpr std::size_t ambiguous = static_cast<std::size_t>(-2);
+
+#ifdef MPARK_CPP14_CONSTEXPR
+    template <typename T, typename... Ts>
+    inline constexpr std::size_t find_index() {
+      constexpr lib::array<bool, sizeof...(Ts)> matches = {
+          {std::is_same<T, Ts>::value...}
+      };
+      std::size_t result = not_found;
+      for (std::size_t i = 0; i < sizeof...(Ts); ++i) {
+        if (matches[i]) {
+          if (result != not_found) {
+            return ambiguous;
+          }
+          result = i;
+        }
+      }
+      return result;
+    }
+#else
+    inline constexpr std::size_t find_index_impl(std::size_t result,
+                                                 std::size_t) {
+      return result;
+    }
+
+    template <typename... Bs>
+    inline constexpr std::size_t find_index_impl(std::size_t result,
+                                                 std::size_t idx,
+                                                 bool b,
+                                                 Bs... bs) {
+      return b ? (result != not_found ? ambiguous
+                                      : find_index_impl(idx, idx + 1, bs...))
+               : find_index_impl(result, idx + 1, bs...);
+    }
+
+    template <typename T, typename... Ts>
+    inline constexpr std::size_t find_index() {
+      return find_index_impl(not_found, 0, std::is_same<T, Ts>::value...);
+    }
+#endif
+
+    template <std::size_t I>
+    using find_index_sfinae_impl =
+        lib::enable_if_t<I != not_found && I != ambiguous,
+                         lib::size_constant<I>>;
+
+    template <typename T, typename... Ts>
+    using find_index_sfinae = find_index_sfinae_impl<find_index<T, Ts...>()>;
+
+    template <std::size_t I>
+    struct find_index_checked_impl : lib::size_constant<I> {
+      static_assert(I != not_found, "the specified type is not found.");
+      static_assert(I != ambiguous, "the specified type is ambiguous.");
+    };
+
+    template <typename T, typename... Ts>
+    using find_index_checked = find_index_checked_impl<find_index<T, Ts...>()>;
+
+    struct valueless_t {};
+
+    enum class Trait { TriviallyAvailable, Available, Unavailable };
+
+    template <typename T,
+              template <typename> class IsTriviallyAvailable,
+              template <typename> class IsAvailable>
+    inline constexpr Trait trait() {
+      return IsTriviallyAvailable<T>::value
+                 ? Trait::TriviallyAvailable
+                 : IsAvailable<T>::value ? Trait::Available
+                                         : Trait::Unavailable;
+    }
+
+#ifdef MPARK_CPP14_CONSTEXPR
+    template <typename... Traits>
+    inline constexpr Trait common_trait(Traits... traits) {
+      Trait result = Trait::TriviallyAvailable;
+      for (Trait t : {traits...}) {
+        if (static_cast<int>(t) > static_cast<int>(result)) {
+          result = t;
+        }
+      }
+      return result;
+    }
+#else
+    inline constexpr Trait common_trait_impl(Trait result) { return result; }
+
+    template <typename... Traits>
+    inline constexpr Trait common_trait_impl(Trait result,
+                                             Trait t,
+                                             Traits... ts) {
+      return static_cast<int>(t) > static_cast<int>(result)
+                 ? common_trait_impl(t, ts...)
+                 : common_trait_impl(result, ts...);
+    }
+
+    template <typename... Traits>
+    inline constexpr Trait common_trait(Traits... ts) {
+      return common_trait_impl(Trait::TriviallyAvailable, ts...);
+    }
+#endif
+
+    template <typename... Ts>
+    struct traits {
+      static constexpr Trait copy_constructible_trait =
+          common_trait(trait<Ts,
+                             lib::is_trivially_copy_constructible,
+                             std::is_copy_constructible>()...);
+
+      static constexpr Trait move_constructible_trait =
+          common_trait(trait<Ts,
+                             lib::is_trivially_move_constructible,
+                             std::is_move_constructible>()...);
+
+      static constexpr Trait copy_assignable_trait =
+          common_trait(copy_constructible_trait,
+                       trait<Ts,
+                             lib::is_trivially_copy_assignable,
+                             std::is_copy_assignable>()...);
+
+      static constexpr Trait move_assignable_trait =
+          common_trait(move_constructible_trait,
+                       trait<Ts,
+                             lib::is_trivially_move_assignable,
+                             std::is_move_assignable>()...);
+
+      static constexpr Trait destructible_trait =
+          common_trait(trait<Ts,
+                             std::is_trivially_destructible,
+                             std::is_destructible>()...);
+    };
+
+    namespace access {
+
+      struct recursive_union {
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+        template <typename V>
+        inline static constexpr auto &&get_alt(V &&v, in_place_index_t<0>) {
+          return lib::forward<V>(v).head_;
+        }
+
+        template <typename V, std::size_t I>
+        inline static constexpr auto &&get_alt(V &&v, in_place_index_t<I>) {
+          return get_alt(lib::forward<V>(v).tail_, in_place_index_t<I - 1>{});
+        }
+#else
+        template <std::size_t I, bool Dummy = true>
+        struct get_alt_impl {
+          template <typename V>
+          inline constexpr AUTO_REFREF operator()(V &&v) const
+            AUTO_REFREF_RETURN(get_alt_impl<I - 1>{}(lib::forward<V>(v).tail_))
+        };
+
+        template <bool Dummy>
+        struct get_alt_impl<0, Dummy> {
+          template <typename V>
+          inline constexpr AUTO_REFREF operator()(V &&v) const
+            AUTO_REFREF_RETURN(lib::forward<V>(v).head_)
+        };
+
+        template <typename V, std::size_t I>
+        inline static constexpr AUTO_REFREF get_alt(V &&v, in_place_index_t<I>)
+          AUTO_REFREF_RETURN(get_alt_impl<I>{}(lib::forward<V>(v)))
+#endif
+      };
+
+      struct base {
+        template <std::size_t I, typename V>
+        inline static constexpr AUTO_REFREF get_alt(V &&v)
+          AUTO_REFREF_RETURN(recursive_union::get_alt(
+              data(lib::forward<V>(v)), in_place_index_t<I>{}))
+      };
+
+      struct variant {
+        template <std::size_t I, typename V>
+        inline static constexpr AUTO_REFREF get_alt(V &&v)
+          AUTO_REFREF_RETURN(base::get_alt<I>(lib::forward<V>(v).impl_))
+      };
+
+    }  // namespace access
+
+    namespace visitation {
+
+      struct base {
+        private:
+        template <typename T>
+        inline static constexpr const T &at(const T &elem) {
+          return elem;
+        }
+
+        template <typename T, std::size_t N, typename... Is>
+        inline static constexpr const lib::remove_all_extents_t<T> &at(
+            const lib::array<T, N> &elems, std::size_t i, Is... is) {
+          return at(elems[i], is...);
+        }
+
+        template <typename F, typename... Fs>
+        inline static constexpr int visit_visitor_return_type_check() {
+          static_assert(lib::all<std::is_same<F, Fs>::value...>::value,
+                        "`mpark::visit` requires the visitor to have a single "
+                        "return type.");
+          return 0;
+        }
+
+        template <typename... Fs>
+        inline static constexpr lib::array<
+            lib::common_type_t<lib::decay_t<Fs>...>,
+            sizeof...(Fs)>
+        make_farray(Fs &&... fs) {
+          using result = lib::array<lib::common_type_t<lib::decay_t<Fs>...>,
+                                    sizeof...(Fs)>;
+          return visit_visitor_return_type_check<lib::decay_t<Fs>...>(),
+                 result{{lib::forward<Fs>(fs)...}};
+        }
+
+        template <std::size_t... Is>
+        struct dispatcher {
+          template <typename F, typename... Vs>
+          struct impl {
+            inline static constexpr DECLTYPE_AUTO dispatch(F f, Vs... vs)
+              DECLTYPE_AUTO_RETURN(lib::invoke(
+                  static_cast<F>(f),
+                  access::base::get_alt<Is>(static_cast<Vs>(vs))...))
+          };
+        };
+
+        template <typename F, typename... Vs, std::size_t... Is>
+        inline static constexpr AUTO make_dispatch(lib::index_sequence<Is...>)
+          AUTO_RETURN(&dispatcher<Is...>::template impl<F, Vs...>::dispatch)
+
+        template <std::size_t I, typename F, typename... Vs>
+        inline static constexpr AUTO make_fdiagonal_impl()
+          AUTO_RETURN(make_dispatch<F, Vs...>(
+              lib::index_sequence<lib::indexed_type<I, Vs>::value...>{}))
+
+        template <typename F, typename... Vs, std::size_t... Is>
+        inline static constexpr AUTO make_fdiagonal_impl(
+            lib::index_sequence<Is...>)
+          AUTO_RETURN(make_farray(make_fdiagonal_impl<Is, F, Vs...>()...))
+
+        template <typename F, typename V, typename... Vs>
+        inline static constexpr /* auto * */ auto make_fdiagonal()
+            -> decltype(make_fdiagonal_impl<F, V, Vs...>(
+                lib::make_index_sequence<lib::decay_t<V>::size()>{})) {
+          static_assert(lib::all<(lib::decay_t<V>::size() ==
+                                  lib::decay_t<Vs>::size())...>::value,
+                        "all of the variants must be the same size.");
+          return make_fdiagonal_impl<F, V, Vs...>(
+              lib::make_index_sequence<lib::decay_t<V>::size()>{});
+        }
+
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+        template <typename F, typename... Vs, std::size_t... Is>
+        inline static constexpr auto make_fmatrix_impl(
+            lib::index_sequence<Is...> is) {
+          return make_dispatch<F, Vs...>(is);
+        }
+
+        template <typename F,
+                  typename... Vs,
+                  std::size_t... Is,
+                  std::size_t... Js,
+                  typename... Ls>
+        inline static constexpr auto make_fmatrix_impl(
+            lib::index_sequence<Is...>, lib::index_sequence<Js...>, Ls... ls) {
+          return make_farray(make_fmatrix_impl<F, Vs...>(
+              lib::index_sequence<Is..., Js>{}, ls...)...);
+        }
+
+        template <typename F, typename... Vs>
+        inline static constexpr auto make_fmatrix() {
+          return make_fmatrix_impl<F, Vs...>(
+              lib::index_sequence<>{},
+              lib::make_index_sequence<lib::decay_t<Vs>::size()>{}...);
+        }
+#else
+        template <typename F, typename... Vs>
+        struct make_fmatrix_impl {
+          template <typename...>
+          struct impl;
+
+          template <std::size_t... Is>
+          struct impl<lib::index_sequence<Is...>> {
+            inline constexpr AUTO operator()() const
+              AUTO_RETURN(
+                  make_dispatch<F, Vs...>(lib::index_sequence<Is...>{}))
+          };
+
+          template <std::size_t... Is, std::size_t... Js, typename... Ls>
+          struct impl<lib::index_sequence<Is...>,
+                      lib::index_sequence<Js...>,
+                      Ls...> {
+            inline constexpr AUTO operator()() const
+              AUTO_RETURN(make_farray(
+                  impl<lib::index_sequence<Is..., Js>, Ls...>{}()...))
+          };
+        };
+
+        template <typename F, typename... Vs>
+        inline static constexpr AUTO make_fmatrix()
+          AUTO_RETURN(
+              typename make_fmatrix_impl<F, Vs...>::template impl<
+                  lib::index_sequence<>,
+                  lib::make_index_sequence<lib::decay_t<Vs>::size()>...>{}())
+#endif
+
+        public:
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index,
+                                                           Visitor &&visitor,
+                                                           Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              at(make_fdiagonal<Visitor &&,
+                                decltype(as_base(lib::forward<Vs>(vs)))...>(),
+                 index)(lib::forward<Visitor>(visitor),
+                        as_base(lib::forward<Vs>(vs))...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor,
+                                                        Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              at(make_fmatrix<Visitor &&,
+                              decltype(as_base(lib::forward<Vs>(vs)))...>(),
+                 vs.index()...)(lib::forward<Visitor>(visitor),
+                                as_base(lib::forward<Vs>(vs))...))
+      };
+
+      struct variant {
+        private:
+        template <typename Visitor, typename... Values>
+        struct visit_exhaustive_visitor_check {
+          static_assert(
+              lib::is_invocable<Visitor, Values...>::value,
+              "`mpark::visit` requires the visitor to be exhaustive.");
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+          inline constexpr DECLTYPE_AUTO operator()(Visitor &&visitor,
+                                                    Values &&... values) const
+            DECLTYPE_AUTO_RETURN(lib::invoke(lib::forward<Visitor>(visitor),
+                                             lib::forward<Values>(values)...))
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+        };
+
+        template <typename Visitor>
+        struct value_visitor {
+          Visitor &&visitor_;
+
+          template <typename... Alts>
+          inline constexpr DECLTYPE_AUTO operator()(Alts &&... alts) const
+            DECLTYPE_AUTO_RETURN(
+                visit_exhaustive_visitor_check<
+                    Visitor,
+                    decltype((lib::forward<Alts>(alts).value))...>{}(
+                    lib::forward<Visitor>(visitor_),
+                    lib::forward<Alts>(alts).value...))
+        };
+
+        template <typename Visitor>
+        inline static constexpr AUTO make_value_visitor(Visitor &&visitor)
+          AUTO_RETURN(value_visitor<Visitor>{lib::forward<Visitor>(visitor)})
+
+        public:
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index,
+                                                           Visitor &&visitor,
+                                                           Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              base::visit_alt_at(index,
+                                 lib::forward<Visitor>(visitor),
+                                 lib::forward<Vs>(vs).impl_...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor,
+                                                        Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(base::visit_alt(lib::forward<Visitor>(visitor),
+                                               lib::forward<Vs>(vs).impl_...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_value_at(std::size_t index,
+                                                             Visitor &&visitor,
+                                                             Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              visit_alt_at(index,
+                           make_value_visitor(lib::forward<Visitor>(visitor)),
+                           lib::forward<Vs>(vs)...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_value(Visitor &&visitor,
+                                                          Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              visit_alt(make_value_visitor(lib::forward<Visitor>(visitor)),
+                        lib::forward<Vs>(vs)...))
+      };
+
+    }  // namespace visitation
+
+    template <std::size_t Index, typename T>
+    struct alt {
+      using value_type = T;
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4244)
+#endif
+      template <typename... Args>
+      inline explicit constexpr alt(in_place_t, Args &&... args)
+          : value(lib::forward<Args>(args)...) {}
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+      T value;
+    };
+
+    template <Trait DestructibleTrait, std::size_t Index, typename... Ts>
+    union recursive_union;
+
+    template <Trait DestructibleTrait, std::size_t Index>
+    union recursive_union<DestructibleTrait, Index> {};
+
+#define MPARK_VARIANT_RECURSIVE_UNION(destructible_trait, destructor)      \
+  template <std::size_t Index, typename T, typename... Ts>                 \
+  union recursive_union<destructible_trait, Index, T, Ts...> {             \
+    public:                                                                \
+    inline explicit constexpr recursive_union(valueless_t) noexcept        \
+        : dummy_{} {}                                                      \
+                                                                           \
+    template <typename... Args>                                            \
+    inline explicit constexpr recursive_union(in_place_index_t<0>,         \
+                                              Args &&... args)             \
+        : head_(in_place_t{}, lib::forward<Args>(args)...) {}              \
+                                                                           \
+    template <std::size_t I, typename... Args>                             \
+    inline explicit constexpr recursive_union(in_place_index_t<I>,         \
+                                              Args &&... args)             \
+        : tail_(in_place_index_t<I - 1>{}, lib::forward<Args>(args)...) {} \
+                                                                           \
+    recursive_union(const recursive_union &) = default;                    \
+    recursive_union(recursive_union &&) = default;                         \
+                                                                           \
+    destructor                                                             \
+                                                                           \
+    recursive_union &operator=(const recursive_union &) = default;         \
+    recursive_union &operator=(recursive_union &&) = default;              \
+                                                                           \
+    private:                                                               \
+    char dummy_;                                                           \
+    alt<Index, T> head_;                                                   \
+    recursive_union<destructible_trait, Index + 1, Ts...> tail_;           \
+                                                                           \
+    friend struct access::recursive_union;                                 \
+  }
+
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::TriviallyAvailable,
+                                  ~recursive_union() = default;);
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::Available,
+                                  ~recursive_union() {});
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::Unavailable,
+                                  ~recursive_union() = delete;);
+
+#undef MPARK_VARIANT_RECURSIVE_UNION
+
+    using index_t = unsigned int;
+
+    template <Trait DestructibleTrait, typename... Ts>
+    class base {
+      public:
+      inline explicit constexpr base(valueless_t tag) noexcept
+          : data_(tag), index_(static_cast<index_t>(-1)) {}
+
+      template <std::size_t I, typename... Args>
+      inline explicit constexpr base(in_place_index_t<I>, Args &&... args)
+          : data_(in_place_index_t<I>{}, lib::forward<Args>(args)...),
+            index_(I) {}
+
+      inline constexpr bool valueless_by_exception() const noexcept {
+        return index_ == static_cast<index_t>(-1);
+      }
+
+      inline constexpr std::size_t index() const noexcept {
+        return valueless_by_exception() ? variant_npos : index_;
+      }
+
+      protected:
+      using data_t = recursive_union<DestructibleTrait, 0, Ts...>;
+
+      friend inline constexpr base &as_base(base &b) { return b; }
+      friend inline constexpr const base &as_base(const base &b) { return b; }
+      friend inline constexpr base &&as_base(base &&b) { return lib::move(b); }
+      friend inline constexpr const base &&as_base(const base &&b) { return lib::move(b); }
+
+      friend inline constexpr data_t &data(base &b) { return b.data_; }
+      friend inline constexpr const data_t &data(const base &b) { return b.data_; }
+      friend inline constexpr data_t &&data(base &&b) { return lib::move(b).data_; }
+      friend inline constexpr const data_t &&data(const base &&b) { return lib::move(b).data_; }
+
+      inline static constexpr std::size_t size() { return sizeof...(Ts); }
+
+      data_t data_;
+      index_t index_;
+
+      friend struct access::base;
+      friend struct visitation::base;
+    };
+
+    struct dtor {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+      template <typename Alt>
+      inline void operator()(Alt &alt) const noexcept { alt.~Alt(); }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+    };
+
+#if defined(_MSC_VER) && _MSC_VER < 1910
+#define INHERITING_CTOR(type, base)               \
+  template <typename... Args>                     \
+  inline explicit constexpr type(Args &&... args) \
+      : base(lib::forward<Args>(args)...) {}
+#else
+#define INHERITING_CTOR(type, base) using base::base;
+#endif
+
+    template <typename Traits, Trait = Traits::destructible_trait>
+    class destructor;
+
+#define MPARK_VARIANT_DESTRUCTOR(destructible_trait, definition, destroy) \
+  template <typename... Ts>                                               \
+  class destructor<traits<Ts...>, destructible_trait>                     \
+      : public base<destructible_trait, Ts...> {                          \
+    using super = base<destructible_trait, Ts...>;                        \
+                                                                          \
+    public:                                                               \
+    INHERITING_CTOR(destructor, super)                                    \
+    using super::operator=;                                               \
+                                                                          \
+    destructor(const destructor &) = default;                             \
+    destructor(destructor &&) = default;                                  \
+    definition                                                            \
+    destructor &operator=(const destructor &) = default;                  \
+    destructor &operator=(destructor &&) = default;                       \
+                                                                          \
+    protected:                                                            \
+    destroy                                                               \
+  }
+
+    MPARK_VARIANT_DESTRUCTOR(
+        Trait::TriviallyAvailable,
+        ~destructor() = default;,
+        inline void destroy() noexcept {
+          this->index_ = static_cast<index_t>(-1);
+        });
+
+    MPARK_VARIANT_DESTRUCTOR(
+        Trait::Available,
+        ~destructor() { destroy(); },
+        inline void destroy() noexcept {
+          if (!this->valueless_by_exception()) {
+            visitation::base::visit_alt(dtor{}, *this);
+          }
+          this->index_ = static_cast<index_t>(-1);
+        });
+
+    MPARK_VARIANT_DESTRUCTOR(
+        Trait::Unavailable,
+        ~destructor() = delete;,
+        inline void destroy() noexcept = delete;);
+
+#undef MPARK_VARIANT_DESTRUCTOR
+
+    template <typename Traits>
+    class constructor : public destructor<Traits> {
+      using super = destructor<Traits>;
+
+      public:
+      INHERITING_CTOR(constructor, super)
+      using super::operator=;
+
+      protected:
+#ifndef MPARK_GENERIC_LAMBDAS
+      struct ctor {
+        template <typename LhsAlt, typename RhsAlt>
+        inline void operator()(LhsAlt &lhs_alt, RhsAlt &&rhs_alt) const {
+          constructor::construct_alt(lhs_alt,
+                                     lib::forward<RhsAlt>(rhs_alt).value);
+        }
+      };
+#endif
+
+      template <std::size_t I, typename T, typename... Args>
+      inline static T &construct_alt(alt<I, T> &a, Args &&... args) {
+        ::new (static_cast<void *>(lib::addressof(a)))
+            alt<I, T>(in_place_t{}, lib::forward<Args>(args)...);
+        return a.value;
+      }
+
+      template <typename Rhs>
+      inline static void generic_construct(constructor &lhs, Rhs &&rhs) {
+        lhs.destroy();
+        if (!rhs.valueless_by_exception()) {
+          visitation::base::visit_alt_at(
+              rhs.index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+              [](auto &lhs_alt, auto &&rhs_alt) {
+                constructor::construct_alt(
+                    lhs_alt, lib::forward<decltype(rhs_alt)>(rhs_alt).value);
+              }
+#else
+              ctor{}
+#endif
+              ,
+              lhs,
+              lib::forward<Rhs>(rhs));
+          lhs.index_ = rhs.index_;
+        }
+      }
+    };
+
+    template <typename Traits, Trait = Traits::move_constructible_trait>
+    class move_constructor;
+
+#define MPARK_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, definition) \
+  template <typename... Ts>                                                  \
+  class move_constructor<traits<Ts...>, move_constructible_trait>            \
+      : public constructor<traits<Ts...>> {                                  \
+    using super = constructor<traits<Ts...>>;                                \
+                                                                             \
+    public:                                                                  \
+    INHERITING_CTOR(move_constructor, super)                                 \
+    using super::operator=;                                                  \
+                                                                             \
+    move_constructor(const move_constructor &) = default;                    \
+    definition                                                               \
+    ~move_constructor() = default;                                           \
+    move_constructor &operator=(const move_constructor &) = default;         \
+    move_constructor &operator=(move_constructor &&) = default;              \
+  }
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::TriviallyAvailable,
+        move_constructor(move_constructor &&that) = default;);
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::Available,
+        move_constructor(move_constructor &&that) noexcept(
+            lib::all<std::is_nothrow_move_constructible<Ts>::value...>::value)
+            : move_constructor(valueless_t{}) {
+          this->generic_construct(*this, lib::move(that));
+        });
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::Unavailable,
+        move_constructor(move_constructor &&) = delete;);
+
+#undef MPARK_VARIANT_MOVE_CONSTRUCTOR
+
+    template <typename Traits, Trait = Traits::copy_constructible_trait>
+    class copy_constructor;
+
+#define MPARK_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, definition) \
+  template <typename... Ts>                                                  \
+  class copy_constructor<traits<Ts...>, copy_constructible_trait>            \
+      : public move_constructor<traits<Ts...>> {                             \
+    using super = move_constructor<traits<Ts...>>;                           \
+                                                                             \
+    public:                                                                  \
+    INHERITING_CTOR(copy_constructor, super)                                 \
+    using super::operator=;                                                  \
+                                                                             \
+    definition                                                               \
+    copy_constructor(copy_constructor &&) = default;                         \
+    ~copy_constructor() = default;                                           \
+    copy_constructor &operator=(const copy_constructor &) = default;         \
+    copy_constructor &operator=(copy_constructor &&) = default;              \
+  }
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::TriviallyAvailable,
+        copy_constructor(const copy_constructor &that) = default;);
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::Available,
+        copy_constructor(const copy_constructor &that)
+            : copy_constructor(valueless_t{}) {
+          this->generic_construct(*this, that);
+        });
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::Unavailable,
+        copy_constructor(const copy_constructor &) = delete;);
+
+#undef MPARK_VARIANT_COPY_CONSTRUCTOR
+
+    template <typename Traits>
+    class assignment : public copy_constructor<Traits> {
+      using super = copy_constructor<Traits>;
+
+      public:
+      INHERITING_CTOR(assignment, super)
+      using super::operator=;
+
+      template <std::size_t I, typename... Args>
+      inline /* auto & */ auto emplace(Args &&... args)
+          -> decltype(this->construct_alt(access::base::get_alt<I>(*this),
+                                          lib::forward<Args>(args)...)) {
+        this->destroy();
+        auto &result = this->construct_alt(access::base::get_alt<I>(*this),
+                                           lib::forward<Args>(args)...);
+        this->index_ = I;
+        return result;
+      }
+
+      protected:
+#ifndef MPARK_GENERIC_LAMBDAS
+      template <typename That>
+      struct assigner {
+        template <typename ThisAlt, typename ThatAlt>
+        inline void operator()(ThisAlt &this_alt, ThatAlt &&that_alt) const {
+          self->assign_alt(this_alt, lib::forward<ThatAlt>(that_alt).value);
+        }
+        assignment *self;
+      };
+#endif
+
+      template <std::size_t I, typename T, typename Arg>
+      inline void assign_alt(alt<I, T> &a, Arg &&arg) {
+        if (this->index() == I) {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4244)
+#endif
+          a.value = lib::forward<Arg>(arg);
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+        } else {
+          struct {
+            void operator()(std::true_type) const {
+              this_->emplace<I>(lib::forward<Arg>(arg_));
+            }
+            void operator()(std::false_type) const {
+              this_->emplace<I>(T(lib::forward<Arg>(arg_)));
+            }
+            assignment *this_;
+            Arg &&arg_;
+          } impl{this, lib::forward<Arg>(arg)};
+          impl(lib::bool_constant<
+                   std::is_nothrow_constructible<T, Arg>::value ||
+                   !std::is_nothrow_move_constructible<T>::value>{});
+        }
+      }
+
+      template <typename That>
+      inline void generic_assign(That &&that) {
+        if (this->valueless_by_exception() && that.valueless_by_exception()) {
+          // do nothing.
+        } else if (that.valueless_by_exception()) {
+          this->destroy();
+        } else {
+          visitation::base::visit_alt_at(
+              that.index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+              [this](auto &this_alt, auto &&that_alt) {
+                this->assign_alt(
+                    this_alt, lib::forward<decltype(that_alt)>(that_alt).value);
+              }
+#else
+              assigner<That>{this}
+#endif
+              ,
+              *this,
+              lib::forward<That>(that));
+        }
+      }
+    };
+
+    template <typename Traits, Trait = Traits::move_assignable_trait>
+    class move_assignment;
+
+#define MPARK_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, definition) \
+  template <typename... Ts>                                              \
+  class move_assignment<traits<Ts...>, move_assignable_trait>            \
+      : public assignment<traits<Ts...>> {                               \
+    using super = assignment<traits<Ts...>>;                             \
+                                                                         \
+    public:                                                              \
+    INHERITING_CTOR(move_assignment, super)                              \
+    using super::operator=;                                              \
+                                                                         \
+    move_assignment(const move_assignment &) = default;                  \
+    move_assignment(move_assignment &&) = default;                       \
+    ~move_assignment() = default;                                        \
+    move_assignment &operator=(const move_assignment &) = default;       \
+    definition                                                           \
+  }
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::TriviallyAvailable,
+        move_assignment &operator=(move_assignment &&that) = default;);
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::Available,
+        move_assignment &
+        operator=(move_assignment &&that) noexcept(
+            lib::all<(std::is_nothrow_move_constructible<Ts>::value &&
+                      std::is_nothrow_move_assignable<Ts>::value)...>::value) {
+          this->generic_assign(lib::move(that));
+          return *this;
+        });
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::Unavailable,
+        move_assignment &operator=(move_assignment &&) = delete;);
+
+#undef MPARK_VARIANT_MOVE_ASSIGNMENT
+
+    template <typename Traits, Trait = Traits::copy_assignable_trait>
+    class copy_assignment;
+
+#define MPARK_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, definition) \
+  template <typename... Ts>                                              \
+  class copy_assignment<traits<Ts...>, copy_assignable_trait>            \
+      : public move_assignment<traits<Ts...>> {                          \
+    using super = move_assignment<traits<Ts...>>;                        \
+                                                                         \
+    public:                                                              \
+    INHERITING_CTOR(copy_assignment, super)                              \
+    using super::operator=;                                              \
+                                                                         \
+    copy_assignment(const copy_assignment &) = default;                  \
+    copy_assignment(copy_assignment &&) = default;                       \
+    ~copy_assignment() = default;                                        \
+    definition                                                           \
+    copy_assignment &operator=(copy_assignment &&) = default;            \
+  }
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::TriviallyAvailable,
+        copy_assignment &operator=(const copy_assignment &that) = default;);
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::Available,
+        copy_assignment &operator=(const copy_assignment &that) {
+          this->generic_assign(that);
+          return *this;
+        });
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::Unavailable,
+        copy_assignment &operator=(const copy_assignment &) = delete;);
+
+#undef MPARK_VARIANT_COPY_ASSIGNMENT
+
+    template <typename... Ts>
+    class impl : public copy_assignment<traits<Ts...>> {
+      using super = copy_assignment<traits<Ts...>>;
+
+      public:
+      INHERITING_CTOR(impl, super)
+      using super::operator=;
+
+      template <std::size_t I, typename Arg>
+      inline void assign(Arg &&arg) {
+        this->assign_alt(access::base::get_alt<I>(*this),
+                         lib::forward<Arg>(arg));
+      }
+
+      inline void swap(impl &that) {
+        if (this->valueless_by_exception() && that.valueless_by_exception()) {
+          // do nothing.
+        } else if (this->index() == that.index()) {
+          visitation::base::visit_alt_at(this->index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+                                         [](auto &this_alt, auto &that_alt) {
+                                           using std::swap;
+                                           swap(this_alt.value,
+                                                that_alt.value);
+                                         }
+#else
+                                         swapper{}
+#endif
+                                         ,
+                                         *this,
+                                         that);
+        } else {
+          impl *lhs = this;
+          impl *rhs = lib::addressof(that);
+          if (lhs->move_nothrow() && !rhs->move_nothrow()) {
+            std::swap(lhs, rhs);
+          }
+          impl tmp(lib::move(*rhs));
+#ifdef MPARK_EXCEPTIONS
+          // EXTENSION: When the move construction of `lhs` into `rhs` throws
+          // and `tmp` is nothrow move constructible then we move `tmp` back
+          // into `rhs` and provide the strong exception safety guarantee.
+          try {
+            this->generic_construct(*rhs, lib::move(*lhs));
+          } catch (...) {
+            if (tmp.move_nothrow()) {
+              this->generic_construct(*rhs, lib::move(tmp));
+            }
+            throw;
+          }
+#else
+          this->generic_construct(*rhs, lib::move(*lhs));
+#endif
+          this->generic_construct(*lhs, lib::move(tmp));
+        }
+      }
+
+      private:
+#ifndef MPARK_GENERIC_LAMBDAS
+      struct swapper {
+        template <typename ThisAlt, typename ThatAlt>
+        inline void operator()(ThisAlt &this_alt, ThatAlt &that_alt) const {
+          using std::swap;
+          swap(this_alt.value, that_alt.value);
+        }
+      };
+#endif
+
+      inline constexpr bool move_nothrow() const {
+        return this->valueless_by_exception() ||
+               lib::array<bool, sizeof...(Ts)>{
+                   {std::is_nothrow_move_constructible<Ts>::value...}
+               }[this->index()];
+      }
+    };
+
+    template <std::size_t I, typename T>
+    struct overload_leaf {
+      using F = lib::size_constant<I> (*)(T);
+      operator F() const { return nullptr; }
+    };
+
+    template <typename... Ts>
+    struct overload_impl {
+      private:
+      template <typename>
+      struct impl;
+
+      template <std::size_t... Is>
+      struct impl<lib::index_sequence<Is...>> : overload_leaf<Is, Ts>... {};
+
+      public:
+      using type = impl<lib::index_sequence_for<Ts...>>;
+    };
+
+    template <typename... Ts>
+    using overload = typename overload_impl<Ts...>::type;
+
+    template <typename T, typename... Ts>
+    using best_match = lib::invoke_result_t<overload<Ts...>, T &&>;
+
+    template <typename T>
+    struct is_in_place_index : std::false_type {};
+
+    template <std::size_t I>
+    struct is_in_place_index<in_place_index_t<I>> : std::true_type {};
+
+    template <typename T>
+    struct is_in_place_type : std::false_type {};
+
+    template <typename T>
+    struct is_in_place_type<in_place_type_t<T>> : std::true_type {};
+
+  }  // detail
+
+  template <typename... Ts>
+  class variant {
+    static_assert(0 < sizeof...(Ts),
+                  "variant must consist of at least one alternative.");
+
+    static_assert(lib::all<!std::is_array<Ts>::value...>::value,
+                  "variant can not have an array type as an alternative.");
+
+    static_assert(lib::all<!std::is_reference<Ts>::value...>::value,
+                  "variant can not have a reference type as an alternative.");
+
+    static_assert(lib::all<!std::is_void<Ts>::value...>::value,
+                  "variant can not have a void type as an alternative.");
+
+    public:
+    template <
+        typename Front = lib::type_pack_element_t<0, Ts...>,
+        lib::enable_if_t<std::is_default_constructible<Front>::value, int> = 0>
+    inline constexpr variant() noexcept(
+        std::is_nothrow_default_constructible<Front>::value)
+        : impl_(in_place_index_t<0>{}) {}
+
+    variant(const variant &) = default;
+    variant(variant &&) = default;
+
+    template <
+        typename Arg,
+        typename Decayed = lib::decay_t<Arg>,
+        lib::enable_if_t<!std::is_same<Decayed, variant>::value, int> = 0,
+        lib::enable_if_t<!detail::is_in_place_index<Decayed>::value, int> = 0,
+        lib::enable_if_t<!detail::is_in_place_type<Decayed>::value, int> = 0,
+        std::size_t I = detail::best_match<Arg, Ts...>::value,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T, Arg>::value, int> = 0>
+    inline constexpr variant(Arg &&arg) noexcept(
+        std::is_nothrow_constructible<T, Arg>::value)
+        : impl_(in_place_index_t<I>{}, lib::forward<Arg>(arg)) {}
+
+    template <
+        std::size_t I,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline explicit constexpr variant(
+        in_place_index_t<I>,
+        Args &&... args) noexcept(std::is_nothrow_constructible<T,
+                                                                Args...>::value)
+        : impl_(in_place_index_t<I>{}, lib::forward<Args>(args)...) {}
+
+    template <
+        std::size_t I,
+        typename Up,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline explicit constexpr variant(
+        in_place_index_t<I>,
+        std::initializer_list<Up> il,
+        Args &&... args) noexcept(std::
+                                      is_nothrow_constructible<
+                                          T,
+                                          std::initializer_list<Up> &,
+                                          Args...>::value)
+        : impl_(in_place_index_t<I>{}, il, lib::forward<Args>(args)...) {}
+
+    template <
+        typename T,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline explicit constexpr variant(
+        in_place_type_t<T>,
+        Args &&... args) noexcept(std::is_nothrow_constructible<T,
+                                                                Args...>::value)
+        : impl_(in_place_index_t<I>{}, lib::forward<Args>(args)...) {}
+
+    template <
+        typename T,
+        typename Up,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline explicit constexpr variant(
+        in_place_type_t<T>,
+        std::initializer_list<Up> il,
+        Args &&... args) noexcept(std::
+                                      is_nothrow_constructible<
+                                          T,
+                                          std::initializer_list<Up> &,
+                                          Args...>::value)
+        : impl_(in_place_index_t<I>{}, il, lib::forward<Args>(args)...) {}
+
+    ~variant() = default;
+
+    variant &operator=(const variant &) = default;
+    variant &operator=(variant &&) = default;
+
+    template <typename Arg,
+              lib::enable_if_t<!std::is_same<lib::decay_t<Arg>, variant>::value,
+                               int> = 0,
+              std::size_t I = detail::best_match<Arg, Ts...>::value,
+              typename T = lib::type_pack_element_t<I, Ts...>,
+              lib::enable_if_t<(std::is_assignable<T &, Arg>::value &&
+                                std::is_constructible<T, Arg>::value),
+                               int> = 0>
+    inline variant &operator=(Arg &&arg) noexcept(
+        (std::is_nothrow_assignable<T &, Arg>::value &&
+         std::is_nothrow_constructible<T, Arg>::value)) {
+      impl_.template assign<I>(lib::forward<Arg>(arg));
+      return *this;
+    }
+
+    template <
+        std::size_t I,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline T &emplace(Args &&... args) {
+      return impl_.template emplace<I>(lib::forward<Args>(args)...);
+    }
+
+    template <
+        std::size_t I,
+        typename Up,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline T &emplace(std::initializer_list<Up> il, Args &&... args) {
+      return impl_.template emplace<I>(il, lib::forward<Args>(args)...);
+    }
+
+    template <
+        typename T,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline T &emplace(Args &&... args) {
+      return impl_.template emplace<I>(lib::forward<Args>(args)...);
+    }
+
+    template <
+        typename T,
+        typename Up,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline T &emplace(std::initializer_list<Up> il, Args &&... args) {
+      return impl_.template emplace<I>(il, lib::forward<Args>(args)...);
+    }
+
+    inline constexpr bool valueless_by_exception() const noexcept {
+      return impl_.valueless_by_exception();
+    }
+
+    inline constexpr std::size_t index() const noexcept {
+      return impl_.index();
+    }
+
+    template <
+        bool Dummy = true,
+        lib::enable_if_t<lib::all<Dummy,
+                                  (std::is_move_constructible<Ts>::value &&
+                                   lib::is_swappable<Ts>::value)...>::value,
+                         int> = 0>
+    inline void swap(variant &that) noexcept(
+        lib::all<(std::is_nothrow_move_constructible<Ts>::value &&
+                  lib::is_nothrow_swappable<Ts>::value)...>::value) {
+      impl_.swap(that.impl_);
+    }
+
+    private:
+    detail::impl<Ts...> impl_;
+
+    friend struct detail::access::variant;
+    friend struct detail::visitation::variant;
+  };
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept {
+    return v.index() == I;
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept {
+    return holds_alternative<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  namespace detail {
+    template <std::size_t I, typename V>
+    struct generic_get_impl {
+      constexpr generic_get_impl(int) {}
+
+      constexpr AUTO_REFREF operator()(V &&v) const
+        AUTO_REFREF_RETURN(
+            access::variant::get_alt<I>(lib::forward<V>(v)).value)
+    };
+
+    template <std::size_t I, typename V>
+    inline constexpr AUTO_REFREF generic_get(V &&v)
+      AUTO_REFREF_RETURN(generic_get_impl<I, V>(
+          holds_alternative<I>(v) ? 0 : (throw_bad_variant_access(), 0))(
+          lib::forward<V>(v)))
+  }  // namespace detail
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr variant_alternative_t<I, variant<Ts...>> &get(
+      variant<Ts...> &v) {
+    return detail::generic_get<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr variant_alternative_t<I, variant<Ts...>> &&get(
+      variant<Ts...> &&v) {
+    return detail::generic_get<I>(lib::move(v));
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr const variant_alternative_t<I, variant<Ts...>> &get(
+      const variant<Ts...> &v) {
+    return detail::generic_get<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr const variant_alternative_t<I, variant<Ts...>> &&get(
+      const variant<Ts...> &&v) {
+    return detail::generic_get<I>(lib::move(v));
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr T &get(variant<Ts...> &v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr T &&get(variant<Ts...> &&v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(lib::move(v));
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr const T &get(const variant<Ts...> &v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr const T &&get(const variant<Ts...> &&v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(lib::move(v));
+  }
+
+  namespace detail {
+
+    template <std::size_t I, typename V>
+    inline constexpr /* auto * */ AUTO generic_get_if(V *v) noexcept
+      AUTO_RETURN(v && holds_alternative<I>(*v)
+                      ? lib::addressof(access::variant::get_alt<I>(*v).value)
+                      : nullptr)
+
+  }  // namespace detail
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr lib::add_pointer_t<variant_alternative_t<I, variant<Ts...>>>
+  get_if(variant<Ts...> *v) noexcept {
+    return detail::generic_get_if<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr lib::add_pointer_t<
+      const variant_alternative_t<I, variant<Ts...>>>
+  get_if(const variant<Ts...> *v) noexcept {
+    return detail::generic_get_if<I>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr lib::add_pointer_t<T>
+  get_if(variant<Ts...> *v) noexcept {
+    return get_if<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr lib::add_pointer_t<const T>
+  get_if(const variant<Ts...> *v) noexcept {
+    return get_if<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator==(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using lib::equal_to;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.index() != rhs.index()) return false;
+    if (lhs.valueless_by_exception()) return true;
+    return variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs);
+#else
+    return lhs.index() == rhs.index() &&
+           (lhs.valueless_by_exception() ||
+            variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator!=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using lib::not_equal_to;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.index() != rhs.index()) return true;
+    if (lhs.valueless_by_exception()) return false;
+    return variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs);
+#else
+    return lhs.index() != rhs.index() ||
+           (!lhs.valueless_by_exception() &&
+            variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator<(const variant<Ts...> &lhs,
+                                  const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using lib::less;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (rhs.valueless_by_exception()) return false;
+    if (lhs.valueless_by_exception()) return true;
+    if (lhs.index() < rhs.index()) return true;
+    if (lhs.index() > rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), less{}, lhs, rhs);
+#else
+    return !rhs.valueless_by_exception() &&
+           (lhs.valueless_by_exception() || lhs.index() < rhs.index() ||
+            (lhs.index() == rhs.index() &&
+             variant::visit_value_at(lhs.index(), less{}, lhs, rhs)));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator>(const variant<Ts...> &lhs,
+                                  const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using lib::greater;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.valueless_by_exception()) return false;
+    if (rhs.valueless_by_exception()) return true;
+    if (lhs.index() > rhs.index()) return true;
+    if (lhs.index() < rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), greater{}, lhs, rhs);
+#else
+    return !lhs.valueless_by_exception() &&
+           (rhs.valueless_by_exception() || lhs.index() > rhs.index() ||
+            (lhs.index() == rhs.index() &&
+             variant::visit_value_at(lhs.index(), greater{}, lhs, rhs)));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator<=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using lib::less_equal;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.valueless_by_exception()) return true;
+    if (rhs.valueless_by_exception()) return false;
+    if (lhs.index() < rhs.index()) return true;
+    if (lhs.index() > rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs);
+#else
+    return lhs.valueless_by_exception() ||
+           (!rhs.valueless_by_exception() &&
+            (lhs.index() < rhs.index() ||
+             (lhs.index() == rhs.index() &&
+              variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs))));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator>=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using lib::greater_equal;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (rhs.valueless_by_exception()) return true;
+    if (lhs.valueless_by_exception()) return false;
+    if (lhs.index() > rhs.index()) return true;
+    if (lhs.index() < rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), greater_equal{}, lhs, rhs);
+#else
+    return rhs.valueless_by_exception() ||
+           (!lhs.valueless_by_exception() &&
+            (lhs.index() > rhs.index() ||
+             (lhs.index() == rhs.index() &&
+              variant::visit_value_at(
+                  lhs.index(), greater_equal{}, lhs, rhs))));
+#endif
+  }
+
+  struct monostate {};
+
+  inline constexpr bool operator<(monostate, monostate) noexcept {
+    return false;
+  }
+
+  inline constexpr bool operator>(monostate, monostate) noexcept {
+    return false;
+  }
+
+  inline constexpr bool operator<=(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator>=(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator==(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator!=(monostate, monostate) noexcept {
+    return false;
+  }
+
+#ifdef MPARK_CPP14_CONSTEXPR
+  namespace detail {
+
+    inline constexpr bool all(std::initializer_list<bool> bs) {
+      for (bool b : bs) {
+        if (!b) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+  }  // namespace detail
+
+  template <typename Visitor, typename... Vs>
+  inline constexpr decltype(auto) visit(Visitor &&visitor, Vs &&... vs) {
+    return (detail::all({!vs.valueless_by_exception()...})
+                ? (void)0
+                : throw_bad_variant_access()),
+           detail::visitation::variant::visit_value(
+               lib::forward<Visitor>(visitor), lib::forward<Vs>(vs)...);
+  }
+#else
+  namespace detail {
+
+    template <std::size_t N>
+    inline constexpr bool all_impl(const lib::array<bool, N> &bs,
+                                   std::size_t idx) {
+      return idx >= N || (bs[idx] && all_impl(bs, idx + 1));
+    }
+
+    template <std::size_t N>
+    inline constexpr bool all(const lib::array<bool, N> &bs) {
+      return all_impl(bs, 0);
+    }
+
+  }  // namespace detail
+
+  template <typename Visitor, typename... Vs>
+  inline constexpr DECLTYPE_AUTO visit(Visitor &&visitor, Vs &&... vs)
+    DECLTYPE_AUTO_RETURN(
+        (detail::all(
+             lib::array<bool, sizeof...(Vs)>{{!vs.valueless_by_exception()...}})
+             ? (void)0
+             : throw_bad_variant_access()),
+        detail::visitation::variant::visit_value(lib::forward<Visitor>(visitor),
+                                                 lib::forward<Vs>(vs)...))
+#endif
+
+  template <typename... Ts>
+  inline auto swap(variant<Ts...> &lhs,
+                   variant<Ts...> &rhs) noexcept(noexcept(lhs.swap(rhs)))
+      -> decltype(lhs.swap(rhs)) {
+    lhs.swap(rhs);
+  }
+
+  namespace detail {
+
+    template <typename T, typename...>
+    using enabled_type = T;
+
+    namespace hash {
+
+      template <typename H, typename K>
+      constexpr bool meets_requirements() {
+        return std::is_copy_constructible<H>::value &&
+               std::is_move_constructible<H>::value &&
+               lib::is_invocable_r<std::size_t, H, const K &>::value;
+      }
+
+      template <typename K>
+      constexpr bool is_enabled() {
+        using H = std::hash<K>;
+        return meets_requirements<H, K>() &&
+               std::is_default_constructible<H>::value &&
+               std::is_copy_assignable<H>::value &&
+               std::is_move_assignable<H>::value;
+      }
+
+    }  // namespace hash
+
+  }  // namespace detail
+
+#undef AUTO
+#undef AUTO_RETURN
+
+#undef AUTO_REFREF
+#undef AUTO_REFREF_RETURN
+
+#undef DECLTYPE_AUTO
+#undef DECLTYPE_AUTO_RETURN
+
+}  // namespace mpark
+
+namespace std {
+
+  template <typename... Ts>
+  struct hash<mpark::detail::enabled_type<
+      mpark::variant<Ts...>,
+      mpark::lib::enable_if_t<mpark::lib::all<mpark::detail::hash::is_enabled<
+          mpark::lib::remove_const_t<Ts>>()...>::value>>> {
+    using argument_type = mpark::variant<Ts...>;
+    using result_type = std::size_t;
+
+    inline result_type operator()(const argument_type &v) const {
+      using mpark::detail::visitation::variant;
+      std::size_t result =
+          v.valueless_by_exception()
+              ? 299792458  // Random value chosen by the universe upon creation
+              : variant::visit_alt(
+#ifdef MPARK_GENERIC_LAMBDAS
+                    [](const auto &alt) {
+                      using alt_type = mpark::lib::decay_t<decltype(alt)>;
+                      using value_type = mpark::lib::remove_const_t<
+                          typename alt_type::value_type>;
+                      return hash<value_type>{}(alt.value);
+                    }
+#else
+                    hasher{}
+#endif
+                    ,
+                    v);
+      return hash_combine(result, hash<std::size_t>{}(v.index()));
+    }
+
+    private:
+#ifndef MPARK_GENERIC_LAMBDAS
+    struct hasher {
+      template <typename Alt>
+      inline std::size_t operator()(const Alt &alt) const {
+        using alt_type = mpark::lib::decay_t<Alt>;
+        using value_type =
+            mpark::lib::remove_const_t<typename alt_type::value_type>;
+        return hash<value_type>{}(alt.value);
+      }
+    };
+#endif
+
+    static std::size_t hash_combine(std::size_t lhs, std::size_t rhs) {
+      return lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
+    }
+  };
+
+  template <>
+  struct hash<mpark::monostate> {
+    using argument_type = mpark::monostate;
+    using result_type = std::size_t;
+
+    inline result_type operator()(const argument_type &) const noexcept {
+      return 66740831;  // return a fundamentally attractive random value.
+    }
+  };
+
+}  // namespace std
+
+#endif  // MPARK_VARIANT_HPP

+ 2457 - 0
src/external/mpark-variant/v1.3.0/variant.hpp

@@ -0,0 +1,2457 @@
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_VARIANT_HPP
+#define MPARK_VARIANT_HPP
+
+/*
+   variant synopsis
+
+namespace std {
+
+  // 20.7.2, class template variant
+  template <class... Types>
+  class variant {
+  public:
+
+    // 20.7.2.1, constructors
+    constexpr variant() noexcept(see below);
+    variant(const variant&);
+    variant(variant&&) noexcept(see below);
+
+    template <class T> constexpr variant(T&&) noexcept(see below);
+
+    template <class T, class... Args>
+    constexpr explicit variant(in_place_type_t<T>, Args&&...);
+
+    template <class T, class U, class... Args>
+    constexpr explicit variant(
+        in_place_type_t<T>, initializer_list<U>, Args&&...);
+
+    template <size_t I, class... Args>
+    constexpr explicit variant(in_place_index_t<I>, Args&&...);
+
+    template <size_t I, class U, class... Args>
+    constexpr explicit variant(
+        in_place_index_t<I>, initializer_list<U>, Args&&...);
+
+    // 20.7.2.2, destructor
+    ~variant();
+
+    // 20.7.2.3, assignment
+    variant& operator=(const variant&);
+    variant& operator=(variant&&) noexcept(see below);
+
+    template <class T> variant& operator=(T&&) noexcept(see below);
+
+    // 20.7.2.4, modifiers
+    template <class T, class... Args>
+    T& emplace(Args&&...);
+
+    template <class T, class U, class... Args>
+    T& emplace(initializer_list<U>, Args&&...);
+
+    template <size_t I, class... Args>
+    variant_alternative<I, variant>& emplace(Args&&...);
+
+    template <size_t I, class U, class...  Args>
+    variant_alternative<I, variant>& emplace(initializer_list<U>, Args&&...);
+
+    // 20.7.2.5, value status
+    constexpr bool valueless_by_exception() const noexcept;
+    constexpr size_t index() const noexcept;
+
+    // 20.7.2.6, swap
+    void swap(variant&) noexcept(see below);
+  };
+
+  // 20.7.3, variant helper classes
+  template <class T> struct variant_size; // undefined
+
+  template <class T>
+  constexpr size_t variant_size_v = variant_size<T>::value;
+
+  template <class T> struct variant_size<const T>;
+  template <class T> struct variant_size<volatile T>;
+  template <class T> struct variant_size<const volatile T>;
+
+  template <class... Types>
+  struct variant_size<variant<Types...>>;
+
+  template <size_t I, class T> struct variant_alternative; // undefined
+
+  template <size_t I, class T>
+  using variant_alternative_t = typename variant_alternative<I, T>::type;
+
+  template <size_t I, class T> struct variant_alternative<I, const T>;
+  template <size_t I, class T> struct variant_alternative<I, volatile T>;
+  template <size_t I, class T> struct variant_alternative<I, const volatile T>;
+
+  template <size_t I, class... Types>
+  struct variant_alternative<I, variant<Types...>>;
+
+  constexpr size_t variant_npos = -1;
+
+  // 20.7.4, value access
+  template <class T, class... Types>
+  constexpr bool holds_alternative(const variant<Types...>&) noexcept;
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>>&
+  get(variant<Types...>&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>>&&
+  get(variant<Types...>&&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>> const&
+  get(const variant<Types...>&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>> const&&
+  get(const variant<Types...>&&);
+
+  template <class T, class...  Types>
+  constexpr T& get(variant<Types...>&);
+
+  template <class T, class... Types>
+  constexpr T&& get(variant<Types...>&&);
+
+  template <class T, class... Types>
+  constexpr const T& get(const variant<Types...>&);
+
+  template <class T, class... Types>
+  constexpr const T&& get(const variant<Types...>&&);
+
+  template <size_t I, class... Types>
+  constexpr add_pointer_t<variant_alternative_t<I, variant<Types...>>>
+  get_if(variant<Types...>*) noexcept;
+
+  template <size_t I, class... Types>
+  constexpr add_pointer_t<const variant_alternative_t<I, variant<Types...>>>
+  get_if(const variant<Types...>*) noexcept;
+
+  template <class T, class... Types>
+  constexpr add_pointer_t<T>
+  get_if(variant<Types...>*) noexcept;
+
+  template <class T, class... Types>
+  constexpr add_pointer_t<const T>
+  get_if(const variant<Types...>*) noexcept;
+
+  // 20.7.5, relational operators
+  template <class... Types>
+  constexpr bool operator==(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator!=(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator<(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator>(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator<=(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator>=(const variant<Types...>&, const variant<Types...>&);
+
+  // 20.7.6, visitation
+  template <class Visitor, class... Variants>
+  constexpr see below visit(Visitor&&, Variants&&...);
+
+  // 20.7.7, class monostate
+  struct monostate;
+
+  // 20.7.8, monostate relational operators
+  constexpr bool operator<(monostate, monostate) noexcept;
+  constexpr bool operator>(monostate, monostate) noexcept;
+  constexpr bool operator<=(monostate, monostate) noexcept;
+  constexpr bool operator>=(monostate, monostate) noexcept;
+  constexpr bool operator==(monostate, monostate) noexcept;
+  constexpr bool operator!=(monostate, monostate) noexcept;
+
+  // 20.7.9, specialized algorithms
+  template <class... Types>
+  void swap(variant<Types...>&, variant<Types...>&) noexcept(see below);
+
+  // 20.7.10, class bad_variant_access
+  class bad_variant_access;
+
+  // 20.7.11, hash support
+  template <class T> struct hash;
+  template <class... Types> struct hash<variant<Types...>>;
+  template <> struct hash<monostate>;
+
+} // namespace std
+
+*/
+
+#include <cstddef>
+#include <exception>
+#include <functional>
+#include <initializer_list>
+#include <new>
+#include <type_traits>
+#include <utility>
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_CONFIG_HPP
+#define MPARK_CONFIG_HPP
+
+// MSVC 2015 Update 3.
+#if __cplusplus < 201103L && (!defined(_MSC_VER) || _MSC_FULL_VER < 190024210)
+#error "MPark.Variant requires C++11 support."
+#endif
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+#ifndef __has_include
+#define __has_include(x) 0
+#endif
+
+#ifndef __has_feature
+#define __has_feature(x) 0
+#endif
+
+#if __has_builtin(__builtin_addressof) || \
+    (defined(__GNUC__) && __GNUC__ >= 7) || defined(_MSC_VER)
+#define MPARK_BUILTIN_ADDRESSOF
+#endif
+
+#if __has_builtin(__builtin_unreachable)
+#define MPARK_BUILTIN_UNREACHABLE
+#endif
+
+#if __has_builtin(__type_pack_element)
+#define MPARK_TYPE_PACK_ELEMENT
+#endif
+
+#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304
+#define MPARK_CPP14_CONSTEXPR
+#endif
+
+#if __has_feature(cxx_exceptions) || defined(__cpp_exceptions) || \
+    (defined(_MSC_VER) && defined(_CPPUNWIND))
+#define MPARK_EXCEPTIONS
+#endif
+
+#if defined(__cpp_generic_lambdas) || defined(_MSC_VER)
+#define MPARK_GENERIC_LAMBDAS
+#endif
+
+#if defined(__cpp_lib_integer_sequence)
+#define MPARK_INTEGER_SEQUENCE
+#endif
+
+#if defined(__cpp_return_type_deduction) || defined(_MSC_VER)
+#define MPARK_RETURN_TYPE_DEDUCTION
+#endif
+
+#if defined(__cpp_lib_transparent_operators) || defined(_MSC_VER)
+#define MPARK_TRANSPARENT_OPERATORS
+#endif
+
+#if defined(__cpp_variable_templates) || defined(_MSC_VER)
+#define MPARK_VARIABLE_TEMPLATES
+#endif
+
+#if !defined(__GLIBCXX__) || __has_include(<codecvt>)  // >= libstdc++-5
+#define MPARK_TRIVIALITY_TYPE_TRAITS
+#endif
+
+#endif  // MPARK_CONFIG_HPP
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_IN_PLACE_HPP
+#define MPARK_IN_PLACE_HPP
+
+#include <cstddef>
+
+
+namespace mpark {
+
+  struct in_place_t { explicit in_place_t() = default; };
+
+  template <std::size_t I>
+  struct in_place_index_t { explicit in_place_index_t() = default; };
+
+  template <typename T>
+  struct in_place_type_t { explicit in_place_type_t() = default; };
+
+#ifdef MPARK_VARIABLE_TEMPLATES
+  constexpr in_place_t in_place{};
+
+  template <std::size_t I> constexpr in_place_index_t<I> in_place_index{};
+
+  template <typename T> constexpr in_place_type_t<T> in_place_type{};
+#endif
+
+}  // namespace mpark
+
+#endif  // MPARK_IN_PLACE_HPP
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_LIB_HPP
+#define MPARK_LIB_HPP
+
+#include <memory>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+
+#define RETURN(...)                                          \
+  noexcept(noexcept(__VA_ARGS__)) -> decltype(__VA_ARGS__) { \
+    return __VA_ARGS__;                                      \
+  }
+
+namespace mpark {
+  namespace lib {
+    template <typename T>
+    struct identity { using type = T; };
+
+    inline namespace cpp14 {
+      template <typename T, std::size_t N>
+      struct array {
+        constexpr const T &operator[](std::size_t index) const {
+          return data[index];
+        }
+
+        T data[N == 0 ? 1 : N];
+      };
+
+      template <typename T>
+      using add_pointer_t = typename std::add_pointer<T>::type;
+
+      template <typename... Ts>
+      using common_type_t = typename std::common_type<Ts...>::type;
+
+      template <typename T>
+      using decay_t = typename std::decay<T>::type;
+
+      template <bool B, typename T = void>
+      using enable_if_t = typename std::enable_if<B, T>::type;
+
+      template <typename T>
+      using remove_const_t = typename std::remove_const<T>::type;
+
+      template <typename T>
+      using remove_reference_t = typename std::remove_reference<T>::type;
+
+      template <typename T>
+      inline constexpr T &&forward(remove_reference_t<T> &t) noexcept {
+        return static_cast<T &&>(t);
+      }
+
+      template <typename T>
+      inline constexpr T &&forward(remove_reference_t<T> &&t) noexcept {
+        static_assert(!std::is_lvalue_reference<T>::value,
+                      "can not forward an rvalue as an lvalue");
+        return static_cast<T &&>(t);
+      }
+
+      template <typename T>
+      inline constexpr remove_reference_t<T> &&move(T &&t) noexcept {
+        return static_cast<remove_reference_t<T> &&>(t);
+      }
+
+#ifdef MPARK_INTEGER_SEQUENCE
+      using std::integer_sequence;
+      using std::index_sequence;
+      using std::make_index_sequence;
+      using std::index_sequence_for;
+#else
+      template <typename T, T... Is>
+      struct integer_sequence {
+        using value_type = T;
+        static constexpr std::size_t size() noexcept { return sizeof...(Is); }
+      };
+
+      template <std::size_t... Is>
+      using index_sequence = integer_sequence<std::size_t, Is...>;
+
+      template <typename Lhs, typename Rhs>
+      struct make_index_sequence_concat;
+
+      template <std::size_t... Lhs, std::size_t... Rhs>
+      struct make_index_sequence_concat<index_sequence<Lhs...>,
+                                        index_sequence<Rhs...>>
+          : identity<index_sequence<Lhs..., (sizeof...(Lhs) + Rhs)...>> {};
+
+      template <std::size_t N>
+      struct make_index_sequence_impl;
+
+      template <std::size_t N>
+      using make_index_sequence = typename make_index_sequence_impl<N>::type;
+
+      template <std::size_t N>
+      struct make_index_sequence_impl
+          : make_index_sequence_concat<make_index_sequence<N / 2>,
+                                       make_index_sequence<N - (N / 2)>> {};
+
+      template <>
+      struct make_index_sequence_impl<0> : identity<index_sequence<>> {};
+
+      template <>
+      struct make_index_sequence_impl<1> : identity<index_sequence<0>> {};
+
+      template <typename... Ts>
+      using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
+#endif
+
+      // <functional>
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using equal_to = std::equal_to<>;
+#else
+      struct equal_to {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          RETURN(lib::forward<Lhs>(lhs) == lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using not_equal_to = std::not_equal_to<>;
+#else
+      struct not_equal_to {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          RETURN(lib::forward<Lhs>(lhs) != lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using less = std::less<>;
+#else
+      struct less {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          RETURN(lib::forward<Lhs>(lhs) < lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using greater = std::greater<>;
+#else
+      struct greater {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          RETURN(lib::forward<Lhs>(lhs) > lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using less_equal = std::less_equal<>;
+#else
+      struct less_equal {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          RETURN(lib::forward<Lhs>(lhs) <= lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using greater_equal = std::greater_equal<>;
+#else
+      struct greater_equal {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          RETURN(lib::forward<Lhs>(lhs) >= lib::forward<Rhs>(rhs))
+      };
+#endif
+    }  // namespace cpp14
+
+    inline namespace cpp17 {
+
+      // <type_traits>
+      template <bool B>
+      using bool_constant = std::integral_constant<bool, B>;
+
+      template <typename...>
+      struct voider : identity<void> {};
+
+      template <typename... Ts>
+      using void_t = typename voider<Ts...>::type;
+
+      namespace detail {
+        namespace swappable {
+
+          using std::swap;
+
+          template <typename T>
+          struct is_swappable {
+            private:
+            template <typename U,
+                      typename = decltype(swap(std::declval<U &>(),
+                                               std::declval<U &>()))>
+            inline static std::true_type test(int);
+
+            template <typename U>
+            inline static std::false_type test(...);
+
+            public:
+            static constexpr bool value = decltype(test<T>(0))::value;
+          };
+
+          template <typename T, bool = is_swappable<T>::value>
+          struct is_nothrow_swappable {
+            static constexpr bool value =
+                noexcept(swap(std::declval<T &>(), std::declval<T &>()));
+          };
+
+          template <typename T>
+          struct is_nothrow_swappable<T, false> : std::false_type {};
+
+        }  // namespace swappable
+      }  // namespace detail
+
+      using detail::swappable::is_swappable;
+      using detail::swappable::is_nothrow_swappable;
+
+      // <functional>
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+      template <typename F, typename... As>
+      inline constexpr auto invoke(F &&f, As &&... as)
+          RETURN(lib::forward<F>(f)(lib::forward<As>(as)...))
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+      template <typename B, typename T, typename D>
+      inline constexpr auto invoke(T B::*pmv, D &&d)
+          RETURN(lib::forward<D>(d).*pmv)
+
+      template <typename Pmv, typename Ptr>
+      inline constexpr auto invoke(Pmv pmv, Ptr &&ptr)
+          RETURN((*lib::forward<Ptr>(ptr)).*pmv)
+
+      template <typename B, typename T, typename D, typename... As>
+      inline constexpr auto invoke(T B::*pmf, D &&d, As &&... as)
+          RETURN((lib::forward<D>(d).*pmf)(lib::forward<As>(as)...))
+
+      template <typename Pmf, typename Ptr, typename... As>
+      inline constexpr auto invoke(Pmf pmf, Ptr &&ptr, As &&... as)
+          RETURN(((*lib::forward<Ptr>(ptr)).*pmf)(lib::forward<As>(as)...))
+
+      namespace detail {
+
+        template <typename Void, typename, typename...>
+        struct invoke_result {};
+
+        template <typename F, typename... Args>
+        struct invoke_result<void_t<decltype(lib::invoke(
+                                 std::declval<F>(), std::declval<Args>()...))>,
+                             F,
+                             Args...>
+            : identity<decltype(
+                  lib::invoke(std::declval<F>(), std::declval<Args>()...))> {};
+
+      }  // namespace detail
+
+      template <typename F, typename... Args>
+      using invoke_result = detail::invoke_result<void, F, Args...>;
+
+      template <typename F, typename... Args>
+      using invoke_result_t = typename invoke_result<F, Args...>::type;
+
+      namespace detail {
+
+        template <typename Void, typename, typename...>
+        struct is_invocable : std::false_type {};
+
+        template <typename F, typename... Args>
+        struct is_invocable<void_t<invoke_result_t<F, Args...>>, F, Args...>
+            : std::true_type {};
+
+        template <typename Void, typename, typename, typename...>
+        struct is_invocable_r : std::false_type {};
+
+        template <typename R, typename F, typename... Args>
+        struct is_invocable_r<void_t<invoke_result_t<F, Args...>>,
+                              R,
+                              F,
+                              Args...>
+            : std::is_convertible<invoke_result_t<F, Args...>, R> {};
+
+      }  // namespace detail
+
+      template <typename F, typename... Args>
+      using is_invocable = detail::is_invocable<void, F, Args...>;
+
+      template <typename R, typename F, typename... Args>
+      using is_invocable_r = detail::is_invocable_r<void, R, F, Args...>;
+
+      // <memory>
+#ifdef MPARK_BUILTIN_ADDRESSOF
+      template <typename T>
+      inline constexpr T *addressof(T &arg) {
+        return __builtin_addressof(arg);
+      }
+#else
+      namespace detail {
+
+        namespace has_addressof_impl {
+
+          struct fail;
+
+          template <typename T>
+          inline fail operator&(T &&);
+
+          template <typename T>
+          inline static constexpr bool impl() {
+            return (std::is_class<T>::value || std::is_union<T>::value) &&
+                   !std::is_same<decltype(&std::declval<T &>()), fail>::value;
+          }
+
+        }  // namespace has_addressof_impl
+
+        template <typename T>
+        using has_addressof = bool_constant<has_addressof_impl::impl<T>()>;
+
+        template <typename T>
+        inline constexpr T *addressof(T &arg, std::true_type) {
+          return std::addressof(arg);
+        }
+
+        template <typename T>
+        inline constexpr T *addressof(T &arg, std::false_type) {
+          return &arg;
+        }
+
+      }  // namespace detail
+
+      template <typename T>
+      inline constexpr T *addressof(T &arg) {
+        return detail::addressof(arg, detail::has_addressof<T>{});
+      }
+#endif
+
+      template <typename T>
+      inline constexpr T *addressof(const T &&) = delete;
+
+    }  // namespace cpp17
+
+    template <typename T>
+    struct remove_all_extents : identity<T> {};
+
+    template <typename T, std::size_t N>
+    struct remove_all_extents<array<T, N>> : remove_all_extents<T> {};
+
+    template <typename T>
+    using remove_all_extents_t = typename remove_all_extents<T>::type;
+
+    template <std::size_t N>
+    using size_constant = std::integral_constant<std::size_t, N>;
+
+    template <std::size_t I, typename T>
+    struct indexed_type : size_constant<I>, identity<T> {};
+
+    template <bool... Bs>
+    using all = std::is_same<integer_sequence<bool, true, Bs...>,
+                             integer_sequence<bool, Bs..., true>>;
+
+#ifdef MPARK_TYPE_PACK_ELEMENT
+    template <std::size_t I, typename... Ts>
+    using type_pack_element_t = __type_pack_element<I, Ts...>;
+#else
+    template <std::size_t I, typename... Ts>
+    struct type_pack_element_impl {
+      private:
+      template <typename>
+      struct set;
+
+      template <std::size_t... Is>
+      struct set<index_sequence<Is...>> : indexed_type<Is, Ts>... {};
+
+      template <typename T>
+      inline static std::enable_if<true, T> impl(indexed_type<I, T>);
+
+      inline static std::enable_if<false> impl(...);
+
+      public:
+      using type = decltype(impl(set<index_sequence_for<Ts...>>{}));
+    };
+
+    template <std::size_t I, typename... Ts>
+    using type_pack_element = typename type_pack_element_impl<I, Ts...>::type;
+
+    template <std::size_t I, typename... Ts>
+    using type_pack_element_t = typename type_pack_element<I, Ts...>::type;
+#endif
+
+#ifdef MPARK_TRIVIALITY_TYPE_TRAITS
+    using std::is_trivially_copy_constructible;
+    using std::is_trivially_move_constructible;
+    using std::is_trivially_copy_assignable;
+    using std::is_trivially_move_assignable;
+#else
+    template <typename T>
+    struct is_trivially_copy_constructible
+        : bool_constant<
+              std::is_copy_constructible<T>::value && __has_trivial_copy(T)> {};
+
+    template <typename T>
+    struct is_trivially_move_constructible : bool_constant<__is_trivial(T)> {};
+
+    template <typename T>
+    struct is_trivially_copy_assignable
+        : bool_constant<
+              std::is_copy_assignable<T>::value && __has_trivial_assign(T)> {};
+
+    template <typename T>
+    struct is_trivially_move_assignable : bool_constant<__is_trivial(T)> {};
+#endif
+
+    template <typename T, bool>
+    struct dependent_type : T {};
+
+    template <typename Is, std::size_t J>
+    struct push_back;
+
+    template <typename Is, std::size_t J>
+    using push_back_t = typename push_back<Is, J>::type;
+
+    template <std::size_t... Is, std::size_t J>
+    struct push_back<index_sequence<Is...>, J> {
+      using type = index_sequence<Is..., J>;
+    };
+
+  }  // namespace lib
+}  // namespace mpark
+
+#undef RETURN
+
+#endif  // MPARK_LIB_HPP
+
+
+namespace mpark {
+
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+
+#define AUTO auto
+#define AUTO_RETURN(...) { return __VA_ARGS__; }
+
+#define AUTO_REFREF auto &&
+#define AUTO_REFREF_RETURN(...) { return __VA_ARGS__; }
+
+#define DECLTYPE_AUTO decltype(auto)
+#define DECLTYPE_AUTO_RETURN(...) { return __VA_ARGS__; }
+
+#else
+
+#define AUTO auto
+#define AUTO_RETURN(...) \
+  -> lib::decay_t<decltype(__VA_ARGS__)> { return __VA_ARGS__; }
+
+#define AUTO_REFREF auto
+#define AUTO_REFREF_RETURN(...)                                           \
+  -> decltype((__VA_ARGS__)) {                                            \
+    static_assert(std::is_reference<decltype((__VA_ARGS__))>::value, ""); \
+    return __VA_ARGS__;                                                   \
+  }
+
+#define DECLTYPE_AUTO auto
+#define DECLTYPE_AUTO_RETURN(...) \
+  -> decltype(__VA_ARGS__) { return __VA_ARGS__; }
+
+#endif
+
+  class bad_variant_access : public std::exception {
+    public:
+    virtual const char *what() const noexcept { return "bad_variant_access"; }
+  };
+
+  [[noreturn]] inline void throw_bad_variant_access() {
+#ifdef MPARK_EXCEPTIONS
+    throw bad_variant_access{};
+#else
+    std::terminate();
+#ifdef MPARK_BUILTIN_UNREACHABLE
+    __builtin_unreachable();
+#endif
+#endif
+  }
+
+  template <typename... Ts>
+  class variant;
+
+  template <typename T>
+  struct variant_size;
+
+#ifdef MPARK_VARIABLE_TEMPLATES
+  template <typename T>
+  constexpr std::size_t variant_size_v = variant_size<T>::value;
+#endif
+
+  template <typename T>
+  struct variant_size<const T> : variant_size<T> {};
+
+  template <typename T>
+  struct variant_size<volatile T> : variant_size<T> {};
+
+  template <typename T>
+  struct variant_size<const volatile T> : variant_size<T> {};
+
+  template <typename... Ts>
+  struct variant_size<variant<Ts...>> : lib::size_constant<sizeof...(Ts)> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative;
+
+  template <std::size_t I, typename T>
+  using variant_alternative_t = typename variant_alternative<I, T>::type;
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, const T>
+      : std::add_const<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, volatile T>
+      : std::add_volatile<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, const volatile T>
+      : std::add_cv<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename... Ts>
+  struct variant_alternative<I, variant<Ts...>> {
+    static_assert(I < sizeof...(Ts),
+                  "Index out of bounds in std::variant_alternative<>");
+    using type = lib::type_pack_element_t<I, Ts...>;
+  };
+
+  constexpr std::size_t variant_npos = static_cast<std::size_t>(-1);
+
+  namespace detail {
+
+    constexpr std::size_t not_found = static_cast<std::size_t>(-1);
+    constexpr std::size_t ambiguous = static_cast<std::size_t>(-2);
+
+#ifdef MPARK_CPP14_CONSTEXPR
+    template <typename T, typename... Ts>
+    inline constexpr std::size_t find_index() {
+      constexpr lib::array<bool, sizeof...(Ts)> matches = {
+          {std::is_same<T, Ts>::value...}
+      };
+      std::size_t result = not_found;
+      for (std::size_t i = 0; i < sizeof...(Ts); ++i) {
+        if (matches[i]) {
+          if (result != not_found) {
+            return ambiguous;
+          }
+          result = i;
+        }
+      }
+      return result;
+    }
+#else
+    inline constexpr std::size_t find_index_impl(std::size_t result,
+                                                 std::size_t) {
+      return result;
+    }
+
+    template <typename... Bs>
+    inline constexpr std::size_t find_index_impl(std::size_t result,
+                                                 std::size_t idx,
+                                                 bool b,
+                                                 Bs... bs) {
+      return b ? (result != not_found ? ambiguous
+                                      : find_index_impl(idx, idx + 1, bs...))
+               : find_index_impl(result, idx + 1, bs...);
+    }
+
+    template <typename T, typename... Ts>
+    inline constexpr std::size_t find_index() {
+      return find_index_impl(not_found, 0, std::is_same<T, Ts>::value...);
+    }
+#endif
+
+    template <std::size_t I>
+    using find_index_sfinae_impl =
+        lib::enable_if_t<I != not_found && I != ambiguous,
+                         lib::size_constant<I>>;
+
+    template <typename T, typename... Ts>
+    using find_index_sfinae = find_index_sfinae_impl<find_index<T, Ts...>()>;
+
+    template <std::size_t I>
+    struct find_index_checked_impl : lib::size_constant<I> {
+      static_assert(I != not_found, "the specified type is not found.");
+      static_assert(I != ambiguous, "the specified type is ambiguous.");
+    };
+
+    template <typename T, typename... Ts>
+    using find_index_checked = find_index_checked_impl<find_index<T, Ts...>()>;
+
+    struct valueless_t {};
+
+    enum class Trait { TriviallyAvailable, Available, Unavailable };
+
+    template <typename T,
+              template <typename> class IsTriviallyAvailable,
+              template <typename> class IsAvailable>
+    inline constexpr Trait trait() {
+      return IsTriviallyAvailable<T>::value
+                 ? Trait::TriviallyAvailable
+                 : IsAvailable<T>::value ? Trait::Available
+                                         : Trait::Unavailable;
+    }
+
+#ifdef MPARK_CPP14_CONSTEXPR
+    template <typename... Traits>
+    inline constexpr Trait common_trait(Traits... traits) {
+      Trait result = Trait::TriviallyAvailable;
+      for (Trait t : {traits...}) {
+        if (static_cast<int>(t) > static_cast<int>(result)) {
+          result = t;
+        }
+      }
+      return result;
+    }
+#else
+    inline constexpr Trait common_trait_impl(Trait result) { return result; }
+
+    template <typename... Traits>
+    inline constexpr Trait common_trait_impl(Trait result,
+                                             Trait t,
+                                             Traits... ts) {
+      return static_cast<int>(t) > static_cast<int>(result)
+                 ? common_trait_impl(t, ts...)
+                 : common_trait_impl(result, ts...);
+    }
+
+    template <typename... Traits>
+    inline constexpr Trait common_trait(Traits... ts) {
+      return common_trait_impl(Trait::TriviallyAvailable, ts...);
+    }
+#endif
+
+    template <typename... Ts>
+    struct traits {
+      static constexpr Trait copy_constructible_trait =
+          common_trait(trait<Ts,
+                             lib::is_trivially_copy_constructible,
+                             std::is_copy_constructible>()...);
+
+      static constexpr Trait move_constructible_trait =
+          common_trait(trait<Ts,
+                             lib::is_trivially_move_constructible,
+                             std::is_move_constructible>()...);
+
+      static constexpr Trait copy_assignable_trait =
+          common_trait(copy_constructible_trait,
+                       trait<Ts,
+                             lib::is_trivially_copy_assignable,
+                             std::is_copy_assignable>()...);
+
+      static constexpr Trait move_assignable_trait =
+          common_trait(move_constructible_trait,
+                       trait<Ts,
+                             lib::is_trivially_move_assignable,
+                             std::is_move_assignable>()...);
+
+      static constexpr Trait destructible_trait =
+          common_trait(trait<Ts,
+                             std::is_trivially_destructible,
+                             std::is_destructible>()...);
+    };
+
+    namespace access {
+
+      struct recursive_union {
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+        template <typename V>
+        inline static constexpr auto &&get_alt(V &&v, in_place_index_t<0>) {
+          return lib::forward<V>(v).head_;
+        }
+
+        template <typename V, std::size_t I>
+        inline static constexpr auto &&get_alt(V &&v, in_place_index_t<I>) {
+          return get_alt(lib::forward<V>(v).tail_, in_place_index_t<I - 1>{});
+        }
+#else
+        template <std::size_t I, bool Dummy = true>
+        struct get_alt_impl {
+          template <typename V>
+          inline constexpr AUTO_REFREF operator()(V &&v) const
+            AUTO_REFREF_RETURN(get_alt_impl<I - 1>{}(lib::forward<V>(v).tail_))
+        };
+
+        template <bool Dummy>
+        struct get_alt_impl<0, Dummy> {
+          template <typename V>
+          inline constexpr AUTO_REFREF operator()(V &&v) const
+            AUTO_REFREF_RETURN(lib::forward<V>(v).head_)
+        };
+
+        template <typename V, std::size_t I>
+        inline static constexpr AUTO_REFREF get_alt(V &&v, in_place_index_t<I>)
+          AUTO_REFREF_RETURN(get_alt_impl<I>{}(lib::forward<V>(v)))
+#endif
+      };
+
+      struct base {
+        template <std::size_t I, typename V>
+        inline static constexpr AUTO_REFREF get_alt(V &&v)
+          AUTO_REFREF_RETURN(recursive_union::get_alt(
+              data(lib::forward<V>(v)), in_place_index_t<I>{}))
+      };
+
+      struct variant {
+        template <std::size_t I, typename V>
+        inline static constexpr AUTO_REFREF get_alt(V &&v)
+          AUTO_REFREF_RETURN(base::get_alt<I>(lib::forward<V>(v).impl_))
+      };
+
+    }  // namespace access
+
+    namespace visitation {
+
+      struct base {
+        template <typename T>
+        inline static constexpr const T &at(const T &elem) {
+          return elem;
+        }
+
+        template <typename T, std::size_t N, typename... Is>
+        inline static constexpr const lib::remove_all_extents_t<T> &at(
+            const lib::array<T, N> &elems, std::size_t i, Is... is) {
+          return at(elems[i], is...);
+        }
+
+        template <typename F, typename... Fs>
+        inline static constexpr int visit_visitor_return_type_check() {
+          static_assert(lib::all<std::is_same<F, Fs>::value...>::value,
+                        "`mpark::visit` requires the visitor to have a single "
+                        "return type.");
+          return 0;
+        }
+
+        template <typename... Fs>
+        inline static constexpr lib::array<
+            lib::common_type_t<lib::decay_t<Fs>...>,
+            sizeof...(Fs)>
+        make_farray(Fs &&... fs) {
+          using result = lib::array<lib::common_type_t<lib::decay_t<Fs>...>,
+                                    sizeof...(Fs)>;
+          return visit_visitor_return_type_check<lib::decay_t<Fs>...>(),
+                 result{{lib::forward<Fs>(fs)...}};
+        }
+
+        template <std::size_t... Is>
+        struct dispatcher {
+          template <typename F, typename... Vs>
+          struct impl {
+            inline static constexpr DECLTYPE_AUTO dispatch(F f, Vs... vs)
+              DECLTYPE_AUTO_RETURN(lib::invoke(
+                  static_cast<F>(f),
+                  access::base::get_alt<Is>(static_cast<Vs>(vs))...))
+          };
+        };
+
+        template <typename F, typename... Vs, std::size_t... Is>
+        inline static constexpr AUTO make_dispatch(lib::index_sequence<Is...>)
+          AUTO_RETURN(&dispatcher<Is...>::template impl<F, Vs...>::dispatch)
+
+        template <std::size_t I, typename F, typename... Vs>
+        inline static constexpr AUTO make_fdiagonal_impl()
+          AUTO_RETURN(make_dispatch<F, Vs...>(
+              lib::index_sequence<lib::indexed_type<I, Vs>::value...>{}))
+
+        template <typename F, typename... Vs, std::size_t... Is>
+        inline static constexpr AUTO make_fdiagonal_impl(
+            lib::index_sequence<Is...>)
+          AUTO_RETURN(make_farray(make_fdiagonal_impl<Is, F, Vs...>()...))
+
+        template <typename F, typename V, typename... Vs>
+        inline static constexpr /* auto * */ auto make_fdiagonal()
+            -> decltype(make_fdiagonal_impl<F, V, Vs...>(
+                lib::make_index_sequence<lib::decay_t<V>::size()>{})) {
+          static_assert(lib::all<(lib::decay_t<V>::size() ==
+                                  lib::decay_t<Vs>::size())...>::value,
+                        "all of the variants must be the same size.");
+          return make_fdiagonal_impl<F, V, Vs...>(
+              lib::make_index_sequence<lib::decay_t<V>::size()>{});
+        }
+
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+        template <typename F, typename... Vs, typename Is>
+        inline static constexpr auto make_fmatrix_impl(Is is) {
+          return make_dispatch<F, Vs...>(is);
+        }
+
+        template <typename F,
+                  typename... Vs,
+                  typename Is,
+                  std::size_t... Js,
+                  typename... Ls>
+        inline static constexpr auto make_fmatrix_impl(
+            Is, lib::index_sequence<Js...>, Ls... ls) {
+          return make_farray(make_fmatrix_impl<F, Vs...>(
+              lib::push_back_t<Is, Js>{}, ls...)...);
+        }
+
+        template <typename F, typename... Vs>
+        inline static constexpr auto make_fmatrix() {
+          return make_fmatrix_impl<F, Vs...>(
+              lib::index_sequence<>{},
+              lib::make_index_sequence<lib::decay_t<Vs>::size()>{}...);
+        }
+#else
+        template <typename F, typename... Vs>
+        struct make_fmatrix_impl {
+          template <typename...>
+          struct impl;
+
+          template <typename Is>
+          struct impl<Is> {
+            inline constexpr AUTO operator()() const
+              AUTO_RETURN(make_dispatch<F, Vs...>(Is{}))
+          };
+
+          template <typename Is, std::size_t... Js, typename... Ls>
+          struct impl<Is, lib::index_sequence<Js...>, Ls...> {
+            inline constexpr AUTO operator()() const
+              AUTO_RETURN(
+                  make_farray(impl<lib::push_back_t<Is, Js>, Ls...>{}()...))
+          };
+        };
+
+        template <typename F, typename... Vs>
+        inline static constexpr AUTO make_fmatrix()
+          AUTO_RETURN(
+              typename make_fmatrix_impl<F, Vs...>::template impl<
+                  lib::index_sequence<>,
+                  lib::make_index_sequence<lib::decay_t<Vs>::size()>...>{}())
+#endif
+      };  // namespace base
+
+      template <typename F, typename... Vs>
+      using FDiagonal = decltype(base::make_fdiagonal<F, Vs...>());
+
+      template <typename F, typename... Vs>
+      struct fdiagonal {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4268)
+#endif
+        static constexpr FDiagonal<F, Vs...> value =
+            base::make_fdiagonal<F, Vs...>();
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+      };
+
+      template <typename F, typename... Vs>
+      constexpr FDiagonal<F, Vs...> fdiagonal<F, Vs...>::value;
+
+      template <typename F, typename... Vs>
+      using FMatrix = decltype(base::make_fmatrix<F, Vs...>());
+
+      template <typename F, typename... Vs>
+      struct fmatrix {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4268)
+#endif
+        static constexpr FMatrix<F, Vs...> value =
+            base::make_fmatrix<F, Vs...>();
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+      };
+
+      template <typename F, typename... Vs>
+      constexpr FMatrix<F, Vs...> fmatrix<F, Vs...>::value;
+
+      struct alt {
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index,
+                                                           Visitor &&visitor,
+                                                           Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(base::at(
+              fdiagonal<Visitor &&,
+                        decltype(as_base(lib::forward<Vs>(vs)))...>::value,
+              index)(lib::forward<Visitor>(visitor),
+                     as_base(lib::forward<Vs>(vs))...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor,
+                                                        Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(base::at(
+              fmatrix<Visitor &&,
+                      decltype(as_base(lib::forward<Vs>(vs)))...>::value,
+              vs.index()...)(lib::forward<Visitor>(visitor),
+                             as_base(lib::forward<Vs>(vs))...))
+      };
+
+      struct variant {
+        private:
+        template <typename Visitor, typename... Values>
+        struct visit_exhaustive_visitor_check {
+          static_assert(
+              lib::is_invocable<Visitor, Values...>::value,
+              "`mpark::visit` requires the visitor to be exhaustive.");
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+          inline constexpr DECLTYPE_AUTO operator()(Visitor &&visitor,
+                                                    Values &&... values) const
+            DECLTYPE_AUTO_RETURN(lib::invoke(lib::forward<Visitor>(visitor),
+                                             lib::forward<Values>(values)...))
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+        };
+
+        template <typename Visitor>
+        struct value_visitor {
+          Visitor &&visitor_;
+
+          template <typename... Alts>
+          inline constexpr DECLTYPE_AUTO operator()(Alts &&... alts) const
+            DECLTYPE_AUTO_RETURN(
+                visit_exhaustive_visitor_check<
+                    Visitor,
+                    decltype((lib::forward<Alts>(alts).value))...>{}(
+                    lib::forward<Visitor>(visitor_),
+                    lib::forward<Alts>(alts).value...))
+        };
+
+        template <typename Visitor>
+        inline static constexpr AUTO make_value_visitor(Visitor &&visitor)
+          AUTO_RETURN(value_visitor<Visitor>{lib::forward<Visitor>(visitor)})
+
+        public:
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index,
+                                                           Visitor &&visitor,
+                                                           Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              alt::visit_alt_at(index,
+                                lib::forward<Visitor>(visitor),
+                                lib::forward<Vs>(vs).impl_...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor,
+                                                        Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(alt::visit_alt(lib::forward<Visitor>(visitor),
+                                              lib::forward<Vs>(vs).impl_...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_value_at(std::size_t index,
+                                                             Visitor &&visitor,
+                                                             Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              visit_alt_at(index,
+                           make_value_visitor(lib::forward<Visitor>(visitor)),
+                           lib::forward<Vs>(vs)...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_value(Visitor &&visitor,
+                                                          Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              visit_alt(make_value_visitor(lib::forward<Visitor>(visitor)),
+                        lib::forward<Vs>(vs)...))
+      };
+
+    }  // namespace visitation
+
+    template <std::size_t Index, typename T>
+    struct alt {
+      using value_type = T;
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4244)
+#endif
+      template <typename... Args>
+      inline explicit constexpr alt(in_place_t, Args &&... args)
+          : value(lib::forward<Args>(args)...) {}
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+      T value;
+    };
+
+    template <Trait DestructibleTrait, std::size_t Index, typename... Ts>
+    union recursive_union;
+
+    template <Trait DestructibleTrait, std::size_t Index>
+    union recursive_union<DestructibleTrait, Index> {};
+
+#define MPARK_VARIANT_RECURSIVE_UNION(destructible_trait, destructor)      \
+  template <std::size_t Index, typename T, typename... Ts>                 \
+  union recursive_union<destructible_trait, Index, T, Ts...> {             \
+    public:                                                                \
+    inline explicit constexpr recursive_union(valueless_t) noexcept        \
+        : dummy_{} {}                                                      \
+                                                                           \
+    template <typename... Args>                                            \
+    inline explicit constexpr recursive_union(in_place_index_t<0>,         \
+                                              Args &&... args)             \
+        : head_(in_place_t{}, lib::forward<Args>(args)...) {}              \
+                                                                           \
+    template <std::size_t I, typename... Args>                             \
+    inline explicit constexpr recursive_union(in_place_index_t<I>,         \
+                                              Args &&... args)             \
+        : tail_(in_place_index_t<I - 1>{}, lib::forward<Args>(args)...) {} \
+                                                                           \
+    recursive_union(const recursive_union &) = default;                    \
+    recursive_union(recursive_union &&) = default;                         \
+                                                                           \
+    destructor                                                             \
+                                                                           \
+    recursive_union &operator=(const recursive_union &) = default;         \
+    recursive_union &operator=(recursive_union &&) = default;              \
+                                                                           \
+    private:                                                               \
+    char dummy_;                                                           \
+    alt<Index, T> head_;                                                   \
+    recursive_union<destructible_trait, Index + 1, Ts...> tail_;           \
+                                                                           \
+    friend struct access::recursive_union;                                 \
+  }
+
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::TriviallyAvailable,
+                                  ~recursive_union() = default;);
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::Available,
+                                  ~recursive_union() {});
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::Unavailable,
+                                  ~recursive_union() = delete;);
+
+#undef MPARK_VARIANT_RECURSIVE_UNION
+
+    using index_t = unsigned int;
+
+    template <Trait DestructibleTrait, typename... Ts>
+    class base {
+      public:
+      inline explicit constexpr base(valueless_t tag) noexcept
+          : data_(tag), index_(static_cast<index_t>(-1)) {}
+
+      template <std::size_t I, typename... Args>
+      inline explicit constexpr base(in_place_index_t<I>, Args &&... args)
+          : data_(in_place_index_t<I>{}, lib::forward<Args>(args)...),
+            index_(I) {}
+
+      inline constexpr bool valueless_by_exception() const noexcept {
+        return index_ == static_cast<index_t>(-1);
+      }
+
+      inline constexpr std::size_t index() const noexcept {
+        return valueless_by_exception() ? variant_npos : index_;
+      }
+
+      protected:
+      using data_t = recursive_union<DestructibleTrait, 0, Ts...>;
+
+      friend inline constexpr base &as_base(base &b) { return b; }
+      friend inline constexpr const base &as_base(const base &b) { return b; }
+      friend inline constexpr base &&as_base(base &&b) { return lib::move(b); }
+      friend inline constexpr const base &&as_base(const base &&b) { return lib::move(b); }
+
+      friend inline constexpr data_t &data(base &b) { return b.data_; }
+      friend inline constexpr const data_t &data(const base &b) { return b.data_; }
+      friend inline constexpr data_t &&data(base &&b) { return lib::move(b).data_; }
+      friend inline constexpr const data_t &&data(const base &&b) { return lib::move(b).data_; }
+
+      inline static constexpr std::size_t size() { return sizeof...(Ts); }
+
+      data_t data_;
+      index_t index_;
+
+      friend struct access::base;
+      friend struct visitation::base;
+    };
+
+    struct dtor {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+      template <typename Alt>
+      inline void operator()(Alt &alt) const noexcept { alt.~Alt(); }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+    };
+
+#if defined(_MSC_VER) && _MSC_VER < 1910
+#define INHERITING_CTOR(type, base)               \
+  template <typename... Args>                     \
+  inline explicit constexpr type(Args &&... args) \
+      : base(lib::forward<Args>(args)...) {}
+#else
+#define INHERITING_CTOR(type, base) using base::base;
+#endif
+
+    template <typename Traits, Trait = Traits::destructible_trait>
+    class destructor;
+
+#define MPARK_VARIANT_DESTRUCTOR(destructible_trait, definition, destroy) \
+  template <typename... Ts>                                               \
+  class destructor<traits<Ts...>, destructible_trait>                     \
+      : public base<destructible_trait, Ts...> {                          \
+    using super = base<destructible_trait, Ts...>;                        \
+                                                                          \
+    public:                                                               \
+    INHERITING_CTOR(destructor, super)                                    \
+    using super::operator=;                                               \
+                                                                          \
+    destructor(const destructor &) = default;                             \
+    destructor(destructor &&) = default;                                  \
+    definition                                                            \
+    destructor &operator=(const destructor &) = default;                  \
+    destructor &operator=(destructor &&) = default;                       \
+                                                                          \
+    protected:                                                            \
+    destroy                                                               \
+  }
+
+    MPARK_VARIANT_DESTRUCTOR(
+        Trait::TriviallyAvailable,
+        ~destructor() = default;,
+        inline void destroy() noexcept {
+          this->index_ = static_cast<index_t>(-1);
+        });
+
+    MPARK_VARIANT_DESTRUCTOR(
+        Trait::Available,
+        ~destructor() { destroy(); },
+        inline void destroy() noexcept {
+          if (!this->valueless_by_exception()) {
+            visitation::alt::visit_alt(dtor{}, *this);
+          }
+          this->index_ = static_cast<index_t>(-1);
+        });
+
+    MPARK_VARIANT_DESTRUCTOR(
+        Trait::Unavailable,
+        ~destructor() = delete;,
+        inline void destroy() noexcept = delete;);
+
+#undef MPARK_VARIANT_DESTRUCTOR
+
+    template <typename Traits>
+    class constructor : public destructor<Traits> {
+      using super = destructor<Traits>;
+
+      public:
+      INHERITING_CTOR(constructor, super)
+      using super::operator=;
+
+      protected:
+#ifndef MPARK_GENERIC_LAMBDAS
+      struct ctor {
+        template <typename LhsAlt, typename RhsAlt>
+        inline void operator()(LhsAlt &lhs_alt, RhsAlt &&rhs_alt) const {
+          constructor::construct_alt(lhs_alt,
+                                     lib::forward<RhsAlt>(rhs_alt).value);
+        }
+      };
+#endif
+
+      template <std::size_t I, typename T, typename... Args>
+      inline static T &construct_alt(alt<I, T> &a, Args &&... args) {
+        ::new (static_cast<void *>(lib::addressof(a)))
+            alt<I, T>(in_place_t{}, lib::forward<Args>(args)...);
+        return a.value;
+      }
+
+      template <typename Rhs>
+      inline static void generic_construct(constructor &lhs, Rhs &&rhs) {
+        lhs.destroy();
+        if (!rhs.valueless_by_exception()) {
+          visitation::alt::visit_alt_at(
+              rhs.index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+              [](auto &lhs_alt, auto &&rhs_alt) {
+                constructor::construct_alt(
+                    lhs_alt, lib::forward<decltype(rhs_alt)>(rhs_alt).value);
+              }
+#else
+              ctor{}
+#endif
+              ,
+              lhs,
+              lib::forward<Rhs>(rhs));
+          lhs.index_ = rhs.index_;
+        }
+      }
+    };
+
+    template <typename Traits, Trait = Traits::move_constructible_trait>
+    class move_constructor;
+
+#define MPARK_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, definition) \
+  template <typename... Ts>                                                  \
+  class move_constructor<traits<Ts...>, move_constructible_trait>            \
+      : public constructor<traits<Ts...>> {                                  \
+    using super = constructor<traits<Ts...>>;                                \
+                                                                             \
+    public:                                                                  \
+    INHERITING_CTOR(move_constructor, super)                                 \
+    using super::operator=;                                                  \
+                                                                             \
+    move_constructor(const move_constructor &) = default;                    \
+    definition                                                               \
+    ~move_constructor() = default;                                           \
+    move_constructor &operator=(const move_constructor &) = default;         \
+    move_constructor &operator=(move_constructor &&) = default;              \
+  }
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::TriviallyAvailable,
+        move_constructor(move_constructor &&that) = default;);
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::Available,
+        move_constructor(move_constructor &&that) noexcept(
+            lib::all<std::is_nothrow_move_constructible<Ts>::value...>::value)
+            : move_constructor(valueless_t{}) {
+          this->generic_construct(*this, lib::move(that));
+        });
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::Unavailable,
+        move_constructor(move_constructor &&) = delete;);
+
+#undef MPARK_VARIANT_MOVE_CONSTRUCTOR
+
+    template <typename Traits, Trait = Traits::copy_constructible_trait>
+    class copy_constructor;
+
+#define MPARK_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, definition) \
+  template <typename... Ts>                                                  \
+  class copy_constructor<traits<Ts...>, copy_constructible_trait>            \
+      : public move_constructor<traits<Ts...>> {                             \
+    using super = move_constructor<traits<Ts...>>;                           \
+                                                                             \
+    public:                                                                  \
+    INHERITING_CTOR(copy_constructor, super)                                 \
+    using super::operator=;                                                  \
+                                                                             \
+    definition                                                               \
+    copy_constructor(copy_constructor &&) = default;                         \
+    ~copy_constructor() = default;                                           \
+    copy_constructor &operator=(const copy_constructor &) = default;         \
+    copy_constructor &operator=(copy_constructor &&) = default;              \
+  }
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::TriviallyAvailable,
+        copy_constructor(const copy_constructor &that) = default;);
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::Available,
+        copy_constructor(const copy_constructor &that)
+            : copy_constructor(valueless_t{}) {
+          this->generic_construct(*this, that);
+        });
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::Unavailable,
+        copy_constructor(const copy_constructor &) = delete;);
+
+#undef MPARK_VARIANT_COPY_CONSTRUCTOR
+
+    template <typename Traits>
+    class assignment : public copy_constructor<Traits> {
+      using super = copy_constructor<Traits>;
+
+      public:
+      INHERITING_CTOR(assignment, super)
+      using super::operator=;
+
+      template <std::size_t I, typename... Args>
+      inline /* auto & */ auto emplace(Args &&... args)
+          -> decltype(this->construct_alt(access::base::get_alt<I>(*this),
+                                          lib::forward<Args>(args)...)) {
+        this->destroy();
+        auto &result = this->construct_alt(access::base::get_alt<I>(*this),
+                                           lib::forward<Args>(args)...);
+        this->index_ = I;
+        return result;
+      }
+
+      protected:
+#ifndef MPARK_GENERIC_LAMBDAS
+      template <typename That>
+      struct assigner {
+        template <typename ThisAlt, typename ThatAlt>
+        inline void operator()(ThisAlt &this_alt, ThatAlt &&that_alt) const {
+          self->assign_alt(this_alt, lib::forward<ThatAlt>(that_alt).value);
+        }
+        assignment *self;
+      };
+#endif
+
+      template <std::size_t I, typename T, typename Arg>
+      inline void assign_alt(alt<I, T> &a, Arg &&arg) {
+        if (this->index() == I) {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4244)
+#endif
+          a.value = lib::forward<Arg>(arg);
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+        } else {
+          struct {
+            void operator()(std::true_type) const {
+              this_->emplace<I>(lib::forward<Arg>(arg_));
+            }
+            void operator()(std::false_type) const {
+              this_->emplace<I>(T(lib::forward<Arg>(arg_)));
+            }
+            assignment *this_;
+            Arg &&arg_;
+          } impl{this, lib::forward<Arg>(arg)};
+          impl(lib::bool_constant<
+                   std::is_nothrow_constructible<T, Arg>::value ||
+                   !std::is_nothrow_move_constructible<T>::value>{});
+        }
+      }
+
+      template <typename That>
+      inline void generic_assign(That &&that) {
+        if (this->valueless_by_exception() && that.valueless_by_exception()) {
+          // do nothing.
+        } else if (that.valueless_by_exception()) {
+          this->destroy();
+        } else {
+          visitation::alt::visit_alt_at(
+              that.index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+              [this](auto &this_alt, auto &&that_alt) {
+                this->assign_alt(
+                    this_alt, lib::forward<decltype(that_alt)>(that_alt).value);
+              }
+#else
+              assigner<That>{this}
+#endif
+              ,
+              *this,
+              lib::forward<That>(that));
+        }
+      }
+    };
+
+    template <typename Traits, Trait = Traits::move_assignable_trait>
+    class move_assignment;
+
+#define MPARK_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, definition) \
+  template <typename... Ts>                                              \
+  class move_assignment<traits<Ts...>, move_assignable_trait>            \
+      : public assignment<traits<Ts...>> {                               \
+    using super = assignment<traits<Ts...>>;                             \
+                                                                         \
+    public:                                                              \
+    INHERITING_CTOR(move_assignment, super)                              \
+    using super::operator=;                                              \
+                                                                         \
+    move_assignment(const move_assignment &) = default;                  \
+    move_assignment(move_assignment &&) = default;                       \
+    ~move_assignment() = default;                                        \
+    move_assignment &operator=(const move_assignment &) = default;       \
+    definition                                                           \
+  }
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::TriviallyAvailable,
+        move_assignment &operator=(move_assignment &&that) = default;);
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::Available,
+        move_assignment &
+        operator=(move_assignment &&that) noexcept(
+            lib::all<(std::is_nothrow_move_constructible<Ts>::value &&
+                      std::is_nothrow_move_assignable<Ts>::value)...>::value) {
+          this->generic_assign(lib::move(that));
+          return *this;
+        });
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::Unavailable,
+        move_assignment &operator=(move_assignment &&) = delete;);
+
+#undef MPARK_VARIANT_MOVE_ASSIGNMENT
+
+    template <typename Traits, Trait = Traits::copy_assignable_trait>
+    class copy_assignment;
+
+#define MPARK_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, definition) \
+  template <typename... Ts>                                              \
+  class copy_assignment<traits<Ts...>, copy_assignable_trait>            \
+      : public move_assignment<traits<Ts...>> {                          \
+    using super = move_assignment<traits<Ts...>>;                        \
+                                                                         \
+    public:                                                              \
+    INHERITING_CTOR(copy_assignment, super)                              \
+    using super::operator=;                                              \
+                                                                         \
+    copy_assignment(const copy_assignment &) = default;                  \
+    copy_assignment(copy_assignment &&) = default;                       \
+    ~copy_assignment() = default;                                        \
+    definition                                                           \
+    copy_assignment &operator=(copy_assignment &&) = default;            \
+  }
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::TriviallyAvailable,
+        copy_assignment &operator=(const copy_assignment &that) = default;);
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::Available,
+        copy_assignment &operator=(const copy_assignment &that) {
+          this->generic_assign(that);
+          return *this;
+        });
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::Unavailable,
+        copy_assignment &operator=(const copy_assignment &) = delete;);
+
+#undef MPARK_VARIANT_COPY_ASSIGNMENT
+
+    template <typename... Ts>
+    class impl : public copy_assignment<traits<Ts...>> {
+      using super = copy_assignment<traits<Ts...>>;
+
+      public:
+      INHERITING_CTOR(impl, super)
+      using super::operator=;
+
+      template <std::size_t I, typename Arg>
+      inline void assign(Arg &&arg) {
+        this->assign_alt(access::base::get_alt<I>(*this),
+                         lib::forward<Arg>(arg));
+      }
+
+      inline void swap(impl &that) {
+        if (this->valueless_by_exception() && that.valueless_by_exception()) {
+          // do nothing.
+        } else if (this->index() == that.index()) {
+          visitation::alt::visit_alt_at(this->index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+                                        [](auto &this_alt, auto &that_alt) {
+                                          using std::swap;
+                                          swap(this_alt.value,
+                                               that_alt.value);
+                                        }
+#else
+                                        swapper{}
+#endif
+                                        ,
+                                        *this,
+                                        that);
+        } else {
+          impl *lhs = this;
+          impl *rhs = lib::addressof(that);
+          if (lhs->move_nothrow() && !rhs->move_nothrow()) {
+            std::swap(lhs, rhs);
+          }
+          impl tmp(lib::move(*rhs));
+#ifdef MPARK_EXCEPTIONS
+          // EXTENSION: When the move construction of `lhs` into `rhs` throws
+          // and `tmp` is nothrow move constructible then we move `tmp` back
+          // into `rhs` and provide the strong exception safety guarantee.
+          try {
+            this->generic_construct(*rhs, lib::move(*lhs));
+          } catch (...) {
+            if (tmp.move_nothrow()) {
+              this->generic_construct(*rhs, lib::move(tmp));
+            }
+            throw;
+          }
+#else
+          this->generic_construct(*rhs, lib::move(*lhs));
+#endif
+          this->generic_construct(*lhs, lib::move(tmp));
+        }
+      }
+
+      private:
+#ifndef MPARK_GENERIC_LAMBDAS
+      struct swapper {
+        template <typename ThisAlt, typename ThatAlt>
+        inline void operator()(ThisAlt &this_alt, ThatAlt &that_alt) const {
+          using std::swap;
+          swap(this_alt.value, that_alt.value);
+        }
+      };
+#endif
+
+      inline constexpr bool move_nothrow() const {
+        return this->valueless_by_exception() ||
+               lib::array<bool, sizeof...(Ts)>{
+                   {std::is_nothrow_move_constructible<Ts>::value...}
+               }[this->index()];
+      }
+    };
+
+    template <std::size_t I, typename T>
+    struct overload_leaf {
+      using F = lib::size_constant<I> (*)(T);
+      operator F() const { return nullptr; }
+    };
+
+    template <typename... Ts>
+    struct overload_impl {
+      private:
+      template <typename>
+      struct impl;
+
+      template <std::size_t... Is>
+      struct impl<lib::index_sequence<Is...>> : overload_leaf<Is, Ts>... {};
+
+      public:
+      using type = impl<lib::index_sequence_for<Ts...>>;
+    };
+
+    template <typename... Ts>
+    using overload = typename overload_impl<Ts...>::type;
+
+    template <typename T, typename... Ts>
+    using best_match = lib::invoke_result_t<overload<Ts...>, T &&>;
+
+    template <typename T>
+    struct is_in_place_index : std::false_type {};
+
+    template <std::size_t I>
+    struct is_in_place_index<in_place_index_t<I>> : std::true_type {};
+
+    template <typename T>
+    struct is_in_place_type : std::false_type {};
+
+    template <typename T>
+    struct is_in_place_type<in_place_type_t<T>> : std::true_type {};
+
+  }  // detail
+
+  template <typename... Ts>
+  class variant {
+    static_assert(0 < sizeof...(Ts),
+                  "variant must consist of at least one alternative.");
+
+    static_assert(lib::all<!std::is_array<Ts>::value...>::value,
+                  "variant can not have an array type as an alternative.");
+
+    static_assert(lib::all<!std::is_reference<Ts>::value...>::value,
+                  "variant can not have a reference type as an alternative.");
+
+    static_assert(lib::all<!std::is_void<Ts>::value...>::value,
+                  "variant can not have a void type as an alternative.");
+
+    public:
+    template <
+        typename Front = lib::type_pack_element_t<0, Ts...>,
+        lib::enable_if_t<std::is_default_constructible<Front>::value, int> = 0>
+    inline constexpr variant() noexcept(
+        std::is_nothrow_default_constructible<Front>::value)
+        : impl_(in_place_index_t<0>{}) {}
+
+    variant(const variant &) = default;
+    variant(variant &&) = default;
+
+    template <
+        typename Arg,
+        typename Decayed = lib::decay_t<Arg>,
+        lib::enable_if_t<!std::is_same<Decayed, variant>::value, int> = 0,
+        lib::enable_if_t<!detail::is_in_place_index<Decayed>::value, int> = 0,
+        lib::enable_if_t<!detail::is_in_place_type<Decayed>::value, int> = 0,
+        std::size_t I = detail::best_match<Arg, Ts...>::value,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T, Arg>::value, int> = 0>
+    inline constexpr variant(Arg &&arg) noexcept(
+        std::is_nothrow_constructible<T, Arg>::value)
+        : impl_(in_place_index_t<I>{}, lib::forward<Arg>(arg)) {}
+
+    template <
+        std::size_t I,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline explicit constexpr variant(
+        in_place_index_t<I>,
+        Args &&... args) noexcept(std::is_nothrow_constructible<T,
+                                                                Args...>::value)
+        : impl_(in_place_index_t<I>{}, lib::forward<Args>(args)...) {}
+
+    template <
+        std::size_t I,
+        typename Up,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline explicit constexpr variant(
+        in_place_index_t<I>,
+        std::initializer_list<Up> il,
+        Args &&... args) noexcept(std::
+                                      is_nothrow_constructible<
+                                          T,
+                                          std::initializer_list<Up> &,
+                                          Args...>::value)
+        : impl_(in_place_index_t<I>{}, il, lib::forward<Args>(args)...) {}
+
+    template <
+        typename T,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline explicit constexpr variant(
+        in_place_type_t<T>,
+        Args &&... args) noexcept(std::is_nothrow_constructible<T,
+                                                                Args...>::value)
+        : impl_(in_place_index_t<I>{}, lib::forward<Args>(args)...) {}
+
+    template <
+        typename T,
+        typename Up,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline explicit constexpr variant(
+        in_place_type_t<T>,
+        std::initializer_list<Up> il,
+        Args &&... args) noexcept(std::
+                                      is_nothrow_constructible<
+                                          T,
+                                          std::initializer_list<Up> &,
+                                          Args...>::value)
+        : impl_(in_place_index_t<I>{}, il, lib::forward<Args>(args)...) {}
+
+    ~variant() = default;
+
+    variant &operator=(const variant &) = default;
+    variant &operator=(variant &&) = default;
+
+    template <typename Arg,
+              lib::enable_if_t<!std::is_same<lib::decay_t<Arg>, variant>::value,
+                               int> = 0,
+              std::size_t I = detail::best_match<Arg, Ts...>::value,
+              typename T = lib::type_pack_element_t<I, Ts...>,
+              lib::enable_if_t<(std::is_assignable<T &, Arg>::value &&
+                                std::is_constructible<T, Arg>::value),
+                               int> = 0>
+    inline variant &operator=(Arg &&arg) noexcept(
+        (std::is_nothrow_assignable<T &, Arg>::value &&
+         std::is_nothrow_constructible<T, Arg>::value)) {
+      impl_.template assign<I>(lib::forward<Arg>(arg));
+      return *this;
+    }
+
+    template <
+        std::size_t I,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline T &emplace(Args &&... args) {
+      return impl_.template emplace<I>(lib::forward<Args>(args)...);
+    }
+
+    template <
+        std::size_t I,
+        typename Up,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline T &emplace(std::initializer_list<Up> il, Args &&... args) {
+      return impl_.template emplace<I>(il, lib::forward<Args>(args)...);
+    }
+
+    template <
+        typename T,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline T &emplace(Args &&... args) {
+      return impl_.template emplace<I>(lib::forward<Args>(args)...);
+    }
+
+    template <
+        typename T,
+        typename Up,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline T &emplace(std::initializer_list<Up> il, Args &&... args) {
+      return impl_.template emplace<I>(il, lib::forward<Args>(args)...);
+    }
+
+    inline constexpr bool valueless_by_exception() const noexcept {
+      return impl_.valueless_by_exception();
+    }
+
+    inline constexpr std::size_t index() const noexcept {
+      return impl_.index();
+    }
+
+    template <bool Dummy = true,
+              lib::enable_if_t<
+                  lib::all<Dummy,
+                           (lib::dependent_type<std::is_move_constructible<Ts>,
+                                                Dummy>::value &&
+                            lib::dependent_type<lib::is_swappable<Ts>,
+                                                Dummy>::value)...>::value,
+                  int> = 0>
+    inline void swap(variant &that) noexcept(
+        lib::all<(std::is_nothrow_move_constructible<Ts>::value &&
+                  lib::is_nothrow_swappable<Ts>::value)...>::value) {
+      impl_.swap(that.impl_);
+    }
+
+    private:
+    detail::impl<Ts...> impl_;
+
+    friend struct detail::access::variant;
+    friend struct detail::visitation::variant;
+  };
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept {
+    return v.index() == I;
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept {
+    return holds_alternative<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  namespace detail {
+    template <std::size_t I, typename V>
+    struct generic_get_impl {
+      constexpr generic_get_impl(int) {}
+
+      constexpr AUTO_REFREF operator()(V &&v) const
+        AUTO_REFREF_RETURN(
+            access::variant::get_alt<I>(lib::forward<V>(v)).value)
+    };
+
+    template <std::size_t I, typename V>
+    inline constexpr AUTO_REFREF generic_get(V &&v)
+      AUTO_REFREF_RETURN(generic_get_impl<I, V>(
+          holds_alternative<I>(v) ? 0 : (throw_bad_variant_access(), 0))(
+          lib::forward<V>(v)))
+  }  // namespace detail
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr variant_alternative_t<I, variant<Ts...>> &get(
+      variant<Ts...> &v) {
+    return detail::generic_get<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr variant_alternative_t<I, variant<Ts...>> &&get(
+      variant<Ts...> &&v) {
+    return detail::generic_get<I>(lib::move(v));
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr const variant_alternative_t<I, variant<Ts...>> &get(
+      const variant<Ts...> &v) {
+    return detail::generic_get<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr const variant_alternative_t<I, variant<Ts...>> &&get(
+      const variant<Ts...> &&v) {
+    return detail::generic_get<I>(lib::move(v));
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr T &get(variant<Ts...> &v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr T &&get(variant<Ts...> &&v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(lib::move(v));
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr const T &get(const variant<Ts...> &v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr const T &&get(const variant<Ts...> &&v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(lib::move(v));
+  }
+
+  namespace detail {
+
+    template <std::size_t I, typename V>
+    inline constexpr /* auto * */ AUTO generic_get_if(V *v) noexcept
+      AUTO_RETURN(v && holds_alternative<I>(*v)
+                      ? lib::addressof(access::variant::get_alt<I>(*v).value)
+                      : nullptr)
+
+  }  // namespace detail
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr lib::add_pointer_t<variant_alternative_t<I, variant<Ts...>>>
+  get_if(variant<Ts...> *v) noexcept {
+    return detail::generic_get_if<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr lib::add_pointer_t<
+      const variant_alternative_t<I, variant<Ts...>>>
+  get_if(const variant<Ts...> *v) noexcept {
+    return detail::generic_get_if<I>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr lib::add_pointer_t<T>
+  get_if(variant<Ts...> *v) noexcept {
+    return get_if<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr lib::add_pointer_t<const T>
+  get_if(const variant<Ts...> *v) noexcept {
+    return get_if<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator==(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using lib::equal_to;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.index() != rhs.index()) return false;
+    if (lhs.valueless_by_exception()) return true;
+    return variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs);
+#else
+    return lhs.index() == rhs.index() &&
+           (lhs.valueless_by_exception() ||
+            variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator!=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using lib::not_equal_to;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.index() != rhs.index()) return true;
+    if (lhs.valueless_by_exception()) return false;
+    return variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs);
+#else
+    return lhs.index() != rhs.index() ||
+           (!lhs.valueless_by_exception() &&
+            variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator<(const variant<Ts...> &lhs,
+                                  const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using lib::less;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (rhs.valueless_by_exception()) return false;
+    if (lhs.valueless_by_exception()) return true;
+    if (lhs.index() < rhs.index()) return true;
+    if (lhs.index() > rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), less{}, lhs, rhs);
+#else
+    return !rhs.valueless_by_exception() &&
+           (lhs.valueless_by_exception() || lhs.index() < rhs.index() ||
+            (lhs.index() == rhs.index() &&
+             variant::visit_value_at(lhs.index(), less{}, lhs, rhs)));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator>(const variant<Ts...> &lhs,
+                                  const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using lib::greater;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.valueless_by_exception()) return false;
+    if (rhs.valueless_by_exception()) return true;
+    if (lhs.index() > rhs.index()) return true;
+    if (lhs.index() < rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), greater{}, lhs, rhs);
+#else
+    return !lhs.valueless_by_exception() &&
+           (rhs.valueless_by_exception() || lhs.index() > rhs.index() ||
+            (lhs.index() == rhs.index() &&
+             variant::visit_value_at(lhs.index(), greater{}, lhs, rhs)));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator<=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using lib::less_equal;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.valueless_by_exception()) return true;
+    if (rhs.valueless_by_exception()) return false;
+    if (lhs.index() < rhs.index()) return true;
+    if (lhs.index() > rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs);
+#else
+    return lhs.valueless_by_exception() ||
+           (!rhs.valueless_by_exception() &&
+            (lhs.index() < rhs.index() ||
+             (lhs.index() == rhs.index() &&
+              variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs))));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator>=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using lib::greater_equal;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (rhs.valueless_by_exception()) return true;
+    if (lhs.valueless_by_exception()) return false;
+    if (lhs.index() > rhs.index()) return true;
+    if (lhs.index() < rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), greater_equal{}, lhs, rhs);
+#else
+    return rhs.valueless_by_exception() ||
+           (!lhs.valueless_by_exception() &&
+            (lhs.index() > rhs.index() ||
+             (lhs.index() == rhs.index() &&
+              variant::visit_value_at(
+                  lhs.index(), greater_equal{}, lhs, rhs))));
+#endif
+  }
+
+  struct monostate {};
+
+  inline constexpr bool operator<(monostate, monostate) noexcept {
+    return false;
+  }
+
+  inline constexpr bool operator>(monostate, monostate) noexcept {
+    return false;
+  }
+
+  inline constexpr bool operator<=(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator>=(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator==(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator!=(monostate, monostate) noexcept {
+    return false;
+  }
+
+#ifdef MPARK_CPP14_CONSTEXPR
+  namespace detail {
+
+    inline constexpr bool all(std::initializer_list<bool> bs) {
+      for (bool b : bs) {
+        if (!b) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+  }  // namespace detail
+
+  template <typename Visitor, typename... Vs>
+  inline constexpr decltype(auto) visit(Visitor &&visitor, Vs &&... vs) {
+    return (detail::all({!vs.valueless_by_exception()...})
+                ? (void)0
+                : throw_bad_variant_access()),
+           detail::visitation::variant::visit_value(
+               lib::forward<Visitor>(visitor), lib::forward<Vs>(vs)...);
+  }
+#else
+  namespace detail {
+
+    template <std::size_t N>
+    inline constexpr bool all_impl(const lib::array<bool, N> &bs,
+                                   std::size_t idx) {
+      return idx >= N || (bs[idx] && all_impl(bs, idx + 1));
+    }
+
+    template <std::size_t N>
+    inline constexpr bool all(const lib::array<bool, N> &bs) {
+      return all_impl(bs, 0);
+    }
+
+  }  // namespace detail
+
+  template <typename Visitor, typename... Vs>
+  inline constexpr DECLTYPE_AUTO visit(Visitor &&visitor, Vs &&... vs)
+    DECLTYPE_AUTO_RETURN(
+        (detail::all(
+             lib::array<bool, sizeof...(Vs)>{{!vs.valueless_by_exception()...}})
+             ? (void)0
+             : throw_bad_variant_access()),
+        detail::visitation::variant::visit_value(lib::forward<Visitor>(visitor),
+                                                 lib::forward<Vs>(vs)...))
+#endif
+
+  template <typename... Ts>
+  inline auto swap(variant<Ts...> &lhs,
+                   variant<Ts...> &rhs) noexcept(noexcept(lhs.swap(rhs)))
+      -> decltype(lhs.swap(rhs)) {
+    lhs.swap(rhs);
+  }
+
+  namespace detail {
+
+    template <typename T, typename...>
+    using enabled_type = T;
+
+    namespace hash {
+
+      template <typename H, typename K>
+      constexpr bool meets_requirements() {
+        return std::is_copy_constructible<H>::value &&
+               std::is_move_constructible<H>::value &&
+               lib::is_invocable_r<std::size_t, H, const K &>::value;
+      }
+
+      template <typename K>
+      constexpr bool is_enabled() {
+        using H = std::hash<K>;
+        return meets_requirements<H, K>() &&
+               std::is_default_constructible<H>::value &&
+               std::is_copy_assignable<H>::value &&
+               std::is_move_assignable<H>::value;
+      }
+
+    }  // namespace hash
+
+  }  // namespace detail
+
+#undef AUTO
+#undef AUTO_RETURN
+
+#undef AUTO_REFREF
+#undef AUTO_REFREF_RETURN
+
+#undef DECLTYPE_AUTO
+#undef DECLTYPE_AUTO_RETURN
+
+}  // namespace mpark
+
+namespace std {
+
+  template <typename... Ts>
+  struct hash<mpark::detail::enabled_type<
+      mpark::variant<Ts...>,
+      mpark::lib::enable_if_t<mpark::lib::all<mpark::detail::hash::is_enabled<
+          mpark::lib::remove_const_t<Ts>>()...>::value>>> {
+    using argument_type = mpark::variant<Ts...>;
+    using result_type = std::size_t;
+
+    inline result_type operator()(const argument_type &v) const {
+      using mpark::detail::visitation::variant;
+      std::size_t result =
+          v.valueless_by_exception()
+              ? 299792458  // Random value chosen by the universe upon creation
+              : variant::visit_alt(
+#ifdef MPARK_GENERIC_LAMBDAS
+                    [](const auto &alt) {
+                      using alt_type = mpark::lib::decay_t<decltype(alt)>;
+                      using value_type = mpark::lib::remove_const_t<
+                          typename alt_type::value_type>;
+                      return hash<value_type>{}(alt.value);
+                    }
+#else
+                    hasher{}
+#endif
+                    ,
+                    v);
+      return hash_combine(result, hash<std::size_t>{}(v.index()));
+    }
+
+    private:
+#ifndef MPARK_GENERIC_LAMBDAS
+    struct hasher {
+      template <typename Alt>
+      inline std::size_t operator()(const Alt &alt) const {
+        using alt_type = mpark::lib::decay_t<Alt>;
+        using value_type =
+            mpark::lib::remove_const_t<typename alt_type::value_type>;
+        return hash<value_type>{}(alt.value);
+      }
+    };
+#endif
+
+    static std::size_t hash_combine(std::size_t lhs, std::size_t rhs) {
+      return lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
+    }
+  };
+
+  template <>
+  struct hash<mpark::monostate> {
+    using argument_type = mpark::monostate;
+    using result_type = std::size_t;
+
+    inline result_type operator()(const argument_type &) const noexcept {
+      return 66740831;  // return a fundamentally attractive random value.
+    }
+  };
+
+}  // namespace std
+
+#endif  // MPARK_VARIANT_HPP

+ 2813 - 0
src/external/mpark-variant/v1.4.0/variant.hpp

@@ -0,0 +1,2813 @@
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_VARIANT_HPP
+#define MPARK_VARIANT_HPP
+
+/*
+   variant synopsis
+
+namespace std {
+
+  // 20.7.2, class template variant
+  template <class... Types>
+  class variant {
+  public:
+
+    // 20.7.2.1, constructors
+    constexpr variant() noexcept(see below);
+    variant(const variant&);
+    variant(variant&&) noexcept(see below);
+
+    template <class T> constexpr variant(T&&) noexcept(see below);
+
+    template <class T, class... Args>
+    constexpr explicit variant(in_place_type_t<T>, Args&&...);
+
+    template <class T, class U, class... Args>
+    constexpr explicit variant(
+        in_place_type_t<T>, initializer_list<U>, Args&&...);
+
+    template <size_t I, class... Args>
+    constexpr explicit variant(in_place_index_t<I>, Args&&...);
+
+    template <size_t I, class U, class... Args>
+    constexpr explicit variant(
+        in_place_index_t<I>, initializer_list<U>, Args&&...);
+
+    // 20.7.2.2, destructor
+    ~variant();
+
+    // 20.7.2.3, assignment
+    variant& operator=(const variant&);
+    variant& operator=(variant&&) noexcept(see below);
+
+    template <class T> variant& operator=(T&&) noexcept(see below);
+
+    // 20.7.2.4, modifiers
+    template <class T, class... Args>
+    T& emplace(Args&&...);
+
+    template <class T, class U, class... Args>
+    T& emplace(initializer_list<U>, Args&&...);
+
+    template <size_t I, class... Args>
+    variant_alternative<I, variant>& emplace(Args&&...);
+
+    template <size_t I, class U, class...  Args>
+    variant_alternative<I, variant>& emplace(initializer_list<U>, Args&&...);
+
+    // 20.7.2.5, value status
+    constexpr bool valueless_by_exception() const noexcept;
+    constexpr size_t index() const noexcept;
+
+    // 20.7.2.6, swap
+    void swap(variant&) noexcept(see below);
+  };
+
+  // 20.7.3, variant helper classes
+  template <class T> struct variant_size; // undefined
+
+  template <class T>
+  constexpr size_t variant_size_v = variant_size<T>::value;
+
+  template <class T> struct variant_size<const T>;
+  template <class T> struct variant_size<volatile T>;
+  template <class T> struct variant_size<const volatile T>;
+
+  template <class... Types>
+  struct variant_size<variant<Types...>>;
+
+  template <size_t I, class T> struct variant_alternative; // undefined
+
+  template <size_t I, class T>
+  using variant_alternative_t = typename variant_alternative<I, T>::type;
+
+  template <size_t I, class T> struct variant_alternative<I, const T>;
+  template <size_t I, class T> struct variant_alternative<I, volatile T>;
+  template <size_t I, class T> struct variant_alternative<I, const volatile T>;
+
+  template <size_t I, class... Types>
+  struct variant_alternative<I, variant<Types...>>;
+
+  constexpr size_t variant_npos = -1;
+
+  // 20.7.4, value access
+  template <class T, class... Types>
+  constexpr bool holds_alternative(const variant<Types...>&) noexcept;
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>>&
+  get(variant<Types...>&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>>&&
+  get(variant<Types...>&&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>> const&
+  get(const variant<Types...>&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>> const&&
+  get(const variant<Types...>&&);
+
+  template <class T, class...  Types>
+  constexpr T& get(variant<Types...>&);
+
+  template <class T, class... Types>
+  constexpr T&& get(variant<Types...>&&);
+
+  template <class T, class... Types>
+  constexpr const T& get(const variant<Types...>&);
+
+  template <class T, class... Types>
+  constexpr const T&& get(const variant<Types...>&&);
+
+  template <size_t I, class... Types>
+  constexpr add_pointer_t<variant_alternative_t<I, variant<Types...>>>
+  get_if(variant<Types...>*) noexcept;
+
+  template <size_t I, class... Types>
+  constexpr add_pointer_t<const variant_alternative_t<I, variant<Types...>>>
+  get_if(const variant<Types...>*) noexcept;
+
+  template <class T, class... Types>
+  constexpr add_pointer_t<T>
+  get_if(variant<Types...>*) noexcept;
+
+  template <class T, class... Types>
+  constexpr add_pointer_t<const T>
+  get_if(const variant<Types...>*) noexcept;
+
+  // 20.7.5, relational operators
+  template <class... Types>
+  constexpr bool operator==(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator!=(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator<(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator>(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator<=(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator>=(const variant<Types...>&, const variant<Types...>&);
+
+  // 20.7.6, visitation
+  template <class Visitor, class... Variants>
+  constexpr see below visit(Visitor&&, Variants&&...);
+
+  // 20.7.7, class monostate
+  struct monostate;
+
+  // 20.7.8, monostate relational operators
+  constexpr bool operator<(monostate, monostate) noexcept;
+  constexpr bool operator>(monostate, monostate) noexcept;
+  constexpr bool operator<=(monostate, monostate) noexcept;
+  constexpr bool operator>=(monostate, monostate) noexcept;
+  constexpr bool operator==(monostate, monostate) noexcept;
+  constexpr bool operator!=(monostate, monostate) noexcept;
+
+  // 20.7.9, specialized algorithms
+  template <class... Types>
+  void swap(variant<Types...>&, variant<Types...>&) noexcept(see below);
+
+  // 20.7.10, class bad_variant_access
+  class bad_variant_access;
+
+  // 20.7.11, hash support
+  template <class T> struct hash;
+  template <class... Types> struct hash<variant<Types...>>;
+  template <> struct hash<monostate>;
+
+} // namespace std
+
+*/
+
+#include <cstddef>
+#include <exception>
+#include <functional>
+#include <initializer_list>
+#include <new>
+#include <type_traits>
+#include <utility>
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_CONFIG_HPP
+#define MPARK_CONFIG_HPP
+
+// MSVC 2015 Update 3.
+#if __cplusplus < 201103L && (!defined(_MSC_VER) || _MSC_FULL_VER < 190024210)
+#error "MPark.Variant requires C++11 support."
+#endif
+
+#ifndef __has_attribute
+#define __has_attribute(x) 0
+#endif
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+#ifndef __has_include
+#define __has_include(x) 0
+#endif
+
+#ifndef __has_feature
+#define __has_feature(x) 0
+#endif
+
+#if __has_attribute(always_inline) || defined(__GNUC__)
+#define MPARK_ALWAYS_INLINE __attribute__((__always_inline__)) inline
+#elif defined(_MSC_VER)
+#define MPARK_ALWAYS_INLINE __forceinline
+#else
+#define MPARK_ALWAYS_INLINE inline
+#endif
+
+#if __has_builtin(__builtin_addressof) || \
+    (defined(__GNUC__) && __GNUC__ >= 7) || defined(_MSC_VER)
+#define MPARK_BUILTIN_ADDRESSOF
+#endif
+
+#if __has_builtin(__builtin_unreachable) || defined(__GNUC__)
+#define MPARK_BUILTIN_UNREACHABLE __builtin_unreachable()
+#elif defined(_MSC_VER)
+#define MPARK_BUILTIN_UNREACHABLE __assume(false)
+#else
+#define MPARK_BUILTIN_UNREACHABLE
+#endif
+
+#if __has_builtin(__type_pack_element)
+#define MPARK_TYPE_PACK_ELEMENT
+#endif
+
+#if defined(__cpp_constexpr) && __cpp_constexpr >= 200704 && \
+    !(defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 9)
+#define MPARK_CPP11_CONSTEXPR
+#endif
+
+#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304
+#define MPARK_CPP14_CONSTEXPR
+#endif
+
+#if __has_feature(cxx_exceptions) || defined(__cpp_exceptions) || \
+    (defined(_MSC_VER) && defined(_CPPUNWIND))
+#define MPARK_EXCEPTIONS
+#endif
+
+#if defined(__cpp_generic_lambdas) || defined(_MSC_VER)
+#define MPARK_GENERIC_LAMBDAS
+#endif
+
+#if defined(__cpp_lib_integer_sequence)
+#define MPARK_INTEGER_SEQUENCE
+#endif
+
+#if defined(__cpp_return_type_deduction) || defined(_MSC_VER)
+#define MPARK_RETURN_TYPE_DEDUCTION
+#endif
+
+#if defined(__cpp_lib_transparent_operators) || defined(_MSC_VER)
+#define MPARK_TRANSPARENT_OPERATORS
+#endif
+
+#if defined(__cpp_variable_templates) || defined(_MSC_VER)
+#define MPARK_VARIABLE_TEMPLATES
+#endif
+
+#if !defined(__GLIBCXX__) || __has_include(<codecvt>)  // >= libstdc++-5
+#define MPARK_TRIVIALITY_TYPE_TRAITS
+#define MPARK_INCOMPLETE_TYPE_TRAITS
+#endif
+
+#endif  // MPARK_CONFIG_HPP
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_IN_PLACE_HPP
+#define MPARK_IN_PLACE_HPP
+
+#include <cstddef>
+
+
+namespace mpark {
+
+  struct in_place_t { explicit in_place_t() = default; };
+
+  template <std::size_t I>
+  struct in_place_index_t { explicit in_place_index_t() = default; };
+
+  template <typename T>
+  struct in_place_type_t { explicit in_place_type_t() = default; };
+
+#ifdef MPARK_VARIABLE_TEMPLATES
+  constexpr in_place_t in_place{};
+
+  template <std::size_t I> constexpr in_place_index_t<I> in_place_index{};
+
+  template <typename T> constexpr in_place_type_t<T> in_place_type{};
+#endif
+
+}  // namespace mpark
+
+#endif  // MPARK_IN_PLACE_HPP
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_LIB_HPP
+#define MPARK_LIB_HPP
+
+#include <memory>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+
+#define MPARK_RETURN(...) \
+  noexcept(noexcept(__VA_ARGS__)) -> decltype(__VA_ARGS__) { return __VA_ARGS__; }
+
+namespace mpark {
+  namespace lib {
+    template <typename T>
+    struct identity { using type = T; };
+
+    inline namespace cpp14 {
+      template <typename T, std::size_t N>
+      struct array {
+        constexpr const T &operator[](std::size_t index) const {
+          return data[index];
+        }
+
+        T data[N == 0 ? 1 : N];
+      };
+
+      template <typename T>
+      using add_pointer_t = typename std::add_pointer<T>::type;
+
+      template <typename... Ts>
+      using common_type_t = typename std::common_type<Ts...>::type;
+
+      template <typename T>
+      using decay_t = typename std::decay<T>::type;
+
+      template <bool B, typename T = void>
+      using enable_if_t = typename std::enable_if<B, T>::type;
+
+      template <typename T>
+      using remove_const_t = typename std::remove_const<T>::type;
+
+      template <typename T>
+      using remove_reference_t = typename std::remove_reference<T>::type;
+
+      template <typename T>
+      inline constexpr T &&forward(remove_reference_t<T> &t) noexcept {
+        return static_cast<T &&>(t);
+      }
+
+      template <typename T>
+      inline constexpr T &&forward(remove_reference_t<T> &&t) noexcept {
+        static_assert(!std::is_lvalue_reference<T>::value,
+                      "can not forward an rvalue as an lvalue");
+        return static_cast<T &&>(t);
+      }
+
+      template <typename T>
+      inline constexpr remove_reference_t<T> &&move(T &&t) noexcept {
+        return static_cast<remove_reference_t<T> &&>(t);
+      }
+
+#ifdef MPARK_INTEGER_SEQUENCE
+      using std::integer_sequence;
+      using std::index_sequence;
+      using std::make_index_sequence;
+      using std::index_sequence_for;
+#else
+      template <typename T, T... Is>
+      struct integer_sequence {
+        using value_type = T;
+        static constexpr std::size_t size() noexcept { return sizeof...(Is); }
+      };
+
+      template <std::size_t... Is>
+      using index_sequence = integer_sequence<std::size_t, Is...>;
+
+      template <typename Lhs, typename Rhs>
+      struct make_index_sequence_concat;
+
+      template <std::size_t... Lhs, std::size_t... Rhs>
+      struct make_index_sequence_concat<index_sequence<Lhs...>,
+                                        index_sequence<Rhs...>>
+          : identity<index_sequence<Lhs..., (sizeof...(Lhs) + Rhs)...>> {};
+
+      template <std::size_t N>
+      struct make_index_sequence_impl;
+
+      template <std::size_t N>
+      using make_index_sequence = typename make_index_sequence_impl<N>::type;
+
+      template <std::size_t N>
+      struct make_index_sequence_impl
+          : make_index_sequence_concat<make_index_sequence<N / 2>,
+                                       make_index_sequence<N - (N / 2)>> {};
+
+      template <>
+      struct make_index_sequence_impl<0> : identity<index_sequence<>> {};
+
+      template <>
+      struct make_index_sequence_impl<1> : identity<index_sequence<0>> {};
+
+      template <typename... Ts>
+      using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
+#endif
+
+      // <functional>
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using equal_to = std::equal_to<>;
+#else
+      struct equal_to {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          MPARK_RETURN(lib::forward<Lhs>(lhs) == lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using not_equal_to = std::not_equal_to<>;
+#else
+      struct not_equal_to {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          MPARK_RETURN(lib::forward<Lhs>(lhs) != lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using less = std::less<>;
+#else
+      struct less {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          MPARK_RETURN(lib::forward<Lhs>(lhs) < lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using greater = std::greater<>;
+#else
+      struct greater {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          MPARK_RETURN(lib::forward<Lhs>(lhs) > lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using less_equal = std::less_equal<>;
+#else
+      struct less_equal {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          MPARK_RETURN(lib::forward<Lhs>(lhs) <= lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using greater_equal = std::greater_equal<>;
+#else
+      struct greater_equal {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          MPARK_RETURN(lib::forward<Lhs>(lhs) >= lib::forward<Rhs>(rhs))
+      };
+#endif
+    }  // namespace cpp14
+
+    inline namespace cpp17 {
+
+      // <type_traits>
+      template <bool B>
+      using bool_constant = std::integral_constant<bool, B>;
+
+      template <typename...>
+      struct voider : identity<void> {};
+
+      template <typename... Ts>
+      using void_t = typename voider<Ts...>::type;
+
+      namespace detail {
+        namespace swappable {
+
+          using std::swap;
+
+          template <typename T>
+          struct is_swappable {
+            private:
+            template <typename U,
+                      typename = decltype(swap(std::declval<U &>(),
+                                               std::declval<U &>()))>
+            inline static std::true_type test(int);
+
+            template <typename U>
+            inline static std::false_type test(...);
+
+            public:
+            static constexpr bool value = decltype(test<T>(0))::value;
+          };
+
+          template <bool IsSwappable, typename T>
+          struct is_nothrow_swappable {
+            static constexpr bool value =
+                noexcept(swap(std::declval<T &>(), std::declval<T &>()));
+          };
+
+          template <typename T>
+          struct is_nothrow_swappable<false, T> : std::false_type {};
+
+        }  // namespace swappable
+      }  // namespace detail
+
+      using detail::swappable::is_swappable;
+
+      template <typename T>
+      using is_nothrow_swappable =
+          detail::swappable::is_nothrow_swappable<is_swappable<T>::value, T>;
+
+      // <functional>
+      namespace detail {
+
+        template <typename T>
+        struct is_reference_wrapper : std::false_type {};
+
+        template <typename T>
+        struct is_reference_wrapper<std::reference_wrapper<T>>
+            : std::true_type {};
+
+        template <bool, int>
+        struct Invoke;
+
+        template <>
+        struct Invoke<true /* pmf */, 0 /* is_base_of */> {
+          template <typename R, typename T, typename Arg, typename... Args>
+          inline static constexpr auto invoke(R T::*pmf, Arg &&arg, Args &&... args)
+            MPARK_RETURN((lib::forward<Arg>(arg).*pmf)(lib::forward<Args>(args)...))
+        };
+
+        template <>
+        struct Invoke<true /* pmf */, 1 /* is_reference_wrapper */> {
+          template <typename R, typename T, typename Arg, typename... Args>
+          inline static constexpr auto invoke(R T::*pmf, Arg &&arg, Args &&... args)
+            MPARK_RETURN((lib::forward<Arg>(arg).get().*pmf)(lib::forward<Args>(args)...))
+        };
+
+        template <>
+        struct Invoke<true /* pmf */, 2 /* otherwise */> {
+          template <typename R, typename T, typename Arg, typename... Args>
+          inline static constexpr auto invoke(R T::*pmf, Arg &&arg, Args &&... args)
+            MPARK_RETURN(((*lib::forward<Arg>(arg)).*pmf)(lib::forward<Args>(args)...))
+        };
+
+        template <>
+        struct Invoke<false /* pmo */, 0 /* is_base_of */> {
+          template <typename R, typename T, typename Arg>
+          inline static constexpr auto invoke(R T::*pmo, Arg &&arg)
+            MPARK_RETURN(lib::forward<Arg>(arg).*pmo)
+        };
+
+        template <>
+        struct Invoke<false /* pmo */, 1 /* is_reference_wrapper */> {
+          template <typename R, typename T, typename Arg>
+          inline static constexpr auto invoke(R T::*pmo, Arg &&arg)
+            MPARK_RETURN(lib::forward<Arg>(arg).get().*pmo)
+        };
+
+        template <>
+        struct Invoke<false /* pmo */, 2 /* otherwise */> {
+          template <typename R, typename T, typename Arg>
+          inline static constexpr auto invoke(R T::*pmo, Arg &&arg)
+              MPARK_RETURN((*lib::forward<Arg>(arg)).*pmo)
+        };
+
+        template <typename R, typename T, typename Arg, typename... Args>
+        inline constexpr auto invoke(R T::*f, Arg &&arg, Args &&... args)
+          MPARK_RETURN(
+              Invoke<std::is_function<R>::value,
+                     (std::is_base_of<T, lib::decay_t<Arg>>::value
+                          ? 0
+                          : is_reference_wrapper<lib::decay_t<Arg>>::value
+                                ? 1
+                                : 2)>::invoke(f,
+                                              lib::forward<Arg>(arg),
+                                              lib::forward<Args>(args)...))
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+        template <typename F, typename... Args>
+        inline constexpr auto invoke(F &&f, Args &&... args)
+          MPARK_RETURN(lib::forward<F>(f)(lib::forward<Args>(args)...))
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+      }  // namespace detail
+
+      template <typename F, typename... Args>
+      inline constexpr auto invoke(F &&f, Args &&... args)
+        MPARK_RETURN(detail::invoke(lib::forward<F>(f),
+                                    lib::forward<Args>(args)...))
+
+      namespace detail {
+
+        template <typename Void, typename, typename...>
+        struct invoke_result {};
+
+        template <typename F, typename... Args>
+        struct invoke_result<void_t<decltype(lib::invoke(
+                                 std::declval<F>(), std::declval<Args>()...))>,
+                             F,
+                             Args...>
+            : identity<decltype(
+                  lib::invoke(std::declval<F>(), std::declval<Args>()...))> {};
+
+      }  // namespace detail
+
+      template <typename F, typename... Args>
+      using invoke_result = detail::invoke_result<void, F, Args...>;
+
+      template <typename F, typename... Args>
+      using invoke_result_t = typename invoke_result<F, Args...>::type;
+
+      namespace detail {
+
+        template <typename Void, typename, typename...>
+        struct is_invocable : std::false_type {};
+
+        template <typename F, typename... Args>
+        struct is_invocable<void_t<invoke_result_t<F, Args...>>, F, Args...>
+            : std::true_type {};
+
+        template <typename Void, typename, typename, typename...>
+        struct is_invocable_r : std::false_type {};
+
+        template <typename R, typename F, typename... Args>
+        struct is_invocable_r<void_t<invoke_result_t<F, Args...>>,
+                              R,
+                              F,
+                              Args...>
+            : std::is_convertible<invoke_result_t<F, Args...>, R> {};
+
+      }  // namespace detail
+
+      template <typename F, typename... Args>
+      using is_invocable = detail::is_invocable<void, F, Args...>;
+
+      template <typename R, typename F, typename... Args>
+      using is_invocable_r = detail::is_invocable_r<void, R, F, Args...>;
+
+      namespace detail {
+
+        template <bool Invocable, typename F, typename... Args>
+        struct is_nothrow_invocable {
+          static constexpr bool value =
+              noexcept(lib::invoke(std::declval<F>(), std::declval<Args>()...));
+        };
+
+        template <typename F, typename... Args>
+        struct is_nothrow_invocable<false, F, Args...> : std::false_type {};
+
+        template <bool Invocable, typename R, typename F, typename... Args>
+        struct is_nothrow_invocable_r {
+          private:
+          inline static R impl() {
+            return lib::invoke(std::declval<F>(), std::declval<Args>()...);
+          }
+
+          public:
+          static constexpr bool value = noexcept(impl());
+        };
+
+        template <typename R, typename F, typename... Args>
+        struct is_nothrow_invocable_r<false, R, F, Args...> : std::false_type {};
+
+      }  // namespace detail
+
+      template <typename F, typename... Args>
+      using is_nothrow_invocable = detail::
+          is_nothrow_invocable<is_invocable<F, Args...>::value, F, Args...>;
+
+      template <typename R, typename F, typename... Args>
+      using is_nothrow_invocable_r =
+          detail::is_nothrow_invocable_r<is_invocable_r<R, F, Args...>::value,
+                                         R,
+                                         F,
+                                         Args...>;
+
+      // <memory>
+#ifdef MPARK_BUILTIN_ADDRESSOF
+      template <typename T>
+      inline constexpr T *addressof(T &arg) noexcept {
+        return __builtin_addressof(arg);
+      }
+#else
+      namespace detail {
+
+        namespace has_addressof_impl {
+
+          struct fail;
+
+          template <typename T>
+          inline fail operator&(T &&);
+
+          template <typename T>
+          inline static constexpr bool impl() {
+            return (std::is_class<T>::value || std::is_union<T>::value) &&
+                   !std::is_same<decltype(&std::declval<T &>()), fail>::value;
+          }
+
+        }  // namespace has_addressof_impl
+
+        template <typename T>
+        using has_addressof = bool_constant<has_addressof_impl::impl<T>()>;
+
+        template <typename T>
+        inline constexpr T *addressof(T &arg, std::true_type) noexcept {
+          return std::addressof(arg);
+        }
+
+        template <typename T>
+        inline constexpr T *addressof(T &arg, std::false_type) noexcept {
+          return &arg;
+        }
+
+      }  // namespace detail
+
+      template <typename T>
+      inline constexpr T *addressof(T &arg) noexcept {
+        return detail::addressof(arg, detail::has_addressof<T>{});
+      }
+#endif
+
+      template <typename T>
+      inline constexpr T *addressof(const T &&) = delete;
+
+    }  // namespace cpp17
+
+    template <typename T>
+    struct remove_all_extents : identity<T> {};
+
+    template <typename T, std::size_t N>
+    struct remove_all_extents<array<T, N>> : remove_all_extents<T> {};
+
+    template <typename T>
+    using remove_all_extents_t = typename remove_all_extents<T>::type;
+
+    template <std::size_t N>
+    using size_constant = std::integral_constant<std::size_t, N>;
+
+    template <std::size_t I, typename T>
+    struct indexed_type : size_constant<I> { using type = T; };
+
+    template <bool... Bs>
+    using all = std::is_same<integer_sequence<bool, true, Bs...>,
+                             integer_sequence<bool, Bs..., true>>;
+
+#ifdef MPARK_TYPE_PACK_ELEMENT
+    template <std::size_t I, typename... Ts>
+    using type_pack_element_t = __type_pack_element<I, Ts...>;
+#else
+    template <std::size_t I, typename... Ts>
+    struct type_pack_element_impl {
+      private:
+      template <typename>
+      struct set;
+
+      template <std::size_t... Is>
+      struct set<index_sequence<Is...>> : indexed_type<Is, Ts>... {};
+
+      template <typename T>
+      inline static std::enable_if<true, T> impl(indexed_type<I, T>);
+
+      inline static std::enable_if<false> impl(...);
+
+      public:
+      using type = decltype(impl(set<index_sequence_for<Ts...>>{}));
+    };
+
+    template <std::size_t I, typename... Ts>
+    using type_pack_element = typename type_pack_element_impl<I, Ts...>::type;
+
+    template <std::size_t I, typename... Ts>
+    using type_pack_element_t = typename type_pack_element<I, Ts...>::type;
+#endif
+
+#ifdef MPARK_TRIVIALITY_TYPE_TRAITS
+    using std::is_trivially_copy_constructible;
+    using std::is_trivially_move_constructible;
+    using std::is_trivially_copy_assignable;
+    using std::is_trivially_move_assignable;
+#else
+    template <typename T>
+    struct is_trivially_copy_constructible
+        : bool_constant<
+              std::is_copy_constructible<T>::value && __has_trivial_copy(T)> {};
+
+    template <typename T>
+    struct is_trivially_move_constructible : bool_constant<__is_trivial(T)> {};
+
+    template <typename T>
+    struct is_trivially_copy_assignable
+        : bool_constant<
+              std::is_copy_assignable<T>::value && __has_trivial_assign(T)> {};
+
+    template <typename T>
+    struct is_trivially_move_assignable : bool_constant<__is_trivial(T)> {};
+#endif
+
+    template <typename T, bool>
+    struct dependent_type : T {};
+
+    template <typename Is, std::size_t J>
+    struct push_back;
+
+    template <typename Is, std::size_t J>
+    using push_back_t = typename push_back<Is, J>::type;
+
+    template <std::size_t... Is, std::size_t J>
+    struct push_back<index_sequence<Is...>, J> {
+      using type = index_sequence<Is..., J>;
+    };
+
+  }  // namespace lib
+}  // namespace mpark
+
+#undef MPARK_RETURN
+
+#endif  // MPARK_LIB_HPP
+
+
+namespace mpark {
+
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+
+#define AUTO auto
+#define AUTO_RETURN(...) { return __VA_ARGS__; }
+
+#define AUTO_REFREF auto &&
+#define AUTO_REFREF_RETURN(...) { return __VA_ARGS__; }
+
+#define DECLTYPE_AUTO decltype(auto)
+#define DECLTYPE_AUTO_RETURN(...) { return __VA_ARGS__; }
+
+#else
+
+#define AUTO auto
+#define AUTO_RETURN(...) \
+  -> lib::decay_t<decltype(__VA_ARGS__)> { return __VA_ARGS__; }
+
+#define AUTO_REFREF auto
+#define AUTO_REFREF_RETURN(...)                                           \
+  -> decltype((__VA_ARGS__)) {                                            \
+    static_assert(std::is_reference<decltype((__VA_ARGS__))>::value, ""); \
+    return __VA_ARGS__;                                                   \
+  }
+
+#define DECLTYPE_AUTO auto
+#define DECLTYPE_AUTO_RETURN(...) \
+  -> decltype(__VA_ARGS__) { return __VA_ARGS__; }
+
+#endif
+
+  class bad_variant_access : public std::exception {
+    public:
+    virtual const char *what() const noexcept override { return "bad_variant_access"; }
+  };
+
+  [[noreturn]] inline void throw_bad_variant_access() {
+#ifdef MPARK_EXCEPTIONS
+    throw bad_variant_access{};
+#else
+    std::terminate();
+    MPARK_BUILTIN_UNREACHABLE;
+#endif
+  }
+
+  template <typename... Ts>
+  class variant;
+
+  template <typename T>
+  struct variant_size;
+
+#ifdef MPARK_VARIABLE_TEMPLATES
+  template <typename T>
+  constexpr std::size_t variant_size_v = variant_size<T>::value;
+#endif
+
+  template <typename T>
+  struct variant_size<const T> : variant_size<T> {};
+
+  template <typename T>
+  struct variant_size<volatile T> : variant_size<T> {};
+
+  template <typename T>
+  struct variant_size<const volatile T> : variant_size<T> {};
+
+  template <typename... Ts>
+  struct variant_size<variant<Ts...>> : lib::size_constant<sizeof...(Ts)> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative;
+
+  template <std::size_t I, typename T>
+  using variant_alternative_t = typename variant_alternative<I, T>::type;
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, const T>
+      : std::add_const<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, volatile T>
+      : std::add_volatile<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, const volatile T>
+      : std::add_cv<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename... Ts>
+  struct variant_alternative<I, variant<Ts...>> {
+    static_assert(I < sizeof...(Ts),
+                  "index out of bounds in `std::variant_alternative<>`");
+    using type = lib::type_pack_element_t<I, Ts...>;
+  };
+
+  constexpr std::size_t variant_npos = static_cast<std::size_t>(-1);
+
+  namespace detail {
+
+    constexpr std::size_t not_found = static_cast<std::size_t>(-1);
+    constexpr std::size_t ambiguous = static_cast<std::size_t>(-2);
+
+#ifdef MPARK_CPP14_CONSTEXPR
+    template <typename T, typename... Ts>
+    inline constexpr std::size_t find_index() {
+      constexpr lib::array<bool, sizeof...(Ts)> matches = {
+          {std::is_same<T, Ts>::value...}
+      };
+      std::size_t result = not_found;
+      for (std::size_t i = 0; i < sizeof...(Ts); ++i) {
+        if (matches[i]) {
+          if (result != not_found) {
+            return ambiguous;
+          }
+          result = i;
+        }
+      }
+      return result;
+    }
+#else
+    inline constexpr std::size_t find_index_impl(std::size_t result,
+                                                 std::size_t) {
+      return result;
+    }
+
+    template <typename... Bs>
+    inline constexpr std::size_t find_index_impl(std::size_t result,
+                                                 std::size_t idx,
+                                                 bool b,
+                                                 Bs... bs) {
+      return b ? (result != not_found ? ambiguous
+                                      : find_index_impl(idx, idx + 1, bs...))
+               : find_index_impl(result, idx + 1, bs...);
+    }
+
+    template <typename T, typename... Ts>
+    inline constexpr std::size_t find_index() {
+      return find_index_impl(not_found, 0, std::is_same<T, Ts>::value...);
+    }
+#endif
+
+    template <std::size_t I>
+    using find_index_sfinae_impl =
+        lib::enable_if_t<I != not_found && I != ambiguous,
+                         lib::size_constant<I>>;
+
+    template <typename T, typename... Ts>
+    using find_index_sfinae = find_index_sfinae_impl<find_index<T, Ts...>()>;
+
+    template <std::size_t I>
+    struct find_index_checked_impl : lib::size_constant<I> {
+      static_assert(I != not_found, "the specified type is not found.");
+      static_assert(I != ambiguous, "the specified type is ambiguous.");
+    };
+
+    template <typename T, typename... Ts>
+    using find_index_checked = find_index_checked_impl<find_index<T, Ts...>()>;
+
+    struct valueless_t {};
+
+    enum class Trait { TriviallyAvailable, Available, Unavailable };
+
+    template <typename T,
+              template <typename> class IsTriviallyAvailable,
+              template <typename> class IsAvailable>
+    inline constexpr Trait trait() {
+      return IsTriviallyAvailable<T>::value
+                 ? Trait::TriviallyAvailable
+                 : IsAvailable<T>::value ? Trait::Available
+                                         : Trait::Unavailable;
+    }
+
+#ifdef MPARK_CPP14_CONSTEXPR
+    template <typename... Traits>
+    inline constexpr Trait common_trait(Traits... traits_) {
+      Trait result = Trait::TriviallyAvailable;
+      lib::array<Trait, sizeof...(Traits)> traits = {{traits_...}};
+      for (std::size_t i = 0; i < sizeof...(Traits); ++i) {
+        Trait t = traits[i];
+        if (static_cast<int>(t) > static_cast<int>(result)) {
+          result = t;
+        }
+      }
+      return result;
+    }
+#else
+    inline constexpr Trait common_trait_impl(Trait result) { return result; }
+
+    template <typename... Traits>
+    inline constexpr Trait common_trait_impl(Trait result,
+                                             Trait t,
+                                             Traits... ts) {
+      return static_cast<int>(t) > static_cast<int>(result)
+                 ? common_trait_impl(t, ts...)
+                 : common_trait_impl(result, ts...);
+    }
+
+    template <typename... Traits>
+    inline constexpr Trait common_trait(Traits... ts) {
+      return common_trait_impl(Trait::TriviallyAvailable, ts...);
+    }
+#endif
+
+    template <typename... Ts>
+    struct traits {
+      static constexpr Trait copy_constructible_trait =
+          common_trait(trait<Ts,
+                             lib::is_trivially_copy_constructible,
+                             std::is_copy_constructible>()...);
+
+      static constexpr Trait move_constructible_trait =
+          common_trait(trait<Ts,
+                             lib::is_trivially_move_constructible,
+                             std::is_move_constructible>()...);
+
+      static constexpr Trait copy_assignable_trait =
+          common_trait(copy_constructible_trait,
+                       trait<Ts,
+                             lib::is_trivially_copy_assignable,
+                             std::is_copy_assignable>()...);
+
+      static constexpr Trait move_assignable_trait =
+          common_trait(move_constructible_trait,
+                       trait<Ts,
+                             lib::is_trivially_move_assignable,
+                             std::is_move_assignable>()...);
+
+      static constexpr Trait destructible_trait =
+          common_trait(trait<Ts,
+                             std::is_trivially_destructible,
+                             std::is_destructible>()...);
+    };
+
+    namespace access {
+
+      struct recursive_union {
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+        template <typename V>
+        inline static constexpr auto &&get_alt(V &&v, in_place_index_t<0>) {
+          return lib::forward<V>(v).head_;
+        }
+
+        template <typename V, std::size_t I>
+        inline static constexpr auto &&get_alt(V &&v, in_place_index_t<I>) {
+          return get_alt(lib::forward<V>(v).tail_, in_place_index_t<I - 1>{});
+        }
+#else
+        template <std::size_t I, bool Dummy = true>
+        struct get_alt_impl {
+          template <typename V>
+          inline constexpr AUTO_REFREF operator()(V &&v) const
+            AUTO_REFREF_RETURN(get_alt_impl<I - 1>{}(lib::forward<V>(v).tail_))
+        };
+
+        template <bool Dummy>
+        struct get_alt_impl<0, Dummy> {
+          template <typename V>
+          inline constexpr AUTO_REFREF operator()(V &&v) const
+            AUTO_REFREF_RETURN(lib::forward<V>(v).head_)
+        };
+
+        template <typename V, std::size_t I>
+        inline static constexpr AUTO_REFREF get_alt(V &&v, in_place_index_t<I>)
+          AUTO_REFREF_RETURN(get_alt_impl<I>{}(lib::forward<V>(v)))
+#endif
+      };
+
+      struct base {
+        template <std::size_t I, typename V>
+        inline static constexpr AUTO_REFREF get_alt(V &&v)
+#ifdef _MSC_VER
+          AUTO_REFREF_RETURN(recursive_union::get_alt(
+              lib::forward<V>(v).data_, in_place_index_t<I>{}))
+#else
+          AUTO_REFREF_RETURN(recursive_union::get_alt(
+              data(lib::forward<V>(v)), in_place_index_t<I>{}))
+#endif
+      };
+
+      struct variant {
+        template <std::size_t I, typename V>
+        inline static constexpr AUTO_REFREF get_alt(V &&v)
+          AUTO_REFREF_RETURN(base::get_alt<I>(lib::forward<V>(v).impl_))
+      };
+
+    }  // namespace access
+
+    namespace visitation {
+
+#if defined(MPARK_CPP14_CONSTEXPR) && !defined(_MSC_VER)
+#define MPARK_VARIANT_SWITCH_VISIT
+#endif
+
+      struct base {
+        template <typename Visitor, typename... Vs>
+        using dispatch_result_t = decltype(
+            lib::invoke(std::declval<Visitor>(),
+                        access::base::get_alt<0>(std::declval<Vs>())...));
+
+        template <typename Expected>
+        struct expected {
+          template <typename Actual>
+          inline static constexpr bool but_got() {
+            return std::is_same<Expected, Actual>::value;
+          }
+        };
+
+        template <typename Expected, typename Actual>
+        struct visit_return_type_check {
+          static_assert(
+              expected<Expected>::template but_got<Actual>(),
+              "`visit` requires the visitor to have a single return type");
+
+          template <typename Visitor, typename... Alts>
+          inline static constexpr DECLTYPE_AUTO invoke(Visitor &&visitor,
+                                                       Alts &&... alts)
+            DECLTYPE_AUTO_RETURN(lib::invoke(lib::forward<Visitor>(visitor),
+                                             lib::forward<Alts>(alts)...))
+        };
+
+#ifdef MPARK_VARIANT_SWITCH_VISIT
+        template <bool B, typename R, typename... ITs>
+        struct dispatcher;
+
+        template <typename R, typename... ITs>
+        struct dispatcher<false, R, ITs...> {
+          template <std::size_t B, typename F, typename... Vs>
+          MPARK_ALWAYS_INLINE static constexpr R dispatch(
+              F &&, typename ITs::type &&..., Vs &&...) {
+            MPARK_BUILTIN_UNREACHABLE;
+          }
+
+          template <std::size_t I, typename F, typename... Vs>
+          MPARK_ALWAYS_INLINE static constexpr R dispatch_case(F &&, Vs &&...) {
+            MPARK_BUILTIN_UNREACHABLE;
+          }
+
+          template <std::size_t B, typename F, typename... Vs>
+          MPARK_ALWAYS_INLINE static constexpr R dispatch_at(std::size_t,
+                                                             F &&,
+                                                             Vs &&...) {
+            MPARK_BUILTIN_UNREACHABLE;
+          }
+        };
+
+        template <typename R, typename... ITs>
+        struct dispatcher<true, R, ITs...> {
+          template <std::size_t B, typename F>
+          MPARK_ALWAYS_INLINE static constexpr R dispatch(
+              F &&f, typename ITs::type &&... visited_vs) {
+            using Expected = R;
+            using Actual = decltype(lib::invoke(
+                lib::forward<F>(f),
+                access::base::get_alt<ITs::value>(
+                    lib::forward<typename ITs::type>(visited_vs))...));
+            return visit_return_type_check<Expected, Actual>::invoke(
+                lib::forward<F>(f),
+                access::base::get_alt<ITs::value>(
+                    lib::forward<typename ITs::type>(visited_vs))...);
+          }
+
+          template <std::size_t B, typename F, typename V, typename... Vs>
+          MPARK_ALWAYS_INLINE static constexpr R dispatch(
+              F &&f, typename ITs::type &&... visited_vs, V &&v, Vs &&... vs) {
+#define MPARK_DISPATCH(I)                                                   \
+  dispatcher<(I < lib::decay_t<V>::size()),                                 \
+             R,                                                             \
+             ITs...,                                                        \
+             lib::indexed_type<I, V>>::                                     \
+      template dispatch<0>(lib::forward<F>(f),                              \
+                           lib::forward<typename ITs::type>(visited_vs)..., \
+                           lib::forward<V>(v),                              \
+                           lib::forward<Vs>(vs)...)
+
+#define MPARK_DEFAULT(I)                                                      \
+  dispatcher<(I < lib::decay_t<V>::size()), R, ITs...>::template dispatch<I>( \
+      lib::forward<F>(f),                                                     \
+      lib::forward<typename ITs::type>(visited_vs)...,                        \
+      lib::forward<V>(v),                                                     \
+      lib::forward<Vs>(vs)...)
+
+            switch (v.index()) {
+              case B + 0: return MPARK_DISPATCH(B + 0);
+              case B + 1: return MPARK_DISPATCH(B + 1);
+              case B + 2: return MPARK_DISPATCH(B + 2);
+              case B + 3: return MPARK_DISPATCH(B + 3);
+              case B + 4: return MPARK_DISPATCH(B + 4);
+              case B + 5: return MPARK_DISPATCH(B + 5);
+              case B + 6: return MPARK_DISPATCH(B + 6);
+              case B + 7: return MPARK_DISPATCH(B + 7);
+              case B + 8: return MPARK_DISPATCH(B + 8);
+              case B + 9: return MPARK_DISPATCH(B + 9);
+              case B + 10: return MPARK_DISPATCH(B + 10);
+              case B + 11: return MPARK_DISPATCH(B + 11);
+              case B + 12: return MPARK_DISPATCH(B + 12);
+              case B + 13: return MPARK_DISPATCH(B + 13);
+              case B + 14: return MPARK_DISPATCH(B + 14);
+              case B + 15: return MPARK_DISPATCH(B + 15);
+              case B + 16: return MPARK_DISPATCH(B + 16);
+              case B + 17: return MPARK_DISPATCH(B + 17);
+              case B + 18: return MPARK_DISPATCH(B + 18);
+              case B + 19: return MPARK_DISPATCH(B + 19);
+              case B + 20: return MPARK_DISPATCH(B + 20);
+              case B + 21: return MPARK_DISPATCH(B + 21);
+              case B + 22: return MPARK_DISPATCH(B + 22);
+              case B + 23: return MPARK_DISPATCH(B + 23);
+              case B + 24: return MPARK_DISPATCH(B + 24);
+              case B + 25: return MPARK_DISPATCH(B + 25);
+              case B + 26: return MPARK_DISPATCH(B + 26);
+              case B + 27: return MPARK_DISPATCH(B + 27);
+              case B + 28: return MPARK_DISPATCH(B + 28);
+              case B + 29: return MPARK_DISPATCH(B + 29);
+              case B + 30: return MPARK_DISPATCH(B + 30);
+              case B + 31: return MPARK_DISPATCH(B + 31);
+              default: return MPARK_DEFAULT(B + 32);
+            }
+
+#undef MPARK_DEFAULT
+#undef MPARK_DISPATCH
+          }
+
+          template <std::size_t I, typename F, typename... Vs>
+          MPARK_ALWAYS_INLINE static constexpr R dispatch_case(F &&f,
+                                                               Vs &&... vs) {
+            using Expected = R;
+            using Actual = decltype(
+                lib::invoke(lib::forward<F>(f),
+                            access::base::get_alt<I>(lib::forward<Vs>(vs))...));
+            return visit_return_type_check<Expected, Actual>::invoke(
+                lib::forward<F>(f),
+                access::base::get_alt<I>(lib::forward<Vs>(vs))...);
+          }
+
+          template <std::size_t B, typename F, typename V, typename... Vs>
+          MPARK_ALWAYS_INLINE static constexpr R dispatch_at(std::size_t index,
+                                                             F &&f,
+                                                             V &&v,
+                                                             Vs &&... vs) {
+            static_assert(lib::all<(lib::decay_t<V>::size() ==
+                                    lib::decay_t<Vs>::size())...>::value,
+                          "all of the variants must be the same size.");
+#define MPARK_DISPATCH_AT(I)                                               \
+  dispatcher<(I < lib::decay_t<V>::size()), R>::template dispatch_case<I>( \
+      lib::forward<F>(f), lib::forward<V>(v), lib::forward<Vs>(vs)...)
+
+#define MPARK_DEFAULT(I)                                                 \
+  dispatcher<(I < lib::decay_t<V>::size()), R>::template dispatch_at<I>( \
+      index, lib::forward<F>(f), lib::forward<V>(v), lib::forward<Vs>(vs)...)
+
+            switch (index) {
+              case B + 0: return MPARK_DISPATCH_AT(B + 0);
+              case B + 1: return MPARK_DISPATCH_AT(B + 1);
+              case B + 2: return MPARK_DISPATCH_AT(B + 2);
+              case B + 3: return MPARK_DISPATCH_AT(B + 3);
+              case B + 4: return MPARK_DISPATCH_AT(B + 4);
+              case B + 5: return MPARK_DISPATCH_AT(B + 5);
+              case B + 6: return MPARK_DISPATCH_AT(B + 6);
+              case B + 7: return MPARK_DISPATCH_AT(B + 7);
+              case B + 8: return MPARK_DISPATCH_AT(B + 8);
+              case B + 9: return MPARK_DISPATCH_AT(B + 9);
+              case B + 10: return MPARK_DISPATCH_AT(B + 10);
+              case B + 11: return MPARK_DISPATCH_AT(B + 11);
+              case B + 12: return MPARK_DISPATCH_AT(B + 12);
+              case B + 13: return MPARK_DISPATCH_AT(B + 13);
+              case B + 14: return MPARK_DISPATCH_AT(B + 14);
+              case B + 15: return MPARK_DISPATCH_AT(B + 15);
+              case B + 16: return MPARK_DISPATCH_AT(B + 16);
+              case B + 17: return MPARK_DISPATCH_AT(B + 17);
+              case B + 18: return MPARK_DISPATCH_AT(B + 18);
+              case B + 19: return MPARK_DISPATCH_AT(B + 19);
+              case B + 20: return MPARK_DISPATCH_AT(B + 20);
+              case B + 21: return MPARK_DISPATCH_AT(B + 21);
+              case B + 22: return MPARK_DISPATCH_AT(B + 22);
+              case B + 23: return MPARK_DISPATCH_AT(B + 23);
+              case B + 24: return MPARK_DISPATCH_AT(B + 24);
+              case B + 25: return MPARK_DISPATCH_AT(B + 25);
+              case B + 26: return MPARK_DISPATCH_AT(B + 26);
+              case B + 27: return MPARK_DISPATCH_AT(B + 27);
+              case B + 28: return MPARK_DISPATCH_AT(B + 28);
+              case B + 29: return MPARK_DISPATCH_AT(B + 29);
+              case B + 30: return MPARK_DISPATCH_AT(B + 30);
+              case B + 31: return MPARK_DISPATCH_AT(B + 31);
+              default: return MPARK_DEFAULT(B + 32);
+            }
+
+#undef MPARK_DEFAULT
+#undef MPARK_DISPATCH_AT
+          }
+        };
+#else
+        template <typename T>
+        inline static constexpr const T &at(const T &elem) noexcept {
+          return elem;
+        }
+
+        template <typename T, std::size_t N, typename... Is>
+        inline static constexpr const lib::remove_all_extents_t<T> &at(
+            const lib::array<T, N> &elems, std::size_t i, Is... is) noexcept {
+          return at(elems[i], is...);
+        }
+
+        template <typename F, typename... Fs>
+        inline static constexpr lib::array<lib::decay_t<F>, sizeof...(Fs) + 1>
+        make_farray(F &&f, Fs &&... fs) {
+          return {{lib::forward<F>(f), lib::forward<Fs>(fs)...}};
+        }
+
+        template <typename F, typename... Vs>
+        struct make_fmatrix_impl {
+
+          template <std::size_t... Is>
+          inline static constexpr dispatch_result_t<F, Vs...> dispatch(
+              F &&f, Vs &&... vs) {
+            using Expected = dispatch_result_t<F, Vs...>;
+            using Actual = decltype(lib::invoke(
+                lib::forward<F>(f),
+                access::base::get_alt<Is>(lib::forward<Vs>(vs))...));
+            return visit_return_type_check<Expected, Actual>::invoke(
+                lib::forward<F>(f),
+                access::base::get_alt<Is>(lib::forward<Vs>(vs))...);
+          }
+
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+          template <std::size_t... Is>
+          inline static constexpr auto impl(lib::index_sequence<Is...>) {
+            return &dispatch<Is...>;
+          }
+
+          template <typename Is, std::size_t... Js, typename... Ls>
+          inline static constexpr auto impl(Is,
+                                            lib::index_sequence<Js...>,
+                                            Ls... ls) {
+            return make_farray(impl(lib::push_back_t<Is, Js>{}, ls...)...);
+          }
+#else
+          template <typename...>
+          struct impl;
+
+          template <std::size_t... Is>
+          struct impl<lib::index_sequence<Is...>> {
+            inline constexpr AUTO operator()() const
+              AUTO_RETURN(&dispatch<Is...>)
+          };
+
+          template <typename Is, std::size_t... Js, typename... Ls>
+          struct impl<Is, lib::index_sequence<Js...>, Ls...> {
+            inline constexpr AUTO operator()() const
+              AUTO_RETURN(
+                  make_farray(impl<lib::push_back_t<Is, Js>, Ls...>{}()...))
+          };
+#endif
+        };
+
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+        template <typename F, typename... Vs>
+        inline static constexpr auto make_fmatrix() {
+          return make_fmatrix_impl<F, Vs...>::impl(
+              lib::index_sequence<>{},
+              lib::make_index_sequence<lib::decay_t<Vs>::size()>{}...);
+        }
+#else
+        template <typename F, typename... Vs>
+        inline static constexpr AUTO make_fmatrix()
+          AUTO_RETURN(
+              typename make_fmatrix_impl<F, Vs...>::template impl<
+                  lib::index_sequence<>,
+                  lib::make_index_sequence<lib::decay_t<Vs>::size()>...>{}())
+#endif
+
+        template <typename F, typename... Vs>
+        struct make_fdiagonal_impl {
+          template <std::size_t I>
+          inline static constexpr dispatch_result_t<F, Vs...> dispatch(
+              F &&f, Vs &&... vs) {
+            using Expected = dispatch_result_t<F, Vs...>;
+            using Actual = decltype(
+                lib::invoke(lib::forward<F>(f),
+                            access::base::get_alt<I>(lib::forward<Vs>(vs))...));
+            return visit_return_type_check<Expected, Actual>::invoke(
+                lib::forward<F>(f),
+                access::base::get_alt<I>(lib::forward<Vs>(vs))...);
+          }
+
+          template <std::size_t... Is>
+          inline static constexpr AUTO impl(lib::index_sequence<Is...>)
+            AUTO_RETURN(make_farray(&dispatch<Is>...))
+        };
+
+        template <typename F, typename V, typename... Vs>
+        inline static constexpr auto make_fdiagonal()
+            -> decltype(make_fdiagonal_impl<F, V, Vs...>::impl(
+                lib::make_index_sequence<lib::decay_t<V>::size()>{})) {
+          static_assert(lib::all<(lib::decay_t<V>::size() ==
+                                  lib::decay_t<Vs>::size())...>::value,
+                        "all of the variants must be the same size.");
+          return make_fdiagonal_impl<F, V, Vs...>::impl(
+              lib::make_index_sequence<lib::decay_t<V>::size()>{});
+        }
+#endif
+      };
+
+#if !defined(MPARK_VARIANT_SWITCH_VISIT) && \
+    (!defined(_MSC_VER) || _MSC_VER >= 1910)
+      template <typename F, typename... Vs>
+      using fmatrix_t = decltype(base::make_fmatrix<F, Vs...>());
+
+      template <typename F, typename... Vs>
+      struct fmatrix {
+        static constexpr fmatrix_t<F, Vs...> value =
+            base::make_fmatrix<F, Vs...>();
+      };
+
+      template <typename F, typename... Vs>
+      constexpr fmatrix_t<F, Vs...> fmatrix<F, Vs...>::value;
+
+      template <typename F, typename... Vs>
+      using fdiagonal_t = decltype(base::make_fdiagonal<F, Vs...>());
+
+      template <typename F, typename... Vs>
+      struct fdiagonal {
+        static constexpr fdiagonal_t<F, Vs...> value =
+            base::make_fdiagonal<F, Vs...>();
+      };
+
+      template <typename F, typename... Vs>
+      constexpr fdiagonal_t<F, Vs...> fdiagonal<F, Vs...>::value;
+#endif
+
+      struct alt {
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor,
+                                                        Vs &&... vs)
+#ifdef MPARK_VARIANT_SWITCH_VISIT
+          DECLTYPE_AUTO_RETURN(
+              base::dispatcher<
+                  true,
+                  base::dispatch_result_t<Visitor,
+                                          decltype(as_base(
+                                              lib::forward<Vs>(vs)))...>>::
+                  template dispatch<0>(lib::forward<Visitor>(visitor),
+                                       as_base(lib::forward<Vs>(vs))...))
+#elif !defined(_MSC_VER) || _MSC_VER >= 1910
+          DECLTYPE_AUTO_RETURN(base::at(
+              fmatrix<Visitor &&,
+                      decltype(as_base(lib::forward<Vs>(vs)))...>::value,
+              vs.index()...)(lib::forward<Visitor>(visitor),
+                             as_base(lib::forward<Vs>(vs))...))
+#else
+          DECLTYPE_AUTO_RETURN(base::at(
+              base::make_fmatrix<Visitor &&,
+                      decltype(as_base(lib::forward<Vs>(vs)))...>(),
+              vs.index()...)(lib::forward<Visitor>(visitor),
+                             as_base(lib::forward<Vs>(vs))...))
+#endif
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index,
+                                                           Visitor &&visitor,
+                                                           Vs &&... vs)
+#ifdef MPARK_VARIANT_SWITCH_VISIT
+          DECLTYPE_AUTO_RETURN(
+              base::dispatcher<
+                  true,
+                  base::dispatch_result_t<Visitor,
+                                          decltype(as_base(
+                                              lib::forward<Vs>(vs)))...>>::
+                  template dispatch_at<0>(index,
+                                          lib::forward<Visitor>(visitor),
+                                          as_base(lib::forward<Vs>(vs))...))
+#elif !defined(_MSC_VER) || _MSC_VER >= 1910
+          DECLTYPE_AUTO_RETURN(base::at(
+              fdiagonal<Visitor &&,
+                        decltype(as_base(lib::forward<Vs>(vs)))...>::value,
+              index)(lib::forward<Visitor>(visitor),
+                     as_base(lib::forward<Vs>(vs))...))
+#else
+          DECLTYPE_AUTO_RETURN(base::at(
+              base::make_fdiagonal<Visitor &&,
+                        decltype(as_base(lib::forward<Vs>(vs)))...>(),
+              index)(lib::forward<Visitor>(visitor),
+                     as_base(lib::forward<Vs>(vs))...))
+#endif
+      };
+
+      struct variant {
+        private:
+        template <typename Visitor>
+        struct visitor {
+          template <typename... Values>
+          inline static constexpr bool does_not_handle() {
+            return lib::is_invocable<Visitor, Values...>::value;
+          }
+        };
+
+        template <typename Visitor, typename... Values>
+        struct visit_exhaustiveness_check {
+          static_assert(visitor<Visitor>::template does_not_handle<Values...>(),
+                        "`visit` requires the visitor to be exhaustive.");
+
+          inline static constexpr DECLTYPE_AUTO invoke(Visitor &&visitor,
+                                                       Values &&... values)
+            DECLTYPE_AUTO_RETURN(lib::invoke(lib::forward<Visitor>(visitor),
+                                             lib::forward<Values>(values)...))
+        };
+
+        template <typename Visitor>
+        struct value_visitor {
+          Visitor &&visitor_;
+
+          template <typename... Alts>
+          inline constexpr DECLTYPE_AUTO operator()(Alts &&... alts) const
+            DECLTYPE_AUTO_RETURN(
+                visit_exhaustiveness_check<
+                    Visitor,
+                    decltype((lib::forward<Alts>(alts).value))...>::
+                    invoke(lib::forward<Visitor>(visitor_),
+                           lib::forward<Alts>(alts).value...))
+        };
+
+        template <typename Visitor>
+        inline static constexpr AUTO make_value_visitor(Visitor &&visitor)
+          AUTO_RETURN(value_visitor<Visitor>{lib::forward<Visitor>(visitor)})
+
+        public:
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor,
+                                                        Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(alt::visit_alt(lib::forward<Visitor>(visitor),
+                                              lib::forward<Vs>(vs).impl_...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index,
+                                                           Visitor &&visitor,
+                                                           Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              alt::visit_alt_at(index,
+                                lib::forward<Visitor>(visitor),
+                                lib::forward<Vs>(vs).impl_...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_value(Visitor &&visitor,
+                                                          Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              visit_alt(make_value_visitor(lib::forward<Visitor>(visitor)),
+                        lib::forward<Vs>(vs)...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_value_at(std::size_t index,
+                                                             Visitor &&visitor,
+                                                             Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              visit_alt_at(index,
+                           make_value_visitor(lib::forward<Visitor>(visitor)),
+                           lib::forward<Vs>(vs)...))
+      };
+
+    }  // namespace visitation
+
+    template <std::size_t Index, typename T>
+    struct alt {
+      using value_type = T;
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4244)
+#endif
+      template <typename... Args>
+      inline explicit constexpr alt(in_place_t, Args &&... args)
+          : value(lib::forward<Args>(args)...) {}
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+      T value;
+    };
+
+    template <Trait DestructibleTrait, std::size_t Index, typename... Ts>
+    union recursive_union;
+
+    template <Trait DestructibleTrait, std::size_t Index>
+    union recursive_union<DestructibleTrait, Index> {};
+
+#define MPARK_VARIANT_RECURSIVE_UNION(destructible_trait, destructor)      \
+  template <std::size_t Index, typename T, typename... Ts>                 \
+  union recursive_union<destructible_trait, Index, T, Ts...> {             \
+    public:                                                                \
+    inline explicit constexpr recursive_union(valueless_t) noexcept        \
+        : dummy_{} {}                                                      \
+                                                                           \
+    template <typename... Args>                                            \
+    inline explicit constexpr recursive_union(in_place_index_t<0>,         \
+                                              Args &&... args)             \
+        : head_(in_place_t{}, lib::forward<Args>(args)...) {}              \
+                                                                           \
+    template <std::size_t I, typename... Args>                             \
+    inline explicit constexpr recursive_union(in_place_index_t<I>,         \
+                                              Args &&... args)             \
+        : tail_(in_place_index_t<I - 1>{}, lib::forward<Args>(args)...) {} \
+                                                                           \
+    recursive_union(const recursive_union &) = default;                    \
+    recursive_union(recursive_union &&) = default;                         \
+                                                                           \
+    destructor                                                             \
+                                                                           \
+    recursive_union &operator=(const recursive_union &) = default;         \
+    recursive_union &operator=(recursive_union &&) = default;              \
+                                                                           \
+    private:                                                               \
+    char dummy_;                                                           \
+    alt<Index, T> head_;                                                   \
+    recursive_union<destructible_trait, Index + 1, Ts...> tail_;           \
+                                                                           \
+    friend struct access::recursive_union;                                 \
+  }
+
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::TriviallyAvailable,
+                                  ~recursive_union() = default;);
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::Available,
+                                  ~recursive_union() {});
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::Unavailable,
+                                  ~recursive_union() = delete;);
+
+#undef MPARK_VARIANT_RECURSIVE_UNION
+
+    using index_t = unsigned int;
+
+    template <Trait DestructibleTrait, typename... Ts>
+    class base {
+      public:
+      inline explicit constexpr base(valueless_t tag) noexcept
+          : data_(tag), index_(static_cast<index_t>(-1)) {}
+
+      template <std::size_t I, typename... Args>
+      inline explicit constexpr base(in_place_index_t<I>, Args &&... args)
+          : data_(in_place_index_t<I>{}, lib::forward<Args>(args)...),
+            index_(I) {}
+
+      inline constexpr bool valueless_by_exception() const noexcept {
+        return index_ == static_cast<index_t>(-1);
+      }
+
+      inline constexpr std::size_t index() const noexcept {
+        return valueless_by_exception() ? variant_npos : index_;
+      }
+
+      protected:
+      using data_t = recursive_union<DestructibleTrait, 0, Ts...>;
+
+      friend inline constexpr base &as_base(base &b) { return b; }
+      friend inline constexpr const base &as_base(const base &b) { return b; }
+      friend inline constexpr base &&as_base(base &&b) { return lib::move(b); }
+      friend inline constexpr const base &&as_base(const base &&b) { return lib::move(b); }
+
+      friend inline constexpr data_t &data(base &b) { return b.data_; }
+      friend inline constexpr const data_t &data(const base &b) { return b.data_; }
+      friend inline constexpr data_t &&data(base &&b) { return lib::move(b).data_; }
+      friend inline constexpr const data_t &&data(const base &&b) { return lib::move(b).data_; }
+
+      inline static constexpr std::size_t size() { return sizeof...(Ts); }
+
+      data_t data_;
+      index_t index_;
+
+      friend struct access::base;
+      friend struct visitation::base;
+    };
+
+    struct dtor {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+      template <typename Alt>
+      inline void operator()(Alt &alt) const noexcept { alt.~Alt(); }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+    };
+
+#if !defined(_MSC_VER) || _MSC_VER >= 1910
+#define MPARK_INHERITING_CTOR(type, base) using base::base;
+#else
+#define MPARK_INHERITING_CTOR(type, base)         \
+  template <typename... Args>                     \
+  inline explicit constexpr type(Args &&... args) \
+      : base(lib::forward<Args>(args)...) {}
+#endif
+
+    template <typename Traits, Trait = Traits::destructible_trait>
+    class destructor;
+
+#define MPARK_VARIANT_DESTRUCTOR(destructible_trait, definition, destroy) \
+  template <typename... Ts>                                               \
+  class destructor<traits<Ts...>, destructible_trait>                     \
+      : public base<destructible_trait, Ts...> {                          \
+    using super = base<destructible_trait, Ts...>;                        \
+                                                                          \
+    public:                                                               \
+    MPARK_INHERITING_CTOR(destructor, super)                              \
+    using super::operator=;                                               \
+                                                                          \
+    destructor(const destructor &) = default;                             \
+    destructor(destructor &&) = default;                                  \
+    definition                                                            \
+    destructor &operator=(const destructor &) = default;                  \
+    destructor &operator=(destructor &&) = default;                       \
+                                                                          \
+    protected:                                                            \
+    destroy                                                               \
+  }
+
+    MPARK_VARIANT_DESTRUCTOR(
+        Trait::TriviallyAvailable,
+        ~destructor() = default;,
+        inline void destroy() noexcept {
+          this->index_ = static_cast<index_t>(-1);
+        });
+
+    MPARK_VARIANT_DESTRUCTOR(
+        Trait::Available,
+        ~destructor() { destroy(); },
+        inline void destroy() noexcept {
+          if (!this->valueless_by_exception()) {
+            visitation::alt::visit_alt(dtor{}, *this);
+          }
+          this->index_ = static_cast<index_t>(-1);
+        });
+
+    MPARK_VARIANT_DESTRUCTOR(
+        Trait::Unavailable,
+        ~destructor() = delete;,
+        inline void destroy() noexcept = delete;);
+
+#undef MPARK_VARIANT_DESTRUCTOR
+
+    template <typename Traits>
+    class constructor : public destructor<Traits> {
+      using super = destructor<Traits>;
+
+      public:
+      MPARK_INHERITING_CTOR(constructor, super)
+      using super::operator=;
+
+      protected:
+#ifndef MPARK_GENERIC_LAMBDAS
+      struct ctor {
+        template <typename LhsAlt, typename RhsAlt>
+        inline void operator()(LhsAlt &lhs_alt, RhsAlt &&rhs_alt) const {
+          constructor::construct_alt(lhs_alt,
+                                     lib::forward<RhsAlt>(rhs_alt).value);
+        }
+      };
+#endif
+
+      template <std::size_t I, typename T, typename... Args>
+      inline static T &construct_alt(alt<I, T> &a, Args &&... args) {
+        auto *result = ::new (static_cast<void *>(lib::addressof(a)))
+            alt<I, T>(in_place_t{}, lib::forward<Args>(args)...);
+        return result->value;
+      }
+
+      template <typename Rhs>
+      inline static void generic_construct(constructor &lhs, Rhs &&rhs) {
+        lhs.destroy();
+        if (!rhs.valueless_by_exception()) {
+          visitation::alt::visit_alt_at(
+              rhs.index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+              [](auto &lhs_alt, auto &&rhs_alt) {
+                constructor::construct_alt(
+                    lhs_alt, lib::forward<decltype(rhs_alt)>(rhs_alt).value);
+              }
+#else
+              ctor{}
+#endif
+              ,
+              lhs,
+              lib::forward<Rhs>(rhs));
+          lhs.index_ = rhs.index_;
+        }
+      }
+    };
+
+    template <typename Traits, Trait = Traits::move_constructible_trait>
+    class move_constructor;
+
+#define MPARK_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, definition) \
+  template <typename... Ts>                                                  \
+  class move_constructor<traits<Ts...>, move_constructible_trait>            \
+      : public constructor<traits<Ts...>> {                                  \
+    using super = constructor<traits<Ts...>>;                                \
+                                                                             \
+    public:                                                                  \
+    MPARK_INHERITING_CTOR(move_constructor, super)                           \
+    using super::operator=;                                                  \
+                                                                             \
+    move_constructor(const move_constructor &) = default;                    \
+    definition                                                               \
+    ~move_constructor() = default;                                           \
+    move_constructor &operator=(const move_constructor &) = default;         \
+    move_constructor &operator=(move_constructor &&) = default;              \
+  }
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::TriviallyAvailable,
+        move_constructor(move_constructor &&that) = default;);
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::Available,
+        move_constructor(move_constructor &&that) noexcept(
+            lib::all<std::is_nothrow_move_constructible<Ts>::value...>::value)
+            : move_constructor(valueless_t{}) {
+          this->generic_construct(*this, lib::move(that));
+        });
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::Unavailable,
+        move_constructor(move_constructor &&) = delete;);
+
+#undef MPARK_VARIANT_MOVE_CONSTRUCTOR
+
+    template <typename Traits, Trait = Traits::copy_constructible_trait>
+    class copy_constructor;
+
+#define MPARK_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, definition) \
+  template <typename... Ts>                                                  \
+  class copy_constructor<traits<Ts...>, copy_constructible_trait>            \
+      : public move_constructor<traits<Ts...>> {                             \
+    using super = move_constructor<traits<Ts...>>;                           \
+                                                                             \
+    public:                                                                  \
+    MPARK_INHERITING_CTOR(copy_constructor, super)                           \
+    using super::operator=;                                                  \
+                                                                             \
+    definition                                                               \
+    copy_constructor(copy_constructor &&) = default;                         \
+    ~copy_constructor() = default;                                           \
+    copy_constructor &operator=(const copy_constructor &) = default;         \
+    copy_constructor &operator=(copy_constructor &&) = default;              \
+  }
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::TriviallyAvailable,
+        copy_constructor(const copy_constructor &that) = default;);
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::Available,
+        copy_constructor(const copy_constructor &that)
+            : copy_constructor(valueless_t{}) {
+          this->generic_construct(*this, that);
+        });
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::Unavailable,
+        copy_constructor(const copy_constructor &) = delete;);
+
+#undef MPARK_VARIANT_COPY_CONSTRUCTOR
+
+    template <typename Traits>
+    class assignment : public copy_constructor<Traits> {
+      using super = copy_constructor<Traits>;
+
+      public:
+      MPARK_INHERITING_CTOR(assignment, super)
+      using super::operator=;
+
+      template <std::size_t I, typename... Args>
+      inline /* auto & */ auto emplace(Args &&... args)
+          -> decltype(this->construct_alt(access::base::get_alt<I>(*this),
+                                          lib::forward<Args>(args)...)) {
+        this->destroy();
+        auto &result = this->construct_alt(access::base::get_alt<I>(*this),
+                                           lib::forward<Args>(args)...);
+        this->index_ = I;
+        return result;
+      }
+
+      protected:
+#ifndef MPARK_GENERIC_LAMBDAS
+      template <typename That>
+      struct assigner {
+        template <typename ThisAlt, typename ThatAlt>
+        inline void operator()(ThisAlt &this_alt, ThatAlt &&that_alt) const {
+          self->assign_alt(this_alt, lib::forward<ThatAlt>(that_alt).value);
+        }
+        assignment *self;
+      };
+#endif
+
+      template <std::size_t I, typename T, typename Arg>
+      inline void assign_alt(alt<I, T> &a, Arg &&arg) {
+        if (this->index() == I) {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4244)
+#endif
+          a.value = lib::forward<Arg>(arg);
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+        } else {
+          struct {
+            void operator()(std::true_type) const {
+              this_->emplace<I>(lib::forward<Arg>(arg_));
+            }
+            void operator()(std::false_type) const {
+              this_->emplace<I>(T(lib::forward<Arg>(arg_)));
+            }
+            assignment *this_;
+            Arg &&arg_;
+          } impl{this, lib::forward<Arg>(arg)};
+          impl(lib::bool_constant<
+                   std::is_nothrow_constructible<T, Arg>::value ||
+                   !std::is_nothrow_move_constructible<T>::value>{});
+        }
+      }
+
+      template <typename That>
+      inline void generic_assign(That &&that) {
+        if (this->valueless_by_exception() && that.valueless_by_exception()) {
+          // do nothing.
+        } else if (that.valueless_by_exception()) {
+          this->destroy();
+        } else {
+          visitation::alt::visit_alt_at(
+              that.index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+              [this](auto &this_alt, auto &&that_alt) {
+                this->assign_alt(
+                    this_alt, lib::forward<decltype(that_alt)>(that_alt).value);
+              }
+#else
+              assigner<That>{this}
+#endif
+              ,
+              *this,
+              lib::forward<That>(that));
+        }
+      }
+    };
+
+    template <typename Traits, Trait = Traits::move_assignable_trait>
+    class move_assignment;
+
+#define MPARK_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, definition) \
+  template <typename... Ts>                                              \
+  class move_assignment<traits<Ts...>, move_assignable_trait>            \
+      : public assignment<traits<Ts...>> {                               \
+    using super = assignment<traits<Ts...>>;                             \
+                                                                         \
+    public:                                                              \
+    MPARK_INHERITING_CTOR(move_assignment, super)                        \
+    using super::operator=;                                              \
+                                                                         \
+    move_assignment(const move_assignment &) = default;                  \
+    move_assignment(move_assignment &&) = default;                       \
+    ~move_assignment() = default;                                        \
+    move_assignment &operator=(const move_assignment &) = default;       \
+    definition                                                           \
+  }
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::TriviallyAvailable,
+        move_assignment &operator=(move_assignment &&that) = default;);
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::Available,
+        move_assignment &
+        operator=(move_assignment &&that) noexcept(
+            lib::all<(std::is_nothrow_move_constructible<Ts>::value &&
+                      std::is_nothrow_move_assignable<Ts>::value)...>::value) {
+          this->generic_assign(lib::move(that));
+          return *this;
+        });
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::Unavailable,
+        move_assignment &operator=(move_assignment &&) = delete;);
+
+#undef MPARK_VARIANT_MOVE_ASSIGNMENT
+
+    template <typename Traits, Trait = Traits::copy_assignable_trait>
+    class copy_assignment;
+
+#define MPARK_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, definition) \
+  template <typename... Ts>                                              \
+  class copy_assignment<traits<Ts...>, copy_assignable_trait>            \
+      : public move_assignment<traits<Ts...>> {                          \
+    using super = move_assignment<traits<Ts...>>;                        \
+                                                                         \
+    public:                                                              \
+    MPARK_INHERITING_CTOR(copy_assignment, super)                        \
+    using super::operator=;                                              \
+                                                                         \
+    copy_assignment(const copy_assignment &) = default;                  \
+    copy_assignment(copy_assignment &&) = default;                       \
+    ~copy_assignment() = default;                                        \
+    definition                                                           \
+    copy_assignment &operator=(copy_assignment &&) = default;            \
+  }
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::TriviallyAvailable,
+        copy_assignment &operator=(const copy_assignment &that) = default;);
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::Available,
+        copy_assignment &operator=(const copy_assignment &that) {
+          this->generic_assign(that);
+          return *this;
+        });
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::Unavailable,
+        copy_assignment &operator=(const copy_assignment &) = delete;);
+
+#undef MPARK_VARIANT_COPY_ASSIGNMENT
+
+    template <typename... Ts>
+    class impl : public copy_assignment<traits<Ts...>> {
+      using super = copy_assignment<traits<Ts...>>;
+
+      public:
+      MPARK_INHERITING_CTOR(impl, super)
+      using super::operator=;
+
+      template <std::size_t I, typename Arg>
+      inline void assign(Arg &&arg) {
+        this->assign_alt(access::base::get_alt<I>(*this),
+                         lib::forward<Arg>(arg));
+      }
+
+      inline void swap(impl &that) {
+        if (this->valueless_by_exception() && that.valueless_by_exception()) {
+          // do nothing.
+        } else if (this->index() == that.index()) {
+          visitation::alt::visit_alt_at(this->index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+                                        [](auto &this_alt, auto &that_alt) {
+                                          using std::swap;
+                                          swap(this_alt.value,
+                                               that_alt.value);
+                                        }
+#else
+                                        swapper{}
+#endif
+                                        ,
+                                        *this,
+                                        that);
+        } else {
+          impl *lhs = this;
+          impl *rhs = lib::addressof(that);
+          if (lhs->move_nothrow() && !rhs->move_nothrow()) {
+            std::swap(lhs, rhs);
+          }
+          impl tmp(lib::move(*rhs));
+#ifdef MPARK_EXCEPTIONS
+          // EXTENSION: When the move construction of `lhs` into `rhs` throws
+          // and `tmp` is nothrow move constructible then we move `tmp` back
+          // into `rhs` and provide the strong exception safety guarantee.
+          try {
+            this->generic_construct(*rhs, lib::move(*lhs));
+          } catch (...) {
+            if (tmp.move_nothrow()) {
+              this->generic_construct(*rhs, lib::move(tmp));
+            }
+            throw;
+          }
+#else
+          this->generic_construct(*rhs, lib::move(*lhs));
+#endif
+          this->generic_construct(*lhs, lib::move(tmp));
+        }
+      }
+
+      private:
+#ifndef MPARK_GENERIC_LAMBDAS
+      struct swapper {
+        template <typename ThisAlt, typename ThatAlt>
+        inline void operator()(ThisAlt &this_alt, ThatAlt &that_alt) const {
+          using std::swap;
+          swap(this_alt.value, that_alt.value);
+        }
+      };
+#endif
+
+      inline constexpr bool move_nothrow() const {
+        return this->valueless_by_exception() ||
+               lib::array<bool, sizeof...(Ts)>{
+                   {std::is_nothrow_move_constructible<Ts>::value...}
+               }[this->index()];
+      }
+    };
+
+#undef MPARK_INHERITING_CTOR
+
+    template <std::size_t I, typename T>
+    struct overload_leaf {
+      using F = lib::size_constant<I> (*)(T);
+      operator F() const { return nullptr; }
+    };
+
+    template <typename... Ts>
+    struct overload_impl {
+      private:
+      template <typename>
+      struct impl;
+
+      template <std::size_t... Is>
+      struct impl<lib::index_sequence<Is...>> : overload_leaf<Is, Ts>... {};
+
+      public:
+      using type = impl<lib::index_sequence_for<Ts...>>;
+    };
+
+    template <typename... Ts>
+    using overload = typename overload_impl<Ts...>::type;
+
+    template <typename T, typename... Ts>
+    using best_match = lib::invoke_result_t<overload<Ts...>, T &&>;
+
+    template <typename T>
+    struct is_in_place_index : std::false_type {};
+
+    template <std::size_t I>
+    struct is_in_place_index<in_place_index_t<I>> : std::true_type {};
+
+    template <typename T>
+    struct is_in_place_type : std::false_type {};
+
+    template <typename T>
+    struct is_in_place_type<in_place_type_t<T>> : std::true_type {};
+
+  }  // detail
+
+  template <typename... Ts>
+  class variant {
+    static_assert(0 < sizeof...(Ts),
+                  "variant must consist of at least one alternative.");
+
+    static_assert(lib::all<!std::is_array<Ts>::value...>::value,
+                  "variant can not have an array type as an alternative.");
+
+    static_assert(lib::all<!std::is_reference<Ts>::value...>::value,
+                  "variant can not have a reference type as an alternative.");
+
+    static_assert(lib::all<!std::is_void<Ts>::value...>::value,
+                  "variant can not have a void type as an alternative.");
+
+    public:
+    template <
+        typename Front = lib::type_pack_element_t<0, Ts...>,
+        lib::enable_if_t<std::is_default_constructible<Front>::value, int> = 0>
+    inline constexpr variant() noexcept(
+        std::is_nothrow_default_constructible<Front>::value)
+        : impl_(in_place_index_t<0>{}) {}
+
+    variant(const variant &) = default;
+    variant(variant &&) = default;
+
+    template <
+        typename Arg,
+        typename Decayed = lib::decay_t<Arg>,
+        lib::enable_if_t<!std::is_same<Decayed, variant>::value, int> = 0,
+        lib::enable_if_t<!detail::is_in_place_index<Decayed>::value, int> = 0,
+        lib::enable_if_t<!detail::is_in_place_type<Decayed>::value, int> = 0,
+        std::size_t I = detail::best_match<Arg, Ts...>::value,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T, Arg>::value, int> = 0>
+    inline constexpr variant(Arg &&arg) noexcept(
+        std::is_nothrow_constructible<T, Arg>::value)
+        : impl_(in_place_index_t<I>{}, lib::forward<Arg>(arg)) {}
+
+    template <
+        std::size_t I,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline explicit constexpr variant(
+        in_place_index_t<I>,
+        Args &&... args) noexcept(std::is_nothrow_constructible<T,
+                                                                Args...>::value)
+        : impl_(in_place_index_t<I>{}, lib::forward<Args>(args)...) {}
+
+    template <
+        std::size_t I,
+        typename Up,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline explicit constexpr variant(
+        in_place_index_t<I>,
+        std::initializer_list<Up> il,
+        Args &&... args) noexcept(std::
+                                      is_nothrow_constructible<
+                                          T,
+                                          std::initializer_list<Up> &,
+                                          Args...>::value)
+        : impl_(in_place_index_t<I>{}, il, lib::forward<Args>(args)...) {}
+
+    template <
+        typename T,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline explicit constexpr variant(
+        in_place_type_t<T>,
+        Args &&... args) noexcept(std::is_nothrow_constructible<T,
+                                                                Args...>::value)
+        : impl_(in_place_index_t<I>{}, lib::forward<Args>(args)...) {}
+
+    template <
+        typename T,
+        typename Up,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline explicit constexpr variant(
+        in_place_type_t<T>,
+        std::initializer_list<Up> il,
+        Args &&... args) noexcept(std::
+                                      is_nothrow_constructible<
+                                          T,
+                                          std::initializer_list<Up> &,
+                                          Args...>::value)
+        : impl_(in_place_index_t<I>{}, il, lib::forward<Args>(args)...) {}
+
+    ~variant() = default;
+
+    variant &operator=(const variant &) = default;
+    variant &operator=(variant &&) = default;
+
+    template <typename Arg,
+              lib::enable_if_t<!std::is_same<lib::decay_t<Arg>, variant>::value,
+                               int> = 0,
+              std::size_t I = detail::best_match<Arg, Ts...>::value,
+              typename T = lib::type_pack_element_t<I, Ts...>,
+              lib::enable_if_t<(std::is_assignable<T &, Arg>::value &&
+                                std::is_constructible<T, Arg>::value),
+                               int> = 0>
+    inline variant &operator=(Arg &&arg) noexcept(
+        (std::is_nothrow_assignable<T &, Arg>::value &&
+         std::is_nothrow_constructible<T, Arg>::value)) {
+      impl_.template assign<I>(lib::forward<Arg>(arg));
+      return *this;
+    }
+
+    template <
+        std::size_t I,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline T &emplace(Args &&... args) {
+      return impl_.template emplace<I>(lib::forward<Args>(args)...);
+    }
+
+    template <
+        std::size_t I,
+        typename Up,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline T &emplace(std::initializer_list<Up> il, Args &&... args) {
+      return impl_.template emplace<I>(il, lib::forward<Args>(args)...);
+    }
+
+    template <
+        typename T,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline T &emplace(Args &&... args) {
+      return impl_.template emplace<I>(lib::forward<Args>(args)...);
+    }
+
+    template <
+        typename T,
+        typename Up,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline T &emplace(std::initializer_list<Up> il, Args &&... args) {
+      return impl_.template emplace<I>(il, lib::forward<Args>(args)...);
+    }
+
+    inline constexpr bool valueless_by_exception() const noexcept {
+      return impl_.valueless_by_exception();
+    }
+
+    inline constexpr std::size_t index() const noexcept {
+      return impl_.index();
+    }
+
+    template <bool Dummy = true,
+              lib::enable_if_t<
+                  lib::all<Dummy,
+                           (lib::dependent_type<std::is_move_constructible<Ts>,
+                                                Dummy>::value &&
+                            lib::dependent_type<lib::is_swappable<Ts>,
+                                                Dummy>::value)...>::value,
+                  int> = 0>
+    inline void swap(variant &that) noexcept(
+        lib::all<(std::is_nothrow_move_constructible<Ts>::value &&
+                  lib::is_nothrow_swappable<Ts>::value)...>::value) {
+      impl_.swap(that.impl_);
+    }
+
+    private:
+    detail::impl<Ts...> impl_;
+
+    friend struct detail::access::variant;
+    friend struct detail::visitation::variant;
+  };
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept {
+    return v.index() == I;
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept {
+    return holds_alternative<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  namespace detail {
+    template <std::size_t I, typename V>
+    struct generic_get_impl {
+      constexpr generic_get_impl(int) noexcept {}
+
+      constexpr AUTO_REFREF operator()(V &&v) const
+        AUTO_REFREF_RETURN(
+            access::variant::get_alt<I>(lib::forward<V>(v)).value)
+    };
+
+    template <std::size_t I, typename V>
+    inline constexpr AUTO_REFREF generic_get(V &&v)
+      AUTO_REFREF_RETURN(generic_get_impl<I, V>(
+          holds_alternative<I>(v) ? 0 : (throw_bad_variant_access(), 0))(
+          lib::forward<V>(v)))
+  }  // namespace detail
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr variant_alternative_t<I, variant<Ts...>> &get(
+      variant<Ts...> &v) {
+    return detail::generic_get<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr variant_alternative_t<I, variant<Ts...>> &&get(
+      variant<Ts...> &&v) {
+    return detail::generic_get<I>(lib::move(v));
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr const variant_alternative_t<I, variant<Ts...>> &get(
+      const variant<Ts...> &v) {
+    return detail::generic_get<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr const variant_alternative_t<I, variant<Ts...>> &&get(
+      const variant<Ts...> &&v) {
+    return detail::generic_get<I>(lib::move(v));
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr T &get(variant<Ts...> &v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr T &&get(variant<Ts...> &&v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(lib::move(v));
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr const T &get(const variant<Ts...> &v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr const T &&get(const variant<Ts...> &&v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(lib::move(v));
+  }
+
+  namespace detail {
+
+    template <std::size_t I, typename V>
+    inline constexpr /* auto * */ AUTO generic_get_if(V *v) noexcept
+      AUTO_RETURN(v && holds_alternative<I>(*v)
+                      ? lib::addressof(access::variant::get_alt<I>(*v).value)
+                      : nullptr)
+
+  }  // namespace detail
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr lib::add_pointer_t<variant_alternative_t<I, variant<Ts...>>>
+  get_if(variant<Ts...> *v) noexcept {
+    return detail::generic_get_if<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr lib::add_pointer_t<
+      const variant_alternative_t<I, variant<Ts...>>>
+  get_if(const variant<Ts...> *v) noexcept {
+    return detail::generic_get_if<I>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr lib::add_pointer_t<T>
+  get_if(variant<Ts...> *v) noexcept {
+    return get_if<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr lib::add_pointer_t<const T>
+  get_if(const variant<Ts...> *v) noexcept {
+    return get_if<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  namespace detail {
+    template <typename RelOp>
+    struct convert_to_bool {
+      template <typename Lhs, typename Rhs>
+      inline constexpr bool operator()(Lhs &&lhs, Rhs &&rhs) const {
+        static_assert(std::is_convertible<lib::invoke_result_t<RelOp, Lhs, Rhs>,
+                                          bool>::value,
+                      "relational operators must return a type"
+                      " implicitly convertible to bool");
+        return lib::invoke(
+            RelOp{}, lib::forward<Lhs>(lhs), lib::forward<Rhs>(rhs));
+      }
+    };
+  }  // namespace detail
+
+  template <typename... Ts>
+  inline constexpr bool operator==(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using equal_to = detail::convert_to_bool<lib::equal_to>;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.index() != rhs.index()) return false;
+    if (lhs.valueless_by_exception()) return true;
+    return variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs);
+#else
+    return lhs.index() == rhs.index() &&
+           (lhs.valueless_by_exception() ||
+            variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator!=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using not_equal_to = detail::convert_to_bool<lib::not_equal_to>;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.index() != rhs.index()) return true;
+    if (lhs.valueless_by_exception()) return false;
+    return variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs);
+#else
+    return lhs.index() != rhs.index() ||
+           (!lhs.valueless_by_exception() &&
+            variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator<(const variant<Ts...> &lhs,
+                                  const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using less = detail::convert_to_bool<lib::less>;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (rhs.valueless_by_exception()) return false;
+    if (lhs.valueless_by_exception()) return true;
+    if (lhs.index() < rhs.index()) return true;
+    if (lhs.index() > rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), less{}, lhs, rhs);
+#else
+    return !rhs.valueless_by_exception() &&
+           (lhs.valueless_by_exception() || lhs.index() < rhs.index() ||
+            (lhs.index() == rhs.index() &&
+             variant::visit_value_at(lhs.index(), less{}, lhs, rhs)));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator>(const variant<Ts...> &lhs,
+                                  const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using greater = detail::convert_to_bool<lib::greater>;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.valueless_by_exception()) return false;
+    if (rhs.valueless_by_exception()) return true;
+    if (lhs.index() > rhs.index()) return true;
+    if (lhs.index() < rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), greater{}, lhs, rhs);
+#else
+    return !lhs.valueless_by_exception() &&
+           (rhs.valueless_by_exception() || lhs.index() > rhs.index() ||
+            (lhs.index() == rhs.index() &&
+             variant::visit_value_at(lhs.index(), greater{}, lhs, rhs)));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator<=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using less_equal = detail::convert_to_bool<lib::less_equal>;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.valueless_by_exception()) return true;
+    if (rhs.valueless_by_exception()) return false;
+    if (lhs.index() < rhs.index()) return true;
+    if (lhs.index() > rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs);
+#else
+    return lhs.valueless_by_exception() ||
+           (!rhs.valueless_by_exception() &&
+            (lhs.index() < rhs.index() ||
+             (lhs.index() == rhs.index() &&
+              variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs))));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator>=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using greater_equal = detail::convert_to_bool<lib::greater_equal>;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (rhs.valueless_by_exception()) return true;
+    if (lhs.valueless_by_exception()) return false;
+    if (lhs.index() > rhs.index()) return true;
+    if (lhs.index() < rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), greater_equal{}, lhs, rhs);
+#else
+    return rhs.valueless_by_exception() ||
+           (!lhs.valueless_by_exception() &&
+            (lhs.index() > rhs.index() ||
+             (lhs.index() == rhs.index() &&
+              variant::visit_value_at(
+                  lhs.index(), greater_equal{}, lhs, rhs))));
+#endif
+  }
+
+  struct monostate {};
+
+  inline constexpr bool operator<(monostate, monostate) noexcept {
+    return false;
+  }
+
+  inline constexpr bool operator>(monostate, monostate) noexcept {
+    return false;
+  }
+
+  inline constexpr bool operator<=(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator>=(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator==(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator!=(monostate, monostate) noexcept {
+    return false;
+  }
+
+#ifdef MPARK_CPP14_CONSTEXPR
+  namespace detail {
+
+    inline constexpr bool all(std::initializer_list<bool> bs) {
+      for (bool b : bs) {
+        if (!b) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+  }  // namespace detail
+
+  template <typename Visitor, typename... Vs>
+  inline constexpr decltype(auto) visit(Visitor &&visitor, Vs &&... vs) {
+    return (detail::all({!vs.valueless_by_exception()...})
+                ? (void)0
+                : throw_bad_variant_access()),
+           detail::visitation::variant::visit_value(
+               lib::forward<Visitor>(visitor), lib::forward<Vs>(vs)...);
+  }
+#else
+  namespace detail {
+
+    template <std::size_t N>
+    inline constexpr bool all_impl(const lib::array<bool, N> &bs,
+                                   std::size_t idx) {
+      return idx >= N || (bs[idx] && all_impl(bs, idx + 1));
+    }
+
+    template <std::size_t N>
+    inline constexpr bool all(const lib::array<bool, N> &bs) {
+      return all_impl(bs, 0);
+    }
+
+  }  // namespace detail
+
+  template <typename Visitor, typename... Vs>
+  inline constexpr DECLTYPE_AUTO visit(Visitor &&visitor, Vs &&... vs)
+    DECLTYPE_AUTO_RETURN(
+        (detail::all(
+             lib::array<bool, sizeof...(Vs)>{{!vs.valueless_by_exception()...}})
+             ? (void)0
+             : throw_bad_variant_access()),
+        detail::visitation::variant::visit_value(lib::forward<Visitor>(visitor),
+                                                 lib::forward<Vs>(vs)...))
+#endif
+
+  template <typename... Ts>
+  inline auto swap(variant<Ts...> &lhs,
+                   variant<Ts...> &rhs) noexcept(noexcept(lhs.swap(rhs)))
+      -> decltype(lhs.swap(rhs)) {
+    lhs.swap(rhs);
+  }
+
+  namespace detail {
+
+    template <typename T, typename...>
+    using enabled_type = T;
+
+    namespace hash {
+
+      template <typename H, typename K>
+      constexpr bool meets_requirements() noexcept {
+        return std::is_copy_constructible<H>::value &&
+               std::is_move_constructible<H>::value &&
+               lib::is_invocable_r<std::size_t, H, const K &>::value;
+      }
+
+      template <typename K>
+      constexpr bool is_enabled() noexcept {
+        using H = std::hash<K>;
+        return meets_requirements<H, K>() &&
+               std::is_default_constructible<H>::value &&
+               std::is_copy_assignable<H>::value &&
+               std::is_move_assignable<H>::value;
+      }
+
+    }  // namespace hash
+
+  }  // namespace detail
+
+#undef AUTO
+#undef AUTO_RETURN
+
+#undef AUTO_REFREF
+#undef AUTO_REFREF_RETURN
+
+#undef DECLTYPE_AUTO
+#undef DECLTYPE_AUTO_RETURN
+
+}  // namespace mpark
+
+namespace std {
+
+  template <typename... Ts>
+  struct hash<mpark::detail::enabled_type<
+      mpark::variant<Ts...>,
+      mpark::lib::enable_if_t<mpark::lib::all<mpark::detail::hash::is_enabled<
+          mpark::lib::remove_const_t<Ts>>()...>::value>>> {
+    using argument_type = mpark::variant<Ts...>;
+    using result_type = std::size_t;
+
+    inline result_type operator()(const argument_type &v) const {
+      using mpark::detail::visitation::variant;
+      std::size_t result =
+          v.valueless_by_exception()
+              ? 299792458  // Random value chosen by the universe upon creation
+              : variant::visit_alt(
+#ifdef MPARK_GENERIC_LAMBDAS
+                    [](const auto &alt) {
+                      using alt_type = mpark::lib::decay_t<decltype(alt)>;
+                      using value_type = mpark::lib::remove_const_t<
+                          typename alt_type::value_type>;
+                      return hash<value_type>{}(alt.value);
+                    }
+#else
+                    hasher{}
+#endif
+                    ,
+                    v);
+      return hash_combine(result, hash<std::size_t>{}(v.index()));
+    }
+
+    private:
+#ifndef MPARK_GENERIC_LAMBDAS
+    struct hasher {
+      template <typename Alt>
+      inline std::size_t operator()(const Alt &alt) const {
+        using alt_type = mpark::lib::decay_t<Alt>;
+        using value_type =
+            mpark::lib::remove_const_t<typename alt_type::value_type>;
+        return hash<value_type>{}(alt.value);
+      }
+    };
+#endif
+
+    static std::size_t hash_combine(std::size_t lhs, std::size_t rhs) {
+      return lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
+    }
+  };
+
+  template <>
+  struct hash<mpark::monostate> {
+    using argument_type = mpark::monostate;
+    using result_type = std::size_t;
+
+    inline result_type operator()(const argument_type &) const noexcept {
+      return 66740831;  // return a fundamentally attractive random value.
+    }
+  };
+
+}  // namespace std
+
+#endif  // MPARK_VARIANT_HPP

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor