Bug 1303443 - Update ANGLE to chromium/2862. r=jgilbert
authorEthan Lin <ethlin@mozilla.com>
Tue, 25 Oct 2016 19:29:00 -0400
changeset 319902 57c5a60badc7073ec99c09b1e6468fde23a87b15
parent 319901 c1ef258c04f1960f6c014394f371fc0d3068ccb8
child 319903 7d22628f37cf9269c288875693c788e7b5b81bc4
push id20749
push userryanvm@gmail.com
push dateSat, 29 Oct 2016 13:21:21 +0000
treeherderfx-team@1b170b39ed6b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgilbert
bugs1303443
milestone52.0a1
Bug 1303443 - Update ANGLE to chromium/2862. r=jgilbert * * * Bug 1303443 - Change option parameters for angle update.
dom/canvas/WebGLShaderValidator.cpp
gfx/angle/BUILD.gn
gfx/angle/include/GLSLANG/ShaderLang.h
gfx/angle/include/export.h
gfx/angle/moz.build
gfx/angle/src/commit.h
gfx/angle/src/compiler.gypi
gfx/angle/src/compiler/translator/AddAndTrueToLoopCondition.cpp
gfx/angle/src/compiler/translator/AddAndTrueToLoopCondition.h
gfx/angle/src/compiler/translator/AddDefaultReturnStatements.cpp
gfx/angle/src/compiler/translator/ArrayReturnValueToOutParameter.cpp
gfx/angle/src/compiler/translator/BaseTypes.h
gfx/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp
gfx/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.h
gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp
gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h
gfx/angle/src/compiler/translator/Compiler.cpp
gfx/angle/src/compiler/translator/Compiler.h
gfx/angle/src/compiler/translator/ConstantUnion.cpp
gfx/angle/src/compiler/translator/ConstantUnion.h
gfx/angle/src/compiler/translator/DeferGlobalInitializers.cpp
gfx/angle/src/compiler/translator/EmulateGLFragColorBroadcast.cpp
gfx/angle/src/compiler/translator/ExpandIntegerPowExpressions.cpp
gfx/angle/src/compiler/translator/Initialize.cpp
gfx/angle/src/compiler/translator/InitializeVariables.cpp
gfx/angle/src/compiler/translator/InitializeVariables.h
gfx/angle/src/compiler/translator/IntermNode.cpp
gfx/angle/src/compiler/translator/IntermNode.h
gfx/angle/src/compiler/translator/IntermNodePatternMatcher.cpp
gfx/angle/src/compiler/translator/IntermNodePatternMatcher.h
gfx/angle/src/compiler/translator/IntermTraverse.cpp
gfx/angle/src/compiler/translator/Intermediate.cpp
gfx/angle/src/compiler/translator/Intermediate.h
gfx/angle/src/compiler/translator/OutputGLSLBase.cpp
gfx/angle/src/compiler/translator/OutputGLSLBase.h
gfx/angle/src/compiler/translator/OutputHLSL.cpp
gfx/angle/src/compiler/translator/OutputHLSL.h
gfx/angle/src/compiler/translator/ParseContext.cpp
gfx/angle/src/compiler/translator/ParseContext.h
gfx/angle/src/compiler/translator/QualifierTypes.cpp
gfx/angle/src/compiler/translator/QualifierTypes.h
gfx/angle/src/compiler/translator/RemoveDynamicIndexing.cpp
gfx/angle/src/compiler/translator/RemovePow.cpp
gfx/angle/src/compiler/translator/RemoveSwitchFallThrough.cpp
gfx/angle/src/compiler/translator/RemoveSwitchFallThrough.h
gfx/angle/src/compiler/translator/RenameFunction.h
gfx/angle/src/compiler/translator/RewriteDoWhile.cpp
gfx/angle/src/compiler/translator/RewriteElseBlocks.cpp
gfx/angle/src/compiler/translator/RewriteTexelFetchOffset.cpp
gfx/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp
gfx/angle/src/compiler/translator/SeparateArrayInitialization.cpp
gfx/angle/src/compiler/translator/SeparateExpressionsReturningArrays.cpp
gfx/angle/src/compiler/translator/ShaderLang.cpp
gfx/angle/src/compiler/translator/SimplifyLoopConditions.cpp
gfx/angle/src/compiler/translator/SplitSequenceOperator.cpp
gfx/angle/src/compiler/translator/SymbolTable.h
gfx/angle/src/compiler/translator/TranslatorESSL.cpp
gfx/angle/src/compiler/translator/TranslatorESSL.h
gfx/angle/src/compiler/translator/TranslatorGLSL.cpp
gfx/angle/src/compiler/translator/TranslatorGLSL.h
gfx/angle/src/compiler/translator/TranslatorHLSL.cpp
gfx/angle/src/compiler/translator/TranslatorHLSL.h
gfx/angle/src/compiler/translator/Types.cpp
gfx/angle/src/compiler/translator/Types.h
gfx/angle/src/compiler/translator/UnfoldShortCircuitAST.cpp
gfx/angle/src/compiler/translator/UnfoldShortCircuitToIf.cpp
gfx/angle/src/compiler/translator/ValidateSwitch.cpp
gfx/angle/src/compiler/translator/ValidateSwitch.h
gfx/angle/src/compiler/translator/depgraph/DependencyGraph.cpp
gfx/angle/src/compiler/translator/depgraph/DependencyGraph.h
gfx/angle/src/compiler/translator/depgraph/DependencyGraphBuilder.cpp
gfx/angle/src/compiler/translator/depgraph/DependencyGraphBuilder.h
gfx/angle/src/compiler/translator/depgraph/DependencyGraphOutput.cpp
gfx/angle/src/compiler/translator/depgraph/DependencyGraphOutput.h
gfx/angle/src/compiler/translator/depgraph/DependencyGraphTraverse.cpp
gfx/angle/src/compiler/translator/glslang.y
gfx/angle/src/compiler/translator/glslang_tab.cpp
gfx/angle/src/compiler/translator/glslang_tab.h
gfx/angle/src/compiler/translator/intermOut.cpp
gfx/angle/src/compiler/translator/timing/RestrictFragmentShaderTiming.cpp
gfx/angle/src/compiler/translator/timing/RestrictFragmentShaderTiming.h
gfx/angle/src/compiler/translator/timing/RestrictVertexShaderTiming.cpp
gfx/angle/src/compiler/translator/timing/RestrictVertexShaderTiming.h
gfx/angle/src/compiler/translator/util.cpp
gfx/angle/src/compiler/translator/util.h
gfx/angle/src/libANGLE/Buffer.cpp
gfx/angle/src/libANGLE/Buffer.h
gfx/angle/src/libANGLE/Context.cpp
gfx/angle/src/libANGLE/Context.h
gfx/angle/src/libANGLE/ImageIndex.cpp
gfx/angle/src/libANGLE/ImageIndex.h
gfx/angle/src/libANGLE/ImageIndexIterator_unittest.cpp
gfx/angle/src/libANGLE/Shader.cpp
gfx/angle/src/libANGLE/formatutils.cpp
gfx/angle/src/libANGLE/moz.build
gfx/angle/src/libANGLE/queryconversions.cpp
gfx/angle/src/libANGLE/queryconversions.h
gfx/angle/src/libANGLE/renderer/BufferImpl.h
gfx/angle/src/libANGLE/renderer/BufferImpl_mock.h
gfx/angle/src/libANGLE/renderer/ShaderImpl.h
gfx/angle/src/libANGLE/renderer/d3d/HLSLCompiler.cpp
gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.cpp
gfx/angle/src/libANGLE/renderer/d3d/ShaderD3D.cpp
gfx/angle/src/libANGLE/renderer/d3d/ShaderD3D.h
gfx/angle/src/libANGLE/renderer/d3d/WorkaroundsD3D.h
gfx/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h
gfx/angle/src/libANGLE/renderer/d3d/d3d11/Clear11.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.h
gfx/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.h
gfx/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d9/Buffer9.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d9/Buffer9.h
gfx/angle/src/libANGLE/renderer/gl/BufferGL.cpp
gfx/angle/src/libANGLE/renderer/gl/BufferGL.h
gfx/angle/src/libANGLE/renderer/gl/RendererGL.cpp
gfx/angle/src/libANGLE/renderer/gl/ShaderGL.cpp
gfx/angle/src/libANGLE/renderer/gl/ShaderGL.h
gfx/angle/src/libANGLE/renderer/gl/StateManagerGL.cpp
gfx/angle/src/libANGLE/renderer/gl/StateManagerGL.h
gfx/angle/src/libANGLE/renderer/gl/WorkaroundsGL.h
gfx/angle/src/libANGLE/renderer/gl/renderergl_utils.cpp
gfx/angle/src/libANGLE/renderer/vulkan/BufferVk.cpp
gfx/angle/src/libANGLE/renderer/vulkan/BufferVk.h
gfx/angle/src/libANGLE/renderer/vulkan/ShaderVk.cpp
gfx/angle/src/libANGLE/renderer/vulkan/ShaderVk.h
gfx/angle/src/libANGLE/validationES.cpp
gfx/angle/src/libANGLE/validationES.h
gfx/angle/src/libANGLE/validationES2.cpp
gfx/angle/src/libANGLE/validationES2.h
gfx/angle/src/libGLESv2/entry_points_gles_2_0.cpp
gfx/angle/src/tests/BUILD.gn
gfx/angle/src/tests/angle_unittests.gypi
gfx/angle/src/tests/compiler_tests/BuiltInFunctionEmulator_test.cpp
gfx/angle/src/tests/compiler_tests/ExpressionLimit_test.cpp
gfx/angle/src/tests/compiler_tests/IntermNode_test.cpp
gfx/angle/src/tests/compiler_tests/MalformedShader_test.cpp
gfx/angle/src/tests/compiler_tests/QualificationOrderESSL31_test.cpp
gfx/angle/src/tests/compiler_tests/QualificationOrder_test.cpp
gfx/angle/src/tests/deqp.gypi
gfx/angle/src/tests/deqp_support/angle_deqp_libtester_main.cpp
gfx/angle/src/tests/deqp_support/deqp_gles2_test_expectations.txt
gfx/angle/src/tests/deqp_support/deqp_gles3_test_expectations.txt
gfx/angle/src/tests/gl_tests/D3D11EmulatedIndexedBufferTest.cpp
gfx/angle/src/tests/gl_tests/GLSLTest.cpp
gfx/angle/src/tests/gl_tests/SwizzleTest.cpp
gfx/angle/src/tests/gl_tests/TextureTest.cpp
gfx/angle/src/tests/gl_tests/UniformBufferTest.cpp
gfx/angle/src/tests/perf_tests/IndexDataManagerTest.cpp
gfx/angle/src/tests/test_utils/ANGLETest.h
gfx/angle/src/tests/test_utils/compiler_test.cpp
gfx/angle/src/tests/test_utils/compiler_test.h
gfx/angle/src/tests/third_party/gpu_test_expectations/HowToMakeChanges.md
gfx/angle/src/tests/third_party/gpu_test_expectations/angle-mods.patch
gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_test_config.cc
--- a/dom/canvas/WebGLShaderValidator.cpp
+++ b/dom/canvas/WebGLShaderValidator.cpp
@@ -29,17 +29,16 @@ IdentifierHashFunc(const char* name, siz
 }
 
 static int
 ChooseValidatorCompileOptions(const ShBuiltInResources& resources,
                               const mozilla::gl::GLContext* gl)
 {
     int options = SH_VARIABLES |
                   SH_ENFORCE_PACKING_RESTRICTIONS |
-                  SH_INIT_VARYINGS_WITHOUT_STATIC_USE |
                   SH_OBJECT_CODE |
                   SH_LIMIT_CALL_STACK_DEPTH |
                   SH_INIT_GL_POSITION;
 
     if (resources.MaxExpressionComplexity > 0) {
         options |= SH_LIMIT_EXPRESSION_COMPLEXITY;
     }
 
@@ -49,48 +48,39 @@ ChooseValidatorCompileOptions(const ShBu
     // Just do it universally.
     options |= SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX;
 
     if (gfxPrefs::WebGLAllANGLEOptions()) {
         return options |
                SH_VALIDATE_LOOP_INDEXING |
                SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX |
                SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX |
-               SH_EMULATE_BUILT_IN_FUNCTIONS |
                SH_CLAMP_INDIRECT_ARRAY_BOUNDS |
                SH_UNFOLD_SHORT_CIRCUIT |
                SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS |
+               SH_INIT_OUTPUT_VARIABLES |
                SH_REGENERATE_STRUCT_NAMES;
     }
 
 #ifndef XP_MACOSX
     // We want to do this everywhere, but to do this on Mac, we need
     // to do it only on Mac OSX > 10.6 as this causes the shader
     // compiler in 10.6 to crash
     options |= SH_CLAMP_INDIRECT_ARRAY_BOUNDS;
 #endif
 
 #ifdef XP_MACOSX
     if (gl->WorkAroundDriverBugs()) {
         // Work around https://bugs.webkit.org/show_bug.cgi?id=124684,
         // https://chromium.googlesource.com/angle/angle/+/5e70cf9d0b1bb
         options |= SH_UNFOLD_SHORT_CIRCUIT;
 
-        // Work around bug 665578 and bug 769810
-        if (gl->Vendor() == gl::GLVendor::ATI) {
-            options |= SH_EMULATE_BUILT_IN_FUNCTIONS;
-        }
-
-        // Work around bug 735560
-        if (gl->Vendor() == gl::GLVendor::Intel) {
-            options |= SH_EMULATE_BUILT_IN_FUNCTIONS;
-        }
-
         // Work around that Mac drivers handle struct scopes incorrectly.
         options |= SH_REGENERATE_STRUCT_NAMES;
+        options |= SH_INIT_OUTPUT_VARIABLES;
     }
 #endif
 
     return options;
 }
 
 } // namespace webgl
 
--- a/gfx/angle/BUILD.gn
+++ b/gfx/angle/BUILD.gn
@@ -492,33 +492,37 @@ util_gypi = exec_script("//build/gypi_to
 
 config("angle_util_config") {
   include_dirs = [ "util" ]
   if (is_linux && use_x11) {
     libs = [ "X11" ]
   }
 }
 
-static_library("angle_util") {
+shared_library("angle_util") {
   sources = rebase_path(util_gypi.util_sources, ".", "util")
 
   if (is_win) {
     sources += rebase_path(util_gypi.util_win32_sources, ".", "util")
   }
 
   if (is_linux) {
     sources += rebase_path(util_gypi.util_linux_sources, ".", "util")
     libs = [
       "rt",
       "dl",
     ]
   }
 
   if (is_mac) {
     sources += rebase_path(util_gypi.util_osx_sources, ".", "util")
+    libs = [
+      "AppKit.framework",
+      "QuartzCore.framework",
+    ]
   }
 
   if (use_x11) {
     sources += rebase_path(util_gypi.util_x11_sources, ".", "util")
   }
 
   if (is_android) {
     # To prevent linux sources filtering on android
@@ -533,26 +537,34 @@ static_library("angle_util") {
 
   if (use_ozone) {
     sources += rebase_path(util_gypi.util_ozone_sources, ".", "util")
   }
 
   defines = [
     "GL_GLEXT_PROTOTYPES",
     "EGL_EGLEXT_PROTOTYPES",
+    "LIBANGLE_UTIL_IMPLEMENTATION",
   ]
 
   configs += [
     ":debug_annotations_config",
     ":extra_warnings",
   ]
 
   public_configs = [
     ":angle_util_config",
     ":internal_config",
   ]
+  if (is_mac && !is_component_build) {
+    ldflags = [
+      "-install_name",
+      "@rpath/lib${target_name}.dylib",
+    ]
+    public_configs += [ ":shared_library_public_config" ]
+  }
 
   deps = [
     ":angle_common",
     ":libEGL",
     ":libGLESv2",
   ]
 }
--- a/gfx/angle/include/GLSLANG/ShaderLang.h
+++ b/gfx/angle/include/GLSLANG/ShaderLang.h
@@ -44,49 +44,28 @@ typedef unsigned int GLenum;
 }
 
 // Must be included after GLenum proxy typedef
 // Note: make sure to increment ANGLE_SH_VERSION when changing ShaderVars.h
 #include "ShaderVars.h"
 
 // Version number for shader translation API.
 // It is incremented every time the API changes.
-#define ANGLE_SH_VERSION 155
+#define ANGLE_SH_VERSION 161
 
 typedef enum {
     SH_GLES2_SPEC,
     SH_WEBGL_SPEC,
 
     SH_GLES3_SPEC,
     SH_WEBGL2_SPEC,
 
     SH_GLES3_1_SPEC,
     SH_WEBGL3_SPEC,
 
-    // The CSS Shaders spec is a subset of the WebGL spec.
-    //
-    // In both CSS vertex and fragment shaders, ANGLE:
-    // (1) Reserves the "css_" prefix.
-    // (2) Renames the main function to css_main.
-    // (3) Disables the gl_MaxDrawBuffers built-in.
-    //
-    // In CSS fragment shaders, ANGLE:
-    // (1) Disables the gl_FragColor built-in.
-    // (2) Disables the gl_FragData built-in.
-    // (3) Enables the css_MixColor built-in.
-    // (4) Enables the css_ColorMatrix built-in.
-    //
-    // After passing a CSS shader through ANGLE, the browser is expected to append
-    // a new main function to it.
-    // This new main function will call the css_main function.
-    // It may also perform additional operations like varying assignment, texture
-    // access, and gl_FragColor assignment in order to implement the CSS Shaders
-    // blend modes.
-    //
-    SH_CSS_SHADERS_SPEC
 } ShShaderSpec;
 
 typedef enum
 {
     // ESSL output only supported in some configurations.
     SH_ESSL_OUTPUT = 0x8B45,
 
     // GLSL output only supported in some configurations.
@@ -111,132 +90,119 @@ typedef enum
 
     // Prefer using these to specify HLSL output type:
     SH_HLSL_3_0_OUTPUT       = 0x8B48,  // D3D 9
     SH_HLSL_4_1_OUTPUT       = 0x8B49,  // D3D 11
     SH_HLSL_4_0_FL9_3_OUTPUT = 0x8B4A   // D3D 11 feature level 9_3
 } ShShaderOutput;
 
 // Compile options.
-typedef enum {
-    SH_VALIDATE                           = 0,
-    SH_VALIDATE_LOOP_INDEXING             = 0x0001,
-    SH_INTERMEDIATE_TREE                  = 0x0002,
-    SH_OBJECT_CODE                        = 0x0004,
-    SH_VARIABLES                          = 0x0008,
-    SH_LINE_DIRECTIVES                    = 0x0010,
-    SH_SOURCE_PATH                        = 0x0020,
-    SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX = 0x0040,
-    // If a sampler array index happens to be a loop index,
-    //   1) if its type is integer, unroll the loop.
-    //   2) if its type is float, fail the shader compile.
-    // This is to work around a mac driver bug.
-    SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX = 0x0080,
+typedef uint64_t ShCompileOptions;
 
-    // This is needed only as a workaround for certain OpenGL driver bugs.
-    SH_EMULATE_BUILT_IN_FUNCTIONS = 0x0100,
+const ShCompileOptions SH_VALIDATE                           = 0;
+const ShCompileOptions SH_VALIDATE_LOOP_INDEXING             = UINT64_C(1) << 0;
+const ShCompileOptions SH_INTERMEDIATE_TREE                  = UINT64_C(1) << 1;
+const ShCompileOptions SH_OBJECT_CODE                        = UINT64_C(1) << 2;
+const ShCompileOptions SH_VARIABLES                          = UINT64_C(1) << 3;
+const ShCompileOptions SH_LINE_DIRECTIVES                    = UINT64_C(1) << 4;
+const ShCompileOptions SH_SOURCE_PATH                        = UINT64_C(1) << 5;
+const ShCompileOptions SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX = UINT64_C(1) << 6;
+// If a sampler array index happens to be a loop index,
+//   1) if its type is integer, unroll the loop.
+//   2) if its type is float, fail the shader compile.
+// This is to work around a mac driver bug.
+const ShCompileOptions SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX = UINT64_C(1) << 7;
 
-    // This is an experimental flag to enforce restrictions that aim to prevent
-    // timing attacks.
-    // It generates compilation errors for shaders that could expose sensitive
-    // texture information via the timing channel.
-    // To use this flag, you must compile the shader under the WebGL spec
-    // (using the SH_WEBGL_SPEC flag).
-    SH_TIMING_RESTRICTIONS = 0x0200,
+// This flag works around bug in Intel Mac drivers related to abs(i) where
+// i is an integer.
+const ShCompileOptions SH_EMULATE_ABS_INT_FUNCTION = UINT64_C(1) << 8;
 
-    // This flag prints the dependency graph that is used to enforce timing
-    // restrictions on fragment shaders.
-    // This flag only has an effect if all of the following are true:
-    // - The shader spec is SH_WEBGL_SPEC.
-    // - The compile options contain the SH_TIMING_RESTRICTIONS flag.
-    // - The shader type is GL_FRAGMENT_SHADER.
-    SH_DEPENDENCY_GRAPH = 0x0400,
+// Enforce the GLSL 1.017 Appendix A section 7 packing restrictions.
+// This flag only enforces (and can only enforce) the packing
+// restrictions for uniform variables in both vertex and fragment
+// shaders. ShCheckVariablesWithinPackingLimits() lets embedders
+// enforce the packing restrictions for varying variables during
+// program link time.
+const ShCompileOptions SH_ENFORCE_PACKING_RESTRICTIONS = UINT64_C(1) << 9;
 
-    // Enforce the GLSL 1.017 Appendix A section 7 packing restrictions.
-    // This flag only enforces (and can only enforce) the packing
-    // restrictions for uniform variables in both vertex and fragment
-    // shaders. ShCheckVariablesWithinPackingLimits() lets embedders
-    // enforce the packing restrictions for varying variables during
-    // program link time.
-    SH_ENFORCE_PACKING_RESTRICTIONS = 0x0800,
+// This flag ensures all indirect (expression-based) array indexing
+// is clamped to the bounds of the array. This ensures, for example,
+// that you cannot read off the end of a uniform, whether an array
+// vec234, or mat234 type. The ShArrayIndexClampingStrategy enum,
+// specified in the ShBuiltInResources when constructing the
+// compiler, selects the strategy for the clamping implementation.
+const ShCompileOptions SH_CLAMP_INDIRECT_ARRAY_BOUNDS = UINT64_C(1) << 10;
 
-    // This flag ensures all indirect (expression-based) array indexing
-    // is clamped to the bounds of the array. This ensures, for example,
-    // that you cannot read off the end of a uniform, whether an array
-    // vec234, or mat234 type. The ShArrayIndexClampingStrategy enum,
-    // specified in the ShBuiltInResources when constructing the
-    // compiler, selects the strategy for the clamping implementation.
-    SH_CLAMP_INDIRECT_ARRAY_BOUNDS = 0x1000,
+// This flag limits the complexity of an expression.
+const ShCompileOptions SH_LIMIT_EXPRESSION_COMPLEXITY = UINT64_C(1) << 11;
+
+// This flag limits the depth of the call stack.
+const ShCompileOptions SH_LIMIT_CALL_STACK_DEPTH = UINT64_C(1) << 12;
 
-    // This flag limits the complexity of an expression.
-    SH_LIMIT_EXPRESSION_COMPLEXITY = 0x2000,
-
-    // This flag limits the depth of the call stack.
-    SH_LIMIT_CALL_STACK_DEPTH = 0x4000,
+// This flag initializes gl_Position to vec4(0,0,0,0) at the
+// beginning of the vertex shader's main(), and has no effect in the
+// fragment shader. It is intended as a workaround for drivers which
+// incorrectly fail to link programs if gl_Position is not written.
+const ShCompileOptions SH_INIT_GL_POSITION = UINT64_C(1) << 13;
 
-    // This flag initializes gl_Position to vec4(0,0,0,0) at the
-    // beginning of the vertex shader's main(), and has no effect in the
-    // fragment shader. It is intended as a workaround for drivers which
-    // incorrectly fail to link programs if gl_Position is not written.
-    SH_INIT_GL_POSITION = 0x8000,
+// This flag replaces
+//   "a && b" with "a ? b : false",
+//   "a || b" with "a ? true : b".
+// This is to work around a MacOSX driver bug that |b| is executed
+// independent of |a|'s value.
+const ShCompileOptions SH_UNFOLD_SHORT_CIRCUIT = UINT64_C(1) << 14;
 
-    // This flag replaces
-    //   "a && b" with "a ? b : false",
-    //   "a || b" with "a ? true : b".
-    // This is to work around a MacOSX driver bug that |b| is executed
-    // independent of |a|'s value.
-    SH_UNFOLD_SHORT_CIRCUIT = 0x10000,
+// This flag initializes output variables to 0 at the beginning of main().
+// It is to avoid undefined behaviors.
+const ShCompileOptions SH_INIT_OUTPUT_VARIABLES = UINT64_C(1) << 15;
 
-    // This flag initializes output variables to 0 at the beginning of main().
-    // It is to avoid undefined behaviors.
-    SH_INIT_OUTPUT_VARIABLES = 0x20000,
-    // TODO(zmo): obsolete, remove after ANGLE roll into Chromium.
-    SH_INIT_VARYINGS_WITHOUT_STATIC_USE = 0x20000,
+// This flag scalarizes vec/ivec/bvec/mat constructor args.
+// It is intended as a workaround for Linux/Mac driver bugs.
+const ShCompileOptions SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS = UINT64_C(1) << 16;
 
-    // This flag scalarizes vec/ivec/bvec/mat constructor args.
-    // It is intended as a workaround for Linux/Mac driver bugs.
-    SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS = 0x40000,
+// This flag overwrites a struct name with a unique prefix.
+// It is intended as a workaround for drivers that do not handle
+// struct scopes correctly, including all Mac drivers and Linux AMD.
+const ShCompileOptions SH_REGENERATE_STRUCT_NAMES = UINT64_C(1) << 17;
 
-    // This flag overwrites a struct name with a unique prefix.
-    // It is intended as a workaround for drivers that do not handle
-    // struct scopes correctly, including all Mac drivers and Linux AMD.
-    SH_REGENERATE_STRUCT_NAMES = 0x80000,
+// This flag makes the compiler not prune unused function early in the
+// compilation process. Pruning coupled with SH_LIMIT_CALL_STACK_DEPTH
+// helps avoid bad shaders causing stack overflows.
+const ShCompileOptions SH_DONT_PRUNE_UNUSED_FUNCTIONS = UINT64_C(1) << 18;
 
-    // This flag makes the compiler not prune unused function early in the
-    // compilation process. Pruning coupled with SH_LIMIT_CALL_STACK_DEPTH
-    // helps avoid bad shaders causing stack overflows.
-    SH_DONT_PRUNE_UNUSED_FUNCTIONS = 0x100000,
+// This flag works around a bug in NVIDIA 331 series drivers related
+// to pow(x, y) where y is a constant vector.
+const ShCompileOptions SH_REMOVE_POW_WITH_CONSTANT_EXPONENT = UINT64_C(1) << 19;
 
-    // This flag works around a bug in NVIDIA 331 series drivers related
-    // to pow(x, y) where y is a constant vector.
-    SH_REMOVE_POW_WITH_CONSTANT_EXPONENT = 0x200000,
+// This flag works around bugs in Mac drivers related to do-while by
+// transforming them into an other construct.
+const ShCompileOptions SH_REWRITE_DO_WHILE_LOOPS = UINT64_C(1) << 20;
 
-    // This flag works around bugs in Mac drivers related to do-while by
-    // transforming them into an other construct.
-    SH_REWRITE_DO_WHILE_LOOPS = 0x400000,
+// This flag works around a bug in the HLSL compiler optimizer that folds certain
+// constant pow expressions incorrectly. Only applies to the HLSL back-end. It works
+// by expanding the integer pow expressions into a series of multiplies.
+const ShCompileOptions SH_EXPAND_SELECT_HLSL_INTEGER_POW_EXPRESSIONS = UINT64_C(1) << 21;
 
-    // This flag works around a bug in the HLSL compiler optimizer that folds certain
-    // constant pow expressions incorrectly. Only applies to the HLSL back-end. It works
-    // by expanding the integer pow expressions into a series of multiplies.
-    SH_EXPAND_SELECT_HLSL_INTEGER_POW_EXPRESSIONS = 0x800000,
-
-    // Flatten "#pragma STDGL invariant(all)" into the declarations of
-    // varying variables and built-in GLSL variables. This compiler
-    // option is enabled automatically when needed.
-    SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL = 0x1000000,
+// Flatten "#pragma STDGL invariant(all)" into the declarations of
+// varying variables and built-in GLSL variables. This compiler
+// option is enabled automatically when needed.
+const ShCompileOptions SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL = UINT64_C(1) << 22;
 
-    // Some drivers do not take into account the base level of the texture in the results of the
-    // HLSL GetDimensions builtin.  This flag instructs the compiler to manually add the base level
-    // offsetting.
-    SH_HLSL_GET_DIMENSIONS_IGNORES_BASE_LEVEL = 0x2000000,
+// Some drivers do not take into account the base level of the texture in the results of the
+// HLSL GetDimensions builtin.  This flag instructs the compiler to manually add the base level
+// offsetting.
+const ShCompileOptions SH_HLSL_GET_DIMENSIONS_IGNORES_BASE_LEVEL = UINT64_C(1) << 23;
 
-    // This flag works around an issue in translating GLSL function texelFetchOffset on
-    // INTEL drivers. It works by translating texelFetchOffset into texelFetch.
-    SH_REWRITE_TEXELFETCHOFFSET_TO_TEXELFETCH = 0x4000000,
-} ShCompileOptions;
+// This flag works around an issue in translating GLSL function texelFetchOffset on
+// INTEL drivers. It works by translating texelFetchOffset into texelFetch.
+const ShCompileOptions SH_REWRITE_TEXELFETCHOFFSET_TO_TEXELFETCH = UINT64_C(1) << 24;
+
+// This flag works around condition bug of for and while loops in Intel Mac OSX drivers.
+// Condition calculation is not correct. Rewrite it from "CONDITION" to "CONDITION && true".
+const ShCompileOptions SH_ADD_AND_TRUE_TO_LOOP_CONDITION = UINT64_C(1) << 25;
 
 // Defines alternate strategies for implementing array index clamping.
 typedef enum {
   // Use the clamp intrinsic for array index clamping.
   SH_CLAMP_WITH_CLAMP_INTRINSIC = 1,
 
   // Use a user-defined function for array index clamping.
   SH_CLAMP_WITH_USER_DEFINED_INT_CLAMP_FUNCTION
@@ -460,21 +426,20 @@ COMPILER_EXPORT void ShDestruct(ShHandle
 //                            compiling for WebGL - it is implied.
 // SH_INTERMEDIATE_TREE: Writes intermediate tree to info log.
 //                       Can be queried by calling ShGetInfoLog().
 // SH_OBJECT_CODE: Translates intermediate tree to glsl or hlsl shader.
 //                 Can be queried by calling ShGetObjectCode().
 // SH_VARIABLES: Extracts attributes, uniforms, and varyings.
 //               Can be queried by calling ShGetVariableInfo().
 //
-COMPILER_EXPORT bool ShCompile(
-    const ShHandle handle,
-    const char * const shaderStrings[],
-    size_t numStrings,
-    int compileOptions);
+COMPILER_EXPORT bool ShCompile(const ShHandle handle,
+                               const char *const shaderStrings[],
+                               size_t numStrings,
+                               ShCompileOptions compileOptions);
 
 // Clears the results from the previous compilation.
 COMPILER_EXPORT void ShClearResults(const ShHandle handle);
 
 // Return the version of the shader language.
 COMPILER_EXPORT int ShGetShaderVersion(const ShHandle handle);
 
 // Return the currently set language output type.
--- a/gfx/angle/include/export.h
+++ b/gfx/angle/include/export.h
@@ -5,23 +5,25 @@
 //
 
 // export.h : Defines ANGLE_EXPORT, a macro for exporting functions from the DLL
 
 #ifndef LIBGLESV2_EXPORT_H_
 #define LIBGLESV2_EXPORT_H_
 
 #if defined(_WIN32)
-#   if defined(LIBGLESV2_IMPLEMENTATION) || defined(LIBANGLE_IMPLEMENTATION)
+#if defined(LIBGLESV2_IMPLEMENTATION) || defined(LIBANGLE_IMPLEMENTATION) || \
+    defined(LIBANGLE_UTIL_IMPLEMENTATION)
 #       define ANGLE_EXPORT __declspec(dllexport)
 #   else
 #       define ANGLE_EXPORT __declspec(dllimport)
 #   endif
 #elif defined(__GNUC__)
-#   if defined(LIBGLESV2_IMPLEMENTATION) || defined(LIBANGLE_IMPLEMENTATION)
+#if defined(LIBGLESV2_IMPLEMENTATION) || defined(LIBANGLE_IMPLEMENTATION) || \
+    defined(LIBANGLE_UTIL_IMPLEMENTATION)
 #       define ANGLE_EXPORT __attribute__((visibility ("default")))
 #   else
 #       define ANGLE_EXPORT
 #   endif
 #else
 #   define ANGLE_EXPORT
 #endif
 
--- a/gfx/angle/moz.build
+++ b/gfx/angle/moz.build
@@ -23,33 +23,32 @@ UNIFIED_SOURCES += [
     'src/compiler/preprocessor/ExpressionParser.cpp',
     'src/compiler/preprocessor/Input.cpp',
     'src/compiler/preprocessor/Lexer.cpp',
     'src/compiler/preprocessor/Macro.cpp',
     'src/compiler/preprocessor/MacroExpander.cpp',
     'src/compiler/preprocessor/Preprocessor.cpp',
     'src/compiler/preprocessor/Token.cpp',
     'src/compiler/preprocessor/Tokenizer.cpp',
+    'src/compiler/translator/AddAndTrueToLoopCondition.cpp',
     'src/compiler/translator/AddDefaultReturnStatements.cpp',
     'src/compiler/translator/ArrayReturnValueToOutParameter.cpp',
     'src/compiler/translator/ASTMetadataHLSL.cpp',
     'src/compiler/translator/blocklayout.cpp',
     'src/compiler/translator/blocklayoutHLSL.cpp',
+    'src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp',
     'src/compiler/translator/BuiltInFunctionEmulator.cpp',
     'src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp',
     'src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp',
     'src/compiler/translator/Cache.cpp',
     'src/compiler/translator/CallDAG.cpp',
     'src/compiler/translator/CodeGen.cpp',
     'src/compiler/translator/Compiler.cpp',
+    'src/compiler/translator/ConstantUnion.cpp',
     'src/compiler/translator/DeferGlobalInitializers.cpp',
-    'src/compiler/translator/depgraph/DependencyGraph.cpp',
-    'src/compiler/translator/depgraph/DependencyGraphBuilder.cpp',
-    'src/compiler/translator/depgraph/DependencyGraphOutput.cpp',
-    'src/compiler/translator/depgraph/DependencyGraphTraverse.cpp',
     'src/compiler/translator/Diagnostics.cpp',
     'src/compiler/translator/DirectiveHandler.cpp',
     'src/compiler/translator/EmulatePrecision.cpp',
     'src/compiler/translator/ExpandIntegerPowExpressions.cpp',
     'src/compiler/translator/ExtensionGLSL.cpp',
     'src/compiler/translator/FlagStd140Structs.cpp',
     'src/compiler/translator/ForLoopUnroll.cpp',
     'src/compiler/translator/InfoSink.cpp',
@@ -66,16 +65,17 @@ UNIFIED_SOURCES += [
     'src/compiler/translator/Operator.cpp',
     'src/compiler/translator/OutputESSL.cpp',
     'src/compiler/translator/OutputGLSL.cpp',
     'src/compiler/translator/OutputGLSLBase.cpp',
     'src/compiler/translator/OutputHLSL.cpp',
     'src/compiler/translator/ParseContext.cpp',
     'src/compiler/translator/PoolAlloc.cpp',
     'src/compiler/translator/PruneEmptyDeclarations.cpp',
+    'src/compiler/translator/QualifierTypes.cpp',
     'src/compiler/translator/RecordConstantPrecision.cpp',
     'src/compiler/translator/RegenerateStructNames.cpp',
     'src/compiler/translator/RemoveDynamicIndexing.cpp',
     'src/compiler/translator/RemovePow.cpp',
     'src/compiler/translator/RemoveSwitchFallThrough.cpp',
     'src/compiler/translator/RewriteDoWhile.cpp',
     'src/compiler/translator/RewriteElseBlocks.cpp',
     'src/compiler/translator/RewriteTexelFetchOffset.cpp',
@@ -86,18 +86,16 @@ UNIFIED_SOURCES += [
     'src/compiler/translator/SeparateExpressionsReturningArrays.cpp',
     'src/compiler/translator/ShaderLang.cpp',
     'src/compiler/translator/ShaderVars.cpp',
     'src/compiler/translator/SimplifyLoopConditions.cpp',
     'src/compiler/translator/SplitSequenceOperator.cpp',
     'src/compiler/translator/StructureHLSL.cpp',
     'src/compiler/translator/SymbolTable.cpp',
     'src/compiler/translator/TextureFunctionHLSL.cpp',
-    'src/compiler/translator/timing/RestrictFragmentShaderTiming.cpp',
-    'src/compiler/translator/timing/RestrictVertexShaderTiming.cpp',
     'src/compiler/translator/TranslatorESSL.cpp',
     'src/compiler/translator/TranslatorGLSL.cpp',
     'src/compiler/translator/TranslatorHLSL.cpp',
     'src/compiler/translator/Types.cpp',
     'src/compiler/translator/UnfoldShortCircuitAST.cpp',
     'src/compiler/translator/UnfoldShortCircuitToIf.cpp',
     'src/compiler/translator/UniformHLSL.cpp',
     'src/compiler/translator/util.cpp',
--- a/gfx/angle/src/commit.h
+++ b/gfx/angle/src/commit.h
@@ -1,3 +1,3 @@
-#define ANGLE_COMMIT_HASH ""
+#define ANGLE_COMMIT_HASH "8b3e8b4d1b09"
 #define ANGLE_COMMIT_HASH_SIZE 12
-#define ANGLE_COMMIT_DATE ""
+#define ANGLE_COMMIT_DATE "2016-10-24 15:38:47 +0800"
--- a/gfx/angle/src/compiler.gypi
+++ b/gfx/angle/src/compiler.gypi
@@ -17,27 +17,32 @@
             '../include/GLES3/gl3.h',
             '../include/GLES3/gl3platform.h',
             '../include/GLES3/gl31.h',
             '../include/GLES3/gl32.h',
             '../include/GLSLANG/ShaderLang.h',
             '../include/GLSLANG/ShaderVars.h',
             '../include/KHR/khrplatform.h',
             '../include/angle_gl.h',
+            'compiler/translator/AddAndTrueToLoopCondition.cpp',
+            'compiler/translator/AddAndTrueToLoopCondition.h',
             'compiler/translator/BaseTypes.h',
             'compiler/translator/BuiltInFunctionEmulator.cpp',
             'compiler/translator/BuiltInFunctionEmulator.h',
+            'compiler/translator/BreakVariableAliasingInInnerLoops.cpp',
+            'compiler/translator/BreakVariableAliasingInInnerLoops.h',
             'compiler/translator/Cache.cpp',
             'compiler/translator/Cache.h',
             'compiler/translator/CallDAG.cpp',
             'compiler/translator/CallDAG.h',
             'compiler/translator/CodeGen.cpp',
             'compiler/translator/Common.h',
             'compiler/translator/Compiler.cpp',
             'compiler/translator/Compiler.h',
+            'compiler/translator/ConstantUnion.cpp',
             'compiler/translator/ConstantUnion.h',
             'compiler/translator/DeferGlobalInitializers.cpp',
             'compiler/translator/DeferGlobalInitializers.h',
             'compiler/translator/Diagnostics.cpp',
             'compiler/translator/Diagnostics.h',
             'compiler/translator/DirectiveHandler.cpp',
             'compiler/translator/DirectiveHandler.h',
             'compiler/translator/EmulateGLFragColorBroadcast.cpp',
@@ -76,27 +81,28 @@
             'compiler/translator/Operator.h',
             'compiler/translator/ParseContext.cpp',
             'compiler/translator/ParseContext.h',
             'compiler/translator/PoolAlloc.cpp',
             'compiler/translator/PoolAlloc.h',
             'compiler/translator/Pragma.h',
             'compiler/translator/PruneEmptyDeclarations.cpp',
             'compiler/translator/PruneEmptyDeclarations.h',
+            'compiler/translator/QualifierTypes.h',
+            'compiler/translator/QualifierTypes.cpp',
             'compiler/translator/RecordConstantPrecision.cpp',
             'compiler/translator/RecordConstantPrecision.h',
             'compiler/translator/RegenerateStructNames.cpp',
             'compiler/translator/RegenerateStructNames.h',
             'compiler/translator/RemovePow.cpp',
             'compiler/translator/RemovePow.h',
             'compiler/translator/RewriteDoWhile.cpp',
             'compiler/translator/RewriteDoWhile.h',
             'compiler/translator/RewriteTexelFetchOffset.cpp',
             'compiler/translator/RewriteTexelFetchOffset.h',
-            'compiler/translator/RenameFunction.h',
             'compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp',
             'compiler/translator/ScalarizeVecAndMatConstructorArgs.h',
             'compiler/translator/SearchSymbol.cpp',
             'compiler/translator/SearchSymbol.h',
             'compiler/translator/SymbolTable.cpp',
             'compiler/translator/SymbolTable.h',
             'compiler/translator/Types.cpp',
             'compiler/translator/Types.h',
@@ -113,35 +119,24 @@
             'compiler/translator/ValidateSwitch.cpp',
             'compiler/translator/ValidateSwitch.h',
             'compiler/translator/VariableInfo.cpp',
             'compiler/translator/VariableInfo.h',
             'compiler/translator/VariablePacker.cpp',
             'compiler/translator/VariablePacker.h',
             'compiler/translator/blocklayout.cpp',
             'compiler/translator/blocklayout.h',
-            'compiler/translator/depgraph/DependencyGraph.cpp',
-            'compiler/translator/depgraph/DependencyGraph.h',
-            'compiler/translator/depgraph/DependencyGraphBuilder.cpp',
-            'compiler/translator/depgraph/DependencyGraphBuilder.h',
-            'compiler/translator/depgraph/DependencyGraphOutput.cpp',
-            'compiler/translator/depgraph/DependencyGraphOutput.h',
-            'compiler/translator/depgraph/DependencyGraphTraverse.cpp',
             'compiler/translator/glslang.h',
             'compiler/translator/glslang.l',
             'compiler/translator/glslang.y',
             'compiler/translator/glslang_lex.cpp',
             'compiler/translator/glslang_tab.cpp',
             'compiler/translator/glslang_tab.h',
             'compiler/translator/intermOut.cpp',
             'compiler/translator/length_limits.h',
-            'compiler/translator/timing/RestrictFragmentShaderTiming.cpp',
-            'compiler/translator/timing/RestrictFragmentShaderTiming.h',
-            'compiler/translator/timing/RestrictVertexShaderTiming.cpp',
-            'compiler/translator/timing/RestrictVertexShaderTiming.h',
             'compiler/translator/util.cpp',
             'compiler/translator/util.h',
             'third_party/compiler/ArrayBoundsClamper.cpp',
             'third_party/compiler/ArrayBoundsClamper.h',
         ],
         'angle_translator_lib_essl_sources':
         [
             'compiler/translator/OutputESSL.cpp',
new file mode 100644
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/AddAndTrueToLoopCondition.cpp
@@ -0,0 +1,59 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/AddAndTrueToLoopCondition.h"
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+namespace
+{
+
+// An AST traverser that rewrites for and while loops by replacing "condition" with
+// "condition && true" to work around condition bug on Intel Mac.
+class AddAndTrueToLoopConditionTraverser : public TIntermTraverser
+{
+  public:
+    AddAndTrueToLoopConditionTraverser() : TIntermTraverser(true, false, false) {}
+
+    bool visitLoop(Visit, TIntermLoop *loop) override
+    {
+        // do-while loop doesn't have this bug.
+        if (loop->getType() != ELoopFor && loop->getType() != ELoopWhile)
+        {
+            return true;
+        }
+
+        // For loop may not have a condition.
+        if (loop->getCondition() == nullptr)
+        {
+            return true;
+        }
+
+        // Constant true.
+        TConstantUnion *trueConstant = new TConstantUnion();
+        trueConstant->setBConst(true);
+        TIntermTyped *trueValue = new TIntermConstantUnion(trueConstant, TType(EbtBool));
+
+        // CONDITION && true.
+        TIntermBinary *andOp = new TIntermBinary(EOpLogicalAnd, loop->getCondition(), trueValue);
+        loop->setCondition(andOp);
+
+        return true;
+    }
+};
+
+}  // anonymous namespace
+
+void AddAndTrueToLoopCondition(TIntermNode *root)
+{
+    AddAndTrueToLoopConditionTraverser traverser;
+    root->traverse(&traverser);
+}
+
+}  // namespace sh
new file mode 100644
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/AddAndTrueToLoopCondition.h
@@ -0,0 +1,20 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// Rewrite condition in for and while loops to work around driver bug on Intel Mac.
+
+#ifndef COMPILER_TRANSLATOR_ADDANDTRUETOLOOPCONDITION_H_
+#define COMPILER_TRANSLATOR_ADDANDTRUETOLOOPCONDITION_H_
+
+class TIntermNode;
+namespace sh
+{
+
+void AddAndTrueToLoopCondition(TIntermNode *root);
+
+}  // namespace sh
+
+#endif  // COMPILER_TRANSLATOR_ADDANDTRUETOLOOPCONDITION_H_
--- a/gfx/angle/src/compiler/translator/AddDefaultReturnStatements.cpp
+++ b/gfx/angle/src/compiler/translator/AddDefaultReturnStatements.cpp
@@ -49,58 +49,23 @@ class AddDefaultReturnStatementsTraverse
         if (returnNode != nullptr && returnNode->getFlowOp() == EOpReturn)
         {
             return false;
         }
 
         return true;
     }
 
-    static TIntermTyped *GenerateTypeConstructor(const TType &returnType)
-    {
-        // Base case, constructing a single element
-        if (!returnType.isArray())
-        {
-            size_t objectSize             = returnType.getObjectSize();
-            TConstantUnion *constantUnion = new TConstantUnion[objectSize];
-            for (size_t constantIdx = 0; constantIdx < objectSize; constantIdx++)
-            {
-                constantUnion[constantIdx].setFConst(0.0f);
-            }
-
-            TIntermConstantUnion *intermConstantUnion =
-                new TIntermConstantUnion(constantUnion, returnType);
-            return intermConstantUnion;
-        }
-
-        // Recursive case, construct an array of single elements
-        TIntermAggregate *constructorAggrigate =
-            new TIntermAggregate(TypeToConstructorOperator(returnType));
-        constructorAggrigate->setType(returnType);
-
-        size_t arraySize = returnType.getArraySize();
-        for (size_t arrayIdx = 0; arrayIdx < arraySize; arrayIdx++)
-        {
-            TType arrayElementType(returnType);
-            arrayElementType.clearArrayness();
-
-            constructorAggrigate->getSequence()->push_back(
-                GenerateTypeConstructor(arrayElementType));
-        }
-
-        return constructorAggrigate;
-    }
-
     bool visitAggregate(Visit, TIntermAggregate *node) override
     {
         TType returnType;
         if (IsFunctionWithoutReturnStatement(node, &returnType))
         {
             TIntermBranch *branch =
-                new TIntermBranch(EOpReturn, GenerateTypeConstructor(returnType));
+                new TIntermBranch(EOpReturn, TIntermTyped::CreateZero(returnType));
 
             TIntermAggregate *lastNode = node->getSequence()->back()->getAsAggregate();
             lastNode->getSequence()->push_back(branch);
 
             return false;
         }
 
         return true;
--- a/gfx/angle/src/compiler/translator/ArrayReturnValueToOutParameter.cpp
+++ b/gfx/angle/src/compiler/translator/ArrayReturnValueToOutParameter.cpp
@@ -162,22 +162,21 @@ bool ArrayReturnValueToOutParameterTrave
 
 bool ArrayReturnValueToOutParameterTraverser::visitBranch(Visit visit, TIntermBranch *node)
 {
     if (mInFunctionWithArrayReturnValue && node->getFlowOp() == EOpReturn)
     {
         // Instead of returning a value, assign to the out parameter and then return.
         TIntermSequence replacements;
 
-        TIntermBinary *replacementAssignment = new TIntermBinary(EOpAssign);
         TIntermTyped *expression = node->getExpression();
         ASSERT(expression != nullptr);
-        replacementAssignment->setLeft(CreateReturnValueSymbol(expression->getType()));
-        replacementAssignment->setRight(node->getExpression());
-        replacementAssignment->setType(expression->getType());
+        TIntermSymbol *returnValueSymbol = CreateReturnValueSymbol(expression->getType());
+        TIntermBinary *replacementAssignment =
+            new TIntermBinary(EOpAssign, returnValueSymbol, expression);
         replacementAssignment->setLine(expression->getLine());
         replacements.push_back(replacementAssignment);
 
         TIntermBranch *replacementBranch = new TIntermBranch(EOpReturn, nullptr);
         replacementBranch->setLine(node->getLine());
         replacements.push_back(replacementBranch);
 
         mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(getParentNode()->getAsAggregate(), node, replacements));
--- a/gfx/angle/src/compiler/translator/BaseTypes.h
+++ b/gfx/angle/src/compiler/translator/BaseTypes.h
@@ -336,20 +336,21 @@ enum TQualifier
     EvqSecondaryFragColorEXT,  // EXT_blend_func_extended
     EvqSecondaryFragDataEXT,   // EXT_blend_func_extended
 
     // built-ins written by the shader_framebuffer_fetch extension(s)
     EvqLastFragColor,
     EvqLastFragData,
 
     // GLSL ES 3.0 vertex output and fragment input
-    EvqSmooth,  // Incomplete qualifier, smooth is the default
-    EvqFlat,    // Incomplete qualifier
-    EvqSmoothOut = EvqSmooth,
-    EvqFlatOut   = EvqFlat,
+    EvqSmooth,    // Incomplete qualifier, smooth is the default
+    EvqFlat,      // Incomplete qualifier
+    EvqCentroid,  // Incomplete qualifier
+    EvqSmoothOut,
+    EvqFlatOut,
     EvqCentroidOut,  // Implies smooth
     EvqSmoothIn,
     EvqFlatIn,
     EvqCentroidIn,  // Implies smooth
 
     // GLSL ES 3.1 compute shader special variables
     EvqComputeIn,
     EvqNumWorkGroups,
@@ -358,16 +359,21 @@ enum TQualifier
     EvqLocalInvocationID,
     EvqGlobalInvocationID,
     EvqLocalInvocationIndex,
 
     // end of list
     EvqLast
 };
 
+inline bool IsQualifierUnspecified(TQualifier qualifier)
+{
+    return (qualifier == EvqTemporary || qualifier == EvqGlobal);
+}
+
 enum TLayoutMatrixPacking
 {
     EmpUnspecified,
     EmpRowMajor,
     EmpColumnMajor
 };
 
 enum TLayoutBlockStorage
@@ -376,27 +382,29 @@ enum TLayoutBlockStorage
     EbsShared,
     EbsPacked,
     EbsStd140
 };
 
 struct TLayoutQualifier
 {
     int location;
+    unsigned int locationsSpecified;
     TLayoutMatrixPacking matrixPacking;
     TLayoutBlockStorage blockStorage;
 
     // Compute shader layout qualifiers.
     sh::WorkGroupSize localSize;
 
     static TLayoutQualifier create()
     {
         TLayoutQualifier layoutQualifier;
 
         layoutQualifier.location = -1;
+        layoutQualifier.locationsSpecified = 0;
         layoutQualifier.matrixPacking = EmpUnspecified;
         layoutQualifier.blockStorage = EbsUnspecified;
 
         layoutQualifier.localSize.fill(-1);
 
         return layoutQualifier;
     }
 
@@ -477,16 +485,19 @@ inline const char* getQualifierString(TQ
     case EvqLastFragColor:          return "LastFragColor";
     case EvqLastFragData:           return "LastFragData";
     case EvqSmoothOut:              return "smooth out";
     case EvqCentroidOut:            return "smooth centroid out";
     case EvqFlatOut:                return "flat out";
     case EvqSmoothIn:               return "smooth in";
     case EvqFlatIn:                 return "flat in";
     case EvqCentroidIn:             return "smooth centroid in";
+    case EvqCentroid:               return "centroid";
+    case EvqFlat:                   return "flat";
+    case EvqSmooth:                 return "smooth";
     case EvqComputeIn:              return "in";
     case EvqNumWorkGroups:          return "NumWorkGroups";
     case EvqWorkGroupSize:          return "WorkGroupSize";
     case EvqWorkGroupID:            return "WorkGroupID";
     case EvqLocalInvocationID:      return "LocalInvocationID";
     case EvqGlobalInvocationID:     return "GlobalInvocationID";
     case EvqLocalInvocationIndex:   return "LocalInvocationIndex";
     default: UNREACHABLE();         return "unknown qualifier";
new file mode 100644
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp
@@ -0,0 +1,106 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// BreakVariableAliasingInInnerLoops.h: To optimize simple assignments, the HLSL compiler frontend
+//      may record a variable as aliasing another. Sometimes the alias information gets garbled
+//      so we work around this issue by breaking the aliasing chain in inner loops.
+
+#include "BreakVariableAliasingInInnerLoops.h"
+
+#include "compiler/translator/IntermNode.h"
+
+// A HLSL compiler developer gave us more details on the root cause and the workaround needed:
+//     The root problem is that if the HLSL compiler is applying aliasing information even on
+//     incomplete simulations (in this case, a single pass). The bug is triggered by an assignment
+//     that comes from a series of assignments, possibly with swizzled or ternary operators with
+//     known conditionals, where the source is before the loop.
+//     So, a workaround is to add a +0 term to variables the first time they are assigned to in
+//     an inner loop (if they are declared in an outside scope, otherwise there is no need).
+//     This will break the aliasing chain.
+
+// For simplicity here we add a +0 to any assignment that is in at least two nested loops. Because
+// the bug only shows up with swizzles, and ternary assignment, whole array or whole structure
+// assignment don't need a workaround.
+
+namespace
+{
+
+class AliasingBreaker : public TIntermTraverser
+{
+  public:
+    AliasingBreaker() : TIntermTraverser(true, false, true) {}
+
+  protected:
+    bool visitBinary(Visit visit, TIntermBinary *binary)
+    {
+        if (visit != PreVisit)
+        {
+            return false;
+        }
+
+        if (mLoopLevel < 2 || !binary->isAssignment())
+        {
+            return true;
+        }
+
+        TIntermTyped *B = binary->getRight();
+        TType type      = B->getType();
+
+        if (!type.isScalar() && !type.isVector() && !type.isMatrix())
+        {
+            return true;
+        }
+
+        if (type.isArray() || IsSampler(type.getBasicType()))
+        {
+            return true;
+        }
+
+        // We have a scalar / vector / matrix assignment with loop depth 2.
+        // Transform it from
+        //    A = B
+        // to
+        //    A = (B + typeof<B>(0));
+
+        TIntermBinary *bPlusZero = new TIntermBinary(EOpAdd, B, TIntermTyped::CreateZero(type));
+        bPlusZero->setLine(B->getLine());
+
+        binary->replaceChildNode(B, bPlusZero);
+
+        return true;
+    }
+
+    bool visitLoop(Visit visit, TIntermLoop *loop)
+    {
+        if (visit == PreVisit)
+        {
+            mLoopLevel++;
+        }
+        else
+        {
+            ASSERT(mLoopLevel > 0);
+            mLoopLevel--;
+        }
+
+        return true;
+    }
+
+  private:
+    int mLoopLevel = 0;
+};
+
+}  // anonymous namespace
+
+namespace sh
+{
+
+void BreakVariableAliasingInInnerLoops(TIntermNode *root)
+{
+    AliasingBreaker breaker;
+    root->traverse(&breaker);
+}
+
+}  // namespace sh
new file mode 100644
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.h
@@ -0,0 +1,23 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// BreakVariableAliasingInInnerLoops.h: To optimize simple assignments, the HLSL compiler frontend
+//      may record a variable as aliasing another. Sometimes the alias information gets garbled
+//      so we work around this issue by breaking the aliasing chain in inner loops.
+
+#ifndef COMPILER_TRANSLATOR_BREAKVARIABLEALIASINGININNERLOOPS_H_
+#define COMPILER_TRANSLATOR_BREAKVARIABLEALIASINGININNERLOOPS_H_
+
+class TIntermNode;
+
+namespace sh
+{
+
+void BreakVariableAliasingInInnerLoops(TIntermNode *root);
+
+}  // namespace sh
+
+#endif  // COMPILER_TRANSLATOR_BREAKVARIABLEALIASINGININNERLOOPS_H_
--- a/gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp
+++ b/gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp
@@ -6,42 +6,24 @@
 
 #include "angle_gl.h"
 #include "compiler/translator/BuiltInFunctionEmulator.h"
 #include "compiler/translator/BuiltInFunctionEmulatorGLSL.h"
 #include "compiler/translator/Cache.h"
 #include "compiler/translator/SymbolTable.h"
 #include "compiler/translator/VersionGLSL.h"
 
-void InitBuiltInFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu, sh::GLenum shaderType)
+void InitBuiltInAbsFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu,
+                                                      sh::GLenum shaderType)
 {
-    // we use macros here instead of function definitions to work around more GLSL
-    // compiler bugs, in particular on NVIDIA hardware on Mac OSX. Macros are
-    // problematic because if the argument has side-effects they will be repeatedly
-    // evaluated. This is unlikely to show up in real shaders, but is something to
-    // consider.
-
-    const TType *float1 = TCache::getType(EbtFloat);
-    const TType *float2 = TCache::getType(EbtFloat, 2);
-    const TType *float3 = TCache::getType(EbtFloat, 3);
-    const TType *float4 = TCache::getType(EbtFloat, 4);
-
-    if (shaderType == GL_FRAGMENT_SHADER)
+    if (shaderType == GL_VERTEX_SHADER)
     {
-        emu->addEmulatedFunction(EOpCos, float1, "webgl_emu_precision float webgl_cos_emu(webgl_emu_precision float a) { return cos(a); }");
-        emu->addEmulatedFunction(EOpCos, float2, "webgl_emu_precision vec2 webgl_cos_emu(webgl_emu_precision vec2 a) { return cos(a); }");
-        emu->addEmulatedFunction(EOpCos, float3, "webgl_emu_precision vec3 webgl_cos_emu(webgl_emu_precision vec3 a) { return cos(a); }");
-        emu->addEmulatedFunction(EOpCos, float4, "webgl_emu_precision vec4 webgl_cos_emu(webgl_emu_precision vec4 a) { return cos(a); }");
+        const TType *int1 = TCache::getType(EbtInt);
+        emu->addEmulatedFunction(EOpAbs, int1, "int webgl_abs_emu(int x) { return x * sign(x); }");
     }
-    emu->addEmulatedFunction(EOpDistance, float1, float1, "#define webgl_distance_emu(x, y) ((x) >= (y) ? (x) - (y) : (y) - (x))");
-    emu->addEmulatedFunction(EOpDot, float1, float1, "#define webgl_dot_emu(x, y) ((x) * (y))");
-    emu->addEmulatedFunction(EOpLength, float1, "#define webgl_length_emu(x) ((x) >= 0.0 ? (x) : -(x))");
-    emu->addEmulatedFunction(EOpNormalize, float1, "#define webgl_normalize_emu(x) ((x) == 0.0 ? 0.0 : ((x) > 0.0 ? 1.0 : -1.0))");
-    emu->addEmulatedFunction(EOpReflect, float1, float1, "#define webgl_reflect_emu(I, N) ((I) - 2.0 * (N) * (I) * (N))");
-    emu->addEmulatedFunction(EOpFaceForward, float1, float1, float1, "#define webgl_faceforward_emu(N, I, Nref) (((Nref) * (I) < 0.0) ? (N) : -(N))");
 }
 
 // Emulate built-in functions missing from GLSL 1.30 and higher
 void InitBuiltInFunctionEmulatorForGLSLMissingFunctions(BuiltInFunctionEmulator *emu, sh::GLenum shaderType,
                                                         int targetGLSLVersion)
 {
     // Emulate packUnorm2x16 and unpackUnorm2x16 (GLSL 4.10)
     if (targetGLSLVersion < GLSL_VERSION_410)
@@ -176,17 +158,19 @@ void InitBuiltInFunctionEmulatorForGLSLM
             "            return uintBitsToFloat(sign | 0x7F800000u | mantissa);\n"
             "        }\n"
             "        else\n"
             "        {\n"
             "            exponent -= 15;\n"
             "            float scale;\n"
             "            if(exponent < 0)\n"
             "            {\n"
-            "                scale = 1.0 / (1 << -exponent);\n"
+            "                // The negative unary operator is buggy on OSX.\n"
+            "                // Work around this by using abs instead.\n"
+            "                scale = 1.0 / (1 << abs(exponent));\n"
             "            }\n"
             "            else\n"
             "            {\n"
             "                scale = 1 << exponent;\n"
             "            }\n"
             "            float decimal = 1.0 + float(mantissa) / float(1 << 10);\n"
             "            f32 = scale * decimal;\n"
             "        }\n"
--- a/gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h
+++ b/gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h
@@ -7,19 +7,20 @@
 #ifndef COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORGLSL_H_
 #define COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORGLSL_H_
 
 #include "GLSLANG/ShaderLang.h"
 
 class BuiltInFunctionEmulator;
 
 //
-// This is only a workaround for OpenGL driver bugs, and isn't needed in general.
+// This works around bug in Intel Mac drivers.
 //
-void InitBuiltInFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu, sh::GLenum shaderType);
+void InitBuiltInAbsFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu,
+                                                      sh::GLenum shaderType);
 
 //
 // This function is emulating built-in functions missing from GLSL 1.30 and higher.
 //
 void InitBuiltInFunctionEmulatorForGLSLMissingFunctions(BuiltInFunctionEmulator *emu, sh::GLenum shaderType,
                                                         int targetGLSLVersion);
 
 #endif  // COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORGLSL_H_
--- a/gfx/angle/src/compiler/translator/Compiler.cpp
+++ b/gfx/angle/src/compiler/translator/Compiler.cpp
@@ -1,47 +1,42 @@
 //
 // Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
 
+#include "compiler/translator/AddAndTrueToLoopCondition.h"
 #include "compiler/translator/Cache.h"
 #include "compiler/translator/Compiler.h"
 #include "compiler/translator/CallDAG.h"
 #include "compiler/translator/DeferGlobalInitializers.h"
 #include "compiler/translator/EmulateGLFragColorBroadcast.h"
 #include "compiler/translator/ForLoopUnroll.h"
 #include "compiler/translator/Initialize.h"
 #include "compiler/translator/InitializeParseContext.h"
 #include "compiler/translator/InitializeVariables.h"
 #include "compiler/translator/ParseContext.h"
 #include "compiler/translator/PruneEmptyDeclarations.h"
 #include "compiler/translator/RegenerateStructNames.h"
 #include "compiler/translator/RemovePow.h"
-#include "compiler/translator/RenameFunction.h"
 #include "compiler/translator/RewriteDoWhile.h"
 #include "compiler/translator/ScalarizeVecAndMatConstructorArgs.h"
 #include "compiler/translator/UnfoldShortCircuitAST.h"
 #include "compiler/translator/ValidateLimitations.h"
 #include "compiler/translator/ValidateMaxParameters.h"
 #include "compiler/translator/ValidateOutputs.h"
 #include "compiler/translator/VariablePacker.h"
-#include "compiler/translator/depgraph/DependencyGraph.h"
-#include "compiler/translator/depgraph/DependencyGraphOutput.h"
-#include "compiler/translator/timing/RestrictFragmentShaderTiming.h"
-#include "compiler/translator/timing/RestrictVertexShaderTiming.h"
 #include "third_party/compiler/ArrayBoundsClamper.h"
 #include "angle_gl.h"
 #include "common/utilities.h"
 
 bool IsWebGLBasedSpec(ShShaderSpec spec)
 {
-    return (spec == SH_WEBGL_SPEC || spec == SH_CSS_SHADERS_SPEC || spec == SH_WEBGL2_SPEC ||
-            spec == SH_WEBGL3_SPEC);
+    return (spec == SH_WEBGL_SPEC || spec == SH_WEBGL2_SPEC || spec == SH_WEBGL3_SPEC);
 }
 
 bool IsGLSL130OrNewer(ShShaderOutput output)
 {
     return (output == SH_GLSL_130_OUTPUT ||
             output == SH_GLSL_140_OUTPUT ||
             output == SH_GLSL_150_CORE_OUTPUT ||
             output == SH_GLSL_330_CORE_OUTPUT ||
@@ -55,17 +50,16 @@ bool IsGLSL130OrNewer(ShShaderOutput out
 
 size_t GetGlobalMaxTokenSize(ShShaderSpec spec)
 {
     // WebGL defines a max token legnth of 256, while ES2 leaves max token
     // size undefined. ES3 defines a max size of 1024 characters.
     switch (spec)
     {
       case SH_WEBGL_SPEC:
-      case SH_CSS_SHADERS_SPEC:
         return 256;
       default:
         return 1024;
     }
 }
 
 namespace {
 
@@ -106,17 +100,16 @@ class TScopedSymbolTableLevel
 };
 
 int MapSpecToShaderVersion(ShShaderSpec spec)
 {
     switch (spec)
     {
       case SH_GLES2_SPEC:
       case SH_WEBGL_SPEC:
-      case SH_CSS_SHADERS_SPEC:
         return 100;
       case SH_GLES3_SPEC:
       case SH_WEBGL2_SPEC:
         return 300;
       case SH_GLES3_1_SPEC:
       case SH_WEBGL3_SPEC:
           return 310;
       default:
@@ -157,17 +150,17 @@ TCompiler::TCompiler(sh::GLenum type, Sh
 {
     mComputeShaderLocalSize.fill(1);
 }
 
 TCompiler::~TCompiler()
 {
 }
 
-bool TCompiler::shouldRunLoopAndIndexingValidation(int compileOptions) const
+bool TCompiler::shouldRunLoopAndIndexingValidation(ShCompileOptions compileOptions) const
 {
     // If compiling an ESSL 1.00 shader for WebGL, or if its been requested through the API,
     // validate loop and indexing as well (to verify that the shader only uses minimal functionality
     // of ESSL 1.00 as in Appendix A of the spec).
     return (IsWebGLBasedSpec(shaderSpec) && shaderVersion == 100) ||
            (compileOptions & SH_VALIDATE_LOOP_INDEXING);
 }
 
@@ -192,25 +185,26 @@ bool TCompiler::Init(const ShBuiltInReso
     arrayBoundsClamper.SetClampingStrategy(resources.ArrayIndexClampingStrategy);
     clampingStrategy = resources.ArrayIndexClampingStrategy;
 
     hashFunction = resources.HashFunction;
 
     return true;
 }
 
-TIntermNode *TCompiler::compileTreeForTesting(const char* const shaderStrings[],
-    size_t numStrings, int compileOptions)
+TIntermNode *TCompiler::compileTreeForTesting(const char *const shaderStrings[],
+                                              size_t numStrings,
+                                              ShCompileOptions compileOptions)
 {
     return compileTreeImpl(shaderStrings, numStrings, compileOptions);
 }
 
 TIntermNode *TCompiler::compileTreeImpl(const char *const shaderStrings[],
                                         size_t numStrings,
-                                        const int compileOptions)
+                                        const ShCompileOptions compileOptions)
 {
     clearResults();
 
     ASSERT(numStrings > 0);
     ASSERT(GetGlobalPoolAllocator());
 
     // Reset the extension behavior for each compilation unit.
     ResetExtensionBehavior(extensionBehavior);
@@ -218,18 +212,17 @@ TIntermNode *TCompiler::compileTreeImpl(
     // First string is path of source file if flag is set. The actual source follows.
     size_t firstSource = 0;
     if (compileOptions & SH_SOURCE_PATH)
     {
         mSourcePath = shaderStrings[0];
         ++firstSource;
     }
 
-    TIntermediate intermediate(infoSink);
-    TParseContext parseContext(symbolTable, extensionBehavior, intermediate, shaderType, shaderSpec,
+    TParseContext parseContext(symbolTable, extensionBehavior, shaderType, shaderSpec,
                                compileOptions, true, infoSink, getResources());
 
     parseContext.setFragmentPrecisionHighOnESSL1(fragmentPrecisionHigh);
     SetGlobalParseContext(&parseContext);
 
     // We preserve symbols at the built-in level from compile-to-compile.
     // Start pushing the user-defined symbols at global level.
     TScopedSymbolTableLevel scopedSymbolLevel(&symbolTable);
@@ -253,17 +246,17 @@ TIntermNode *TCompiler::compileTreeImpl(
     {
         mPragma = parseContext.pragma();
         symbolTable.setGlobalInvariant(mPragma.stdgl.invariantAll);
 
         mComputeShaderLocalSizeDeclared = parseContext.isComputeShaderLocalSizeDeclared();
         mComputeShaderLocalSize         = parseContext.getComputeShaderLocalSize();
 
         root = parseContext.getTreeRoot();
-        root = intermediate.postProcess(root);
+        root = TIntermediate::PostProcess(root);
 
         // Highp might have been auto-enabled based on shader version
         fragmentPrecisionHigh = parseContext.getFragmentPrecisionHigh();
 
         // Disallow expressions deemed too complex.
         if (success && (compileOptions & SH_LIMIT_EXPRESSION_COMPLEXITY))
             success = limitExpressionComplexity(root);
 
@@ -290,22 +283,16 @@ TIntermNode *TCompiler::compileTreeImpl(
             PruneEmptyDeclarations(root);
 
         if (success && shaderVersion == 300 && shaderType == GL_FRAGMENT_SHADER)
             success = validateOutputs(root);
 
         if (success && shouldRunLoopAndIndexingValidation(compileOptions))
             success = validateLimitations(root);
 
-        if (success && (compileOptions & SH_TIMING_RESTRICTIONS))
-            success = enforceTimingRestrictions(root, (compileOptions & SH_DEPENDENCY_GRAPH) != 0);
-
-        if (success && shaderSpec == SH_CSS_SHADERS_SPEC)
-            rewriteCSSShader(root);
-
         // Unroll for-loop markup needs to happen after validateLimitations pass.
         if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX))
         {
             ForLoopUnrollMarker marker(ForLoopUnrollMarker::kIntegerIndex,
                                        shouldRunLoopAndIndexingValidation(compileOptions));
             root->traverse(&marker);
         }
         if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX))
@@ -340,16 +327,19 @@ TIntermNode *TCompiler::compileTreeImpl(
             ((compileOptions & SH_INIT_GL_POSITION) ||
              (outputType == SH_GLSL_COMPATIBILITY_OUTPUT)))
             initializeGLPosition(root);
 
         // This pass might emit short circuits so keep it before the short circuit unfolding
         if (success && (compileOptions & SH_REWRITE_DO_WHILE_LOOPS))
             RewriteDoWhile(root, getTemporaryIndex());
 
+        if (success && (compileOptions & SH_ADD_AND_TRUE_TO_LOOP_CONDITION))
+            sh::AddAndTrueToLoopCondition(root);
+
         if (success && (compileOptions & SH_UNFOLD_SHORT_CIRCUIT))
         {
             UnfoldShortCircuitAST unfoldShortCircuit;
             root->traverse(&unfoldShortCircuit);
             unfoldShortCircuit.updateTree();
         }
 
         if (success && (compileOptions & SH_REMOVE_POW_WITH_CONSTANT_EXPONENT))
@@ -403,22 +393,24 @@ TIntermNode *TCompiler::compileTreeImpl(
 
     SetGlobalParseContext(NULL);
     if (success)
         return root;
 
     return NULL;
 }
 
-bool TCompiler::compile(const char *const shaderStrings[], size_t numStrings, int compileOptionsIn)
+bool TCompiler::compile(const char *const shaderStrings[],
+                        size_t numStrings,
+                        ShCompileOptions compileOptionsIn)
 {
     if (numStrings == 0)
         return true;
 
-    int compileOptions = compileOptionsIn;
+    ShCompileOptions compileOptions = compileOptionsIn;
 
     // Apply key workarounds.
     if (shouldFlattenPragmaStdglInvariantAll())
     {
         // This should be harmless to do in all cases, but for the moment, do it only conditionally.
         compileOptions |= SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL;
     }
 
@@ -447,25 +439,23 @@ bool TCompiler::InitBuiltInSymbolTable(c
 
     assert(symbolTable.isEmpty());
     symbolTable.push();   // COMMON_BUILTINS
     symbolTable.push();   // ESSL1_BUILTINS
     symbolTable.push();   // ESSL3_BUILTINS
     symbolTable.push();   // ESSL3_1_BUILTINS
 
     TPublicType integer;
-    integer.type = EbtInt;
-    integer.primarySize = 1;
-    integer.secondarySize = 1;
+    integer.setBasicType(EbtInt);
+    integer.initializeSizeForScalarTypes();
     integer.array = false;
 
     TPublicType floatingPoint;
-    floatingPoint.type = EbtFloat;
-    floatingPoint.primarySize = 1;
-    floatingPoint.secondarySize = 1;
+    floatingPoint.setBasicType(EbtFloat);
+    floatingPoint.initializeSizeForScalarTypes();
     floatingPoint.array = false;
 
     switch(shaderType)
     {
       case GL_FRAGMENT_SHADER:
         symbolTable.setDefaultPrecision(integer, EbpMedium);
         break;
       case GL_VERTEX_SHADER:
@@ -495,20 +485,19 @@ bool TCompiler::InitBuiltInSymbolTable(c
 
     return true;
 }
 
 void TCompiler::initSamplerDefaultPrecision(TBasicType samplerType)
 {
     ASSERT(samplerType > EbtGuardSamplerBegin && samplerType < EbtGuardSamplerEnd);
     TPublicType sampler;
-    sampler.primarySize   = 1;
-    sampler.secondarySize = 1;
+    sampler.initializeSizeForScalarTypes();
+    sampler.setBasicType(samplerType);
     sampler.array         = false;
-    sampler.type          = samplerType;
     symbolTable.setDefaultPrecision(sampler, EbpLow);
 }
 
 void TCompiler::setResourceString()
 {
     std::ostringstream strstream;
 
     // clang-format off
@@ -758,59 +747,23 @@ bool TCompiler::pruneUnusedFunctions(TIn
 
 bool TCompiler::validateOutputs(TIntermNode* root)
 {
     ValidateOutputs validateOutputs(getExtensionBehavior(), compileResources.MaxDrawBuffers);
     root->traverse(&validateOutputs);
     return (validateOutputs.validateAndCountErrors(infoSink.info) == 0);
 }
 
-void TCompiler::rewriteCSSShader(TIntermNode* root)
-{
-    RenameFunction renamer("main(", "css_main(");
-    root->traverse(&renamer);
-}
-
 bool TCompiler::validateLimitations(TIntermNode* root)
 {
     ValidateLimitations validate(shaderType, &infoSink.info);
     root->traverse(&validate);
     return validate.numErrors() == 0;
 }
 
-bool TCompiler::enforceTimingRestrictions(TIntermNode* root, bool outputGraph)
-{
-    if (shaderSpec != SH_WEBGL_SPEC)
-    {
-        infoSink.info << "Timing restrictions must be enforced under the WebGL spec.";
-        return false;
-    }
-
-    if (shaderType == GL_FRAGMENT_SHADER)
-    {
-        TDependencyGraph graph(root);
-
-        // Output any errors first.
-        bool success = enforceFragmentShaderTimingRestrictions(graph);
-
-        // Then, output the dependency graph.
-        if (outputGraph)
-        {
-            TDependencyGraphOutput output(infoSink.info);
-            output.outputAllSpanningTrees(graph);
-        }
-
-        return success;
-    }
-    else
-    {
-        return enforceVertexShaderTimingRestrictions(root);
-    }
-}
-
 bool TCompiler::limitExpressionComplexity(TIntermNode* root)
 {
     TMaxDepthTraverser traverser(maxExpressionComplexity+1);
     root->traverse(&traverser);
 
     if (traverser.getMaxDepth() > maxExpressionComplexity)
     {
         infoSink.info << "Expression too complex.";
@@ -821,30 +774,16 @@ bool TCompiler::limitExpressionComplexit
     {
         infoSink.info << "Function has too many parameters.";
         return false;
     }
 
     return true;
 }
 
-bool TCompiler::enforceFragmentShaderTimingRestrictions(const TDependencyGraph& graph)
-{
-    RestrictFragmentShaderTiming restrictor(infoSink.info);
-    restrictor.enforceRestrictions(graph);
-    return restrictor.numErrors() == 0;
-}
-
-bool TCompiler::enforceVertexShaderTimingRestrictions(TIntermNode* root)
-{
-    RestrictVertexShaderTiming restrictor(infoSink.info);
-    restrictor.enforceRestrictions(root);
-    return restrictor.numErrors() == 0;
-}
-
 void TCompiler::collectVariables(TIntermNode* root)
 {
     if (!variablesCollected)
     {
         sh::CollectVariables collect(&attributes, &outputVariables, &uniforms, &varyings,
                                      &interfaceBlocks, hashFunction, symbolTable, extensionBehavior);
         root->traverse(&collect);
 
@@ -915,17 +854,17 @@ ShArrayIndexClampingStrategy TCompiler::
     return clampingStrategy;
 }
 
 const BuiltInFunctionEmulator& TCompiler::getBuiltInFunctionEmulator() const
 {
     return builtInFunctionEmulator;
 }
 
-void TCompiler::writePragma(int compileOptions)
+void TCompiler::writePragma(ShCompileOptions compileOptions)
 {
     if (!(compileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL))
     {
         TInfoSinkBase &sink = infoSink.obj;
         if (mPragma.stdgl.invariantAll)
             sink << "#pragma STDGL invariant(all)\n";
     }
 }
--- a/gfx/angle/src/compiler/translator/Compiler.h
+++ b/gfx/angle/src/compiler/translator/Compiler.h
@@ -20,24 +20,22 @@
 #include "compiler/translator/HashNames.h"
 #include "compiler/translator/InfoSink.h"
 #include "compiler/translator/Pragma.h"
 #include "compiler/translator/SymbolTable.h"
 #include "compiler/translator/VariableInfo.h"
 #include "third_party/compiler/ArrayBoundsClamper.h"
 
 class TCompiler;
-class TDependencyGraph;
 #ifdef ANGLE_ENABLE_HLSL
 class TranslatorHLSL;
 #endif // ANGLE_ENABLE_HLSL
 
 //
-// Helper function to identify specs that are based on the WebGL spec,
-// like the CSS Shaders spec.
+// Helper function to identify specs that are based on the WebGL spec.
 //
 bool IsWebGLBasedSpec(ShShaderSpec spec);
 
 //
 // Helper function to check if the shader type is GLSL.
 //
 bool IsGLSL130OrNewer(ShShaderOutput output);
 
@@ -70,21 +68,23 @@ class TCompiler : public TShHandleBase
     ~TCompiler() override;
     TCompiler *getAsCompiler() override { return this; }
 
     bool Init(const ShBuiltInResources& resources);
 
     // compileTreeForTesting should be used only when tests require access to
     // the AST. Users of this function need to manually manage the global pool
     // allocator. Returns NULL whenever there are compilation errors.
-    TIntermNode *compileTreeForTesting(const char* const shaderStrings[],
-        size_t numStrings, int compileOptions);
+    TIntermNode *compileTreeForTesting(const char *const shaderStrings[],
+                                       size_t numStrings,
+                                       ShCompileOptions compileOptions);
 
-    bool compile(const char* const shaderStrings[],
-        size_t numStrings, int compileOptions);
+    bool compile(const char *const shaderStrings[],
+                 size_t numStrings,
+                 ShCompileOptions compileOptions);
 
     // Get results of the last compilation.
     int getShaderVersion() const { return shaderVersion; }
     TInfoSink& getInfoSink() { return infoSink; }
 
     bool isComputeShaderLocalSizeDeclared() const { return mComputeShaderLocalSizeDeclared; }
     const sh::WorkGroupSize &getComputeShaderLocalSize() { return mComputeShaderLocalSize; }
 
@@ -99,76 +99,68 @@ class TCompiler : public TShHandleBase
 
     ShHashFunction64 getHashFunction() const { return hashFunction; }
     NameMap& getNameMap() { return nameMap; }
     TSymbolTable& getSymbolTable() { return symbolTable; }
     ShShaderSpec getShaderSpec() const { return shaderSpec; }
     ShShaderOutput getOutputType() const { return outputType; }
     const std::string &getBuiltInResourcesString() const { return builtInResourcesString; }
 
-    bool shouldRunLoopAndIndexingValidation(int compileOptions) const;
+    bool shouldRunLoopAndIndexingValidation(ShCompileOptions compileOptions) const;
 
     // Get the resources set by InitBuiltInSymbolTable
     const ShBuiltInResources& getResources() const;
 
   protected:
     sh::GLenum getShaderType() const { return shaderType; }
     // Initialize symbol-table with built-in symbols.
     bool InitBuiltInSymbolTable(const ShBuiltInResources& resources);
     // Compute the string representation of the built-in resources
     void setResourceString();
     // Return false if the call depth is exceeded.
     bool checkCallDepth();
     // Returns true if a program has no conflicting or missing fragment outputs
     bool validateOutputs(TIntermNode* root);
-    // Rewrites a shader's intermediate tree according to the CSS Shaders spec.
-    void rewriteCSSShader(TIntermNode* root);
     // Returns true if the given shader does not exceed the minimum
     // functionality mandated in GLSL 1.0 spec Appendix A.
     bool validateLimitations(TIntermNode* root);
     // Collect info for all attribs, uniforms, varyings.
     void collectVariables(TIntermNode* root);
     // Add emulated functions to the built-in function emulator.
-    virtual void initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu, int compileOptions) {};
+    virtual void initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu,
+                                             ShCompileOptions compileOptions){};
     // Translate to object code.
-    virtual void translate(TIntermNode *root, int compileOptions) = 0;
+    virtual void translate(TIntermNode *root, ShCompileOptions compileOptions) = 0;
     // Returns true if, after applying the packing rules in the GLSL 1.017 spec
     // Appendix A, section 7, the shader does not use too many uniforms.
     bool enforcePackingRestrictions();
     // Insert statements to initialize output variables in the beginning of main().
     // This is to avoid undefined behaviors.
     void initializeOutputVariables(TIntermNode *root);
     // Insert gl_Position = vec4(0,0,0,0) to the beginning of main().
     // It is to work around a Linux driver bug where missing this causes compile failure
     // while spec says it is allowed.
     // This function should only be applied to vertex shaders.
     void initializeGLPosition(TIntermNode* root);
-    // Returns true if the shader passes the restrictions that aim to prevent timing attacks.
-    bool enforceTimingRestrictions(TIntermNode* root, bool outputGraph);
-    // Returns true if the shader does not use samplers.
-    bool enforceVertexShaderTimingRestrictions(TIntermNode* root);
-    // Returns true if the shader does not use sampler dependent values to affect control
-    // flow or in operations whose time can depend on the input values.
-    bool enforceFragmentShaderTimingRestrictions(const TDependencyGraph& graph);
     // Return true if the maximum expression complexity is below the limit.
     bool limitExpressionComplexity(TIntermNode* root);
     // Get built-in extensions with default behavior.
     const TExtensionBehavior& getExtensionBehavior() const;
     const char *getSourcePath() const;
     const TPragma& getPragma() const { return mPragma; }
-    void writePragma(int compileOptions);
+    void writePragma(ShCompileOptions compileOptions);
     unsigned int *getTemporaryIndex() { return &mTemporaryIndex; }
     // Relies on collectVariables having been called.
     bool isVaryingDefined(const char *varyingName);
 
     const ArrayBoundsClamper& getArrayBoundsClamper() const;
     ShArrayIndexClampingStrategy getArrayIndexClampingStrategy() const;
     const BuiltInFunctionEmulator& getBuiltInFunctionEmulator() const;
 
-    virtual bool shouldCollectVariables(int compileOptions)
+    virtual bool shouldCollectVariables(ShCompileOptions compileOptions)
     {
         return (compileOptions & SH_VARIABLES) != 0;
     }
 
     virtual bool shouldFlattenPragmaStdglInvariantAll() = 0;
 
     std::vector<sh::Attribute> attributes;
     std::vector<sh::OutputVariable> outputVariables;
@@ -188,17 +180,17 @@ class TCompiler : public TShHandleBase
     void initSamplerDefaultPrecision(TBasicType samplerType);
 
     // Removes unused function declarations and prototypes from the AST
     class UnusedPredicate;
     bool pruneUnusedFunctions(TIntermNode *root);
 
     TIntermNode *compileTreeImpl(const char *const shaderStrings[],
                                  size_t numStrings,
-                                 const int compileOptions);
+                                 const ShCompileOptions compileOptions);
 
     sh::GLenum shaderType;
     ShShaderSpec shaderSpec;
     ShShaderOutput outputType;
 
     struct FunctionMetadata
     {
         FunctionMetadata()
new file mode 100644
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ConstantUnion.cpp
@@ -0,0 +1,443 @@
+//
+// Copyright 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// ConstantUnion: Constant folding helper class.
+
+#include "compiler/translator/ConstantUnion.h"
+
+#include "compiler/translator/Diagnostics.h"
+
+TConstantUnion::TConstantUnion()
+{
+    iConst = 0;
+    type   = EbtVoid;
+}
+
+bool TConstantUnion::cast(TBasicType newType, const TConstantUnion &constant)
+{
+    switch (newType)
+    {
+        case EbtFloat:
+            switch (constant.type)
+            {
+                case EbtInt:
+                    setFConst(static_cast<float>(constant.getIConst()));
+                    break;
+                case EbtUInt:
+                    setFConst(static_cast<float>(constant.getUConst()));
+                    break;
+                case EbtBool:
+                    setFConst(static_cast<float>(constant.getBConst()));
+                    break;
+                case EbtFloat:
+                    setFConst(static_cast<float>(constant.getFConst()));
+                    break;
+                default:
+                    return false;
+            }
+            break;
+        case EbtInt:
+            switch (constant.type)
+            {
+                case EbtInt:
+                    setIConst(static_cast<int>(constant.getIConst()));
+                    break;
+                case EbtUInt:
+                    setIConst(static_cast<int>(constant.getUConst()));
+                    break;
+                case EbtBool:
+                    setIConst(static_cast<int>(constant.getBConst()));
+                    break;
+                case EbtFloat:
+                    setIConst(static_cast<int>(constant.getFConst()));
+                    break;
+                default:
+                    return false;
+            }
+            break;
+        case EbtUInt:
+            switch (constant.type)
+            {
+                case EbtInt:
+                    setUConst(static_cast<unsigned int>(constant.getIConst()));
+                    break;
+                case EbtUInt:
+                    setUConst(static_cast<unsigned int>(constant.getUConst()));
+                    break;
+                case EbtBool:
+                    setUConst(static_cast<unsigned int>(constant.getBConst()));
+                    break;
+                case EbtFloat:
+                    setUConst(static_cast<unsigned int>(constant.getFConst()));
+                    break;
+                default:
+                    return false;
+            }
+            break;
+        case EbtBool:
+            switch (constant.type)
+            {
+                case EbtInt:
+                    setBConst(constant.getIConst() != 0);
+                    break;
+                case EbtUInt:
+                    setBConst(constant.getUConst() != 0);
+                    break;
+                case EbtBool:
+                    setBConst(constant.getBConst());
+                    break;
+                case EbtFloat:
+                    setBConst(constant.getFConst() != 0.0f);
+                    break;
+                default:
+                    return false;
+            }
+            break;
+        case EbtStruct:  // Struct fields don't get cast
+            switch (constant.type)
+            {
+                case EbtInt:
+                    setIConst(constant.getIConst());
+                    break;
+                case EbtUInt:
+                    setUConst(constant.getUConst());
+                    break;
+                case EbtBool:
+                    setBConst(constant.getBConst());
+                    break;
+                case EbtFloat:
+                    setFConst(constant.getFConst());
+                    break;
+                default:
+                    return false;
+            }
+            break;
+        default:
+            return false;
+    }
+
+    return true;
+}
+
+bool TConstantUnion::operator==(const int i) const
+{
+    return i == iConst;
+}
+
+bool TConstantUnion::operator==(const unsigned int u) const
+{
+    return u == uConst;
+}
+
+bool TConstantUnion::operator==(const float f) const
+{
+    return f == fConst;
+}
+
+bool TConstantUnion::operator==(const bool b) const
+{
+    return b == bConst;
+}
+
+bool TConstantUnion::operator==(const TConstantUnion &constant) const
+{
+    if (constant.type != type)
+        return false;
+
+    switch (type)
+    {
+        case EbtInt:
+            return constant.iConst == iConst;
+        case EbtUInt:
+            return constant.uConst == uConst;
+        case EbtFloat:
+            return constant.fConst == fConst;
+        case EbtBool:
+            return constant.bConst == bConst;
+        default:
+            return false;
+    }
+}
+
+bool TConstantUnion::operator!=(const int i) const
+{
+    return !operator==(i);
+}
+
+bool TConstantUnion::operator!=(const unsigned int u) const
+{
+    return !operator==(u);
+}
+
+bool TConstantUnion::operator!=(const float f) const
+{
+    return !operator==(f);
+}
+
+bool TConstantUnion::operator!=(const bool b) const
+{
+    return !operator==(b);
+}
+
+bool TConstantUnion::operator!=(const TConstantUnion &constant) const
+{
+    return !operator==(constant);
+}
+
+bool TConstantUnion::operator>(const TConstantUnion &constant) const
+{
+    ASSERT(type == constant.type);
+    switch (type)
+    {
+        case EbtInt:
+            return iConst > constant.iConst;
+        case EbtUInt:
+            return uConst > constant.uConst;
+        case EbtFloat:
+            return fConst > constant.fConst;
+        default:
+            return false;  // Invalid operation, handled at semantic analysis
+    }
+}
+
+bool TConstantUnion::operator<(const TConstantUnion &constant) const
+{
+    ASSERT(type == constant.type);
+    switch (type)
+    {
+        case EbtInt:
+            return iConst < constant.iConst;
+        case EbtUInt:
+            return uConst < constant.uConst;
+        case EbtFloat:
+            return fConst < constant.fConst;
+        default:
+            return false;  // Invalid operation, handled at semantic analysis
+    }
+}
+
+// static
+TConstantUnion TConstantUnion::add(const TConstantUnion &lhs,
+                                   const TConstantUnion &rhs,
+                                   TDiagnostics *diag)
+{
+    TConstantUnion returnValue;
+    ASSERT(lhs.type == rhs.type);
+    switch (lhs.type)
+    {
+        case EbtInt:
+            returnValue.setIConst(lhs.iConst + rhs.iConst);
+            break;
+        case EbtUInt:
+            returnValue.setUConst(lhs.uConst + rhs.uConst);
+            break;
+        case EbtFloat:
+            returnValue.setFConst(lhs.fConst + rhs.fConst);
+            break;
+        default:
+            UNREACHABLE();
+    }
+
+    return returnValue;
+}
+
+// static
+TConstantUnion TConstantUnion::sub(const TConstantUnion &lhs,
+                                   const TConstantUnion &rhs,
+                                   TDiagnostics *diag)
+{
+    TConstantUnion returnValue;
+    ASSERT(lhs.type == rhs.type);
+    switch (lhs.type)
+    {
+        case EbtInt:
+            returnValue.setIConst(lhs.iConst - rhs.iConst);
+            break;
+        case EbtUInt:
+            returnValue.setUConst(lhs.uConst - rhs.uConst);
+            break;
+        case EbtFloat:
+            returnValue.setFConst(lhs.fConst - rhs.fConst);
+            break;
+        default:
+            UNREACHABLE();
+    }
+
+    return returnValue;
+}
+
+// static
+TConstantUnion TConstantUnion::mul(const TConstantUnion &lhs,
+                                   const TConstantUnion &rhs,
+                                   TDiagnostics *diag)
+{
+    TConstantUnion returnValue;
+    ASSERT(lhs.type == rhs.type);
+    switch (lhs.type)
+    {
+        case EbtInt:
+            returnValue.setIConst(lhs.iConst * rhs.iConst);
+            break;
+        case EbtUInt:
+            returnValue.setUConst(lhs.uConst * rhs.uConst);
+            break;
+        case EbtFloat:
+            returnValue.setFConst(lhs.fConst * rhs.fConst);
+            break;
+        default:
+            UNREACHABLE();
+    }
+
+    return returnValue;
+}
+
+TConstantUnion TConstantUnion::operator%(const TConstantUnion &constant) const
+{
+    TConstantUnion returnValue;
+    ASSERT(type == constant.type);
+    switch (type)
+    {
+        case EbtInt:
+            returnValue.setIConst(iConst % constant.iConst);
+            break;
+        case EbtUInt:
+            returnValue.setUConst(uConst % constant.uConst);
+            break;
+        default:
+            UNREACHABLE();
+    }
+
+    return returnValue;
+}
+
+TConstantUnion TConstantUnion::operator>>(const TConstantUnion &constant) const
+{
+    TConstantUnion returnValue;
+    ASSERT(type == constant.type);
+    switch (type)
+    {
+        case EbtInt:
+            returnValue.setIConst(iConst >> constant.iConst);
+            break;
+        case EbtUInt:
+            returnValue.setUConst(uConst >> constant.uConst);
+            break;
+        default:
+            UNREACHABLE();
+    }
+
+    return returnValue;
+}
+
+TConstantUnion TConstantUnion::operator<<(const TConstantUnion &constant) const
+{
+    TConstantUnion returnValue;
+    // The signedness of the second parameter might be different, but we
+    // don't care, since the result is undefined if the second parameter is
+    // negative, and aliasing should not be a problem with unions.
+    ASSERT(constant.type == EbtInt || constant.type == EbtUInt);
+    switch (type)
+    {
+        case EbtInt:
+            returnValue.setIConst(iConst << constant.iConst);
+            break;
+        case EbtUInt:
+            returnValue.setUConst(uConst << constant.uConst);
+            break;
+        default:
+            UNREACHABLE();
+    }
+
+    return returnValue;
+}
+
+TConstantUnion TConstantUnion::operator&(const TConstantUnion &constant) const
+{
+    TConstantUnion returnValue;
+    ASSERT(constant.type == EbtInt || constant.type == EbtUInt);
+    switch (type)
+    {
+        case EbtInt:
+            returnValue.setIConst(iConst & constant.iConst);
+            break;
+        case EbtUInt:
+            returnValue.setUConst(uConst & constant.uConst);
+            break;
+        default:
+            UNREACHABLE();
+    }
+
+    return returnValue;
+}
+
+TConstantUnion TConstantUnion::operator|(const TConstantUnion &constant) const
+{
+    TConstantUnion returnValue;
+    ASSERT(type == constant.type);
+    switch (type)
+    {
+        case EbtInt:
+            returnValue.setIConst(iConst | constant.iConst);
+            break;
+        case EbtUInt:
+            returnValue.setUConst(uConst | constant.uConst);
+            break;
+        default:
+            UNREACHABLE();
+    }
+
+    return returnValue;
+}
+
+TConstantUnion TConstantUnion::operator^(const TConstantUnion &constant) const
+{
+    TConstantUnion returnValue;
+    ASSERT(type == constant.type);
+    switch (type)
+    {
+        case EbtInt:
+            returnValue.setIConst(iConst ^ constant.iConst);
+            break;
+        case EbtUInt:
+            returnValue.setUConst(uConst ^ constant.uConst);
+            break;
+        default:
+            UNREACHABLE();
+    }
+
+    return returnValue;
+}
+
+TConstantUnion TConstantUnion::operator&&(const TConstantUnion &constant) const
+{
+    TConstantUnion returnValue;
+    ASSERT(type == constant.type);
+    switch (type)
+    {
+        case EbtBool:
+            returnValue.setBConst(bConst && constant.bConst);
+            break;
+        default:
+            UNREACHABLE();
+    }
+
+    return returnValue;
+}
+
+TConstantUnion TConstantUnion::operator||(const TConstantUnion &constant) const
+{
+    TConstantUnion returnValue;
+    ASSERT(type == constant.type);
+    switch (type)
+    {
+        case EbtBool:
+            returnValue.setBConst(bConst || constant.bConst);
+            break;
+        default:
+            UNREACHABLE();
+    }
+
+    return returnValue;
+}
--- a/gfx/angle/src/compiler/translator/ConstantUnion.h
+++ b/gfx/angle/src/compiler/translator/ConstantUnion.h
@@ -4,345 +4,74 @@
 // found in the LICENSE file.
 //
 
 #ifndef COMPILER_TRANSLATOR_CONSTANTUNION_H_
 #define COMPILER_TRANSLATOR_CONSTANTUNION_H_
 
 #include <assert.h>
 
+#include "compiler/translator/Common.h"
 #include "compiler/translator/BaseTypes.h"
 
-class TConstantUnion {
-public:
-    POOL_ALLOCATOR_NEW_DELETE();
-    TConstantUnion()
-    {
-        iConst = 0;
-        type = EbtVoid;
-    }
+class TDiagnostics;
 
-    bool cast(TBasicType newType, const TConstantUnion &constant)
-    {
-        switch (newType)
-        {
-          case EbtFloat:
-            switch (constant.type)
-            {
-              case EbtInt:   setFConst(static_cast<float>(constant.getIConst())); break;
-              case EbtUInt:  setFConst(static_cast<float>(constant.getUConst())); break;
-              case EbtBool:  setFConst(static_cast<float>(constant.getBConst())); break;
-              case EbtFloat: setFConst(static_cast<float>(constant.getFConst())); break;
-              default:       return false;
-            }
-            break;
-          case EbtInt:
-            switch (constant.type)
-            {
-              case EbtInt:   setIConst(static_cast<int>(constant.getIConst())); break;
-              case EbtUInt:  setIConst(static_cast<int>(constant.getUConst())); break;
-              case EbtBool:  setIConst(static_cast<int>(constant.getBConst())); break;
-              case EbtFloat: setIConst(static_cast<int>(constant.getFConst())); break;
-              default:       return false;
-            }
-            break;
-          case EbtUInt:
-            switch (constant.type)
-            {
-              case EbtInt:   setUConst(static_cast<unsigned int>(constant.getIConst())); break;
-              case EbtUInt:  setUConst(static_cast<unsigned int>(constant.getUConst())); break;
-              case EbtBool:  setUConst(static_cast<unsigned int>(constant.getBConst())); break;
-              case EbtFloat: setUConst(static_cast<unsigned int>(constant.getFConst())); break;
-              default:       return false;
-            }
-            break;
-          case EbtBool:
-            switch (constant.type)
-            {
-              case EbtInt:   setBConst(constant.getIConst() != 0);    break;
-              case EbtUInt:  setBConst(constant.getUConst() != 0);    break;
-              case EbtBool:  setBConst(constant.getBConst());         break;
-              case EbtFloat: setBConst(constant.getFConst() != 0.0f); break;
-              default:       return false;
-            }
-            break;
-          case EbtStruct:    // Struct fields don't get cast
-            switch (constant.type)
-            {
-              case EbtInt:   setIConst(constant.getIConst()); break;
-              case EbtUInt:  setUConst(constant.getUConst()); break;
-              case EbtBool:  setBConst(constant.getBConst()); break;
-              case EbtFloat: setFConst(constant.getFConst()); break;
-              default:       return false;
-            }
-            break;
-          default:
-            return false;
-        }
+class TConstantUnion
+{
+  public:
+    POOL_ALLOCATOR_NEW_DELETE();
+    TConstantUnion();
 
-        return true;
-    }
+    bool cast(TBasicType newType, const TConstantUnion &constant);
 
     void setIConst(int i) {iConst = i; type = EbtInt; }
     void setUConst(unsigned int u) { uConst = u; type = EbtUInt; }
     void setFConst(float f) {fConst = f; type = EbtFloat; }
     void setBConst(bool b) {bConst = b; type = EbtBool; }
 
     int getIConst() const { return iConst; }
     unsigned int getUConst() const { return uConst; }
     float getFConst() const { return fConst; }
     bool getBConst() const { return bConst; }
 
-    bool operator==(const int i) const
-    {
-        return i == iConst;
-    }
-
-    bool operator==(const unsigned int u) const
-    {
-        return u == uConst;
-    }
-
-    bool operator==(const float f) const
-    {
-        return f == fConst;
-    }
-
-    bool operator==(const bool b) const
-    {
-        return b == bConst;
-    }
-
-    bool operator==(const TConstantUnion& constant) const
-    {
-        if (constant.type != type)
-            return false;
-
-        switch (type) {
-        case EbtInt:
-            return constant.iConst == iConst;
-        case EbtUInt:
-            return constant.uConst == uConst;
-        case EbtFloat:
-            return constant.fConst == fConst;
-        case EbtBool:
-            return constant.bConst == bConst;
-        default:
-            return false;
-        }
-    }
-
-    bool operator!=(const int i) const
-    {
-        return !operator==(i);
-    }
-
-    bool operator!=(const unsigned int u) const
-    {
-        return !operator==(u);
-    }
-
-    bool operator!=(const float f) const
-    {
-        return !operator==(f);
-    }
-
-    bool operator!=(const bool b) const
-    {
-        return !operator==(b);
-    }
-
-    bool operator!=(const TConstantUnion& constant) const
-    {
-        return !operator==(constant);
-    }
-
-    bool operator>(const TConstantUnion& constant) const
-    { 
-        assert(type == constant.type);
-        switch (type) {
-        case EbtInt:
-            return iConst > constant.iConst;
-        case EbtUInt:
-            return uConst > constant.uConst;
-        case EbtFloat:
-            return fConst > constant.fConst;
-        default:
-            return false;   // Invalid operation, handled at semantic analysis
-        }
-    }
-
-    bool operator<(const TConstantUnion& constant) const
-    { 
-        assert(type == constant.type);
-        switch (type) {
-        case EbtInt:
-            return iConst < constant.iConst;
-        case EbtUInt:
-            return uConst < constant.uConst;
-        case EbtFloat:
-            return fConst < constant.fConst;
-        default:
-            return false;   // Invalid operation, handled at semantic analysis
-        }
-    }
-
-    TConstantUnion operator+(const TConstantUnion& constant) const
-    { 
-        TConstantUnion returnValue;
-        assert(type == constant.type);
-        switch (type) {
-        case EbtInt: returnValue.setIConst(iConst + constant.iConst); break;
-        case EbtUInt: returnValue.setUConst(uConst + constant.uConst); break;
-        case EbtFloat: returnValue.setFConst(fConst + constant.fConst); break;
-        default: assert(false && "Default missing");
-        }
-
-        return returnValue;
-    }
-
-    TConstantUnion operator-(const TConstantUnion& constant) const
-    { 
-        TConstantUnion returnValue;
-        assert(type == constant.type);
-        switch (type) {
-        case EbtInt: returnValue.setIConst(iConst - constant.iConst); break;
-        case EbtUInt: returnValue.setUConst(uConst - constant.uConst); break;
-        case EbtFloat: returnValue.setFConst(fConst - constant.fConst); break;
-        default: assert(false && "Default missing");
-        }
-
-        return returnValue;
-    }
-
-    TConstantUnion operator*(const TConstantUnion& constant) const
-    { 
-        TConstantUnion returnValue;
-        assert(type == constant.type);
-        switch (type) {
-        case EbtInt: returnValue.setIConst(iConst * constant.iConst); break;
-        case EbtUInt: returnValue.setUConst(uConst * constant.uConst); break;
-        case EbtFloat: returnValue.setFConst(fConst * constant.fConst); break; 
-        default: assert(false && "Default missing");
-        }
-
-        return returnValue;
-    }
-
-    TConstantUnion operator%(const TConstantUnion& constant) const
-    { 
-        TConstantUnion returnValue;
-        assert(type == constant.type);
-        switch (type) {
-        case EbtInt: returnValue.setIConst(iConst % constant.iConst); break;
-        case EbtUInt: returnValue.setUConst(uConst % constant.uConst); break;
-        default:     assert(false && "Default missing");
-        }
-
-        return returnValue;
-    }
-
-    TConstantUnion operator>>(const TConstantUnion& constant) const
-    { 
-        TConstantUnion returnValue;
-        assert(type == constant.type);
-        switch (type) {
-        case EbtInt: returnValue.setIConst(iConst >> constant.iConst); break;
-        case EbtUInt: returnValue.setUConst(uConst >> constant.uConst); break;
-        default:     assert(false && "Default missing");
-        }
-
-        return returnValue;
-    }
-
-    TConstantUnion operator<<(const TConstantUnion& constant) const
-    { 
-        TConstantUnion returnValue;
-        // The signedness of the second parameter might be different, but we
-        // don't care, since the result is undefined if the second parameter is
-        // negative, and aliasing should not be a problem with unions.
-        assert(constant.type == EbtInt || constant.type == EbtUInt);
-        switch (type) {
-        case EbtInt: returnValue.setIConst(iConst << constant.iConst); break;
-        case EbtUInt: returnValue.setUConst(uConst << constant.uConst); break;
-        default:     assert(false && "Default missing");
-        }
-
-        return returnValue;
-    }
-
-    TConstantUnion operator&(const TConstantUnion& constant) const
-    { 
-        TConstantUnion returnValue;
-        assert(constant.type == EbtInt || constant.type == EbtUInt);
-        switch (type) {
-        case EbtInt:  returnValue.setIConst(iConst & constant.iConst); break;
-        case EbtUInt:  returnValue.setUConst(uConst & constant.uConst); break;
-        default:     assert(false && "Default missing");
-        }
-
-        return returnValue;
-    }
-
-    TConstantUnion operator|(const TConstantUnion& constant) const
-    { 
-        TConstantUnion returnValue;
-        assert(type == constant.type);
-        switch (type) {
-        case EbtInt:  returnValue.setIConst(iConst | constant.iConst); break;
-        case EbtUInt:  returnValue.setUConst(uConst | constant.uConst); break;
-        default:     assert(false && "Default missing");
-        }
-
-        return returnValue;
-    }
-
-    TConstantUnion operator^(const TConstantUnion& constant) const
-    { 
-        TConstantUnion returnValue;
-        assert(type == constant.type);
-        switch (type) {
-        case EbtInt:  returnValue.setIConst(iConst ^ constant.iConst); break;
-        case EbtUInt:  returnValue.setUConst(uConst ^ constant.uConst); break;
-        default:     assert(false && "Default missing");
-        }
-
-        return returnValue;
-    }
-
-    TConstantUnion operator&&(const TConstantUnion& constant) const
-    { 
-        TConstantUnion returnValue;
-        assert(type == constant.type);
-        switch (type) {
-        case EbtBool: returnValue.setBConst(bConst && constant.bConst); break;
-        default:     assert(false && "Default missing");
-        }
-
-        return returnValue;
-    }
-
-    TConstantUnion operator||(const TConstantUnion& constant) const
-    { 
-        TConstantUnion returnValue;
-        assert(type == constant.type);
-        switch (type) {
-        case EbtBool: returnValue.setBConst(bConst || constant.bConst); break;
-        default:     assert(false && "Default missing");
-        }
-
-        return returnValue;
-    }
+    bool operator==(const int i) const;
+    bool operator==(const unsigned int u) const;
+    bool operator==(const float f) const;
+    bool operator==(const bool b) const;
+    bool operator==(const TConstantUnion &constant) const;
+    bool operator!=(const int i) const;
+    bool operator!=(const unsigned int u) const;
+    bool operator!=(const float f) const;
+    bool operator!=(const bool b) const;
+    bool operator!=(const TConstantUnion &constant) const;
+    bool operator>(const TConstantUnion &constant) const;
+    bool operator<(const TConstantUnion &constant) const;
+    static TConstantUnion add(const TConstantUnion &lhs,
+                              const TConstantUnion &rhs,
+                              TDiagnostics *diag);
+    static TConstantUnion sub(const TConstantUnion &lhs,
+                              const TConstantUnion &rhs,
+                              TDiagnostics *diag);
+    static TConstantUnion mul(const TConstantUnion &lhs,
+                              const TConstantUnion &rhs,
+                              TDiagnostics *diag);
+    TConstantUnion operator%(const TConstantUnion &constant) const;
+    TConstantUnion operator>>(const TConstantUnion &constant) const;
+    TConstantUnion operator<<(const TConstantUnion &constant) const;
+    TConstantUnion operator&(const TConstantUnion &constant) const;
+    TConstantUnion operator|(const TConstantUnion &constant) const;
+    TConstantUnion operator^(const TConstantUnion &constant) const;
+    TConstantUnion operator&&(const TConstantUnion &constant) const;
+    TConstantUnion operator||(const TConstantUnion &constant) const;
 
     TBasicType getType() const { return type; }
-private:
-
-    union  {
-        int iConst;  // used for ivec, scalar ints
-        unsigned int uConst; // used for uvec, scalar uints
-        bool bConst; // used for bvec, scalar bools
-        float fConst;   // used for vec, mat, scalar floats
-    } ;
+  private:
+    union {
+        int iConst;           // used for ivec, scalar ints
+        unsigned int uConst;  // used for uvec, scalar uints
+        bool bConst;          // used for bvec, scalar bools
+        float fConst;         // used for vec, mat, scalar floats
+    };
 
     TBasicType type;
 };
 
 #endif // COMPILER_TRANSLATOR_CONSTANTUNION_H_
--- a/gfx/angle/src/compiler/translator/DeferGlobalInitializers.cpp
+++ b/gfx/angle/src/compiler/translator/DeferGlobalInitializers.cpp
@@ -96,20 +96,18 @@ bool DeferGlobalInitializersTraverser::v
                                (expression->getAsConstantUnion() == nullptr &&
                                 !expression->isConstructorWithOnlyConstantUnionParameters())))
         {
             // For variables which are not constant, defer their real initialization until
             // after we initialize uniforms.
             // Deferral is done also in any cases where the variable has not been constant folded,
             // since otherwise there's a chance that HLSL output will generate extra statements
             // from the initializer expression.
-            TIntermBinary *deferredInit = new TIntermBinary(EOpAssign);
-            deferredInit->setLeft(symbolNode->deepCopy());
-            deferredInit->setRight(node->getRight());
-            deferredInit->setType(node->getType());
+            TIntermBinary *deferredInit =
+                new TIntermBinary(EOpAssign, symbolNode->deepCopy(), node->getRight());
             mDeferredInitializers.push_back(deferredInit);
 
             // Change const global to a regular global if its initialization is deferred.
             // This can happen if ANGLE has not been able to fold the constant expression used
             // as an initializer.
             ASSERT(symbolNode->getQualifier() == EvqConst ||
                    symbolNode->getQualifier() == EvqGlobal);
             if (symbolNode->getQualifier() == EvqConst)
--- a/gfx/angle/src/compiler/translator/EmulateGLFragColorBroadcast.cpp
+++ b/gfx/angle/src/compiler/translator/EmulateGLFragColorBroadcast.cpp
@@ -12,66 +12,64 @@
 //
 
 #include "compiler/translator/EmulateGLFragColorBroadcast.h"
 #include "compiler/translator/IntermNode.h"
 
 namespace
 {
 
-TIntermConstantUnion *constructIndexNode(int index)
-{
-    TConstantUnion *u = new TConstantUnion[1];
-    u[0].setIConst(index);
-
-    TType type(EbtInt, EbpUndefined, EvqConst, 1);
-    TIntermConstantUnion *node = new TIntermConstantUnion(u, type);
-    return node;
-}
-
-TIntermBinary *constructGLFragDataNode(int index)
-{
-    TIntermBinary *indexDirect = new TIntermBinary(EOpIndexDirect);
-    TIntermSymbol *symbol      = new TIntermSymbol(0, "gl_FragData", TType(EbtFloat, 4));
-    indexDirect->setLeft(symbol);
-    TIntermConstantUnion *indexNode = constructIndexNode(index);
-    indexDirect->setRight(indexNode);
-    return indexDirect;
-}
-
-TIntermBinary *constructGLFragDataAssignNode(int index)
-{
-    TIntermBinary *assign = new TIntermBinary(EOpAssign);
-    assign->setLeft(constructGLFragDataNode(index));
-    assign->setRight(constructGLFragDataNode(0));
-    assign->setType(TType(EbtFloat, 4));
-    return assign;
-}
-
 class GLFragColorBroadcastTraverser : public TIntermTraverser
 {
   public:
-    GLFragColorBroadcastTraverser()
-        : TIntermTraverser(true, false, false), mMainSequence(nullptr), mGLFragColorUsed(false)
+    GLFragColorBroadcastTraverser(int maxDrawBuffers)
+        : TIntermTraverser(true, false, false),
+          mMainSequence(nullptr),
+          mGLFragColorUsed(false),
+          mMaxDrawBuffers(maxDrawBuffers)
     {
     }
 
-    void broadcastGLFragColor(int maxDrawBuffers);
+    void broadcastGLFragColor();
 
     bool isGLFragColorUsed() const { return mGLFragColorUsed; }
 
   protected:
     void visitSymbol(TIntermSymbol *node) override;
     bool visitAggregate(Visit visit, TIntermAggregate *node) override;
 
+    TIntermBinary *constructGLFragDataNode(int index) const;
+    TIntermBinary *constructGLFragDataAssignNode(int index) const;
+
   private:
     TIntermSequence *mMainSequence;
     bool mGLFragColorUsed;
+    int mMaxDrawBuffers;
 };
 
+TIntermBinary *GLFragColorBroadcastTraverser::constructGLFragDataNode(int index) const
+{
+    TType gl_FragDataType = TType(EbtFloat, EbpMedium, EvqFragData, 4);
+    gl_FragDataType.setArraySize(mMaxDrawBuffers);
+
+    TIntermSymbol *symbol   = new TIntermSymbol(0, "gl_FragData", gl_FragDataType);
+    TIntermTyped *indexNode = TIntermTyped::CreateIndexNode(index);
+
+    TIntermBinary *binary = new TIntermBinary(EOpIndexDirect, symbol, indexNode);
+    return binary;
+}
+
+TIntermBinary *GLFragColorBroadcastTraverser::constructGLFragDataAssignNode(int index) const
+{
+    TIntermTyped *fragDataIndex = constructGLFragDataNode(index);
+    TIntermTyped *fragDataZero  = constructGLFragDataNode(0);
+
+    return new TIntermBinary(EOpAssign, fragDataIndex, fragDataZero);
+}
+
 void GLFragColorBroadcastTraverser::visitSymbol(TIntermSymbol *node)
 {
     if (node->getSymbol() == "gl_FragColor")
     {
         queueReplacement(node, constructGLFragDataNode(0), OriginalNode::IS_DROPPED);
         mGLFragColorUsed = true;
     }
 }
@@ -96,47 +94,47 @@ bool GLFragColorBroadcastTraverser::visi
             }
             break;
         default:
             break;
     }
     return true;
 }
 
-void GLFragColorBroadcastTraverser::broadcastGLFragColor(int maxDrawBuffers)
+void GLFragColorBroadcastTraverser::broadcastGLFragColor()
 {
-    ASSERT(maxDrawBuffers > 1);
+    ASSERT(mMaxDrawBuffers > 1);
     if (!mGLFragColorUsed)
     {
         return;
     }
     ASSERT(mMainSequence);
     // Now insert statements
     //   gl_FragData[1] = gl_FragData[0];
     //   ...
     //   gl_FragData[maxDrawBuffers - 1] = gl_FragData[0];
-    for (int colorIndex = 1; colorIndex < maxDrawBuffers; ++colorIndex)
+    for (int colorIndex = 1; colorIndex < mMaxDrawBuffers; ++colorIndex)
     {
         mMainSequence->insert(mMainSequence->end(), constructGLFragDataAssignNode(colorIndex));
     }
 }
 
 }  // namespace anonymous
 
 void EmulateGLFragColorBroadcast(TIntermNode *root,
                                  int maxDrawBuffers,
                                  std::vector<sh::OutputVariable> *outputVariables)
 {
     ASSERT(maxDrawBuffers > 1);
-    GLFragColorBroadcastTraverser traverser;
+    GLFragColorBroadcastTraverser traverser(maxDrawBuffers);
     root->traverse(&traverser);
     if (traverser.isGLFragColorUsed())
     {
         traverser.updateTree();
-        traverser.broadcastGLFragColor(maxDrawBuffers);
+        traverser.broadcastGLFragColor();
         for (auto &var : *outputVariables)
         {
             if (var.name == "gl_FragColor")
             {
                 // TODO(zmo): Find a way to keep the original variable information.
                 var.name       = "gl_FragData";
                 var.mappedName = "gl_FragData";
                 var.arraySize  = maxDrawBuffers;
--- a/gfx/angle/src/compiler/translator/ExpandIntegerPowExpressions.cpp
+++ b/gfx/angle/src/compiler/translator/ExpandIntegerPowExpressions.cpp
@@ -119,33 +119,28 @@ bool Traverser::visitAggregate(Visit vis
     TIntermAggregate *init = createTempInitDeclaration(lhs);
     TIntermTyped *current  = createTempSymbol(lhs->getType());
 
     insertStatementInParentBlock(init);
 
     // Create a chain of n-1 multiples.
     for (int i = 1; i < n; ++i)
     {
-        TIntermBinary *mul = new TIntermBinary(EOpMul);
-        mul->setLeft(current);
-        mul->setRight(createTempSymbol(lhs->getType()));
-        mul->setType(node->getType());
+        TIntermBinary *mul = new TIntermBinary(EOpMul, current, createTempSymbol(lhs->getType()));
         mul->setLine(node->getLine());
         current = mul;
     }
 
     // For negative pow, compute the reciprocal of the positive pow.
     if (exponent < 0)
     {
         TConstantUnion *oneVal = new TConstantUnion();
         oneVal->setFConst(1.0f);
         TIntermConstantUnion *oneNode = new TIntermConstantUnion(oneVal, node->getType());
-        TIntermBinary *div            = new TIntermBinary(EOpDiv);
-        div->setLeft(oneNode);
-        div->setRight(current);
+        TIntermBinary *div            = new TIntermBinary(EOpDiv, oneNode, current);
         current = div;
     }
 
     queueReplacement(node, current, OriginalNode::IS_DROPPED);
     mFound = true;
     return false;
 }
 
--- a/gfx/angle/src/compiler/translator/Initialize.cpp
+++ b/gfx/angle/src/compiler/translator/Initialize.cpp
@@ -506,26 +506,23 @@ void InsertBuiltInFunctions(sh::GLenum t
     symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxTextureImageUnits",
                                resources.MaxTextureImageUnits, EbpMedium);
     symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxFragmentUniformVectors",
                                resources.MaxFragmentUniformVectors, EbpMedium);
 
     symbolTable.insertConstInt(ESSL1_BUILTINS, "gl_MaxVaryingVectors", resources.MaxVaryingVectors,
                                EbpMedium);
 
-    if (spec != SH_CSS_SHADERS_SPEC)
+    symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxDrawBuffers", resources.MaxDrawBuffers,
+                               EbpMedium);
+    if (resources.EXT_blend_func_extended)
     {
-        symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxDrawBuffers", resources.MaxDrawBuffers,
-                                   EbpMedium);
-        if (resources.EXT_blend_func_extended)
-        {
-            symbolTable.insertConstIntExt(COMMON_BUILTINS, "GL_EXT_blend_func_extended",
-                                          "gl_MaxDualSourceDrawBuffersEXT",
-                                          resources.MaxDualSourceDrawBuffers);
-        }
+        symbolTable.insertConstIntExt(COMMON_BUILTINS, "GL_EXT_blend_func_extended",
+                                      "gl_MaxDualSourceDrawBuffersEXT",
+                                      resources.MaxDualSourceDrawBuffers);
     }
 
     symbolTable.insertConstInt(ESSL3_BUILTINS, "gl_MaxVertexOutputVectors",
                                resources.MaxVertexOutputVectors, EbpMedium);
     symbolTable.insertConstInt(ESSL3_BUILTINS, "gl_MaxFragmentInputVectors",
                                resources.MaxFragmentInputVectors, EbpMedium);
     symbolTable.insertConstInt(ESSL3_BUILTINS, "gl_MinProgramTexelOffset",
                                resources.MinProgramTexelOffset, EbpMedium);
@@ -585,95 +582,83 @@ void IdentifyBuiltIns(sh::GLenum type, S
 {
     //
     // Insert some special built-in variables that are not in
     // the built-in header files.
     //
     switch (type)
     {
       case GL_FRAGMENT_SHADER:
-        symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_FragCoord"),
-            TType(EbtFloat, EbpMedium, EvqFragCoord, 4)));
-        symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_FrontFacing"),
-            TType(EbtBool,  EbpUndefined, EvqFrontFacing, 1)));
-        symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_PointCoord"),
-            TType(EbtFloat, EbpMedium, EvqPointCoord, 2)));
+      {
+          symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_FragCoord"),
+              TType(EbtFloat, EbpMedium, EvqFragCoord, 4)));
+          symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_FrontFacing"),
+              TType(EbtBool,  EbpUndefined, EvqFrontFacing, 1)));
+          symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_PointCoord"),
+              TType(EbtFloat, EbpMedium, EvqPointCoord, 2)));
 
-        //
-        // In CSS Shaders, gl_FragColor, gl_FragData, and gl_MaxDrawBuffers are not available.
-        // Instead, css_MixColor and css_ColorMatrix are available.
-        //
-        if (spec != SH_CSS_SHADERS_SPEC)
-        {
-            symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("gl_FragColor"),
-                TType(EbtFloat, EbpMedium, EvqFragColor, 4)));
-            TType fragData(EbtFloat, EbpMedium, EvqFragData, 4, 1, true);
-            fragData.setArraySize(resources.MaxDrawBuffers);
-            symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("gl_FragData"), fragData));
+          symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("gl_FragColor"),
+              TType(EbtFloat, EbpMedium, EvqFragColor, 4)));
+          TType fragData(EbtFloat, EbpMedium, EvqFragData, 4, 1, true);
+          fragData.setArraySize(resources.MaxDrawBuffers);
+          symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("gl_FragData"), fragData));
 
-            if (resources.EXT_blend_func_extended)
-            {
-                symbolTable.insert(
-                    ESSL1_BUILTINS, "GL_EXT_blend_func_extended",
-                    new TVariable(NewPoolTString("gl_SecondaryFragColorEXT"),
-                                  TType(EbtFloat, EbpMedium, EvqSecondaryFragColorEXT, 4)));
-                TType secondaryFragData(EbtFloat, EbpMedium, EvqSecondaryFragDataEXT, 4, 1, true);
-                secondaryFragData.setArraySize(resources.MaxDualSourceDrawBuffers);
-                symbolTable.insert(
-                    ESSL1_BUILTINS, "GL_EXT_blend_func_extended",
-                    new TVariable(NewPoolTString("gl_SecondaryFragDataEXT"), secondaryFragData));
-            }
+          if (resources.EXT_blend_func_extended)
+          {
+              symbolTable.insert(
+                  ESSL1_BUILTINS, "GL_EXT_blend_func_extended",
+                  new TVariable(NewPoolTString("gl_SecondaryFragColorEXT"),
+                                TType(EbtFloat, EbpMedium, EvqSecondaryFragColorEXT, 4)));
+              TType secondaryFragData(EbtFloat, EbpMedium, EvqSecondaryFragDataEXT, 4, 1, true);
+              secondaryFragData.setArraySize(resources.MaxDualSourceDrawBuffers);
+              symbolTable.insert(
+                  ESSL1_BUILTINS, "GL_EXT_blend_func_extended",
+                  new TVariable(NewPoolTString("gl_SecondaryFragDataEXT"), secondaryFragData));
+          }
 
-            if (resources.EXT_frag_depth)
-            {
-                symbolTable.insert(
-                    ESSL1_BUILTINS, "GL_EXT_frag_depth",
-                    new TVariable(
-                        NewPoolTString("gl_FragDepthEXT"),
-                        TType(EbtFloat, resources.FragmentPrecisionHigh ? EbpHigh : EbpMedium,
-                              EvqFragDepthEXT, 1)));
-            }
+          if (resources.EXT_frag_depth)
+          {
+              symbolTable.insert(
+                  ESSL1_BUILTINS, "GL_EXT_frag_depth",
+                  new TVariable(
+                      NewPoolTString("gl_FragDepthEXT"),
+                      TType(EbtFloat, resources.FragmentPrecisionHigh ? EbpHigh : EbpMedium,
+                            EvqFragDepthEXT, 1)));
+          }
 
-            symbolTable.insert(ESSL3_BUILTINS,
-                               new TVariable(NewPoolTString("gl_FragDepth"),
-                                             TType(EbtFloat, EbpHigh, EvqFragDepth, 1)));
+          symbolTable.insert(ESSL3_BUILTINS,
+                             new TVariable(NewPoolTString("gl_FragDepth"),
+                                           TType(EbtFloat, EbpHigh, EvqFragDepth, 1)));
 
-            if (resources.EXT_shader_framebuffer_fetch || resources.NV_shader_framebuffer_fetch)
-            {
-                TType lastFragData(EbtFloat, EbpMedium, EvqLastFragData, 4, 1, true);
-                lastFragData.setArraySize(resources.MaxDrawBuffers);
+          if (resources.EXT_shader_framebuffer_fetch || resources.NV_shader_framebuffer_fetch)
+          {
+              TType lastFragData(EbtFloat, EbpMedium, EvqLastFragData, 4, 1, true);
+              lastFragData.setArraySize(resources.MaxDrawBuffers);
 
-                if (resources.EXT_shader_framebuffer_fetch)
-                {
-                    symbolTable.insert(ESSL1_BUILTINS, "GL_EXT_shader_framebuffer_fetch",
-                        new TVariable(NewPoolTString("gl_LastFragData"), lastFragData));
-                }
-                else if (resources.NV_shader_framebuffer_fetch)
-                {
-                    symbolTable.insert(ESSL1_BUILTINS, "GL_NV_shader_framebuffer_fetch",
-                        new TVariable(NewPoolTString("gl_LastFragColor"),
-                        TType(EbtFloat, EbpMedium, EvqLastFragColor, 4)));
-                    symbolTable.insert(ESSL1_BUILTINS, "GL_NV_shader_framebuffer_fetch",
-                        new TVariable(NewPoolTString("gl_LastFragData"), lastFragData));
-                }
-            }
-            else if (resources.ARM_shader_framebuffer_fetch)
-            {
-                symbolTable.insert(ESSL1_BUILTINS, "GL_ARM_shader_framebuffer_fetch",
-                    new TVariable(NewPoolTString("gl_LastFragColorARM"),
-                    TType(EbtFloat, EbpMedium, EvqLastFragColor, 4)));
-            }
-        }
-        else
-        {
-            symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("css_MixColor"),
-                TType(EbtFloat, EbpMedium, EvqGlobal, 4)));
-            symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("css_ColorMatrix"),
-                TType(EbtFloat, EbpMedium, EvqGlobal, 4, 4)));
-        }
+              if (resources.EXT_shader_framebuffer_fetch)
+              {
+                  symbolTable.insert(ESSL1_BUILTINS, "GL_EXT_shader_framebuffer_fetch",
+                      new TVariable(NewPoolTString("gl_LastFragData"), lastFragData));
+              }
+              else if (resources.NV_shader_framebuffer_fetch)
+              {
+                  symbolTable.insert(ESSL1_BUILTINS, "GL_NV_shader_framebuffer_fetch",
+                      new TVariable(NewPoolTString("gl_LastFragColor"),
+                      TType(EbtFloat, EbpMedium, EvqLastFragColor, 4)));
+                  symbolTable.insert(ESSL1_BUILTINS, "GL_NV_shader_framebuffer_fetch",
+                      new TVariable(NewPoolTString("gl_LastFragData"), lastFragData));
+              }
+          }
+          else if (resources.ARM_shader_framebuffer_fetch)
+          {
+              symbolTable.insert(ESSL1_BUILTINS, "GL_ARM_shader_framebuffer_fetch",
+                  new TVariable(NewPoolTString("gl_LastFragColorARM"),
+                  TType(EbtFloat, EbpMedium, EvqLastFragColor, 4)));
+          }
+      }
 
         break;
 
       case GL_VERTEX_SHADER:
         symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_Position"),
             TType(EbtFloat, EbpHigh, EvqPosition, 4)));
         symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_PointSize"),
             TType(EbtFloat, EbpMedium, EvqPointSize, 1)));
--- a/gfx/angle/src/compiler/translator/InitializeVariables.cpp
+++ b/gfx/angle/src/compiler/translator/InitializeVariables.cpp
@@ -9,56 +9,16 @@
 #include "angle_gl.h"
 #include "common/debug.h"
 #include "compiler/translator/IntermNode.h"
 #include "compiler/translator/util.h"
 
 namespace
 {
 
-TIntermConstantUnion *constructConstUnionNode(const TType &type)
-{
-    TType myType = type;
-    myType.clearArrayness();
-    myType.setQualifier(EvqConst);
-    size_t size          = myType.getObjectSize();
-    TConstantUnion *u = new TConstantUnion[size];
-    for (size_t ii = 0; ii < size; ++ii)
-    {
-        switch (type.getBasicType())
-        {
-            case EbtFloat:
-                u[ii].setFConst(0.0f);
-                break;
-            case EbtInt:
-                u[ii].setIConst(0);
-                break;
-            case EbtUInt:
-                u[ii].setUConst(0u);
-                break;
-            default:
-                UNREACHABLE();
-                return nullptr;
-        }
-    }
-
-    TIntermConstantUnion *node = new TIntermConstantUnion(u, myType);
-    return node;
-}
-
-TIntermConstantUnion *constructIndexNode(int index)
-{
-    TConstantUnion *u = new TConstantUnion[1];
-    u[0].setIConst(index);
-
-    TType type(EbtInt, EbpUndefined, EvqConst, 1);
-    TIntermConstantUnion *node = new TIntermConstantUnion(u, type);
-    return node;
-}
-
 class VariableInitializer : public TIntermTraverser
 {
   public:
     VariableInitializer(const InitVariableList &vars)
         : TIntermTraverser(true, false, false), mVariables(vars), mCodeInserted(false)
     {
     }
 
@@ -115,83 +75,53 @@ bool VariableInitializer::visitAggregate
         visitChildren = false;
         break;
     }
     return visitChildren;
 }
 
 void VariableInitializer::insertInitCode(TIntermSequence *sequence)
 {
-    for (size_t ii = 0; ii < mVariables.size(); ++ii)
+    for (const auto &var : mVariables)
     {
-        const sh::ShaderVariable &var = mVariables[ii];
         TString name = TString(var.name.c_str());
+        TType type   = sh::GetShaderVariableType(var);
+
+        // Assign the array elements one by one to keep the AST compatible with ESSL 1.00 which
+        // doesn't have array assignment.
         if (var.isArray())
         {
-            TType type = sh::ConvertShaderVariableTypeToTType(var.type);
             size_t pos = name.find_last_of('[');
             if (pos != TString::npos)
+            {
                 name = name.substr(0, pos);
-            for (int index = static_cast<int>(var.arraySize) - 1; index >= 0; --index)
-            {
-                TIntermBinary *assign = new TIntermBinary(EOpAssign);
-                sequence->insert(sequence->begin(), assign);
-
-                TIntermBinary *indexDirect = new TIntermBinary(EOpIndexDirect);
-                TIntermSymbol *symbol      = new TIntermSymbol(0, name, type);
-                indexDirect->setLeft(symbol);
-                TIntermConstantUnion *indexNode = constructIndexNode(index);
-                indexDirect->setRight(indexNode);
-
-                assign->setLeft(indexDirect);
-
-                TIntermConstantUnion *zeroConst = constructConstUnionNode(type);
-                assign->setRight(zeroConst);
             }
-        }
-        else if (var.isStruct())
-        {
-            TFieldList *fields = new TFieldList;
-            TSourceLoc loc;
-            for (auto field : var.fields)
-            {
-                fields->push_back(new TField(nullptr, new TString(field.name.c_str()), loc));
-            }
-            TStructure *structure = new TStructure(new TString(var.structName.c_str()), fields);
-            TType type;
-            type.setStruct(structure);
-            for (int fieldIndex = 0; fieldIndex < static_cast<int>(var.fields.size()); ++fieldIndex)
+            TType elementType = type;
+            elementType.clearArrayness();
+
+            for (unsigned int i = 0; i < var.arraySize; ++i)
             {
-                TIntermBinary *assign = new TIntermBinary(EOpAssign);
-                sequence->insert(sequence->begin(), assign);
+                TIntermSymbol *arraySymbol = new TIntermSymbol(0, name, type);
+                TIntermBinary *element     = new TIntermBinary(EOpIndexDirect, arraySymbol,
+                                                           TIntermTyped::CreateIndexNode(i));
 
-                TIntermBinary *indexDirectStruct = new TIntermBinary(EOpIndexDirectStruct);
-                TIntermSymbol *symbol            = new TIntermSymbol(0, name, type);
-                indexDirectStruct->setLeft(symbol);
-                TIntermConstantUnion *indexNode = constructIndexNode(fieldIndex);
-                indexDirectStruct->setRight(indexNode);
-                assign->setLeft(indexDirectStruct);
+                TIntermTyped *zero        = TIntermTyped::CreateZero(elementType);
+                TIntermBinary *assignment = new TIntermBinary(EOpAssign, element, zero);
 
-                const sh::ShaderVariable &field = var.fields[fieldIndex];
-                TType fieldType                 = sh::ConvertShaderVariableTypeToTType(field.type);
-                TIntermConstantUnion *zeroConst = constructConstUnionNode(fieldType);
-                assign->setRight(zeroConst);
+                sequence->insert(sequence->begin(), assignment);
             }
         }
         else
         {
-            TType type            = sh::ConvertShaderVariableTypeToTType(var.type);
-            TIntermBinary *assign = new TIntermBinary(EOpAssign);
+            TIntermSymbol *symbol = new TIntermSymbol(0, name, type);
+            TIntermTyped *zero    = TIntermTyped::CreateZero(type);
+
+            TIntermBinary *assign = new TIntermBinary(EOpAssign, symbol, zero);
             sequence->insert(sequence->begin(), assign);
-            TIntermSymbol *symbol = new TIntermSymbol(0, name, type);
-            assign->setLeft(symbol);
-            TIntermConstantUnion *zeroConst = constructConstUnionNode(type);
-            assign->setRight(zeroConst);
         }
-
     }
 }
 
 }  // namespace anonymous
 
 void InitializeVariables(TIntermNode *root, const InitVariableList &vars)
 {
     VariableInitializer initializer(vars);
--- a/gfx/angle/src/compiler/translator/InitializeVariables.h
+++ b/gfx/angle/src/compiler/translator/InitializeVariables.h
@@ -8,11 +8,12 @@
 #define COMPILER_TRANSLATOR_INITIALIZEVARIABLES_H_
 
 #include <GLSLANG/ShaderLang.h>
 
 class TIntermNode;
 
 typedef std::vector<sh::ShaderVariable> InitVariableList;
 
+// This function cannot currently initialize structures containing arrays for an ESSL 1.00 backend.
 void InitializeVariables(TIntermNode *root, const InitVariableList &vars);
 
 #endif  // COMPILER_TRANSLATOR_INITIALIZEVARIABLES_H_
--- a/gfx/angle/src/compiler/translator/IntermNode.cpp
+++ b/gfx/angle/src/compiler/translator/IntermNode.cpp
@@ -16,16 +16,17 @@
 #include <vector>
 
 #include "common/mathutil.h"
 #include "common/matrix_utils.h"
 #include "compiler/translator/Diagnostics.h"
 #include "compiler/translator/HashNames.h"
 #include "compiler/translator/IntermNode.h"
 #include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/util.h"
 
 namespace
 {
 
 const float kPi = 3.14159265358979323846f;
 const float kDegreesToRadiansMultiplier = kPi / 180.0f;
 const float kRadiansToDegreesMultiplier = 180.0f / kPi;
 
@@ -38,23 +39,24 @@ TConstantUnion *Vectorize(const TConstan
 {
     TConstantUnion *constUnion = new TConstantUnion[size];
     for (unsigned int i = 0; i < size; ++i)
         constUnion[i] = constant;
 
     return constUnion;
 }
 
-void UndefinedConstantFoldingError(const TSourceLoc &loc, TOperator op, TBasicType basicType,
-                                   TInfoSink &infoSink, TConstantUnion *result)
+void UndefinedConstantFoldingError(const TSourceLoc &loc,
+                                   TOperator op,
+                                   TBasicType basicType,
+                                   TDiagnostics *diagnostics,
+                                   TConstantUnion *result)
 {
-    std::stringstream constantFoldingErrorStream;
-    constantFoldingErrorStream << "'" << GetOperatorString(op)
-                               << "' operation result is undefined for the values passed in";
-    infoSink.info.message(EPrefixWarning, loc, constantFoldingErrorStream.str().c_str());
+    diagnostics->warning(loc, "operation result is undefined for the values passed in",
+                         GetOperatorString(op), "");
 
     switch (basicType)
     {
       case EbtFloat :
         result->setFConst(0.0f);
         break;
       case EbtInt:
         result->setIConst(0);
@@ -86,17 +88,17 @@ float VectorDotProduct(const TConstantUn
                        size_t paramArraySize)
 {
     float result = 0.0f;
     for (size_t i = 0; i < paramArraySize; i++)
         result += paramArray1[i].getFConst() * paramArray2[i].getFConst();
     return result;
 }
 
-TIntermTyped *CreateFoldedNode(TConstantUnion *constArray,
+TIntermTyped *CreateFoldedNode(const TConstantUnion *constArray,
                                const TIntermTyped *originalNode,
                                TQualifier qualifier)
 {
     if (constArray == nullptr)
     {
         return nullptr;
     }
     TIntermTyped *folded = new TIntermConstantUnion(constArray, originalNode->getType());
@@ -185,16 +187,17 @@ bool TIntermBinary::replaceChildNode(
     REPLACE_IF_IS(mLeft, TIntermTyped, original, replacement);
     REPLACE_IF_IS(mRight, TIntermTyped, original, replacement);
     return false;
 }
 
 bool TIntermUnary::replaceChildNode(
     TIntermNode *original, TIntermNode *replacement)
 {
+    ASSERT(original->getAsTyped()->getType() == replacement->getAsTyped()->getType());
     REPLACE_IF_IS(mOperand, TIntermTyped, original, replacement);
     return false;
 }
 
 bool TIntermAggregate::replaceChildNode(
     TIntermNode *original, TIntermNode *replacement)
 {
     for (size_t ii = 0; ii < mSequence.size(); ++ii)
@@ -284,16 +287,24 @@ void TIntermAggregate::setBuiltInFunctio
     // ESSL 3.0 spec section 8: textureSize always gets highp precision.
     // All other functions that take a sampler are assumed to be texture functions.
     if (mName.getString().find("textureSize") == 0)
         mType.setPrecision(EbpHigh);
     else
         mType.setPrecision(precision);
 }
 
+bool TIntermTernary::replaceChildNode(TIntermNode *original, TIntermNode *replacement)
+{
+    REPLACE_IF_IS(mCondition, TIntermTyped, original, replacement);
+    REPLACE_IF_IS(mTrueExpression, TIntermTyped, original, replacement);
+    REPLACE_IF_IS(mFalseExpression, TIntermTyped, original, replacement);
+    return false;
+}
+
 bool TIntermSelection::replaceChildNode(
     TIntermNode *original, TIntermNode *replacement)
 {
     REPLACE_IF_IS(mCondition, TIntermTyped, original, replacement);
     REPLACE_IF_IS(mTrueBlock, TIntermNode, original, replacement);
     REPLACE_IF_IS(mFalseBlock, TIntermNode, original, replacement);
     return false;
 }
@@ -331,16 +342,93 @@ bool TIntermTyped::isConstructorWithOnly
     for (TIntermNode *&node : *constructor->getSequence())
     {
         if (!node->getAsConstantUnion())
             return false;
     }
     return true;
 }
 
+// static
+TIntermTyped *TIntermTyped::CreateIndexNode(int index)
+{
+    TConstantUnion *u = new TConstantUnion[1];
+    u[0].setIConst(index);
+
+    TType type(EbtInt, EbpUndefined, EvqConst, 1);
+    TIntermConstantUnion *node = new TIntermConstantUnion(u, type);
+    return node;
+}
+
+// static
+TIntermTyped *TIntermTyped::CreateZero(const TType &type)
+{
+    TType constType(type);
+    constType.setQualifier(EvqConst);
+
+    if (!type.isArray() && type.getBasicType() != EbtStruct)
+    {
+        ASSERT(type.isScalar() || type.isVector() || type.isMatrix());
+
+        size_t size       = constType.getObjectSize();
+        TConstantUnion *u = new TConstantUnion[size];
+        for (size_t i = 0; i < size; ++i)
+        {
+            switch (type.getBasicType())
+            {
+                case EbtFloat:
+                    u[i].setFConst(0.0f);
+                    break;
+                case EbtInt:
+                    u[i].setIConst(0);
+                    break;
+                case EbtUInt:
+                    u[i].setUConst(0u);
+                    break;
+                case EbtBool:
+                    u[i].setBConst(false);
+                    break;
+                default:
+                    UNREACHABLE();
+                    return nullptr;
+            }
+        }
+
+        TIntermConstantUnion *node = new TIntermConstantUnion(u, constType);
+        return node;
+    }
+
+    TIntermAggregate *constructor = new TIntermAggregate(sh::TypeToConstructorOperator(type));
+    constructor->setType(constType);
+
+    if (type.isArray())
+    {
+        TType elementType(type);
+        elementType.clearArrayness();
+
+        size_t arraySize = type.getArraySize();
+        for (size_t i = 0; i < arraySize; ++i)
+        {
+            constructor->getSequence()->push_back(CreateZero(elementType));
+        }
+    }
+    else
+    {
+        ASSERT(type.getBasicType() == EbtStruct);
+
+        TStructure *structure = type.getStruct();
+        for (const auto &field : structure->fields())
+        {
+            constructor->getSequence()->push_back(CreateZero(*field->type()));
+        }
+    }
+
+    return constructor;
+}
+
 TIntermConstantUnion::TIntermConstantUnion(const TIntermConstantUnion &node) : TIntermTyped(node)
 {
     mUnionArrayPointer = node.mUnionArrayPointer;
 }
 
 TIntermAggregate::TIntermAggregate(const TIntermAggregate &node)
     : TIntermOperator(node),
       mName(node.mName),
@@ -371,30 +459,25 @@ TIntermBinary::TIntermBinary(const TInte
 TIntermUnary::TIntermUnary(const TIntermUnary &node)
     : TIntermOperator(node), mUseEmulatedFunction(node.mUseEmulatedFunction)
 {
     TIntermTyped *operandCopy = node.mOperand->deepCopy();
     ASSERT(operandCopy != nullptr);
     mOperand = operandCopy;
 }
 
-TIntermSelection::TIntermSelection(const TIntermSelection &node) : TIntermTyped(node)
+TIntermTernary::TIntermTernary(const TIntermTernary &node) : TIntermTyped(node)
 {
-    // Only supported for ternary nodes, not if statements.
-    TIntermTyped *trueTyped  = node.mTrueBlock->getAsTyped();
-    TIntermTyped *falseTyped = node.mFalseBlock->getAsTyped();
-    ASSERT(trueTyped != nullptr);
-    ASSERT(falseTyped != nullptr);
     TIntermTyped *conditionCopy = node.mCondition->deepCopy();
-    TIntermTyped *trueCopy      = trueTyped->deepCopy();
-    TIntermTyped *falseCopy = falseTyped->deepCopy();
+    TIntermTyped *trueCopy      = node.mTrueExpression->deepCopy();
+    TIntermTyped *falseCopy     = node.mFalseExpression->deepCopy();
     ASSERT(conditionCopy != nullptr && trueCopy != nullptr && falseCopy != nullptr);
-    mCondition  = conditionCopy;
-    mTrueBlock  = trueCopy;
-    mFalseBlock = falseCopy;
+    mCondition       = conditionCopy;
+    mTrueExpression  = trueCopy;
+    mFalseExpression = falseCopy;
 }
 
 bool TIntermOperator::isAssignment() const
 {
     return IsAssignment(mOp);
 }
 
 bool TIntermOperator::isMultiplication() const
@@ -539,96 +622,190 @@ TOperator TIntermBinary::GetMulAssignOpB
         }
     }
 }
 
 //
 // Make sure the type of a unary operator is appropriate for its
 // combination of operation and operand type.
 //
-void TIntermUnary::promote(const TType *funcReturnType)
+void TIntermUnary::promote()
 {
+    TQualifier resultQualifier = EvqTemporary;
+    if (mOperand->getQualifier() == EvqConst)
+        resultQualifier = EvqConst;
+
+    unsigned char operandPrimarySize =
+        static_cast<unsigned char>(mOperand->getType().getNominalSize());
     switch (mOp)
     {
-      case EOpFloatBitsToInt:
-      case EOpFloatBitsToUint:
-      case EOpIntBitsToFloat:
-      case EOpUintBitsToFloat:
-      case EOpPackSnorm2x16:
-      case EOpPackUnorm2x16:
-      case EOpPackHalf2x16:
-      case EOpUnpackSnorm2x16:
-      case EOpUnpackUnorm2x16:
-        mType.setPrecision(EbpHigh);
-        break;
-      case EOpUnpackHalf2x16:
-        mType.setPrecision(EbpMedium);
-        break;
-      default:
-        setType(mOperand->getType());
+        case EOpFloatBitsToInt:
+            setType(TType(EbtInt, EbpHigh, resultQualifier, operandPrimarySize));
+            break;
+        case EOpFloatBitsToUint:
+            setType(TType(EbtUInt, EbpHigh, resultQualifier, operandPrimarySize));
+            break;
+        case EOpIntBitsToFloat:
+        case EOpUintBitsToFloat:
+            setType(TType(EbtFloat, EbpHigh, resultQualifier, operandPrimarySize));
+            break;
+        case EOpPackSnorm2x16:
+        case EOpPackUnorm2x16:
+        case EOpPackHalf2x16:
+            setType(TType(EbtUInt, EbpHigh, resultQualifier));
+            break;
+        case EOpUnpackSnorm2x16:
+        case EOpUnpackUnorm2x16:
+            setType(TType(EbtFloat, EbpHigh, resultQualifier, 2));
+            break;
+        case EOpUnpackHalf2x16:
+            setType(TType(EbtFloat, EbpMedium, resultQualifier, 2));
+            break;
+        case EOpAny:
+        case EOpAll:
+            setType(TType(EbtBool, EbpUndefined, resultQualifier));
+            break;
+        case EOpLength:
+        case EOpDeterminant:
+            setType(TType(EbtFloat, mOperand->getType().getPrecision(), resultQualifier));
+            break;
+        case EOpTranspose:
+            setType(TType(EbtFloat, mOperand->getType().getPrecision(), resultQualifier,
+                          static_cast<unsigned char>(mOperand->getType().getRows()),
+                          static_cast<unsigned char>(mOperand->getType().getCols())));
+            break;
+        case EOpIsInf:
+        case EOpIsNan:
+            setType(TType(EbtBool, EbpUndefined, resultQualifier, operandPrimarySize));
+            break;
+        default:
+            setType(mOperand->getType());
+            mType.setQualifier(resultQualifier);
+            break;
     }
+}
 
-    if (funcReturnType != nullptr)
-    {
-        if (funcReturnType->getBasicType() == EbtBool)
-        {
-            // Bool types should not have precision.
-            setType(*funcReturnType);
-        }
-        else
-        {
-            // Precision of the node has been set based on the operand.
-            setTypePreservePrecision(*funcReturnType);
-        }
-    }
-
-    if (mOperand->getQualifier() == EvqConst)
-        mType.setQualifier(EvqConst);
-    else
-        mType.setQualifier(EvqTemporary);
+TIntermUnary::TIntermUnary(TOperator op, TIntermTyped *operand)
+    : TIntermOperator(op), mOperand(operand), mUseEmulatedFunction(false)
+{
+    promote();
 }
 
 TIntermBinary::TIntermBinary(TOperator op, TIntermTyped *left, TIntermTyped *right)
     : TIntermOperator(op), mLeft(left), mRight(right), mAddIndexClamp(false)
 {
     promote();
 }
 
+TIntermTernary::TIntermTernary(TIntermTyped *cond,
+                               TIntermTyped *trueExpression,
+                               TIntermTyped *falseExpression)
+    : TIntermTyped(trueExpression->getType()),
+      mCondition(cond),
+      mTrueExpression(trueExpression),
+      mFalseExpression(falseExpression)
+{
+    getTypePointer()->setQualifier(
+        TIntermTernary::DetermineQualifier(cond, trueExpression, falseExpression));
+}
+
+// static
+TQualifier TIntermTernary::DetermineQualifier(TIntermTyped *cond,
+                                              TIntermTyped *trueExpression,
+                                              TIntermTyped *falseExpression)
+{
+    if (cond->getQualifier() == EvqConst && trueExpression->getQualifier() == EvqConst &&
+        falseExpression->getQualifier() == EvqConst)
+    {
+        return EvqConst;
+    }
+    return EvqTemporary;
+}
+
 //
 // Establishes the type of the resultant operation, as well as
 // makes the operator the correct one for the operands.
 //
 // For lots of operations it should already be established that the operand
 // combination is valid, but returns false if operator can't work on operands.
 //
 void TIntermBinary::promote()
 {
-    ASSERT(mLeft->isArray() == mRight->isArray());
-
     ASSERT(!isMultiplication() ||
            mOp == GetMulOpBasedOnOperands(mLeft->getType(), mRight->getType()));
 
     // Base assumption:  just make the type the same as the left
     // operand.  Then only deviations from this need be coded.
     setType(mLeft->getType());
 
-    // The result gets promoted to the highest precision.
-    TPrecision higherPrecision = GetHigherPrecision(
-        mLeft->getPrecision(), mRight->getPrecision());
-    getTypePointer()->setPrecision(higherPrecision);
-
     TQualifier resultQualifier = EvqConst;
     // Binary operations results in temporary variables unless both
     // operands are const.
     if (mLeft->getQualifier() != EvqConst || mRight->getQualifier() != EvqConst)
     {
         resultQualifier = EvqTemporary;
         getTypePointer()->setQualifier(EvqTemporary);
     }
 
+    // Handle indexing ops.
+    switch (mOp)
+    {
+        case EOpIndexDirect:
+        case EOpIndexIndirect:
+            if (mLeft->isArray())
+            {
+                mType.clearArrayness();
+            }
+            else if (mLeft->isMatrix())
+            {
+                setType(TType(mLeft->getBasicType(), mLeft->getPrecision(), resultQualifier,
+                              static_cast<unsigned char>(mLeft->getRows())));
+            }
+            else if (mLeft->isVector())
+            {
+                setType(TType(mLeft->getBasicType(), mLeft->getPrecision(), resultQualifier));
+            }
+            else
+            {
+                UNREACHABLE();
+            }
+            return;
+        case EOpIndexDirectStruct:
+        {
+            const TFieldList &fields = mLeft->getType().getStruct()->fields();
+            const int i              = mRight->getAsConstantUnion()->getIConst(0);
+            setType(*fields[i]->type());
+            getTypePointer()->setQualifier(resultQualifier);
+            return;
+        }
+        case EOpIndexDirectInterfaceBlock:
+        {
+            const TFieldList &fields = mLeft->getType().getInterfaceBlock()->fields();
+            const int i              = mRight->getAsConstantUnion()->getIConst(0);
+            setType(*fields[i]->type());
+            getTypePointer()->setQualifier(resultQualifier);
+            return;
+        }
+        case EOpVectorSwizzle:
+        {
+            auto numFields = mRight->getAsAggregate()->getSequence()->size();
+            setType(TType(mLeft->getBasicType(), mLeft->getPrecision(), resultQualifier,
+                          static_cast<unsigned char>(numFields)));
+            return;
+        }
+        default:
+            break;
+    }
+
+    ASSERT(mLeft->isArray() == mRight->isArray());
+
+    // The result gets promoted to the highest precision.
+    TPrecision higherPrecision = GetHigherPrecision(mLeft->getPrecision(), mRight->getPrecision());
+    getTypePointer()->setPrecision(higherPrecision);
+
     const int nominalSize =
         std::max(mLeft->getNominalSize(), mRight->getNominalSize());
 
     //
     // All scalars or structs. Code after this test assumes this case is removed!
     //
     if (nominalSize == 1)
     {
@@ -638,27 +815,27 @@ void TIntermBinary::promote()
           // Promote to conditional
           //
           case EOpEqual:
           case EOpNotEqual:
           case EOpLessThan:
           case EOpGreaterThan:
           case EOpLessThanEqual:
           case EOpGreaterThanEqual:
-            setType(TType(EbtBool, EbpUndefined));
-            break;
+              setType(TType(EbtBool, EbpUndefined, resultQualifier));
+              break;
 
           //
           // And and Or operate on conditionals
           //
           case EOpLogicalAnd:
           case EOpLogicalXor:
           case EOpLogicalOr:
             ASSERT(mLeft->getBasicType() == EbtBool && mRight->getBasicType() == EbtBool);
-            setType(TType(EbtBool, EbpUndefined));
+            setType(TType(EbtBool, EbpUndefined, resultQualifier));
             break;
 
           default:
             break;
         }
         return;
     }
 
@@ -744,46 +921,126 @@ void TIntermBinary::promote()
                    (mLeft->getSecondarySize() == mRight->getSecondarySize()));
             setType(TType(EbtBool, EbpUndefined, resultQualifier));
             break;
 
         case EOpIndexDirect:
         case EOpIndexIndirect:
         case EOpIndexDirectInterfaceBlock:
         case EOpIndexDirectStruct:
-            // TODO (oetuaho): These ops could be handled here as well (should be done closer to the
-            // top of the function).
+        case EOpVectorSwizzle:
+            // These ops should be already fully handled.
             UNREACHABLE();
             break;
         default:
             UNREACHABLE();
             break;
     }
 }
 
-TIntermTyped *TIntermBinary::fold(TDiagnostics *diagnostics)
+const TConstantUnion *TIntermConstantUnion::foldIndexing(int index)
 {
-    TIntermConstantUnion *leftConstant = mLeft->getAsConstantUnion();
-    TIntermConstantUnion *rightConstant = mRight->getAsConstantUnion();
-    if (leftConstant == nullptr || rightConstant == nullptr)
+    if (isArray())
+    {
+        ASSERT(index < static_cast<int>(getType().getArraySize()));
+        TType arrayElementType = getType();
+        arrayElementType.clearArrayness();
+        size_t arrayElementSize = arrayElementType.getObjectSize();
+        return &mUnionArrayPointer[arrayElementSize * index];
+    }
+    else if (isMatrix())
     {
+        ASSERT(index < getType().getCols());
+        int size = getType().getRows();
+        return &mUnionArrayPointer[size * index];
+    }
+    else if (isVector())
+    {
+        ASSERT(index < getType().getNominalSize());
+        return &mUnionArrayPointer[index];
+    }
+    else
+    {
+        UNREACHABLE();
         return nullptr;
     }
-    TConstantUnion *constArray = leftConstant->foldBinary(mOp, rightConstant, diagnostics);
-
-    // Nodes may be constant folded without being qualified as constant.
-    TQualifier resultQualifier = EvqConst;
-    if (mLeft->getQualifier() != EvqConst || mRight->getQualifier() != EvqConst)
-    {
-        resultQualifier = EvqTemporary;
-    }
-    return CreateFoldedNode(constArray, this, resultQualifier);
 }
 
-TIntermTyped *TIntermUnary::fold(TInfoSink &infoSink)
+TIntermTyped *TIntermBinary::fold(TDiagnostics *diagnostics)
+{
+    TIntermConstantUnion *leftConstant  = mLeft->getAsConstantUnion();
+    TIntermConstantUnion *rightConstant = mRight->getAsConstantUnion();
+    switch (mOp)
+    {
+        case EOpIndexDirect:
+        {
+            if (leftConstant == nullptr || rightConstant == nullptr)
+            {
+                return nullptr;
+            }
+            int index = rightConstant->getIConst(0);
+
+            const TConstantUnion *constArray = leftConstant->foldIndexing(index);
+            return CreateFoldedNode(constArray, this, mType.getQualifier());
+        }
+        case EOpIndexDirectStruct:
+        {
+            if (leftConstant == nullptr || rightConstant == nullptr)
+            {
+                return nullptr;
+            }
+            const TFieldList &fields = mLeft->getType().getStruct()->fields();
+            size_t index             = static_cast<size_t>(rightConstant->getIConst(0));
+
+            size_t previousFieldsSize = 0;
+            for (size_t i = 0; i < index; ++i)
+            {
+                previousFieldsSize += fields[i]->type()->getObjectSize();
+            }
+
+            const TConstantUnion *constArray = leftConstant->getUnionArrayPointer();
+            return CreateFoldedNode(constArray + previousFieldsSize, this, mType.getQualifier());
+        }
+        case EOpIndexIndirect:
+        case EOpIndexDirectInterfaceBlock:
+            // Can never be constant folded.
+            return nullptr;
+        case EOpVectorSwizzle:
+        {
+            if (leftConstant == nullptr)
+            {
+                return nullptr;
+            }
+            TIntermAggregate *fieldsAgg     = mRight->getAsAggregate();
+            TIntermSequence *fieldsSequence = fieldsAgg->getSequence();
+            size_t numFields                = fieldsSequence->size();
+
+            TConstantUnion *constArray = new TConstantUnion[numFields];
+            for (size_t i = 0; i < numFields; i++)
+            {
+                int fieldOffset = fieldsSequence->at(i)->getAsConstantUnion()->getIConst(0);
+                constArray[i]   = *leftConstant->foldIndexing(fieldOffset);
+            }
+            return CreateFoldedNode(constArray, this, mType.getQualifier());
+        }
+        default:
+        {
+            if (leftConstant == nullptr || rightConstant == nullptr)
+            {
+                return nullptr;
+            }
+            TConstantUnion *constArray = leftConstant->foldBinary(mOp, rightConstant, diagnostics);
+
+            // Nodes may be constant folded without being qualified as constant.
+            return CreateFoldedNode(constArray, this, mType.getQualifier());
+        }
+    }
+}
+
+TIntermTyped *TIntermUnary::fold(TDiagnostics *diagnostics)
 {
     TIntermConstantUnion *operandConstant = mOperand->getAsConstantUnion();
     if (operandConstant == nullptr)
     {
         return nullptr;
     }
 
     TConstantUnion *constArray = nullptr;
@@ -796,43 +1053,42 @@ TIntermTyped *TIntermUnary::fold(TInfoSi
       case EOpDeterminant:
       case EOpInverse:
       case EOpPackSnorm2x16:
       case EOpUnpackSnorm2x16:
       case EOpPackUnorm2x16:
       case EOpUnpackUnorm2x16:
       case EOpPackHalf2x16:
       case EOpUnpackHalf2x16:
-        constArray = operandConstant->foldUnaryWithDifferentReturnType(mOp, infoSink);
-        break;
+          constArray = operandConstant->foldUnaryNonComponentWise(mOp);
+          break;
       default:
-        constArray = operandConstant->foldUnaryWithSameReturnType(mOp, infoSink);
-        break;
+          constArray = operandConstant->foldUnaryComponentWise(mOp, diagnostics);
+          break;
     }
 
     // Nodes may be constant folded without being qualified as constant.
-    TQualifier resultQualifier = mOperand->getQualifier() == EvqConst ? EvqConst : EvqTemporary;
-    return CreateFoldedNode(constArray, this, resultQualifier);
+    return CreateFoldedNode(constArray, this, mType.getQualifier());
 }
 
-TIntermTyped *TIntermAggregate::fold(TInfoSink &infoSink)
+TIntermTyped *TIntermAggregate::fold(TDiagnostics *diagnostics)
 {
     // Make sure that all params are constant before actual constant folding.
     for (auto *param : *getSequence())
     {
         if (param->getAsConstantUnion() == nullptr)
         {
             return nullptr;
         }
     }
     TConstantUnion *constArray = nullptr;
     if (isConstructor())
-        constArray = TIntermConstantUnion::FoldAggregateConstructor(this, infoSink);
+        constArray = TIntermConstantUnion::FoldAggregateConstructor(this);
     else
-        constArray = TIntermConstantUnion::FoldAggregateBuiltIn(this, infoSink);
+        constArray = TIntermConstantUnion::FoldAggregateBuiltIn(this, diagnostics);
 
     // Nodes may be constant folded without being qualified as constant.
     TQualifier resultQualifier = areChildrenConstQualified() ? EvqConst : EvqTemporary;
     return CreateFoldedNode(constArray, this, resultQualifier);
 }
 
 //
 // The fold functions see if an operation on a constant can be done in place,
@@ -842,20 +1098,17 @@ TIntermTyped *TIntermAggregate::fold(TIn
 //
 TConstantUnion *TIntermConstantUnion::foldBinary(TOperator op,
                                                  TIntermConstantUnion *rightNode,
                                                  TDiagnostics *diagnostics)
 {
     const TConstantUnion *leftArray  = getUnionArrayPointer();
     const TConstantUnion *rightArray = rightNode->getUnionArrayPointer();
 
-    if (!leftArray)
-        return nullptr;
-    if (!rightArray)
-        return nullptr;
+    ASSERT(leftArray && rightArray);
 
     size_t objectSize = getType().getObjectSize();
 
     // for a case like float f = vec4(2, 3, 4, 5) + 1.2;
     if (rightNode->getType().getObjectSize() == 1 && objectSize > 1)
     {
         rightArray = Vectorize(*rightNode->getUnionArrayPointer(), objectSize);
     }
@@ -868,30 +1121,30 @@ TConstantUnion *TIntermConstantUnion::fo
 
     TConstantUnion *resultArray = nullptr;
 
     switch(op)
     {
       case EOpAdd:
         resultArray = new TConstantUnion[objectSize];
         for (size_t i = 0; i < objectSize; i++)
-            resultArray[i] = leftArray[i] + rightArray[i];
+            resultArray[i] = TConstantUnion::add(leftArray[i], rightArray[i], diagnostics);
         break;
       case EOpSub:
         resultArray = new TConstantUnion[objectSize];
         for (size_t i = 0; i < objectSize; i++)
-            resultArray[i] = leftArray[i] - rightArray[i];
+            resultArray[i] = TConstantUnion::sub(leftArray[i], rightArray[i], diagnostics);
         break;
 
       case EOpMul:
       case EOpVectorTimesScalar:
       case EOpMatrixTimesScalar:
         resultArray = new TConstantUnion[objectSize];
         for (size_t i = 0; i < objectSize; i++)
-            resultArray[i] = leftArray[i] * rightArray[i];
+            resultArray[i] = TConstantUnion::mul(leftArray[i], rightArray[i], diagnostics);
         break;
 
       case EOpMatrixTimesMatrix:
         {
             ASSERT(getType().getBasicType() == EbtFloat && rightNode->getBasicType() == EbtFloat);
 
             const int leftCols = getCols();
             const int leftRows = getRows();
@@ -1142,690 +1395,539 @@ TConstantUnion *TIntermConstantUnion::fo
 
       default:
           UNREACHABLE();
           return nullptr;
     }
     return resultArray;
 }
 
-//
-// The fold functions see if an operation on a constant can be done in place,
-// without generating run-time code.
-//
-// Returns the constant value to keep using or nullptr.
-//
-TConstantUnion *TIntermConstantUnion::foldUnaryWithDifferentReturnType(TOperator op, TInfoSink &infoSink)
+// The fold functions do operations on a constant at GLSL compile time, without generating run-time
+// code. Returns the constant value to keep using. Nullptr should not be returned.
+TConstantUnion *TIntermConstantUnion::foldUnaryNonComponentWise(TOperator op)
 {
-    //
-    // Do operations where the return type has a different number of components compared to the operand type.
-    //
+    // Do operations where the return type may have a different number of components compared to the
+    // operand type.
 
     const TConstantUnion *operandArray = getUnionArrayPointer();
-    if (!operandArray)
-        return nullptr;
+    ASSERT(operandArray);
 
     size_t objectSize = getType().getObjectSize();
     TConstantUnion *resultArray = nullptr;
     switch (op)
     {
-      case EOpAny:
-        if (getType().getBasicType() == EbtBool)
-        {
+        case EOpAny:
+            ASSERT(getType().getBasicType() == EbtBool);
             resultArray = new TConstantUnion();
             resultArray->setBConst(false);
             for (size_t i = 0; i < objectSize; i++)
             {
                 if (operandArray[i].getBConst())
                 {
                     resultArray->setBConst(true);
                     break;
                 }
             }
             break;
-        }
-        else
-        {
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
-        }
 
-      case EOpAll:
-        if (getType().getBasicType() == EbtBool)
-        {
+        case EOpAll:
+            ASSERT(getType().getBasicType() == EbtBool);
             resultArray = new TConstantUnion();
             resultArray->setBConst(true);
             for (size_t i = 0; i < objectSize; i++)
             {
                 if (!operandArray[i].getBConst())
                 {
                     resultArray->setBConst(false);
                     break;
                 }
             }
             break;
-        }
-        else
-        {
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
-        }
 
-      case EOpLength:
-        if (getType().getBasicType() == EbtFloat)
-        {
+        case EOpLength:
+            ASSERT(getType().getBasicType() == EbtFloat);
             resultArray = new TConstantUnion();
             resultArray->setFConst(VectorLength(operandArray, objectSize));
             break;
-        }
-        else
+
+        case EOpTranspose:
         {
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
-        }
-
-      case EOpTranspose:
-        if (getType().getBasicType() == EbtFloat)
-        {
+            ASSERT(getType().getBasicType() == EbtFloat);
             resultArray = new TConstantUnion[objectSize];
             angle::Matrix<float> result =
                 GetMatrix(operandArray, getType().getRows(), getType().getCols()).transpose();
             SetUnionArrayFromMatrix(result, resultArray);
             break;
         }
-        else
+
+        case EOpDeterminant:
         {
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
-        }
-
-      case EOpDeterminant:
-        if (getType().getBasicType() == EbtFloat)
-        {
+            ASSERT(getType().getBasicType() == EbtFloat);
             unsigned int size = getType().getNominalSize();
             ASSERT(size >= 2 && size <= 4);
             resultArray = new TConstantUnion();
             resultArray->setFConst(GetMatrix(operandArray, size).determinant());
             break;
         }
-        else
+
+        case EOpInverse:
         {
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
-        }
-
-      case EOpInverse:
-        if (getType().getBasicType() == EbtFloat)
-        {
+            ASSERT(getType().getBasicType() == EbtFloat);
             unsigned int size = getType().getNominalSize();
             ASSERT(size >= 2 && size <= 4);
-            resultArray = new TConstantUnion[objectSize];
+            resultArray                 = new TConstantUnion[objectSize];
             angle::Matrix<float> result = GetMatrix(operandArray, size).inverse();
             SetUnionArrayFromMatrix(result, resultArray);
             break;
         }
-        else
-        {
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
-        }
 
-      case EOpPackSnorm2x16:
-        if (getType().getBasicType() == EbtFloat)
-        {
+        case EOpPackSnorm2x16:
+            ASSERT(getType().getBasicType() == EbtFloat);
             ASSERT(getType().getNominalSize() == 2);
             resultArray = new TConstantUnion();
-            resultArray->setUConst(gl::packSnorm2x16(operandArray[0].getFConst(), operandArray[1].getFConst()));
+            resultArray->setUConst(
+                gl::packSnorm2x16(operandArray[0].getFConst(), operandArray[1].getFConst()));
             break;
-        }
-        else
+
+        case EOpUnpackSnorm2x16:
         {
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
-        }
-
-      case EOpUnpackSnorm2x16:
-        if (getType().getBasicType() == EbtUInt)
-        {
+            ASSERT(getType().getBasicType() == EbtUInt);
             resultArray = new TConstantUnion[2];
             float f1, f2;
             gl::unpackSnorm2x16(operandArray[0].getUConst(), &f1, &f2);
             resultArray[0].setFConst(f1);
             resultArray[1].setFConst(f2);
             break;
         }
-        else
-        {
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
-        }
 
-      case EOpPackUnorm2x16:
-        if (getType().getBasicType() == EbtFloat)
-        {
+        case EOpPackUnorm2x16:
+            ASSERT(getType().getBasicType() == EbtFloat);
             ASSERT(getType().getNominalSize() == 2);
             resultArray = new TConstantUnion();
-            resultArray->setUConst(gl::packUnorm2x16(operandArray[0].getFConst(), operandArray[1].getFConst()));
+            resultArray->setUConst(
+                gl::packUnorm2x16(operandArray[0].getFConst(), operandArray[1].getFConst()));
             break;
-        }
-        else
+
+        case EOpUnpackUnorm2x16:
         {
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
-        }
-
-      case EOpUnpackUnorm2x16:
-        if (getType().getBasicType() == EbtUInt)
-        {
+            ASSERT(getType().getBasicType() == EbtUInt);
             resultArray = new TConstantUnion[2];
             float f1, f2;
             gl::unpackUnorm2x16(operandArray[0].getUConst(), &f1, &f2);
             resultArray[0].setFConst(f1);
             resultArray[1].setFConst(f2);
             break;
         }
-        else
-        {
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
-        }
 
-      case EOpPackHalf2x16:
-        if (getType().getBasicType() == EbtFloat)
-        {
+        case EOpPackHalf2x16:
+            ASSERT(getType().getBasicType() == EbtFloat);
             ASSERT(getType().getNominalSize() == 2);
             resultArray = new TConstantUnion();
-            resultArray->setUConst(gl::packHalf2x16(operandArray[0].getFConst(), operandArray[1].getFConst()));
+            resultArray->setUConst(
+                gl::packHalf2x16(operandArray[0].getFConst(), operandArray[1].getFConst()));
             break;
-        }
-        else
+
+        case EOpUnpackHalf2x16:
         {
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
-        }
-
-      case EOpUnpackHalf2x16:
-        if (getType().getBasicType() == EbtUInt)
-        {
+            ASSERT(getType().getBasicType() == EbtUInt);
             resultArray = new TConstantUnion[2];
             float f1, f2;
             gl::unpackHalf2x16(operandArray[0].getUConst(), &f1, &f2);
             resultArray[0].setFConst(f1);
             resultArray[1].setFConst(f2);
             break;
         }
-        else
-        {
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
-        }
-        break;
 
-      default:
-        break;
+        default:
+            UNREACHABLE();
+            break;
     }
 
     return resultArray;
 }
 
-TConstantUnion *TIntermConstantUnion::foldUnaryWithSameReturnType(TOperator op, TInfoSink &infoSink)
+TConstantUnion *TIntermConstantUnion::foldUnaryComponentWise(TOperator op,
+                                                             TDiagnostics *diagnostics)
 {
-    //
-    // Do unary operations where the return type is the same as operand type.
-    //
+    // Do unary operations where each component of the result is computed based on the corresponding
+    // component of the operand. Also folds normalize, though the divisor in that case takes all
+    // components into account.
 
     const TConstantUnion *operandArray = getUnionArrayPointer();
-    if (!operandArray)
-        return nullptr;
+    ASSERT(operandArray);
 
     size_t objectSize = getType().getObjectSize();
 
     TConstantUnion *resultArray = new TConstantUnion[objectSize];
     for (size_t i = 0; i < objectSize; i++)
     {
         switch(op)
         {
-          case EOpNegative:
-            switch (getType().getBasicType())
-            {
-              case EbtFloat:
-                resultArray[i].setFConst(-operandArray[i].getFConst());
-                break;
-              case EbtInt:
-                resultArray[i].setIConst(-operandArray[i].getIConst());
-                break;
-              case EbtUInt:
-                resultArray[i].setUConst(static_cast<unsigned int>(
-                    -static_cast<int>(operandArray[i].getUConst())));
-                break;
-              default:
-                infoSink.info.message(
-                    EPrefixInternalError, getLine(),
-                    "Unary operation not folded into constant");
-                return nullptr;
-            }
-            break;
-
-          case EOpPositive:
-            switch (getType().getBasicType())
-            {
-              case EbtFloat:
-                resultArray[i].setFConst(operandArray[i].getFConst());
-                break;
-              case EbtInt:
-                resultArray[i].setIConst(operandArray[i].getIConst());
-                break;
-              case EbtUInt:
-                resultArray[i].setUConst(static_cast<unsigned int>(
-                    static_cast<int>(operandArray[i].getUConst())));
+            case EOpNegative:
+                switch (getType().getBasicType())
+                {
+                    case EbtFloat:
+                        resultArray[i].setFConst(-operandArray[i].getFConst());
+                        break;
+                    case EbtInt:
+                        resultArray[i].setIConst(-operandArray[i].getIConst());
+                        break;
+                    case EbtUInt:
+                        resultArray[i].setUConst(static_cast<unsigned int>(
+                            -static_cast<int>(operandArray[i].getUConst())));
+                        break;
+                    default:
+                        UNREACHABLE();
+                        return nullptr;
+                }
                 break;
-              default:
-                infoSink.info.message(
-                    EPrefixInternalError, getLine(),
-                    "Unary operation not folded into constant");
-                return nullptr;
-            }
-            break;
 
-          case EOpLogicalNot:
-            // this code is written for possible future use,
-            // will not get executed currently
-            switch (getType().getBasicType())
-            {
-              case EbtBool:
-                resultArray[i].setBConst(!operandArray[i].getBConst());
+            case EOpPositive:
+                switch (getType().getBasicType())
+                {
+                    case EbtFloat:
+                        resultArray[i].setFConst(operandArray[i].getFConst());
+                        break;
+                    case EbtInt:
+                        resultArray[i].setIConst(operandArray[i].getIConst());
+                        break;
+                    case EbtUInt:
+                        resultArray[i].setUConst(static_cast<unsigned int>(
+                            static_cast<int>(operandArray[i].getUConst())));
+                        break;
+                    default:
+                        UNREACHABLE();
+                        return nullptr;
+                }
                 break;
-              default:
-                infoSink.info.message(
-                    EPrefixInternalError, getLine(),
-                    "Unary operation not folded into constant");
-                return nullptr;
-            }
-            break;
 
-          case EOpBitwiseNot:
-            switch (getType().getBasicType())
-            {
-              case EbtInt:
-                resultArray[i].setIConst(~operandArray[i].getIConst());
+            case EOpLogicalNot:
+                switch (getType().getBasicType())
+                {
+                    case EbtBool:
+                        resultArray[i].setBConst(!operandArray[i].getBConst());
+                        break;
+                    default:
+                        UNREACHABLE();
+                        return nullptr;
+                }
                 break;
-              case EbtUInt:
-                resultArray[i].setUConst(~operandArray[i].getUConst());
+
+            case EOpBitwiseNot:
+                switch (getType().getBasicType())
+                {
+                    case EbtInt:
+                        resultArray[i].setIConst(~operandArray[i].getIConst());
+                        break;
+                    case EbtUInt:
+                        resultArray[i].setUConst(~operandArray[i].getUConst());
+                        break;
+                    default:
+                        UNREACHABLE();
+                        return nullptr;
+                }
                 break;
-              default:
-                infoSink.info.message(
-                    EPrefixInternalError, getLine(),
-                    "Unary operation not folded into constant");
-                return nullptr;
-            }
-            break;
 
-          case EOpRadians:
-            if (getType().getBasicType() == EbtFloat)
-            {
+            case EOpRadians:
+                ASSERT(getType().getBasicType() == EbtFloat);
                 resultArray[i].setFConst(kDegreesToRadiansMultiplier * operandArray[i].getFConst());
                 break;
-            }
-            infoSink.info.message(
-                EPrefixInternalError, getLine(),
-                "Unary operation not folded into constant");
-            return nullptr;
 
-          case EOpDegrees:
-            if (getType().getBasicType() == EbtFloat)
-            {
+            case EOpDegrees:
+                ASSERT(getType().getBasicType() == EbtFloat);
                 resultArray[i].setFConst(kRadiansToDegreesMultiplier * operandArray[i].getFConst());
                 break;
-            }
-            infoSink.info.message(
-                EPrefixInternalError, getLine(),
-                "Unary operation not folded into constant");
-            return nullptr;
+
+            case EOpSin:
+                foldFloatTypeUnary(operandArray[i], &sinf, &resultArray[i]);
+                break;
 
-          case EOpSin:
-            if (!foldFloatTypeUnary(operandArray[i], &sinf, infoSink, &resultArray[i]))
-               return nullptr;
-            break;
+            case EOpCos:
+                foldFloatTypeUnary(operandArray[i], &cosf, &resultArray[i]);
+                break;
 
-          case EOpCos:
-            if (!foldFloatTypeUnary(operandArray[i], &cosf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
-
-          case EOpTan:
-            if (!foldFloatTypeUnary(operandArray[i], &tanf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
+            case EOpTan:
+                foldFloatTypeUnary(operandArray[i], &tanf, &resultArray[i]);
+                break;
 
-          case EOpAsin:
-            // For asin(x), results are undefined if |x| > 1, we are choosing to set result to 0.
-            if (getType().getBasicType() == EbtFloat && fabsf(operandArray[i].getFConst()) > 1.0f)
-                UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
-            else if (!foldFloatTypeUnary(operandArray[i], &asinf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
+            case EOpAsin:
+                // For asin(x), results are undefined if |x| > 1, we are choosing to set result to
+                // 0.
+                if (fabsf(operandArray[i].getFConst()) > 1.0f)
+                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+                                                  diagnostics, &resultArray[i]);
+                else
+                    foldFloatTypeUnary(operandArray[i], &asinf, &resultArray[i]);
+                break;
 
-          case EOpAcos:
-            // For acos(x), results are undefined if |x| > 1, we are choosing to set result to 0.
-            if (getType().getBasicType() == EbtFloat && fabsf(operandArray[i].getFConst()) > 1.0f)
-                UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
-            else if (!foldFloatTypeUnary(operandArray[i], &acosf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
+            case EOpAcos:
+                // For acos(x), results are undefined if |x| > 1, we are choosing to set result to
+                // 0.
+                if (fabsf(operandArray[i].getFConst()) > 1.0f)
+                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+                                                  diagnostics, &resultArray[i]);
+                else
+                    foldFloatTypeUnary(operandArray[i], &acosf, &resultArray[i]);
+                break;
 
-          case EOpAtan:
-            if (!foldFloatTypeUnary(operandArray[i], &atanf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
+            case EOpAtan:
+                foldFloatTypeUnary(operandArray[i], &atanf, &resultArray[i]);
+                break;
 
-          case EOpSinh:
-            if (!foldFloatTypeUnary(operandArray[i], &sinhf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
-
-          case EOpCosh:
-            if (!foldFloatTypeUnary(operandArray[i], &coshf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
+            case EOpSinh:
+                foldFloatTypeUnary(operandArray[i], &sinhf, &resultArray[i]);
+                break;
 
-          case EOpTanh:
-            if (!foldFloatTypeUnary(operandArray[i], &tanhf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
+            case EOpCosh:
+                foldFloatTypeUnary(operandArray[i], &coshf, &resultArray[i]);
+                break;
 
-          case EOpAsinh:
-            if (!foldFloatTypeUnary(operandArray[i], &asinhf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
+            case EOpTanh:
+                foldFloatTypeUnary(operandArray[i], &tanhf, &resultArray[i]);
+                break;
 
-          case EOpAcosh:
-            // For acosh(x), results are undefined if x < 1, we are choosing to set result to 0.
-            if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() < 1.0f)
-                UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
-            else if (!foldFloatTypeUnary(operandArray[i], &acoshf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
+            case EOpAsinh:
+                foldFloatTypeUnary(operandArray[i], &asinhf, &resultArray[i]);
+                break;
 
-          case EOpAtanh:
-            // For atanh(x), results are undefined if |x| >= 1, we are choosing to set result to 0.
-            if (getType().getBasicType() == EbtFloat && fabsf(operandArray[i].getFConst()) >= 1.0f)
-                UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
-            else if (!foldFloatTypeUnary(operandArray[i], &atanhf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
+            case EOpAcosh:
+                // For acosh(x), results are undefined if x < 1, we are choosing to set result to 0.
+                if (operandArray[i].getFConst() < 1.0f)
+                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+                                                  diagnostics, &resultArray[i]);
+                else
+                    foldFloatTypeUnary(operandArray[i], &acoshf, &resultArray[i]);
+                break;
 
-          case EOpAbs:
-            switch (getType().getBasicType())
-            {
-              case EbtFloat:
-                resultArray[i].setFConst(fabsf(operandArray[i].getFConst()));
-                break;
-              case EbtInt:
-                resultArray[i].setIConst(abs(operandArray[i].getIConst()));
+            case EOpAtanh:
+                // For atanh(x), results are undefined if |x| >= 1, we are choosing to set result to
+                // 0.
+                if (fabsf(operandArray[i].getFConst()) >= 1.0f)
+                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+                                                  diagnostics, &resultArray[i]);
+                else
+                    foldFloatTypeUnary(operandArray[i], &atanhf, &resultArray[i]);
                 break;
-              default:
-                infoSink.info.message(
-                    EPrefixInternalError, getLine(),
-                    "Unary operation not folded into constant");
-                return nullptr;
-            }
-            break;
 
-          case EOpSign:
-            switch (getType().getBasicType())
-            {
-              case EbtFloat:
+            case EOpAbs:
+                switch (getType().getBasicType())
                 {
-                    float fConst = operandArray[i].getFConst();
-                    float fResult = 0.0f;
-                    if (fConst > 0.0f)
-                        fResult = 1.0f;
-                    else if (fConst < 0.0f)
-                        fResult = -1.0f;
-                    resultArray[i].setFConst(fResult);
+                    case EbtFloat:
+                        resultArray[i].setFConst(fabsf(operandArray[i].getFConst()));
+                        break;
+                    case EbtInt:
+                        resultArray[i].setIConst(abs(operandArray[i].getIConst()));
+                        break;
+                    default:
+                        UNREACHABLE();
+                        return nullptr;
                 }
                 break;
-              case EbtInt:
+
+            case EOpSign:
+                switch (getType().getBasicType())
                 {
-                    int iConst = operandArray[i].getIConst();
-                    int iResult = 0;
-                    if (iConst > 0)
-                        iResult = 1;
-                    else if (iConst < 0)
-                        iResult = -1;
-                    resultArray[i].setIConst(iResult);
+                    case EbtFloat:
+                    {
+                        float fConst  = operandArray[i].getFConst();
+                        float fResult = 0.0f;
+                        if (fConst > 0.0f)
+                            fResult = 1.0f;
+                        else if (fConst < 0.0f)
+                            fResult = -1.0f;
+                        resultArray[i].setFConst(fResult);
+                        break;
+                    }
+                    case EbtInt:
+                    {
+                        int iConst  = operandArray[i].getIConst();
+                        int iResult = 0;
+                        if (iConst > 0)
+                            iResult = 1;
+                        else if (iConst < 0)
+                            iResult = -1;
+                        resultArray[i].setIConst(iResult);
+                        break;
+                    }
+                    default:
+                        UNREACHABLE();
+                        return nullptr;
                 }
                 break;
-              default:
-                infoSink.info.message(
-                    EPrefixInternalError, getLine(),
-                    "Unary operation not folded into constant");
-                return nullptr;
-            }
-            break;
 
-          case EOpFloor:
-            if (!foldFloatTypeUnary(operandArray[i], &floorf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
+            case EOpFloor:
+                foldFloatTypeUnary(operandArray[i], &floorf, &resultArray[i]);
+                break;
 
-          case EOpTrunc:
-            if (!foldFloatTypeUnary(operandArray[i], &truncf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
+            case EOpTrunc:
+                foldFloatTypeUnary(operandArray[i], &truncf, &resultArray[i]);
+                break;
 
-          case EOpRound:
-            if (!foldFloatTypeUnary(operandArray[i], &roundf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
+            case EOpRound:
+                foldFloatTypeUnary(operandArray[i], &roundf, &resultArray[i]);
+                break;
 
-          case EOpRoundEven:
-            if (getType().getBasicType() == EbtFloat)
+            case EOpRoundEven:
             {
+                ASSERT(getType().getBasicType() == EbtFloat);
                 float x = operandArray[i].getFConst();
                 float result;
                 float fractPart = modff(x, &result);
                 if (fabsf(fractPart) == 0.5f)
                     result = 2.0f * roundf(x / 2.0f);
                 else
                     result = roundf(x);
                 resultArray[i].setFConst(result);
                 break;
             }
-            infoSink.info.message(
-                EPrefixInternalError, getLine(),
-                "Unary operation not folded into constant");
-            return nullptr;
 
-          case EOpCeil:
-            if (!foldFloatTypeUnary(operandArray[i], &ceilf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
+            case EOpCeil:
+                foldFloatTypeUnary(operandArray[i], &ceilf, &resultArray[i]);
+                break;
 
-          case EOpFract:
-            if (getType().getBasicType() == EbtFloat)
+            case EOpFract:
             {
+                ASSERT(getType().getBasicType() == EbtFloat);
                 float x = operandArray[i].getFConst();
                 resultArray[i].setFConst(x - floorf(x));
                 break;
             }
-            infoSink.info.message(
-                EPrefixInternalError, getLine(),
-                "Unary operation not folded into constant");
-            return nullptr;
 
-          case EOpIsNan:
-            if (getType().getBasicType() == EbtFloat)
-            {
+            case EOpIsNan:
+                ASSERT(getType().getBasicType() == EbtFloat);
                 resultArray[i].setBConst(gl::isNaN(operandArray[0].getFConst()));
                 break;
-            }
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
 
-          case EOpIsInf:
-            if (getType().getBasicType() == EbtFloat)
-            {
+            case EOpIsInf:
+                ASSERT(getType().getBasicType() == EbtFloat);
                 resultArray[i].setBConst(gl::isInf(operandArray[0].getFConst()));
                 break;
-            }
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
 
-          case EOpFloatBitsToInt:
-            if (getType().getBasicType() == EbtFloat)
-            {
+            case EOpFloatBitsToInt:
+                ASSERT(getType().getBasicType() == EbtFloat);
                 resultArray[i].setIConst(gl::bitCast<int32_t>(operandArray[0].getFConst()));
                 break;
-            }
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
 
-          case EOpFloatBitsToUint:
-            if (getType().getBasicType() == EbtFloat)
-            {
+            case EOpFloatBitsToUint:
+                ASSERT(getType().getBasicType() == EbtFloat);
                 resultArray[i].setUConst(gl::bitCast<uint32_t>(operandArray[0].getFConst()));
                 break;
-            }
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
 
-          case EOpIntBitsToFloat:
-            if (getType().getBasicType() == EbtInt)
-            {
+            case EOpIntBitsToFloat:
+                ASSERT(getType().getBasicType() == EbtInt);
                 resultArray[i].setFConst(gl::bitCast<float>(operandArray[0].getIConst()));
                 break;
-            }
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
 
-          case EOpUintBitsToFloat:
-            if (getType().getBasicType() == EbtUInt)
-            {
+            case EOpUintBitsToFloat:
+                ASSERT(getType().getBasicType() == EbtUInt);
                 resultArray[i].setFConst(gl::bitCast<float>(operandArray[0].getUConst()));
                 break;
-            }
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
 
-          case EOpExp:
-            if (!foldFloatTypeUnary(operandArray[i], &expf, infoSink, &resultArray[i]))
-              return nullptr;
-            break;
+            case EOpExp:
+                foldFloatTypeUnary(operandArray[i], &expf, &resultArray[i]);
+                break;
 
-          case EOpLog:
-            // For log(x), results are undefined if x <= 0, we are choosing to set result to 0.
-            if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() <= 0.0f)
-                UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
-            else if (!foldFloatTypeUnary(operandArray[i], &logf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
+            case EOpLog:
+                // For log(x), results are undefined if x <= 0, we are choosing to set result to 0.
+                if (operandArray[i].getFConst() <= 0.0f)
+                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+                                                  diagnostics, &resultArray[i]);
+                else
+                    foldFloatTypeUnary(operandArray[i], &logf, &resultArray[i]);
+                break;
 
-          case EOpExp2:
-            if (!foldFloatTypeUnary(operandArray[i], &exp2f, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
+            case EOpExp2:
+                foldFloatTypeUnary(operandArray[i], &exp2f, &resultArray[i]);
+                break;
 
-          case EOpLog2:
-            // For log2(x), results are undefined if x <= 0, we are choosing to set result to 0.
-            // And log2f is not available on some plarforms like old android, so just using log(x)/log(2) here.
-            if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() <= 0.0f)
-                UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
-            else if (!foldFloatTypeUnary(operandArray[i], &logf, infoSink, &resultArray[i]))
-                return nullptr;
-            else
-                resultArray[i].setFConst(resultArray[i].getFConst() / logf(2.0f));
-            break;
+            case EOpLog2:
+                // For log2(x), results are undefined if x <= 0, we are choosing to set result to 0.
+                // And log2f is not available on some plarforms like old android, so just using
+                // log(x)/log(2) here.
+                if (operandArray[i].getFConst() <= 0.0f)
+                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+                                                  diagnostics, &resultArray[i]);
+                else
+                {
+                    foldFloatTypeUnary(operandArray[i], &logf, &resultArray[i]);
+                    resultArray[i].setFConst(resultArray[i].getFConst() / logf(2.0f));
+                }
+                break;
 
-          case EOpSqrt:
-            // For sqrt(x), results are undefined if x < 0, we are choosing to set result to 0.
-            if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() < 0.0f)
-                UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
-            else if (!foldFloatTypeUnary(operandArray[i], &sqrtf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
+            case EOpSqrt:
+                // For sqrt(x), results are undefined if x < 0, we are choosing to set result to 0.
+                if (operandArray[i].getFConst() < 0.0f)
+                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+                                                  diagnostics, &resultArray[i]);
+                else
+                    foldFloatTypeUnary(operandArray[i], &sqrtf, &resultArray[i]);
+                break;
 
-          case EOpInverseSqrt:
-            // There is no stdlib built-in function equavalent for GLES built-in inversesqrt(),
-            // so getting the square root first using builtin function sqrt() and then taking its inverse.
-            // Also, for inversesqrt(x), results are undefined if x <= 0, we are choosing to set result to 0.
-            if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() <= 0.0f)
-                UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
-            else if (!foldFloatTypeUnary(operandArray[i], &sqrtf, infoSink, &resultArray[i]))
-                return nullptr;
-            else
-                resultArray[i].setFConst(1.0f / resultArray[i].getFConst());
-            break;
+            case EOpInverseSqrt:
+                // There is no stdlib built-in function equavalent for GLES built-in inversesqrt(),
+                // so getting the square root first using builtin function sqrt() and then taking
+                // its inverse.
+                // Also, for inversesqrt(x), results are undefined if x <= 0, we are choosing to set
+                // result to 0.
+                if (operandArray[i].getFConst() <= 0.0f)
+                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+                                                  diagnostics, &resultArray[i]);
+                else
+                {
+                    foldFloatTypeUnary(operandArray[i], &sqrtf, &resultArray[i]);
+                    resultArray[i].setFConst(1.0f / resultArray[i].getFConst());
+                }
+                break;
 
-          case EOpVectorLogicalNot:
-            if (getType().getBasicType() == EbtBool)
-            {
+            case EOpVectorLogicalNot:
+                ASSERT(getType().getBasicType() == EbtBool);
                 resultArray[i].setBConst(!operandArray[i].getBConst());
                 break;
-            }
-            infoSink.info.message(
-                EPrefixInternalError, getLine(),
-                "Unary operation not folded into constant");
-            return nullptr;
 
-          case EOpNormalize:
-            if (getType().getBasicType() == EbtFloat)
+            case EOpNormalize:
             {
-                float x = operandArray[i].getFConst();
+                ASSERT(getType().getBasicType() == EbtFloat);
+                float x      = operandArray[i].getFConst();
                 float length = VectorLength(operandArray, objectSize);
                 if (length)
                     resultArray[i].setFConst(x / length);
                 else
-                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink,
-                                                  &resultArray[i]);
+                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+                                                  diagnostics, &resultArray[i]);
                 break;
             }
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
 
-          case EOpDFdx:
-          case EOpDFdy:
-          case EOpFwidth:
-            if (getType().getBasicType() == EbtFloat)
-            {
+            case EOpDFdx:
+            case EOpDFdy:
+            case EOpFwidth:
+                ASSERT(getType().getBasicType() == EbtFloat);
                 // Derivatives of constant arguments should be 0.
                 resultArray[i].setFConst(0.0f);
                 break;
-            }
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
 
-          default:
-            return nullptr;
+            default:
+                return nullptr;
         }
     }
 
     return resultArray;
 }
 
-bool TIntermConstantUnion::foldFloatTypeUnary(const TConstantUnion &parameter, FloatTypeUnaryFunc builtinFunc,
-                                              TInfoSink &infoSink, TConstantUnion *result) const
+void TIntermConstantUnion::foldFloatTypeUnary(const TConstantUnion &parameter,
+                                              FloatTypeUnaryFunc builtinFunc,
+                                              TConstantUnion *result) const
 {
     ASSERT(builtinFunc);
 
-    if (getType().getBasicType() == EbtFloat)
-    {
-        result->setFConst(builtinFunc(parameter.getFConst()));
-        return true;
-    }
-
-    infoSink.info.message(
-        EPrefixInternalError, getLine(),
-        "Unary operation not folded into constant");
-    return false;
+    ASSERT(getType().getBasicType() == EbtFloat);
+    result->setFConst(builtinFunc(parameter.getFConst()));
 }
 
 // static
-TConstantUnion *TIntermConstantUnion::FoldAggregateConstructor(TIntermAggregate *aggregate,
-                                                               TInfoSink &infoSink)
+TConstantUnion *TIntermConstantUnion::FoldAggregateConstructor(TIntermAggregate *aggregate)
 {
     ASSERT(aggregate->getSequence()->size() > 0u);
     size_t resultSize           = aggregate->getType().getObjectSize();
     TConstantUnion *resultArray = new TConstantUnion[resultSize];
     TBasicType basicType        = aggregate->getBasicType();
 
     size_t resultIndex = 0u;
 
@@ -1914,17 +2016,18 @@ TConstantUnion *TIntermConstantUnion::Fo
             ++resultIndex;
         }
     }
     ASSERT(resultIndex == resultSize);
     return resultArray;
 }
 
 // static
-TConstantUnion *TIntermConstantUnion::FoldAggregateBuiltIn(TIntermAggregate *aggregate, TInfoSink &infoSink)
+TConstantUnion *TIntermConstantUnion::FoldAggregateBuiltIn(TIntermAggregate *aggregate,
+                                                           TDiagnostics *diagnostics)
 {
     TOperator op = aggregate->getOp();
     TIntermSequence *sequence = aggregate->getSequence();
     unsigned int paramsCount = static_cast<unsigned int>(sequence->size());
     std::vector<const TConstantUnion *> unionArrays(paramsCount);
     std::vector<size_t> objectSizes(paramsCount);
     size_t maxObjectSize = 0;
     TBasicType basicType = EbtVoid;
@@ -1955,560 +2058,560 @@ TConstantUnion *TIntermConstantUnion::Fo
     TConstantUnion *resultArray = nullptr;
     if (paramsCount == 2)
     {
         //
         // Binary built-in
         //
         switch (op)
         {
-          case EOpAtan:
+            case EOpAtan:
             {
-                if (basicType == EbtFloat)
+                ASSERT(basicType == EbtFloat);
+                resultArray = new TConstantUnion[maxObjectSize];
+                for (size_t i = 0; i < maxObjectSize; i++)
                 {
-                    resultArray = new TConstantUnion[maxObjectSize];
-                    for (size_t i = 0; i < maxObjectSize; i++)
-                    {
-                        float y = unionArrays[0][i].getFConst();
-                        float x = unionArrays[1][i].getFConst();
-                        // Results are undefined if x and y are both 0.
-                        if (x == 0.0f && y == 0.0f)
-                            UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
-                        else
-                            resultArray[i].setFConst(atan2f(y, x));
-                    }
+                    float y = unionArrays[0][i].getFConst();
+                    float x = unionArrays[1][i].getFConst();
+                    // Results are undefined if x and y are both 0.
+                    if (x == 0.0f && y == 0.0f)
+                        UndefinedConstantFoldingError(loc, op, basicType, diagnostics,
+                                                      &resultArray[i]);
+                    else
+                        resultArray[i].setFConst(atan2f(y, x));
                 }
-                else
-                    UNREACHABLE();
+                break;
             }
-            break;
 
-          case EOpPow:
+            case EOpPow:
             {
-                if (basicType == EbtFloat)
+                ASSERT(basicType == EbtFloat);
+                resultArray = new TConstantUnion[maxObjectSize];
+                for (size_t i = 0; i < maxObjectSize; i++)
                 {
-                    resultArray = new TConstantUnion[maxObjectSize];
-                    for (size_t i = 0; i < maxObjectSize; i++)
-                    {
-                        float x = unionArrays[0][i].getFConst();
-                        float y = unionArrays[1][i].getFConst();
-                        // Results are undefined if x < 0.
-                        // Results are undefined if x = 0 and y <= 0.
-                        if (x < 0.0f)
-                            UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
-                        else if (x == 0.0f && y <= 0.0f)
-                            UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
-                        else
-                            resultArray[i].setFConst(powf(x, y));
-                    }
+                    float x = unionArrays[0][i].getFConst();
+                    float y = unionArrays[1][i].getFConst();
+                    // Results are undefined if x < 0.
+                    // Results are undefined if x = 0 and y <= 0.
+                    if (x < 0.0f)
+                        UndefinedConstantFoldingError(loc, op, basicType, diagnostics,
+                                                      &resultArray[i]);
+                    else if (x == 0.0f && y <= 0.0f)
+                        UndefinedConstantFoldingError(loc, op, basicType, diagnostics,
+                                                      &resultArray[i]);
+                    else
+                        resultArray[i].setFConst(powf(x, y));
                 }
-                else
-                    UNREACHABLE();
+                break;
             }
-            break;
 
-          case EOpMod:
+            case EOpMod:
             {
-                if (basicType == EbtFloat)
+                ASSERT(basicType == EbtFloat);
+                resultArray = new TConstantUnion[maxObjectSize];
+                for (size_t i = 0; i < maxObjectSize; i++)
                 {
-                    resultArray = new TConstantUnion[maxObjectSize];
-                    for (size_t i = 0; i < maxObjectSize; i++)
-                    {
-                        float x = unionArrays[0][i].getFConst();
-                        float y = unionArrays[1][i].getFConst();
-                        resultArray[i].setFConst(x - y * floorf(x / y));
-                    }
+                    float x = unionArrays[0][i].getFConst();
+                    float y = unionArrays[1][i].getFConst();
+                    resultArray[i].setFConst(x - y * floorf(x / y));
                 }
-                else
-                    UNREACHABLE();
+                break;
             }
-            break;
 
-          case EOpMin:
+            case EOpMin:
             {
                 resultArray = new TConstantUnion[maxObjectSize];
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
                     switch (basicType)
                     {
-                      case EbtFloat:
-                        resultArray[i].setFConst(std::min(unionArrays[0][i].getFConst(), unionArrays[1][i].getFConst()));
-                        break;
-                      case EbtInt:
-                        resultArray[i].setIConst(std::min(unionArrays[0][i].getIConst(), unionArrays[1][i].getIConst()));
-                        break;
-                      case EbtUInt:
-                        resultArray[i].setUConst(std::min(unionArrays[0][i].getUConst(), unionArrays[1][i].getUConst()));
-                        break;
-                      default:
-                        UNREACHABLE();
-                        break;
+                        case EbtFloat:
+                            resultArray[i].setFConst(std::min(unionArrays[0][i].getFConst(),
+                                                              unionArrays[1][i].getFConst()));
+                            break;
+                        case EbtInt:
+                            resultArray[i].setIConst(std::min(unionArrays[0][i].getIConst(),
+                                                              unionArrays[1][i].getIConst()));
+                            break;
+                        case EbtUInt:
+                            resultArray[i].setUConst(std::min(unionArrays[0][i].getUConst(),
+                                                              unionArrays[1][i].getUConst()));
+                            break;
+                        default:
+                            UNREACHABLE();
+                            break;
                     }
                 }
+                break;
             }
-            break;
 
-          case EOpMax:
+            case EOpMax:
             {
                 resultArray = new TConstantUnion[maxObjectSize];
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
                     switch (basicType)
                     {
-                      case EbtFloat:
-                        resultArray[i].setFConst(std::max(unionArrays[0][i].getFConst(), unionArrays[1][i].getFConst()));
-                        break;
-                      case EbtInt:
-                        resultArray[i].setIConst(std::max(unionArrays[0][i].getIConst(), unionArrays[1][i].getIConst()));
-                        break;
-                      case EbtUInt:
-                        resultArray[i].setUConst(std::max(unionArrays[0][i].getUConst(), unionArrays[1][i].getUConst()));
-                        break;
-                      default:
-                        UNREACHABLE();
-                        break;
+                        case EbtFloat:
+                            resultArray[i].setFConst(std::max(unionArrays[0][i].getFConst(),
+                                                              unionArrays[1][i].getFConst()));
+                            break;
+                        case EbtInt:
+                            resultArray[i].setIConst(std::max(unionArrays[0][i].getIConst(),
+                                                              unionArrays[1][i].getIConst()));
+                            break;
+                        case EbtUInt:
+                            resultArray[i].setUConst(std::max(unionArrays[0][i].getUConst(),
+                                                              unionArrays[1][i].getUConst()));
+                            break;
+                        default:
+                            UNREACHABLE();
+                            break;
                     }
                 }
+                break;
             }
-            break;
 
-          case EOpStep:
+            case EOpStep:
             {
-                if (basicType == EbtFloat)
-                {
-                    resultArray = new TConstantUnion[maxObjectSize];
-                    for (size_t i = 0; i < maxObjectSize; i++)
-                        resultArray[i].setFConst(unionArrays[1][i].getFConst() < unionArrays[0][i].getFConst() ? 0.0f : 1.0f);
-                }
-                else
-                    UNREACHABLE();
+                ASSERT(basicType == EbtFloat);
+                resultArray = new TConstantUnion[maxObjectSize];
+                for (size_t i = 0; i < maxObjectSize; i++)
+                    resultArray[i].setFConst(
+                        unionArrays[1][i].getFConst() < unionArrays[0][i].getFConst() ? 0.0f
+                                                                                      : 1.0f);
+                break;
             }
-            break;
 
-          case EOpLessThan:
+            case EOpLessThan:
             {
                 resultArray = new TConstantUnion[maxObjectSize];
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
                     switch (basicType)
                     {
-                      case EbtFloat:
-                        resultArray[i].setBConst(unionArrays[0][i].getFConst() < unionArrays[1][i].getFConst());
-                        break;
-                      case EbtInt:
-                        resultArray[i].setBConst(unionArrays[0][i].getIConst() < unionArrays[1][i].getIConst());
-                        break;
-                      case EbtUInt:
-                        resultArray[i].setBConst(unionArrays[0][i].getUConst() < unionArrays[1][i].getUConst());
-                        break;
-                      default:
-                        UNREACHABLE();
-                        break;
+                        case EbtFloat:
+                            resultArray[i].setBConst(unionArrays[0][i].getFConst() <
+                                                     unionArrays[1][i].getFConst());
+                            break;
+                        case EbtInt:
+                            resultArray[i].setBConst(unionArrays[0][i].getIConst() <
+                                                     unionArrays[1][i].getIConst());
+                            break;
+                        case EbtUInt:
+                            resultArray[i].setBConst(unionArrays[0][i].getUConst() <
+                                                     unionArrays[1][i].getUConst());
+                            break;
+                        default:
+                            UNREACHABLE();
+                            break;
                     }
                 }
+                break;
             }
-            break;
 
-          case EOpLessThanEqual:
+            case EOpLessThanEqual:
             {
                 resultArray = new TConstantUnion[maxObjectSize];
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
                     switch (basicType)
                     {
-                      case EbtFloat:
-                        resultArray[i].setBConst(unionArrays[0][i].getFConst() <= unionArrays[1][i].getFConst());
-                        break;
-                      case EbtInt:
-                        resultArray[i].setBConst(unionArrays[0][i].getIConst() <= unionArrays[1][i].getIConst());
-                        break;
-                      case EbtUInt:
-                        resultArray[i].setBConst(unionArrays[0][i].getUConst() <= unionArrays[1][i].getUConst());
-                        break;
-                      default:
-                        UNREACHABLE();
-                        break;
+                        case EbtFloat:
+                            resultArray[i].setBConst(unionArrays[0][i].getFConst() <=
+                                                     unionArrays[1][i].getFConst());
+                            break;
+                        case EbtInt:
+                            resultArray[i].setBConst(unionArrays[0][i].getIConst() <=
+                                                     unionArrays[1][i].getIConst());
+                            break;
+                        case EbtUInt:
+                            resultArray[i].setBConst(unionArrays[0][i].getUConst() <=
+                                                     unionArrays[1][i].getUConst());
+                            break;
+                        default:
+                            UNREACHABLE();
+                            break;
                     }
                 }
+                break;
             }
-            break;
 
-          case EOpGreaterThan:
+            case EOpGreaterThan:
             {
                 resultArray = new TConstantUnion[maxObjectSize];
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
                     switch (basicType)
                     {
-                      case EbtFloat:
-                        resultArray[i].setBConst(unionArrays[0][i].getFConst() > unionArrays[1][i].getFConst());
-                        break;
-                      case EbtInt:
-                        resultArray[i].setBConst(unionArrays[0][i].getIConst() > unionArrays[1][i].getIConst());
-                        break;
-                      case EbtUInt:
-                        resultArray[i].setBConst(unionArrays[0][i].getUConst() > unionArrays[1][i].getUConst());
-                        break;
-                      default:
-                        UNREACHABLE();
-                        break;
+                        case EbtFloat:
+                            resultArray[i].setBConst(unionArrays[0][i].getFConst() >
+                                                     unionArrays[1][i].getFConst());
+                            break;
+                        case EbtInt:
+                            resultArray[i].setBConst(unionArrays[0][i].getIConst() >
+                                                     unionArrays[1][i].getIConst());
+                            break;
+                        case EbtUInt:
+                            resultArray[i].setBConst(unionArrays[0][i].getUConst() >
+                                                     unionArrays[1][i].getUConst());
+                            break;
+                        default:
+                            UNREACHABLE();
+                            break;
                     }
                 }
+                break;
             }
-            break;
-
-          case EOpGreaterThanEqual:
+            case EOpGreaterThanEqual:
             {
                 resultArray = new TConstantUnion[maxObjectSize];
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
                     switch (basicType)
                     {
-                      case EbtFloat:
-                        resultArray[i].setBConst(unionArrays[0][i].getFConst() >= unionArrays[1][i].getFConst());
-                        break;
-                      case EbtInt:
-                        resultArray[i].setBConst(unionArrays[0][i].getIConst() >= unionArrays[1][i].getIConst());
-                        break;
-                      case EbtUInt:
-                        resultArray[i].setBConst(unionArrays[0][i].getUConst() >= unionArrays[1][i].getUConst());
-                        break;
-                      default:
-                        UNREACHABLE();
-                        break;
+                        case EbtFloat:
+                            resultArray[i].setBConst(unionArrays[0][i].getFConst() >=
+                                                     unionArrays[1][i].getFConst());
+                            break;
+                        case EbtInt:
+                            resultArray[i].setBConst(unionArrays[0][i].getIConst() >=
+                                                     unionArrays[1][i].getIConst());
+                            break;
+                        case EbtUInt:
+                            resultArray[i].setBConst(unionArrays[0][i].getUConst() >=
+                                                     unionArrays[1][i].getUConst());
+                            break;
+                        default:
+                            UNREACHABLE();
+                            break;
                     }
                 }
             }
             break;
 
-          case EOpVectorEqual:
+            case EOpVectorEqual:
             {
                 resultArray = new TConstantUnion[maxObjectSize];
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
                     switch (basicType)
                     {
-                      case EbtFloat:
-                        resultArray[i].setBConst(unionArrays[0][i].getFConst() == unionArrays[1][i].getFConst());
-                        break;
-                      case EbtInt:
-                        resultArray[i].setBConst(unionArrays[0][i].getIConst() == unionArrays[1][i].getIConst());
-                        break;
-                      case EbtUInt:
-                        resultArray[i].setBConst(unionArrays[0][i].getUConst() == unionArrays[1][i].getUConst());
-                        break;
-                      case EbtBool:
-                        resultArray[i].setBConst(unionArrays[0][i].getBConst() == unionArrays[1][i].getBConst());
-                        break;
-                      default:
-                        UNREACHABLE();
-                        break;
+                        case EbtFloat:
+                            resultArray[i].setBConst(unionArrays[0][i].getFConst() ==
+                                                     unionArrays[1][i].getFConst());
+                            break;
+                        case EbtInt:
+                            resultArray[i].setBConst(unionArrays[0][i].getIConst() ==
+                                                     unionArrays[1][i].getIConst());
+                            break;
+                        case EbtUInt:
+                            resultArray[i].setBConst(unionArrays[0][i].getUConst() ==
+                                                     unionArrays[1][i].getUConst());
+                            break;
+                        case EbtBool:
+                            resultArray[i].setBConst(unionArrays[0][i].getBConst() ==
+                                                     unionArrays[1][i].getBConst());
+                            break;
+                        default:
+                            UNREACHABLE();
+                            break;
                     }
                 }
+                break;
             }
-            break;
 
-          case EOpVectorNotEqual:
+            case EOpVectorNotEqual:
             {
                 resultArray = new TConstantUnion[maxObjectSize];
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
                     switch (basicType)
                     {
-                      case EbtFloat:
-                        resultArray[i].setBConst(unionArrays[0][i].getFConst() != unionArrays[1][i].getFConst());
-                        break;
-                      case EbtInt:
-                        resultArray[i].setBConst(unionArrays[0][i].getIConst() != unionArrays[1][i].getIConst());
-                        break;
-                      case EbtUInt:
-                        resultArray[i].setBConst(unionArrays[0][i].getUConst() != unionArrays[1][i].getUConst());
-                        break;
-                      case EbtBool:
-                        resultArray[i].setBConst(unionArrays[0][i].getBConst() != unionArrays[1][i].getBConst());
-                        break;
-                      default:
-                        UNREACHABLE();
-                        break;
+                        case EbtFloat:
+                            resultArray[i].setBConst(unionArrays[0][i].getFConst() !=
+                                                     unionArrays[1][i].getFConst());
+                            break;
+                        case EbtInt:
+                            resultArray[i].setBConst(unionArrays[0][i].getIConst() !=
+                                                     unionArrays[1][i].getIConst());
+                            break;
+                        case EbtUInt:
+                            resultArray[i].setBConst(unionArrays[0][i].getUConst() !=
+                                                     unionArrays[1][i].getUConst());
+                            break;
+                        case EbtBool:
+                            resultArray[i].setBConst(unionArrays[0][i].getBConst() !=
+                                                     unionArrays[1][i].getBConst());
+                            break;
+                        default:
+                            UNREACHABLE();
+                            break;
                     }
                 }
+                break;
             }
-            break;
 
-          case EOpDistance:
-            if (basicType == EbtFloat)
+            case EOpDistance:
             {
+                ASSERT(basicType == EbtFloat);
                 TConstantUnion *distanceArray = new TConstantUnion[maxObjectSize];
-                resultArray = new TConstantUnion();
+                resultArray                   = new TConstantUnion();
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
                     float x = unionArrays[0][i].getFConst();
                     float y = unionArrays[1][i].getFConst();
                     distanceArray[i].setFConst(x - y);
                 }
                 resultArray->setFConst(VectorLength(distanceArray, maxObjectSize));
-            }
-            else
-                UNREACHABLE();
-            break;
-
-          case EOpDot:
-
-            if (basicType == EbtFloat)
-            {
-                resultArray = new TConstantUnion();
-                resultArray->setFConst(VectorDotProduct(unionArrays[0], unionArrays[1], maxObjectSize));
+                break;
             }
-            else
-                UNREACHABLE();
-            break;
 
-          case EOpCross:
-            if (basicType == EbtFloat && maxObjectSize == 3)
+            case EOpDot:
+                ASSERT(basicType == EbtFloat);
+                resultArray = new TConstantUnion();
+                resultArray->setFConst(
+                    VectorDotProduct(unionArrays[0], unionArrays[1], maxObjectSize));
+                break;
+
+            case EOpCross:
             {
+                ASSERT(basicType == EbtFloat && maxObjectSize == 3);
                 resultArray = new TConstantUnion[maxObjectSize];
-                float x0 = unionArrays[0][0].getFConst();
-                float x1 = unionArrays[0][1].getFConst();
-                float x2 = unionArrays[0][2].getFConst();
-                float y0 = unionArrays[1][0].getFConst();
-                float y1 = unionArrays[1][1].getFConst();
-                float y2 = unionArrays[1][2].getFConst();
+                float x0    = unionArrays[0][0].getFConst();
+                float x1    = unionArrays[0][1].getFConst();
+                float x2    = unionArrays[0][2].getFConst();
+                float y0    = unionArrays[1][0].getFConst();
+                float y1    = unionArrays[1][1].getFConst();
+                float y2    = unionArrays[1][2].getFConst();
                 resultArray[0].setFConst(x1 * y2 - y1 * x2);
                 resultArray[1].setFConst(x2 * y0 - y2 * x0);
                 resultArray[2].setFConst(x0 * y1 - y0 * x1);
+                break;
             }
-            else
-                UNREACHABLE();
-            break;
 
-          case EOpReflect:
-            if (basicType == EbtFloat)
+            case EOpReflect:
             {
+                ASSERT(basicType == EbtFloat);
                 // genType reflect (genType I, genType N) :
-                //     For the incident vector I and surface orientation N, returns the reflection direction:
+                //     For the incident vector I and surface orientation N, returns the reflection
+                //     direction:
                 //     I - 2 * dot(N, I) * N.
-                resultArray = new TConstantUnion[maxObjectSize];
+                resultArray      = new TConstantUnion[maxObjectSize];
                 float dotProduct = VectorDotProduct(unionArrays[1], unionArrays[0], maxObjectSize);
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
                     float result = unionArrays[0][i].getFConst() -
                                    2.0f * dotProduct * unionArrays[1][i].getFConst();
                     resultArray[i].setFConst(result);
                 }
+                break;
             }
-            else
-                UNREACHABLE();
-            break;
 
-          case EOpMul:
-            if (basicType == EbtFloat && (*sequence)[0]->getAsTyped()->isMatrix() &&
-                (*sequence)[1]->getAsTyped()->isMatrix())
+            case EOpMul:
             {
+                ASSERT(basicType == EbtFloat && (*sequence)[0]->getAsTyped()->isMatrix() &&
+                       (*sequence)[1]->getAsTyped()->isMatrix());
                 // Perform component-wise matrix multiplication.
                 resultArray = new TConstantUnion[maxObjectSize];
-                int size = (*sequence)[0]->getAsTyped()->getNominalSize();
+                int size    = (*sequence)[0]->getAsTyped()->getNominalSize();
                 angle::Matrix<float> result =
                     GetMatrix(unionArrays[0], size).compMult(GetMatrix(unionArrays[1], size));
                 SetUnionArrayFromMatrix(result, resultArray);
+                break;
             }
-            else
-                UNREACHABLE();
-            break;
 
-          case EOpOuterProduct:
-            if (basicType == EbtFloat)
+            case EOpOuterProduct:
             {
+                ASSERT(basicType == EbtFloat);
                 size_t numRows = (*sequence)[0]->getAsTyped()->getType().getObjectSize();
                 size_t numCols = (*sequence)[1]->getAsTyped()->getType().getObjectSize();
-                resultArray = new TConstantUnion[numRows * numCols];
+                resultArray    = new TConstantUnion[numRows * numCols];
                 angle::Matrix<float> result =
                     GetMatrix(unionArrays[0], static_cast<int>(numRows), 1)
                         .outerProduct(GetMatrix(unionArrays[1], 1, static_cast<int>(numCols)));
                 SetUnionArrayFromMatrix(result, resultArray);
+                break;
             }
-            else
-                UNREACHABLE();
-            break;
 
-          default:
-            UNREACHABLE();
-            // TODO: Add constant folding support for other built-in operations that take 2 parameters and not handled above.
-            return nullptr;
+            default:
+                UNREACHABLE();
+                // TODO: Add constant folding support for other built-in operations that take 2
+                // parameters and not handled above.
+                return nullptr;
         }
     }
     else if (paramsCount == 3)
     {
         //
         // Ternary built-in
         //
         switch (op)
         {
-          case EOpClamp:
+            case EOpClamp:
             {
                 resultArray = new TConstantUnion[maxObjectSize];
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
                     switch (basicType)
                     {
-                      case EbtFloat:
+                        case EbtFloat:
                         {
-                            float x = unionArrays[0][i].getFConst();
+                            float x   = unionArrays[0][i].getFConst();
                             float min = unionArrays[1][i].getFConst();
                             float max = unionArrays[2][i].getFConst();
                             // Results are undefined if min > max.
                             if (min > max)
-                                UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
+                                UndefinedConstantFoldingError(loc, op, basicType, diagnostics,
+                                                              &resultArray[i]);
                             else
                                 resultArray[i].setFConst(gl::clamp(x, min, max));
+                            break;
                         }
-                        break;
-                      case EbtInt:
+
+                        case EbtInt:
                         {
-                            int x = unionArrays[0][i].getIConst();
+                            int x   = unionArrays[0][i].getIConst();
                             int min = unionArrays[1][i].getIConst();
                             int max = unionArrays[2][i].getIConst();
                             // Results are undefined if min > max.
                             if (min > max)
-                                UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
+                                UndefinedConstantFoldingError(loc, op, basicType, diagnostics,
+                                                              &resultArray[i]);
                             else
                                 resultArray[i].setIConst(gl::clamp(x, min, max));
+                            break;
                         }
-                        break;
-                      case EbtUInt:
+                        case EbtUInt:
                         {
-                            unsigned int x = unionArrays[0][i].getUConst();
+                            unsigned int x   = unionArrays[0][i].getUConst();
                             unsigned int min = unionArrays[1][i].getUConst();
                             unsigned int max = unionArrays[2][i].getUConst();
                             // Results are undefined if min > max.
                             if (min > max)
-                                UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
+                                UndefinedConstantFoldingError(loc, op, basicType, diagnostics,
+                                                              &resultArray[i]);
                             else
                                 resultArray[i].setUConst(gl::clamp(x, min, max));
+                            break;
                         }
-                        break;
-                      default:
-                        UNREACHABLE();
-                        break;
-                    }
-                }
-            }
-            break;
-
-          case EOpMix:
-            {
-                if (basicType == EbtFloat)
-                {
-                    resultArray = new TConstantUnion[maxObjectSize];
-                    for (size_t i = 0; i < maxObjectSize; i++)
-                    {
-                        float x = unionArrays[0][i].getFConst();
-                        float y = unionArrays[1][i].getFConst();
-                        TBasicType type = (*sequence)[2]->getAsTyped()->getType().getBasicType();
-                        if (type == EbtFloat)
-                        {
-                            // Returns the linear blend of x and y, i.e., x * (1 - a) + y * a.
-                            float a = unionArrays[2][i].getFConst();
-                            resultArray[i].setFConst(x * (1.0f - a) + y * a);
-                        }
-                        else // 3rd parameter is EbtBool
-                        {
-                            ASSERT(type == EbtBool);
-                            // Selects which vector each returned component comes from.
-                            // For a component of a that is false, the corresponding component of x is returned.
-                            // For a component of a that is true, the corresponding component of y is returned.
-                            bool a = unionArrays[2][i].getBConst();
-                            resultArray[i].setFConst(a ? y : x);
-                        }
+                        default:
+                            UNREACHABLE();
+                            break;
                     }
                 }
-                else
-                    UNREACHABLE();
+                break;
             }
-            break;
 
-          case EOpSmoothStep:
+            case EOpMix:
             {
-                if (basicType == EbtFloat)
+                ASSERT(basicType == EbtFloat);
+                resultArray = new TConstantUnion[maxObjectSize];
+                for (size_t i = 0; i < maxObjectSize; i++)
                 {
-                    resultArray = new TConstantUnion[maxObjectSize];
-                    for (size_t i = 0; i < maxObjectSize; i++)
+                    float x         = unionArrays[0][i].getFConst();
+                    float y         = unionArrays[1][i].getFConst();
+                    TBasicType type = (*sequence)[2]->getAsTyped()->getType().getBasicType();
+                    if (type == EbtFloat)
                     {
-                        float edge0 = unionArrays[0][i].getFConst();
-                        float edge1 = unionArrays[1][i].getFConst();
-                        float x = unionArrays[2][i].getFConst();
-                        // Results are undefined if edge0 >= edge1.
-                        if (edge0 >= edge1)
-                        {
-                            UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
-                        }
-                        else
-                        {
-                            // Returns 0.0 if x <= edge0 and 1.0 if x >= edge1 and performs smooth
-                            // Hermite interpolation between 0 and 1 when edge0 < x < edge1.
-                            float t = gl::clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
-                            resultArray[i].setFConst(t * t * (3.0f - 2.0f * t));
-                        }
+                        // Returns the linear blend of x and y, i.e., x * (1 - a) + y * a.
+                        float a = unionArrays[2][i].getFConst();
+                        resultArray[i].setFConst(x * (1.0f - a) + y * a);
+                    }
+                    else  // 3rd parameter is EbtBool
+                    {
+                        ASSERT(type == EbtBool);
+                        // Selects which vector each returned component comes from.
+                        // For a component of a that is false, the corresponding component of x is
+                        // returned.
+                        // For a component of a that is true, the corresponding component of y is
+                        // returned.
+                        bool a = unionArrays[2][i].getBConst();
+                        resultArray[i].setFConst(a ? y : x);
                     }
                 }
-                else
-                    UNREACHABLE();
+                break;
             }
-            break;
 
-          case EOpFaceForward:
-            if (basicType == EbtFloat)
+            case EOpSmoothStep:
             {
+                ASSERT(basicType == EbtFloat);
+                resultArray = new TConstantUnion[maxObjectSize];
+                for (size_t i = 0; i < maxObjectSize; i++)
+                {
+                    float edge0 = unionArrays[0][i].getFConst();
+                    float edge1 = unionArrays[1][i].getFConst();
+                    float x     = unionArrays[2][i].getFConst();
+                    // Results are undefined if edge0 >= edge1.
+                    if (edge0 >= edge1)
+                    {
+                        UndefinedConstantFoldingError(loc, op, basicType, diagnostics,
+                                                      &resultArray[i]);
+                    }
+                    else
+                    {
+                        // Returns 0.0 if x <= edge0 and 1.0 if x >= edge1 and performs smooth
+                        // Hermite interpolation between 0 and 1 when edge0 < x < edge1.
+                        float t = gl::clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
+                        resultArray[i].setFConst(t * t * (3.0f - 2.0f * t));
+                    }
+                }
+                break;
+            }
+
+            case EOpFaceForward:
+            {
+                ASSERT(basicType == EbtFloat);
                 // genType faceforward(genType N, genType I, genType Nref) :
                 //     If dot(Nref, I) < 0 return N, otherwise return -N.
-                resultArray = new TConstantUnion[maxObjectSize];
+                resultArray      = new TConstantUnion[maxObjectSize];
                 float dotProduct = VectorDotProduct(unionArrays[2], unionArrays[1], maxObjectSize);
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
                     if (dotProduct < 0)
                         resultArray[i].setFConst(unionArrays[0][i].getFConst());
                     else
                         resultArray[i].setFConst(-unionArrays[0][i].getFConst());
                 }
+                break;
             }
-            else
-                UNREACHABLE();
-            break;
 
-          case EOpRefract:
-            if (basicType == EbtFloat)
+            case EOpRefract:
             {
+                ASSERT(basicType == EbtFloat);
                 // genType refract(genType I, genType N, float eta) :
-                //     For the incident vector I and surface normal N, and the ratio of indices of refraction eta,
+                //     For the incident vector I and surface normal N, and the ratio of indices of
+                //     refraction eta,
                 //     return the refraction vector. The result is computed by
                 //         k = 1.0 - eta * eta * (1.0 - dot(N, I) * dot(N, I))
                 //         if (k < 0.0)
                 //             return genType(0.0)
                 //         else
                 //             return eta * I - (eta * dot(N, I) + sqrt(k)) * N
-                resultArray = new TConstantUnion[maxObjectSize];
+                resultArray      = new TConstantUnion[maxObjectSize];
                 float dotProduct = VectorDotProduct(unionArrays[1], unionArrays[0], maxObjectSize);
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
                     float eta = unionArrays[2][i].getFConst();
-                    float k = 1.0f - eta * eta * (1.0f - dotProduct * dotProduct);
+                    float k   = 1.0f - eta * eta * (1.0f - dotProduct * dotProduct);
                     if (k < 0.0f)
                         resultArray[i].setFConst(0.0f);
                     else
                         resultArray[i].setFConst(eta * unionArrays[0][i].getFConst() -
-                                                    (eta * dotProduct + sqrtf(k)) * unionArrays[1][i].getFConst());
+                                                 (eta * dotProduct + sqrtf(k)) *
+                                                     unionArrays[1][i].getFConst());
                 }
+                break;
             }
-            else
+
+            default:
                 UNREACHABLE();
-            break;
-
-          default:
-            UNREACHABLE();
-            // TODO: Add constant folding support for other built-in operations that take 3 parameters and not handled above.
-            return nullptr;
+                // TODO: Add constant folding support for other built-in operations that take 3
+                // parameters and not handled above.
+                return nullptr;
         }
     }
     return resultArray;
 }
 
 // static
 TString TIntermTraverser::hash(const TString &name, ShHashFunction64 hashFunction)
 {
--- a/gfx/angle/src/compiler/translator/IntermNode.h
+++ b/gfx/angle/src/compiler/translator/IntermNode.h
@@ -29,16 +29,17 @@
 
 class TDiagnostics;
 
 class TIntermTraverser;
 class TIntermAggregate;
 class TIntermBinary;
 class TIntermUnary;
 class TIntermConstantUnion;
+class TIntermTernary;
 class TIntermSelection;
 class TIntermSwitch;
 class TIntermCase;
 class TIntermTyped;
 class TIntermSymbol;
 class TIntermLoop;
 class TInfoSink;
 class TInfoSinkBase;
@@ -88,16 +89,17 @@ class TIntermNode : angle::NonCopyable
     void setLine(const TSourceLoc &l) { mLine = l; }
 
     virtual void traverse(TIntermTraverser *) = 0;
     virtual TIntermTyped *getAsTyped() { return 0; }
     virtual TIntermConstantUnion *getAsConstantUnion() { return 0; }
     virtual TIntermAggregate *getAsAggregate() { return 0; }
     virtual TIntermBinary *getAsBinaryNode() { return 0; }
     virtual TIntermUnary *getAsUnaryNode() { return 0; }
+    virtual TIntermTernary *getAsTernaryNode() { return nullptr; }
     virtual TIntermSelection *getAsSelectionNode() { return 0; }
     virtual TIntermSwitch *getAsSwitchNode() { return 0; }
     virtual TIntermCase *getAsCaseNode() { return 0; }
     virtual TIntermSymbol *getAsSymbolNode() { return 0; }
     virtual TIntermLoop *getAsLoopNode() { return 0; }
     virtual TIntermRaw *getAsRawNode() { return 0; }
     virtual TIntermBranch *getAsBranchNode() { return 0; }
 
@@ -154,16 +156,19 @@ class TIntermTyped : public TIntermNode
     bool isScalarInt() const { return mType.isScalarInt(); }
     const char *getBasicString() const { return mType.getBasicString(); }
     TString getCompleteString() const { return mType.getCompleteString(); }
 
     unsigned int getArraySize() const { return mType.getArraySize(); }
 
     bool isConstructorWithOnlyConstantUnionParameters();
 
+    static TIntermTyped *CreateIndexNode(int index);
+    static TIntermTyped *CreateZero(const TType &type);
+
   protected:
     TType mType;
 
     TIntermTyped(const TIntermTyped &node);
 };
 
 //
 // Handle for, do-while, and while loops.
@@ -311,16 +316,17 @@ class TIntermRaw : public TIntermTyped
 // Other nodes than TIntermConstantUnion may also be constant expressions.
 //
 class TIntermConstantUnion : public TIntermTyped
 {
   public:
     TIntermConstantUnion(const TConstantUnion *unionPointer, const TType &type)
         : TIntermTyped(type), mUnionArrayPointer(unionPointer)
     {
+        ASSERT(unionPointer);
     }
 
     TIntermTyped *deepCopy() const override { return new TIntermConstantUnion(*this); }
 
     bool hasSideEffects() const override { return false; }
 
     const TConstantUnion *getUnionArrayPointer() const { return mUnionArrayPointer; }
 
@@ -338,53 +344,56 @@ class TIntermConstantUnion : public TInt
     }
     bool getBConst(size_t index) const
     {
         return mUnionArrayPointer ? mUnionArrayPointer[index].getBConst() : false;
     }
 
     void replaceConstantUnion(const TConstantUnion *safeConstantUnion)
     {
+        ASSERT(safeConstantUnion);
         // Previous union pointer freed on pool deallocation.
         mUnionArrayPointer = safeConstantUnion;
     }
 
     TIntermConstantUnion *getAsConstantUnion() override { return this; }
     void traverse(TIntermTraverser *it) override;
     bool replaceChildNode(TIntermNode *, TIntermNode *) override { return false; }
 
     TConstantUnion *foldBinary(TOperator op,
                                TIntermConstantUnion *rightNode,
                                TDiagnostics *diagnostics);
-    TConstantUnion *foldUnaryWithDifferentReturnType(TOperator op, TInfoSink &infoSink);
-    TConstantUnion *foldUnaryWithSameReturnType(TOperator op, TInfoSink &infoSink);
+    const TConstantUnion *foldIndexing(int index);
+    TConstantUnion *foldUnaryNonComponentWise(TOperator op);
+    TConstantUnion *foldUnaryComponentWise(TOperator op, TDiagnostics *diagnostics);
 
-    static TConstantUnion *FoldAggregateConstructor(TIntermAggregate *aggregate,
-                                                    TInfoSink &infoSink);
-    static TConstantUnion *FoldAggregateBuiltIn(TIntermAggregate *aggregate, TInfoSink &infoSink);
+    static TConstantUnion *FoldAggregateConstructor(TIntermAggregate *aggregate);
+    static TConstantUnion *FoldAggregateBuiltIn(TIntermAggregate *aggregate,
+                                                TDiagnostics *diagnostics);
 
   protected:
     // Same data may be shared between multiple constant unions, so it can't be modified.
     const TConstantUnion *mUnionArrayPointer;
 
   private:
     typedef float(*FloatTypeUnaryFunc) (float);
-    bool foldFloatTypeUnary(const TConstantUnion &parameter, FloatTypeUnaryFunc builtinFunc, TInfoSink &infoSink, TConstantUnion *result) const;
+    void foldFloatTypeUnary(const TConstantUnion &parameter,
+                            FloatTypeUnaryFunc builtinFunc,
+                            TConstantUnion *result) const;
 
     TIntermConstantUnion(const TIntermConstantUnion &node);  // Note: not deleted, just private!
 };
 
 //
 // Intermediate class for node types that hold operators.
 //
 class TIntermOperator : public TIntermTyped
 {
   public:
     TOperator getOp() const { return mOp; }
-    void setOp(TOperator op) { mOp = op; }
 
     bool isAssignment() const;
     bool isMultiplication() const;
     bool isConstructor() const;
 
     bool hasSideEffects() const override { return isAssignment(); }
 
   protected:
@@ -401,40 +410,33 @@ class TIntermOperator : public TIntermTy
 };
 
 //
 // Nodes for all the basic binary math operators.
 //
 class TIntermBinary : public TIntermOperator
 {
   public:
-    TIntermBinary(TOperator op)
-        : TIntermOperator(op),
-          mAddIndexClamp(false) {}
-
     // This constructor determines the type of the binary node based on the operands and op.
-    // This is only supported for math/logical ops, not indexing.
     TIntermBinary(TOperator op, TIntermTyped *left, TIntermTyped *right);
 
     TIntermTyped *deepCopy() const override { return new TIntermBinary(*this); }
 
     static TOperator GetMulOpBasedOnOperands(const TType &left, const TType &right);
     static TOperator GetMulAssignOpBasedOnOperands(const TType &left, const TType &right);
 
     TIntermBinary *getAsBinaryNode() override { return this; };
     void traverse(TIntermTraverser *it) override;
     bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
 
     bool hasSideEffects() const override
     {
         return isAssignment() || mLeft->hasSideEffects() || mRight->hasSideEffects();
     }
 
-    void setLeft(TIntermTyped *node) { mLeft = node; }
-    void setRight(TIntermTyped *node) { mRight = node; }
     TIntermTyped *getLeft() const { return mLeft; }
     TIntermTyped *getRight() const { return mRight; }
     TIntermTyped *fold(TDiagnostics *diagnostics);
 
     void setAddIndexClamp() { mAddIndexClamp = true; }
     bool getAddIndexClamp() { return mAddIndexClamp; }
 
   protected:
@@ -451,49 +453,42 @@ class TIntermBinary : public TIntermOper
 };
 
 //
 // Nodes for unary math operators.
 //
 class TIntermUnary : public TIntermOperator
 {
   public:
-    TIntermUnary(TOperator op, const TType &type)
-        : TIntermOperator(op, type),
-          mOperand(NULL),
-          mUseEmulatedFunction(false) {}
-    TIntermUnary(TOperator op)
-        : TIntermOperator(op),
-          mOperand(NULL),
-          mUseEmulatedFunction(false) {}
+    TIntermUnary(TOperator op, TIntermTyped *operand);
 
     TIntermTyped *deepCopy() const override { return new TIntermUnary(*this); }
 
     void traverse(TIntermTraverser *it) override;
     TIntermUnary *getAsUnaryNode() override { return this; }
     bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
 
     bool hasSideEffects() const override { return isAssignment() || mOperand->hasSideEffects(); }
 
-    void setOperand(TIntermTyped *operand) { mOperand = operand; }
     TIntermTyped *getOperand() { return mOperand; }
-    void promote(const TType *funcReturnType);
-    TIntermTyped *fold(TInfoSink &infoSink);
+    TIntermTyped *fold(TDiagnostics *diagnostics);
 
     void setUseEmulatedFunction() { mUseEmulatedFunction = true; }
     bool getUseEmulatedFunction() { return mUseEmulatedFunction; }
 
   protected:
     TIntermTyped *mOperand;
 
     // If set to true, replace the built-in function call with an emulated one
     // to work around driver bugs.
     bool mUseEmulatedFunction;
 
   private:
+    void promote();
+
     TIntermUnary(const TIntermUnary &node);  // note: not deleted, just private!
 };
 
 typedef TVector<TIntermNode *> TIntermSequence;
 typedef TVector<int> TQualifierList;
 
 //
 // Nodes that operate on an arbitrary sized set of children.
@@ -517,26 +512,29 @@ class TIntermAggregate : public TIntermO
           mGotPrecisionFromChildren(false)
     {
     }
     ~TIntermAggregate() { }
 
     // Note: only supported for nodes that can be a part of an expression.
     TIntermTyped *deepCopy() const override { return new TIntermAggregate(*this); }
 
+    void setOp(TOperator op) { mOp = op; }
+
     TIntermAggregate *getAsAggregate() override { return this; }
     void traverse(TIntermTraverser *it) override;
     bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
     bool replaceChildNodeWithMultiple(TIntermNode *original, TIntermSequence replacements);
     bool insertChildNodes(TIntermSequence::size_type position, TIntermSequence insertions);
     // Conservatively assume function calls and other aggregate operators have side-effects
     bool hasSideEffects() const override { return true; }
-    TIntermTyped *fold(TInfoSink &infoSink);
+    TIntermTyped *fold(TDiagnostics *diagnostics);
 
     TIntermSequence *getSequence() { return &mSequence; }
+    const TIntermSequence *getSequence() const { return &mSequence; }
 
     void setNameObj(const TName &name) { mName = name; }
     const TName &getNameObj() const { return mName; }
 
     void setName(const TString &name) { mName.setString(name); }
     const TString &getName() const { return mName.getString(); }
 
     void setUserDefined() { mUserDefined = true; }
@@ -566,56 +564,71 @@ class TIntermAggregate : public TIntermO
     bool mUseEmulatedFunction;
 
     bool mGotPrecisionFromChildren;
 
   private:
     TIntermAggregate(const TIntermAggregate &node);  // note: not deleted, just private!
 };
 
-//
-// For if tests.
-//
-class TIntermSelection : public TIntermTyped
+// For ternary operators like a ? b : c.
+class TIntermTernary : public TIntermTyped
 {
   public:
-    TIntermSelection(TIntermTyped *cond, TIntermNode *trueB, TIntermNode *falseB)
-        : TIntermTyped(TType(EbtVoid, EbpUndefined)),
-          mCondition(cond),
-          mTrueBlock(trueB),
-          mFalseBlock(falseB) {}
-    TIntermSelection(TIntermTyped *cond, TIntermNode *trueB, TIntermNode *falseB,
-                     const TType &type)
-        : TIntermTyped(type),
-          mCondition(cond),
-          mTrueBlock(trueB),
-          mFalseBlock(falseB) {}
-
-    // Note: only supported for ternary operator nodes.
-    TIntermTyped *deepCopy() const override { return new TIntermSelection(*this); }
+    TIntermTernary(TIntermTyped *cond, TIntermTyped *trueExpression, TIntermTyped *falseExpression);
 
     void traverse(TIntermTraverser *it) override;
     bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
 
-    // Conservatively assume selections have side-effects
-    bool hasSideEffects() const override { return true; }
+    TIntermTyped *getCondition() const { return mCondition; }
+    TIntermTyped *getTrueExpression() const { return mTrueExpression; }
+    TIntermTyped *getFalseExpression() const { return mFalseExpression; }
+    TIntermTernary *getAsTernaryNode() override { return this; }
+
+    TIntermTyped *deepCopy() const override { return new TIntermTernary(*this); }
+
+    bool hasSideEffects() const override
+    {
+        return mCondition->hasSideEffects() || mTrueExpression->hasSideEffects() ||
+               mFalseExpression->hasSideEffects();
+    }
+
+    static TQualifier DetermineQualifier(TIntermTyped *cond,
+                                         TIntermTyped *trueExpression,
+                                         TIntermTyped *falseExpression);
 
-    bool usesTernaryOperator() const { return getBasicType() != EbtVoid; }
-    TIntermNode *getCondition() const { return mCondition; }
+  private:
+    TIntermTernary(const TIntermTernary &node);  // Note: not deleted, just private!
+
+    TIntermTyped *mCondition;
+    TIntermTyped *mTrueExpression;
+    TIntermTyped *mFalseExpression;
+};
+
+// For if tests.
+class TIntermSelection : public TIntermNode
+{
+  public:
+    TIntermSelection(TIntermTyped *cond, TIntermNode *trueB, TIntermNode *falseB)
+        : TIntermNode(), mCondition(cond), mTrueBlock(trueB), mFalseBlock(falseB)
+    {
+    }
+
+    void traverse(TIntermTraverser *it) override;
+    bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
+
+    TIntermTyped *getCondition() const { return mCondition; }
     TIntermNode *getTrueBlock() const { return mTrueBlock; }
     TIntermNode *getFalseBlock() const { return mFalseBlock; }
     TIntermSelection *getAsSelectionNode() override { return this; }
 
   protected:
     TIntermTyped *mCondition;
     TIntermNode *mTrueBlock;
     TIntermNode *mFalseBlock;
-
-  private:
-    TIntermSelection(const TIntermSelection &node);  // Note: not deleted, just private!
 };
 
 //
 // Switch statement.
 //
 class TIntermSwitch : public TIntermNode
 {
   public:
@@ -691,31 +704,33 @@ class TIntermTraverser : angle::NonCopya
     TIntermTraverser(bool preVisit, bool inVisit, bool postVisit);
     virtual ~TIntermTraverser();
 
     virtual void visitSymbol(TIntermSymbol *node) {}
     virtual void visitRaw(TIntermRaw *node) {}
     virtual void visitConstantUnion(TIntermConstantUnion *node) {}
     virtual bool visitBinary(Visit visit, TIntermBinary *node) { return true; }
     virtual bool visitUnary(Visit visit, TIntermUnary *node) { return true; }
+    virtual bool visitTernary(Visit visit, TIntermTernary *node) { return true; }
     virtual bool visitSelection(Visit visit, TIntermSelection *node) { return true; }
     virtual bool visitSwitch(Visit visit, TIntermSwitch *node) { return true; }
     virtual bool visitCase(Visit visit, TIntermCase *node) { return true; }
     virtual bool visitAggregate(Visit visit, TIntermAggregate *node) { return true; }
     virtual bool visitLoop(Visit visit, TIntermLoop *node) { return true; }
     virtual bool visitBranch(Visit visit, TIntermBranch *node) { return true; }
 
     // The traverse functions contain logic for iterating over the children of the node
     // and calling the visit functions in the appropriate places. They also track some
     // context that may be used by the visit functions.
     virtual void traverseSymbol(TIntermSymbol *node);
     virtual void traverseRaw(TIntermRaw *node);
     virtual void traverseConstantUnion(TIntermConstantUnion *node);
     virtual void traverseBinary(TIntermBinary *node);
     virtual void traverseUnary(TIntermUnary *node);
+    virtual void traverseTernary(TIntermTernary *node);
     virtual void traverseSelection(TIntermSelection *node);
     virtual void traverseSwitch(TIntermSwitch *node);
     virtual void traverseCase(TIntermCase *node);
     virtual void traverseAggregate(TIntermAggregate *node);
     virtual void traverseLoop(TIntermLoop *node);
     virtual void traverseBranch(TIntermBranch *node);
 
     int getMaxDepth() const { return mMaxDepth; }
@@ -993,16 +1008,17 @@ class TMaxDepthTraverser : public TInter
   public:
     POOL_ALLOCATOR_NEW_DELETE();
     TMaxDepthTraverser(int depthLimit)
         : TIntermTraverser(true, true, false),
           mDepthLimit(depthLimit) { }
 
     bool visitBinary(Visit, TIntermBinary *) override { return depthCheck(); }
     bool visitUnary(Visit, TIntermUnary *) override { return depthCheck(); }
+    bool visitTernary(Visit, TIntermTernary *) override { return depthCheck(); }
     bool visitSelection(Visit, TIntermSelection *) override { return depthCheck(); }
     bool visitAggregate(Visit, TIntermAggregate *) override { return depthCheck(); }
     bool visitLoop(Visit, TIntermLoop *) override { return depthCheck(); }
     bool visitBranch(Visit, TIntermBranch *) override { return depthCheck(); }
 
   protected:
     bool depthCheck() const { return mMaxDepth < mDepthLimit; }
 
--- a/gfx/angle/src/compiler/translator/IntermNodePatternMatcher.cpp
+++ b/gfx/angle/src/compiler/translator/IntermNodePatternMatcher.cpp
@@ -100,19 +100,16 @@ bool IntermNodePatternMatcher::match(TIn
             {
                 return true;
             }
         }
     }
     return false;
 }
 
-bool IntermNodePatternMatcher::match(TIntermSelection *node)
+bool IntermNodePatternMatcher::match(TIntermTernary *node)
 {
     if ((mMask & kUnfoldedShortCircuitExpression) != 0)
     {
-        if (node->usesTernaryOperator())
-        {
-            return true;
-        }
+        return true;
     }
     return false;
 }
--- a/gfx/angle/src/compiler/translator/IntermNodePatternMatcher.h
+++ b/gfx/angle/src/compiler/translator/IntermNodePatternMatcher.h
@@ -9,17 +9,17 @@
 //
 
 #ifndef COMPILER_TRANSLATOR_INTERMNODEPATTERNMATCHER_H_
 #define COMPILER_TRANSLATOR_INTERMNODEPATTERNMATCHER_H_
 
 class TIntermAggregate;
 class TIntermBinary;
 class TIntermNode;
-class TIntermSelection;
+class TIntermTernary;
 
 class IntermNodePatternMatcher
 {
   public:
     static bool IsDynamicIndexingOfVectorOrMatrix(TIntermBinary *node);
 
     enum PatternType
     {
@@ -37,17 +37,17 @@ class IntermNodePatternMatcher
 
     bool match(TIntermBinary *node, TIntermNode *parentNode);
 
     // Use this version for checking binary node matches in case you're using flag
     // kDynamicIndexingOfVectorOrMatrixInLValue.
     bool match(TIntermBinary *node, TIntermNode *parentNode, bool isLValueRequiredHere);
 
     bool match(TIntermAggregate *node, TIntermNode *parentNode);
-    bool match(TIntermSelection *node);
+    bool match(TIntermTernary *node);
 
   private:
     const unsigned int mMask;
 
     bool matchInternal(TIntermBinary *node, TIntermNode *parentNode);
 };
 
 #endif
--- a/gfx/angle/src/compiler/translator/IntermTraverse.cpp
+++ b/gfx/angle/src/compiler/translator/IntermTraverse.cpp
@@ -28,16 +28,21 @@ void TIntermBinary::traverse(TIntermTrav
     it->traverseBinary(this);
 }
 
 void TIntermUnary::traverse(TIntermTraverser *it)
 {
     it->traverseUnary(this);
 }
 
+void TIntermTernary::traverse(TIntermTraverser *it)
+{
+    it->traverseTernary(this);
+}
+
 void TIntermSelection::traverse(TIntermTraverser *it)
 {
     it->traverseSelection(this);
 }
 
 void TIntermSwitch::traverse(TIntermTraverser *it)
 {
     it->traverseSwitch(this);
@@ -142,37 +147,31 @@ TIntermAggregate *TIntermTraverser::crea
     return tempDeclaration;
 }
 
 TIntermAggregate *TIntermTraverser::createTempInitDeclaration(TIntermTyped *initializer, TQualifier qualifier)
 {
     ASSERT(initializer != nullptr);
     TIntermSymbol *tempSymbol = createTempSymbol(initializer->getType(), qualifier);
     TIntermAggregate *tempDeclaration = new TIntermAggregate(EOpDeclaration);
-    TIntermBinary *tempInit = new TIntermBinary(EOpInitialize);
-    tempInit->setLeft(tempSymbol);
-    tempInit->setRight(initializer);
-    tempInit->setType(tempSymbol->getType());
+    TIntermBinary *tempInit           = new TIntermBinary(EOpInitialize, tempSymbol, initializer);
     tempDeclaration->getSequence()->push_back(tempInit);
     return tempDeclaration;
 }
 
 TIntermAggregate *TIntermTraverser::createTempInitDeclaration(TIntermTyped *initializer)
 {
     return createTempInitDeclaration(initializer, EvqTemporary);
 }
 
 TIntermBinary *TIntermTraverser::createTempAssignment(TIntermTyped *rightNode)
 {
     ASSERT(rightNode != nullptr);
     TIntermSymbol *tempSymbol = createTempSymbol(rightNode->getType());
-    TIntermBinary *assignment = new TIntermBinary(EOpAssign);
-    assignment->setLeft(tempSymbol);
-    assignment->setRight(rightNode);
-    assignment->setType(tempSymbol->getType());
+    TIntermBinary *assignment = new TIntermBinary(EOpAssign, tempSymbol, rightNode);
     return assignment;
 }
 
 void TIntermTraverser::useTemporaryIndex(unsigned int *temporaryIndex)
 {
     mTemporaryIndex = temporaryIndex;
 }
 
@@ -568,16 +567,41 @@ void TLValueTrackingTraverser::traverseA
         decrementDepth();
     }
 
     if (visit && postVisit)
         visitAggregate(PostVisit, node);
 }
 
 //
+// Traverse a ternary node.  Same comments in binary node apply here.
+//
+void TIntermTraverser::traverseTernary(TIntermTernary *node)
+{
+    bool visit = true;
+
+    if (preVisit)
+        visit = visitTernary(PreVisit, node);
+
+    if (visit)
+    {
+        incrementDepth(node);
+        node->getCondition()->traverse(this);
+        if (node->getTrueExpression())
+            node->getTrueExpression()->traverse(this);
+        if (node->getFalseExpression())
+            node->getFalseExpression()->traverse(this);
+        decrementDepth();
+    }
+
+    if (visit && postVisit)
+        visitTernary(PostVisit, node);
+}
+
+//
 // Traverse a selection node.  Same comments in binary node apply here.
 //
 void TIntermTraverser::traverseSelection(TIntermSelection *node)
 {
     bool visit = true;
 
     if (preVisit)
         visit = visitSelection(PreVisit, node);
--- a/gfx/angle/src/compiler/translator/Intermediate.cpp
+++ b/gfx/angle/src/compiler/translator/Intermediate.cpp
@@ -39,48 +39,30 @@ TIntermSymbol *TIntermediate::addSymbol(
 
 //
 // Connect two nodes through an index operator, where the left node is the base
 // of an array or struct, and the right node is a direct or indirect offset.
 //
 // Returns the added node.
 // The caller should set the type of the returned node.
 //
-TIntermTyped *TIntermediate::addIndex(
-    TOperator op, TIntermTyped *base, TIntermTyped *index, const TSourceLoc &line)
+TIntermTyped *TIntermediate::addIndex(TOperator op,
+                                      TIntermTyped *base,
+                                      TIntermTyped *index,
+                                      const TSourceLoc &line,
+                                      TDiagnostics *diagnostics)
 {
-    TIntermBinary *node = new TIntermBinary(op);
+    TIntermBinary *node = new TIntermBinary(op, base, index);
     node->setLine(line);
-    node->setLeft(base);
-    node->setRight(index);
-
-    // caller should set the type
-
-    return node;
-}
 
-//
-// Add one node as the parent of another that it operates on.
-//
-// Returns the added node.
-//
-TIntermTyped *TIntermediate::addUnaryMath(
-    TOperator op, TIntermTyped *child, const TSourceLoc &line, const TType *funcReturnType)
-{
-    //
-    // Make a new node for the operator.
-    //
-    TIntermUnary *node = new TIntermUnary(op);
-    node->setLine(line);
-    node->setOperand(child);
-    node->promote(funcReturnType);
-
-    TIntermTyped *foldedNode = node->fold(mInfoSink);
-    if (foldedNode)
-        return foldedNode;
+    TIntermTyped *folded = node->fold(diagnostics);
+    if (folded)
+    {
+        return folded;
+    }
 
     return node;
 }
 
 //
 // This is the safe way to change the operator on an aggregate, as it
 // does lots of error checking and fixing.  Especially for establishing
 // a function call's operation on it's set of parameters.  Sequences
@@ -249,53 +231,47 @@ TIntermTyped *TIntermediate::addComma(TI
         commaNode = growAggregate(left, right, line);
         commaNode->getAsAggregate()->setOp(EOpComma);
         commaNode->setType(right->getType());
     }
     commaNode->getTypePointer()->setQualifier(resultQualifier);
     return commaNode;
 }
 
-//
 // For "?:" test nodes.  There are three children; a condition,
 // a true path, and a false path.  The two paths are specified
 // as separate parameters.
 //
-// Returns the selection node created, or one of trueBlock and falseBlock if the expression could be folded.
-//
-TIntermTyped *TIntermediate::addSelection(TIntermTyped *cond, TIntermTyped *trueBlock, TIntermTyped *falseBlock,
-                                          const TSourceLoc &line)
+// Returns the ternary node created, or one of trueExpression and falseExpression if the expression
+// could be folded.
+TIntermTyped *TIntermediate::AddTernarySelection(TIntermTyped *cond,
+                                                 TIntermTyped *trueExpression,
+                                                 TIntermTyped *falseExpression,
+                                                 const TSourceLoc &line)
 {
-    TQualifier resultQualifier = EvqTemporary;
-    if (cond->getQualifier() == EvqConst && trueBlock->getQualifier() == EvqConst &&
-        falseBlock->getQualifier() == EvqConst)
-    {
-        resultQualifier = EvqConst;
-    }
     // Note that the node resulting from here can be a constant union without being qualified as
     // constant.
     if (cond->getAsConstantUnion())
     {
+        TQualifier resultQualifier =
+            TIntermTernary::DetermineQualifier(cond, trueExpression, falseExpression);
         if (cond->getAsConstantUnion()->getBConst(0))
         {
-            trueBlock->getTypePointer()->setQualifier(resultQualifier);
-            return trueBlock;
+            trueExpression->getTypePointer()->setQualifier(resultQualifier);
+            return trueExpression;
         }
         else
         {
-            falseBlock->getTypePointer()->setQualifier(resultQualifier);
-            return falseBlock;
+            falseExpression->getTypePointer()->setQualifier(resultQualifier);
+            return falseExpression;
         }
     }
 
-    //
-    // Make a selection node.
-    //
-    TIntermSelection *node = new TIntermSelection(cond, trueBlock, falseBlock, trueBlock->getType());
-    node->getTypePointer()->setQualifier(resultQualifier);
+    // Make a ternary node.
+    TIntermTernary *node = new TIntermTernary(cond, trueExpression, falseExpression);
     node->setLine(line);
 
     return node;
 }
 
 TIntermSwitch *TIntermediate::addSwitch(
     TIntermTyped *init, TIntermAggregate *statementList, const TSourceLoc &line)
 {
@@ -330,16 +306,17 @@ TIntermConstantUnion *TIntermediate::add
     return node;
 }
 
 TIntermTyped *TIntermediate::addSwizzle(
     TVectorFields &fields, const TSourceLoc &line)
 {
 
     TIntermAggregate *node = new TIntermAggregate(EOpSequence);
+    node->getTypePointer()->setQualifier(EvqConst);
 
     node->setLine(line);
     TIntermConstantUnion *constIntNode;
     TIntermSequence *sequenceVector = node->getSequence();
     TConstantUnion *unionArray;
 
     for (int i = 0; i < fields.num; i++)
     {
@@ -383,17 +360,17 @@ TIntermBranch* TIntermediate::addBranch(
 
     return node;
 }
 
 //
 // This is to be executed once the final root is put on top by the parsing
 // process.
 //
-TIntermAggregate *TIntermediate::postProcess(TIntermNode *root)
+TIntermAggregate *TIntermediate::PostProcess(TIntermNode *root)
 {
     if (root == nullptr)
         return nullptr;
 
     //
     // Finish off the top level sequence, if any
     //
     TIntermAggregate *aggRoot = root->getAsAggregate();
@@ -406,17 +383,18 @@ TIntermAggregate *TIntermediate::postPro
         aggRoot = new TIntermAggregate(EOpSequence);
         aggRoot->setLine(root->getLine());
         aggRoot->getSequence()->push_back(root);
     }
 
     return aggRoot;
 }
 
-TIntermTyped *TIntermediate::foldAggregateBuiltIn(TIntermAggregate *aggregate)
+TIntermTyped *TIntermediate::foldAggregateBuiltIn(TIntermAggregate *aggregate,
+                                                  TDiagnostics *diagnostics)
 {
     switch (aggregate->getOp())
     {
         case EOpAtan:
         case EOpPow:
         case EOpMod:
         case EOpMin:
         case EOpMax:
@@ -433,19 +411,19 @@ TIntermTyped *TIntermediate::foldAggrega
         case EOpVectorEqual:
         case EOpVectorNotEqual:
         case EOpDistance:
         case EOpDot:
         case EOpCross:
         case EOpFaceForward:
         case EOpReflect:
         case EOpRefract:
-            return aggregate->fold(mInfoSink);
+            return aggregate->fold(diagnostics);
         default:
             // TODO: Add support for folding array constructors
             if (aggregate->isConstructor() && !aggregate->isArray())
             {
-                return aggregate->fold(mInfoSink);
+                return aggregate->fold(diagnostics);
             }
             // Constant folding not supported for the built-in.
             return nullptr;
     }
 }
--- a/gfx/angle/src/compiler/translator/Intermediate.h
+++ b/gfx/angle/src/compiler/translator/Intermediate.h
@@ -11,61 +11,62 @@
 
 struct TVectorFields
 {
     int offsets[4];
     int num;
 };
 
 //
-// Set of helper functions to help parse and build the tree.
+// Set of helper functions to help build the tree.
 //
-class TInfoSink;
 class TIntermediate
 {
   public:
     POOL_ALLOCATOR_NEW_DELETE();
-    TIntermediate(TInfoSink &i)
-        : mInfoSink(i) { }
+    TIntermediate() {}
 
     TIntermSymbol *addSymbol(
         int id, const TString &, const TType &, const TSourceLoc &);
-    TIntermTyped *addIndex(
-        TOperator op, TIntermTyped *base, TIntermTyped *index, const TSourceLoc &);
+    TIntermTyped *addIndex(TOperator op,
+                           TIntermTyped *base,
+                           TIntermTyped *index,
+                           const TSourceLoc &line,
+                           TDiagnostics *diagnostics);
     TIntermTyped *addUnaryMath(
         TOperator op, TIntermTyped *child, const TSourceLoc &line, const TType *funcReturnType);
     TIntermAggregate *growAggregate(
         TIntermNode *left, TIntermNode *right, const TSourceLoc &);
     TIntermAggregate *makeAggregate(TIntermNode *node, const TSourceLoc &);
     TIntermAggregate *ensureSequence(TIntermNode *node);
     TIntermAggregate *setAggregateOperator(TIntermNode *, TOperator, const TSourceLoc &);
-    TIntermNode *addSelection(TIntermTyped *cond, TIntermNodePair code, const TSourceLoc &);
-    TIntermTyped *addSelection(TIntermTyped *cond, TIntermTyped *trueBlock, TIntermTyped *falseBlock,
-                               const TSourceLoc &line);
+    TIntermNode *addSelection(TIntermTyped *cond, TIntermNodePair code, const TSourceLoc &line);
+    static TIntermTyped *AddTernarySelection(TIntermTyped *cond,
+                                             TIntermTyped *trueExpression,
+                                             TIntermTyped *falseExpression,
+                                             const TSourceLoc &line);
     TIntermSwitch *addSwitch(
         TIntermTyped *init, TIntermAggregate *statementList, const TSourceLoc &line);
     TIntermCase *addCase(
         TIntermTyped *condition, const TSourceLoc &line);
     TIntermTyped *addComma(TIntermTyped *left,
                            TIntermTyped *right,
                            const TSourceLoc &line,
                            int shaderVersion);
     TIntermConstantUnion *addConstantUnion(const TConstantUnion *constantUnion,
                                            const TType &type,
                                            const TSourceLoc &line);
     TIntermNode *addLoop(TLoopType, TIntermNode *, TIntermTyped *, TIntermTyped *,
                          TIntermNode *, const TSourceLoc &);
     TIntermBranch *addBranch(TOperator, const TSourceLoc &);
     TIntermBranch *addBranch(TOperator, TIntermTyped *, const TSourceLoc &);
     TIntermTyped *addSwizzle(TVectorFields &, const TSourceLoc &);
-    TIntermAggregate *postProcess(TIntermNode *root);
+    static TIntermAggregate *PostProcess(TIntermNode *root);
 
     static void outputTree(TIntermNode *, TInfoSinkBase &);
 
-    TIntermTyped *foldAggregateBuiltIn(TIntermAggregate *aggregate);
+    TIntermTyped *foldAggregateBuiltIn(TIntermAggregate *aggregate, TDiagnostics *diagnostics);
 
   private:
     void operator=(TIntermediate &); // prevent assignments
-
-    TInfoSink & mInfoSink;
 };
 
 #endif  // COMPILER_TRANSLATOR_INTERMEDIATE_H_
--- a/gfx/angle/src/compiler/translator/OutputGLSLBase.cpp
+++ b/gfx/angle/src/compiler/translator/OutputGLSLBase.cpp
@@ -22,21 +22,19 @@ TString arrayBrackets(const TType &type)
 
 bool isSingleStatement(TIntermNode *node)
 {
     if (const TIntermAggregate *aggregate = node->getAsAggregate())
     {
         return (aggregate->getOp() != EOpFunction) &&
                (aggregate->getOp() != EOpSequence);
     }
-    else if (const TIntermSelection *selection = node->getAsSelectionNode())
+    else if (node->getAsSelectionNode())
     {
-        // Ternary operators are usually part of an assignment operator.
-        // This handles those rare cases in which they are all by themselves.
-        return selection->usesTernaryOperator();
+        return false;
     }
     else if (node->getAsLoopNode())
     {
         return false;
     }
     else if (node->getAsSwitchNode())
     {
         return false;
@@ -706,50 +704,50 @@ bool TOutputGLSLBase::visitUnary(Visit v
 
     if (visit == PreVisit && node->getUseEmulatedFunction())
         preString = BuiltInFunctionEmulator::GetEmulatedFunctionName(preString);
     writeTriplet(visit, preString.c_str(), NULL, postString.c_str());
 
     return true;
 }
 
+bool TOutputGLSLBase::visitTernary(Visit visit, TIntermTernary *node)
+{
+    TInfoSinkBase &out = objSink();
+    // Notice two brackets at the beginning and end. The outer ones
+    // encapsulate the whole ternary expression. This preserves the
+    // order of precedence when ternary expressions are used in a
+    // compound expression, i.e., c = 2 * (a < b ? 1 : 2).
+    out << "((";
+    node->getCondition()->traverse(this);
+    out << ") ? (";
+    node->getTrueExpression()->traverse(this);
+    out << ") : (";
+    node->getFalseExpression()->traverse(this);
+    out << "))";
+    return false;
+}
+
 bool TOutputGLSLBase::visitSelection(Visit visit, TIntermSelection *node)
 {
     TInfoSinkBase &out = objSink();
 
-    if (node->usesTernaryOperator())
+    out << "if (";
+    node->getCondition()->traverse(this);
+    out << ")\n";
+
+    incrementDepth(node);
+    visitCodeBlock(node->getTrueBlock());
+
+    if (node->getFalseBlock())
     {
-        // Notice two brackets at the beginning and end. The outer ones
-        // encapsulate the whole ternary expression. This preserves the
-        // order of precedence when ternary expressions are used in a
-        // compound expression, i.e., c = 2 * (a < b ? 1 : 2).
-        out << "((";
-        node->getCondition()->traverse(this);
-        out << ") ? (";
-        node->getTrueBlock()->traverse(this);
-        out << ") : (";
-        node->getFalseBlock()->traverse(this);
-        out << "))";
+        out << "else\n";
+        visitCodeBlock(node->getFalseBlock());
     }
-    else
-    {
-        out << "if (";
-        node->getCondition()->traverse(this);
-        out << ")\n";
-
-        incrementDepth(node);
-        visitCodeBlock(node->getTrueBlock());
-
-        if (node->getFalseBlock())
-        {
-            out << "else\n";
-            visitCodeBlock(node->getFalseBlock());
-        }
-        decrementDepth();
-    }
+    decrementDepth();
     return false;
 }
 
 bool TOutputGLSLBase::visitSwitch(Visit visit, TIntermSwitch *node)
 {
     if (node->getStatementList())
     {
         writeTriplet(visit, "switch (", ") ", nullptr);
--- a/gfx/angle/src/compiler/translator/OutputGLSLBase.h
+++ b/gfx/angle/src/compiler/translator/OutputGLSLBase.h
@@ -39,16 +39,17 @@ class TOutputGLSLBase : public TIntermTr
     const TConstantUnion *writeConstantUnion(const TType &type, const TConstantUnion *pConstUnion);
     void writeConstructorTriplet(Visit visit, const TType &type);
     TString getTypeName(const TType &type);
 
     void visitSymbol(TIntermSymbol *node) override;
     void visitConstantUnion(TIntermConstantUnion *node) override;
     bool visitBinary(Visit visit, TIntermBinary *node) override;
     bool visitUnary(Visit visit, TIntermUnary *node) override;
+    bool visitTernary(Visit visit, TIntermTernary *node) override;
     bool visitSelection(Visit visit, TIntermSelection *node) override;
     bool visitSwitch(Visit visit, TIntermSwitch *node) override;
     bool visitCase(Visit visit, TIntermCase *node) override;
     bool visitAggregate(Visit visit, TIntermAggregate *node) override;
     bool visitLoop(Visit visit, TIntermLoop *node) override;
     bool visitBranch(Visit visit, TIntermBranch *node) override;
 
     void visitCodeBlock(TIntermNode *node);
--- a/gfx/angle/src/compiler/translator/OutputHLSL.cpp
+++ b/gfx/angle/src/compiler/translator/OutputHLSL.cpp
@@ -75,21 +75,24 @@ const TConstantUnion *WriteConstantUnion
     return constUnionIterated;
 }
 
 } // namespace
 
 namespace sh
 {
 
-OutputHLSL::OutputHLSL(sh::GLenum shaderType, int shaderVersion,
-    const TExtensionBehavior &extensionBehavior,
-    const char *sourcePath, ShShaderOutput outputType,
-    int numRenderTargets, const std::vector<Uniform> &uniforms,
-    int compileOptions)
+OutputHLSL::OutputHLSL(sh::GLenum shaderType,
+                       int shaderVersion,
+                       const TExtensionBehavior &extensionBehavior,
+                       const char *sourcePath,
+                       ShShaderOutput outputType,
+                       int numRenderTargets,
+                       const std::vector<Uniform> &uniforms,
+                       ShCompileOptions compileOptions)
     : TIntermTraverser(true, true, true),
       mShaderType(shaderType),
       mShaderVersion(shaderVersion),
       mExtensionBehavior(extensionBehavior),
       mSourcePath(sourcePath),
       mOutputType(outputType),
       mCompileOptions(compileOptions),
       mNumRenderTargets(numRenderTargets),
@@ -1451,19 +1454,18 @@ bool OutputHLSL::visitAggregate(Visit vi
 
                 (*sit)->traverse(this);
 
                 // Don't output ; after case labels, they're terminated by :
                 // This is needed especially since outputting a ; after a case statement would turn empty
                 // case statements into non-empty case statements, disallowing fall-through from them.
                 // Also no need to output ; after selection (if) statements or sequences. This is done just
                 // for code clarity.
-                TIntermSelection *asSelection = (*sit)->getAsSelectionNode();
-                ASSERT(asSelection == nullptr || !asSelection->usesTernaryOperator());
-                if ((*sit)->getAsCaseNode() == nullptr && asSelection == nullptr && !IsSequence(*sit))
+                if ((*sit)->getAsCaseNode() == nullptr && (*sit)->getAsSelectionNode() == nullptr &&
+                    !IsSequence(*sit))
                     out << ";\n";
             }
 
             if (mInsideFunction)
             {
                 outputLineDirective(out, node->getLine().last_line);
                 out << "}\n";
             }
@@ -1977,21 +1979,28 @@ void OutputHLSL::writeSelection(TInfoSin
 
     // ANGLE issue 486: Detect problematic conditional discard
     if (discard)
     {
         mUsesDiscardRewriting = true;
     }
 }
 
+bool OutputHLSL::visitTernary(Visit, TIntermTernary *)
+{
+    // Ternary ops should have been already converted to something else in the AST. HLSL ternary
+    // operator doesn't short-circuit, so it's not the same as the GLSL ternary operator.
+    UNREACHABLE();
+    return false;
+}
+
 bool OutputHLSL::visitSelection(Visit visit, TIntermSelection *node)
 {
     TInfoSinkBase &out = getInfoSink();
 
-    ASSERT(!node->usesTernaryOperator());
     ASSERT(mInsideFunction);
 
     // D3D errors when there is a gradient operation in a loop in an unflattened if.
     if (mShaderType == GL_FRAGMENT_SHADER && mCurrentFunctionMetadata->hasGradientLoop(node))
     {
         out << "FLATTEN ";
     }
 
--- a/gfx/angle/src/compiler/translator/OutputHLSL.h
+++ b/gfx/angle/src/compiler/translator/OutputHLSL.h
@@ -25,21 +25,24 @@ class TextureFunctionHLSL;
 class UnfoldShortCircuit;
 class UniformHLSL;
 
 typedef std::map<TString, TIntermSymbol*> ReferencedSymbols;
 
 class OutputHLSL : public TIntermTraverser
 {
   public:
-    OutputHLSL(sh::GLenum shaderType, int shaderVersion,
-        const TExtensionBehavior &extensionBehavior,
-        const char *sourcePath, ShShaderOutput outputType,
-        int numRenderTargets, const std::vector<Uniform> &uniforms,
-        int compileOptions);
+    OutputHLSL(sh::GLenum shaderType,
+               int shaderVersion,
+               const TExtensionBehavior &extensionBehavior,
+               const char *sourcePath,
+               ShShaderOutput outputType,
+               int numRenderTargets,
+               const std::vector<Uniform> &uniforms,
+               ShCompileOptions compileOptions);
 
     ~OutputHLSL();
 
     void output(TIntermNode *treeRoot, TInfoSinkBase &objSink);
 
     const std::map<std::string, unsigned int> &getInterfaceBlockRegisterMap() const;
     const std::map<std::string, unsigned int> &getUniformRegisterMap() const;
 
@@ -53,16 +56,17 @@ class OutputHLSL : public TIntermTravers
     void header(TInfoSinkBase &out, const BuiltInFunctionEmulator *builtInFunctionEmulator);
 
     // Visit AST nodes and output their code to the body stream
     void visitSymbol(TIntermSymbol*);
     void visitRaw(TIntermRaw*);
     void visitConstantUnion(TIntermConstantUnion*);
     bool visitBinary(Visit visit, TIntermBinary*);
     bool visitUnary(Visit visit, TIntermUnary*);
+    bool visitTernary(Visit visit, TIntermTernary *);
     bool visitSelection(Visit visit, TIntermSelection*);
     bool visitSwitch(Visit visit, TIntermSwitch *);
     bool visitCase(Visit visit, TIntermCase *);
     bool visitAggregate(Visit visit, TIntermAggregate*);
     bool visitLoop(Visit visit, TIntermLoop*);
     bool visitBranch(Visit visit, TIntermBranch*);
 
     bool isSingleStatement(TIntermNode *node);
@@ -112,17 +116,17 @@ class OutputHLSL : public TIntermTravers
     // Ensures if the type is a struct, the struct is defined
     void ensureStructDefined(const TType &type);
 
     sh::GLenum mShaderType;
     int mShaderVersion;
     const TExtensionBehavior &mExtensionBehavior;
     const char *mSourcePath;
     const ShShaderOutput mOutputType;
-    int mCompileOptions;
+    ShCompileOptions mCompileOptions;
 
     bool mInsideFunction;
 
     // Output streams
     TInfoSinkBase mHeader;
     TInfoSinkBase mBody;
     TInfoSinkBase mFooter;
 
--- a/gfx/angle/src/compiler/translator/ParseContext.cpp
+++ b/gfx/angle/src/compiler/translator/ParseContext.cpp
@@ -208,16 +208,22 @@ void TParseContext::binaryOpError(const 
 }
 
 void TParseContext::checkPrecisionSpecified(const TSourceLoc &line,
                                             TPrecision precision,
                                             TBasicType type)
 {
     if (!mChecksPrecisionErrors)
         return;
+
+    if (precision != EbpUndefined && !SupportsPrecision(type))
+    {
+        error(line, "illegal type for precision qualifier", getBasicString(type));
+    }
+
     if (precision == EbpUndefined)
     {
         switch (type)
         {
             case EbtFloat:
                 error(line, "No precision specified for (float)", "");
                 return;
             case EbtInt:
@@ -442,21 +448,16 @@ bool TParseContext::checkIsNotReserved(c
                 error(line, reservedErrMsg, "webgl_");
                 return false;
             }
             if (identifier.compare(0, 7, "_webgl_") == 0)
             {
                 error(line, reservedErrMsg, "_webgl_");
                 return false;
             }
-            if (mShaderSpec == SH_CSS_SHADERS_SPEC && identifier.compare(0, 4, "css_") == 0)
-            {
-                error(line, reservedErrMsg, "css_");
-                return false;
-            }
         }
         if (identifier.find("__") != TString::npos)
         {
             error(line,
                   "identifiers containing two consecutive underscores (__) are reserved as "
                   "possible future keywords",
                   identifier.c_str());
             return false;
@@ -656,24 +657,24 @@ void TParseContext::checkIsScalarBool(co
         error(line, "boolean expression expected", "");
     }
 }
 
 // This function checks to see if the node (for the expression) contains a scalar boolean expression
 // or not.
 void TParseContext::checkIsScalarBool(const TSourceLoc &line, const TPublicType &pType)
 {
-    if (pType.type != EbtBool || pType.isAggregate())
+    if (pType.getBasicType() != EbtBool || pType.isAggregate())
     {
         error(line, "boolean expression expected", "");
     }
 }
 
 bool TParseContext::checkIsNotSampler(const TSourceLoc &line,
-                                      const TPublicType &pType,
+                                      const TTypeSpecifierNonArray &pType,
                                       const char *reason)
 {
     if (pType.type == EbtStruct)
     {
         if (containsSampler(*pType.userDef))
         {
             error(line, reason, getBasicString(pType.type), "(structure contains a sampler)");
             return false;
@@ -818,17 +819,17 @@ bool TParseContext::checkIsValidTypeForA
     {
         error(line, "cannot declare arrays of arrays",
               TType(elementType).getCompleteString().c_str());
         return false;
     }
     // In ESSL1.00 shaders, structs cannot be varying (section 4.3.5). This is checked elsewhere.
     // In ESSL3.00 shaders, struct inputs/outputs are allowed but not arrays of structs (section
     // 4.3.4).
-    if (mShaderVersion >= 300 && elementType.type == EbtStruct &&
+    if (mShaderVersion >= 300 && elementType.getBasicType() == EbtStruct &&
         sh::IsVarying(elementType.qualifier))
     {
         error(line, "cannot declare arrays of structs of this qualifier",
               TType(elementType).getCompleteString().c_str());
         return false;
     }
 
     return true;
@@ -923,37 +924,34 @@ bool TParseContext::declareVariable(cons
     }
 
     if (!checkIsNonVoid(line, identifier, type.getBasicType()))
         return false;
 
     return true;
 }
 
-void TParseContext::checkIsParameterQualifierValid(const TSourceLoc &line,
-                                                   TQualifier qualifier,
-                                                   TQualifier paramQualifier,
-                                                   TType *type)
+void TParseContext::checkIsParameterQualifierValid(
+    const TSourceLoc &line,
+    const TTypeQualifierBuilder &typeQualifierBuilder,
+    TType *type)
 {
-    if (qualifier != EvqConst && qualifier != EvqTemporary)
+    TTypeQualifier typeQualifier = typeQualifierBuilder.getParameterTypeQualifier(&mDiagnostics);
+
+    if (typeQualifier.qualifier == EvqOut || typeQualifier.qualifier == EvqInOut)
     {
-        error(line, "qualifier not allowed on function parameter", getQualifierString(qualifier));
-        return;
+        checkOutParameterIsNotSampler(line, typeQualifier.qualifier, *type);
     }
-    if (qualifier == EvqConst && paramQualifier != EvqIn)
+
+    type->setQualifier(typeQualifier.qualifier);
+
+    if (typeQualifier.precision != EbpUndefined)
     {
-        error(line, "qualifier not allowed with ", getQualifierString(qualifier),
-              getQualifierString(paramQualifier));
-        return;
+        type->setPrecision(typeQualifier.precision);
     }
-
-    if (qualifier == EvqConst)
-        type->setQualifier(EvqConstReadOnly);
-    else
-        type->setQualifier(paramQualifier);
 }
 
 bool TParseContext::checkCanUseExtension(const TSourceLoc &line, const TString &extension)
 {
     const TExtensionBehavior &extBehavior   = extensionBehavior();
     TExtensionBehavior::const_iterator iter = extBehavior.find(extension.c_str());
     if (iter == extBehavior.end())
     {
@@ -983,29 +981,30 @@ void TParseContext::singleDeclarationErr
     switch (publicType.qualifier)
     {
         case EvqVaryingIn:
         case EvqVaryingOut:
         case EvqAttribute:
         case EvqVertexIn:
         case EvqFragmentOut:
         case EvqComputeIn:
-            if (publicType.type == EbtStruct)
+            if (publicType.getBasicType() == EbtStruct)
             {
                 error(identifierLocation, "cannot be used with a structure",
                       getQualifierString(publicType.qualifier));
                 return;
             }
 
         default:
             break;
     }
 
     if (publicType.qualifier != EvqUniform &&
-        !checkIsNotSampler(identifierLocation, publicType, "samplers must be uniform"))
+        !checkIsNotSampler(identifierLocation, publicType.typeSpecifierNonArray,
+                           "samplers must be uniform"))
     {
         return;
     }
 
     // check for layout qualifier issues
     const TLayoutQualifier layoutQualifier = publicType.layoutQualifier;
 
     if (layoutQualifier.matrixPacking != EmpUnspecified)
@@ -1072,22 +1071,37 @@ void TParseContext::functionCallLValueEr
                 error(argument->getLine(),
                       "Constant value cannot be passed for 'out' or 'inout' parameters.", "Error");
                 return;
             }
         }
     }
 }
 
-void TParseContext::checkInvariantIsOutVariableES3(const TQualifier qualifier,
-                                                   const TSourceLoc &invariantLocation)
+void TParseContext::checkInvariantVariableQualifier(bool invariant,
+                                                    const TQualifier qualifier,
+                                                    const TSourceLoc &invariantLocation)
 {
-    if (!sh::IsVaryingOut(qualifier) && qualifier != EvqFragmentOut)
+    if (!invariant)
+        return;
+
+    if (mShaderVersion < 300)
     {
-        error(invariantLocation, "Only out variables can be invariant.", "invariant");
+        // input variables in the fragment shader can be also qualified as invariant
+        if (!sh::CanBeInvariantESSL1(qualifier))
+        {
+            error(invariantLocation, "Cannot be qualified as invariant.", "invariant");
+        }
+    }
+    else
+    {
+        if (!sh::CanBeInvariantESSL3OrGreater(qualifier))
+        {
+            error(invariantLocation, "Cannot be qualified as invariant.", "invariant");
+        }
     }
 }
 
 bool TParseContext::supportsExtension(const char *extension)
 {
     const TExtensionBehavior &extbehavior   = extensionBehavior();
     TExtensionBehavior::const_iterator iter = extbehavior.find(extension);
     return (iter != extbehavior.end());
@@ -1380,74 +1394,90 @@ bool TParseContext::executeInitializer(c
     {
         assignError(line, "=", intermSymbol->getCompleteString(), initializer->getCompleteString());
         return true;
     }
 
     return false;
 }
 
-TPublicType TParseContext::addFullySpecifiedType(TQualifier qualifier,
-                                                 bool invariant,
-                                                 TLayoutQualifier layoutQualifier,
+TPublicType TParseContext::addFullySpecifiedType(const TTypeQualifierBuilder &typeQualifierBuilder,
                                                  const TPublicType &typeSpecifier)
 {
+    TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics);
+
     TPublicType returnType     = typeSpecifier;
-    returnType.qualifier       = qualifier;
-    returnType.invariant       = invariant;
-    returnType.layoutQualifier = layoutQualifier;
-
-    checkWorkGroupSizeIsNotSpecified(typeSpecifier.line, layoutQualifier);
+    returnType.qualifier       = typeQualifier.qualifier;
+    returnType.invariant       = typeQualifier.invariant;
+    returnType.layoutQualifier = typeQualifier.layoutQualifier;
+    returnType.precision       = typeSpecifier.precision;
+
+    if (typeQualifier.precision != EbpUndefined)
+    {
+        returnType.precision = typeQualifier.precision;
+    }
+
+    checkPrecisionSpecified(typeSpecifier.getLine(), returnType.precision,
+                            typeSpecifier.getBasicType());
+
+    checkInvariantVariableQualifier(returnType.invariant, returnType.qualifier,
+                                    typeSpecifier.getLine());
+
+    checkWorkGroupSizeIsNotSpecified(typeSpecifier.getLine(), returnType.layoutQualifier);
 
     if (mShaderVersion < 300)
     {
         if (typeSpecifier.array)
         {
-            error(typeSpecifier.line, "not supported", "first-class array");
+            error(typeSpecifier.getLine(), "not supported", "first-class array");
             returnType.clearArrayness();
         }
 
-        if (qualifier == EvqAttribute &&
-            (typeSpecifier.type == EbtBool || typeSpecifier.type == EbtInt))
+        if (returnType.qualifier == EvqAttribute &&
+            (typeSpecifier.getBasicType() == EbtBool || typeSpecifier.getBasicType() == EbtInt))
         {
-            error(typeSpecifier.line, "cannot be bool or int", getQualifierString(qualifier));
+            error(typeSpecifier.getLine(), "cannot be bool or int",
+                  getQualifierString(returnType.qualifier));
         }
 
-        if ((qualifier == EvqVaryingIn || qualifier == EvqVaryingOut) &&
-            (typeSpecifier.type == EbtBool || typeSpecifier.type == EbtInt))
+        if ((returnType.qualifier == EvqVaryingIn || returnType.qualifier == EvqVaryingOut) &&
+            (typeSpecifier.getBasicType() == EbtBool || typeSpecifier.getBasicType() == EbtInt))
         {
-            error(typeSpecifier.line, "cannot be bool or int", getQualifierString(qualifier));
+            error(typeSpecifier.getLine(), "cannot be bool or int",
+                  getQualifierString(returnType.qualifier));
         }
     }
     else
     {
-        if (!layoutQualifier.isEmpty())
+        if (!returnType.layoutQualifier.isEmpty())
         {
-            checkIsAtGlobalLevel(typeSpecifier.line, "layout");
+            checkIsAtGlobalLevel(typeSpecifier.getLine(), "layout");
         }
-        if (sh::IsVarying(qualifier) || qualifier == EvqVertexIn || qualifier == EvqFragmentOut)
+        if (sh::IsVarying(returnType.qualifier) || returnType.qualifier == EvqVertexIn ||
+            returnType.qualifier == EvqFragmentOut)
         {
-            checkInputOutputTypeIsValidES3(qualifier, typeSpecifier, typeSpecifier.line);
+            checkInputOutputTypeIsValidES3(returnType.qualifier, typeSpecifier,
+                                           typeSpecifier.getLine());
         }
-        if (qualifier == EvqComputeIn)
+        if (returnType.qualifier == EvqComputeIn)
         {
-            error(typeSpecifier.line, "'in' can be only used to specify the local group size",
+            error(typeSpecifier.getLine(), "'in' can be only used to specify the local group size",
                   "in");
         }
     }
 
     return returnType;
 }
 
 void TParseContext::checkInputOutputTypeIsValidES3(const TQualifier qualifier,
                                                    const TPublicType &type,
                                                    const TSourceLoc &qualifierLocation)
 {
     // An input/output variable can never be bool or a sampler. Samplers are checked elsewhere.
-    if (type.type == EbtBool)
+    if (type.getBasicType() == EbtBool)
     {
         error(qualifierLocation, "cannot be bool", getQualifierString(qualifier));
     }
 
     // Specific restrictions apply for vertex shader inputs and fragment shader outputs.
     switch (qualifier)
     {
         case EvqVertexIn:
@@ -1455,38 +1485,38 @@ void TParseContext::checkInputOutputType
             if (type.array)
             {
                 error(qualifierLocation, "cannot be array", getQualifierString(qualifier));
             }
             // Vertex inputs with a struct type are disallowed in singleDeclarationErrorCheck
             return;
         case EvqFragmentOut:
             // ESSL 3.00 section 4.3.6
-            if (type.isMatrix())
+            if (type.typeSpecifierNonArray.isMatrix())
             {
                 error(qualifierLocation, "cannot be matrix", getQualifierString(qualifier));
             }
             // Fragment outputs with a struct type are disallowed in singleDeclarationErrorCheck
             return;
         default:
             break;
     }
 
     // Vertex shader outputs / fragment shader inputs have a different, slightly more lenient set of
     // restrictions.
     bool typeContainsIntegers =
-        (type.type == EbtInt || type.type == EbtUInt || type.isStructureContainingType(EbtInt) ||
-         type.isStructureContainingType(EbtUInt));
+        (type.getBasicType() == EbtInt || type.getBasicType() == EbtUInt ||
+         type.isStructureContainingType(EbtInt) || type.isStructureContainingType(EbtUInt));
     if (typeContainsIntegers && qualifier != EvqFlatIn && qualifier != EvqFlatOut)
     {
         error(qualifierLocation, "must use 'flat' interpolation here",
               getQualifierString(qualifier));
     }
 
-    if (type.type == EbtStruct)
+    if (type.getBasicType() == EbtStruct)
     {
         // ESSL 3.00 sections 4.3.4 and 4.3.6.
         // These restrictions are only implied by the ESSL 3.00 spec, but
         // the ESSL 3.10 spec lists these restrictions explicitly.
         if (type.array)
         {
             error(qualifierLocation, "cannot be an array of structures",
                   getQualifierString(qualifier));
@@ -1663,50 +1693,68 @@ TIntermAggregate *TParseContext::parseSi
         return initNode ? intermediate.makeAggregate(initNode, initLocation) : nullptr;
     }
     else
     {
         return nullptr;
     }
 }
 
-TIntermAggregate *TParseContext::parseInvariantDeclaration(const TSourceLoc &invariantLoc,
-                                                           const TSourceLoc &identifierLoc,
-                                                           const TString *identifier,
-                                                           const TSymbol *symbol)
+TIntermAggregate *TParseContext::parseInvariantDeclaration(
+    const TTypeQualifierBuilder &typeQualifierBuilder,
+    const TSourceLoc &identifierLoc,
+    const TString *identifier,
+    const TSymbol *symbol)
 {
-    // invariant declaration
-    if (!checkIsAtGlobalLevel(invariantLoc, "invariant varying"))
+    TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics);
+
+    if (!typeQualifier.invariant)
+    {
+        error(identifierLoc, "Expected invariant", identifier->c_str());
         return nullptr;
-
+    }
+    if (!checkIsAtGlobalLevel(identifierLoc, "invariant varying"))
+    {
+        return nullptr;
+    }
     if (!symbol)
     {
         error(identifierLoc, "undeclared identifier declared as invariant", identifier->c_str());
         return nullptr;
     }
-    else
+    if (!IsQualifierUnspecified(typeQualifier.qualifier))
+    {
+        error(identifierLoc, "invariant declaration specifies qualifier",
+              getQualifierString(typeQualifier.qualifier));
+    }
+    if (typeQualifier.precision != EbpUndefined)
+    {
+        error(identifierLoc, "invariant declaration specifies precision",
+              getPrecisionString(typeQualifier.precision));
+    }
+    if (!typeQualifier.layoutQualifier.isEmpty())
     {
-        const TString kGlFrontFacing("gl_FrontFacing");
-        if (*identifier == kGlFrontFacing)
-        {
-            error(identifierLoc, "identifier should not be declared as invariant",
-                  identifier->c_str());
-            return nullptr;
-        }
-        symbolTable.addInvariantVarying(std::string(identifier->c_str()));
-        const TVariable *variable = getNamedVariable(identifierLoc, identifier, symbol);
-        ASSERT(variable);
-        const TType &type = variable->getType();
-        TIntermSymbol *intermSymbol =
-            intermediate.addSymbol(variable->getUniqueId(), *identifier, type, identifierLoc);
-
-        TIntermAggregate *aggregate = intermediate.makeAggregate(intermSymbol, identifierLoc);
-        aggregate->setOp(EOpInvariantDeclaration);
-        return aggregate;
+        error(identifierLoc, "invariant declaration specifies layout", "'layout'");
     }
+
+    const TVariable *variable = getNamedVariable(identifierLoc, identifier, symbol);
+    ASSERT(variable);
+    const TType &type = variable->getType();
+
+    checkInvariantVariableQualifier(typeQualifier.invariant, type.getQualifier(),
+                                    typeQualifier.line);
+
+    symbolTable.addInvariantVarying(std::string(identifier->c_str()));
+
+    TIntermSymbol *intermSymbol =
+        intermediate.addSymbol(variable->getUniqueId(), *identifier, type, identifierLoc);
+
+    TIntermAggregate *aggregate = intermediate.makeAggregate(intermSymbol, identifierLoc);
+    aggregate->setOp(EOpInvariantDeclaration);
+    return aggregate;
 }
 
 TIntermAggregate *TParseContext::parseDeclarator(TPublicType &publicType,
                                                  TIntermAggregate *aggregateDeclaration,
                                                  const TSourceLoc &identifierLocation,
                                                  const TString &identifier)
 {
     // If the declaration starting this declarator list was empty (example: int,), some checks were
@@ -1857,20 +1905,24 @@ TIntermAggregate *TParseContext::parseAr
         }
     }
     else
     {
         return nullptr;
     }
 }
 
-void TParseContext::parseGlobalLayoutQualifier(const TPublicType &typeQualifier)
+void TParseContext::parseGlobalLayoutQualifier(const TTypeQualifierBuilder &typeQualifierBuilder)
 {
+    TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics);
     const TLayoutQualifier layoutQualifier = typeQualifier.layoutQualifier;
 
+    checkInvariantVariableQualifier(typeQualifier.invariant, typeQualifier.qualifier,
+                                    typeQualifier.line);
+
     // It should never be the case, but some strange parser errors can send us here.
     if (layoutQualifier.isEmpty())
     {
         error(typeQualifier.line, "Error during layout qualifier parsing.", "?");
         return;
     }
 
     if (!layoutQualifier.isCombinationValid())
@@ -2216,17 +2268,18 @@ TFunction *TParseContext::parseFunctionH
         error(location, "no qualifiers allowed for function return",
               getQualifierString(type.qualifier));
     }
     if (!type.layoutQualifier.isEmpty())
     {
         error(location, "no qualifiers allowed for function return", "layout");
     }
     // make sure a sampler is not involved as well...
-    checkIsNotSampler(location, type, "samplers can't be function return values");
+    checkIsNotSampler(location, type.typeSpecifierNonArray,
+                      "samplers can't be function return values");
     if (mShaderVersion < 300)
     {
         // Array return values are forbidden, but there's also no valid syntax for declaring array
         // return values in ESSL 1.00.
         ASSERT(type.arraySize == 0 || mDiagnostics.numErrors() > 0);
 
         if (type.isStructureContainingArrays())
         {
@@ -2238,34 +2291,35 @@ TFunction *TParseContext::parseFunctionH
 
     // Add the function as a prototype after parsing it (we do not support recursion)
     return new TFunction(name, new TType(type));
 }
 
 TFunction *TParseContext::addConstructorFunc(const TPublicType &publicTypeIn)
 {
     TPublicType publicType = publicTypeIn;
-    if (publicType.isStructSpecifier)
+    if (publicType.isStructSpecifier())
     {
-        error(publicType.line, "constructor can't be a structure definition",
-              getBasicString(publicType.type));
+        error(publicType.getLine(), "constructor can't be a structure definition",
+              getBasicString(publicType.getBasicType()));
     }
 
     TOperator op = EOpNull;
-    if (publicType.userDef)
+    if (publicType.getUserDef())
     {
         op = EOpConstructStruct;
     }
     else
     {
         op = sh::TypeToConstructorOperator(TType(publicType));
         if (op == EOpNull)
         {
-            error(publicType.line, "cannot construct this type", getBasicString(publicType.type));
-            publicType.type = EbtFloat;
+            error(publicType.getLine(), "cannot construct this type",
+                  getBasicString(publicType.getBasicType()));
+            publicType.setBasicType(EbtFloat);
             op              = EOpConstructFloat;
         }
     }
 
     TString tempString;
     const TType *type = new TType(publicType);
     return new TFunction(&tempString, type, op);
 }
@@ -2317,137 +2371,53 @@ TIntermTyped *TParseContext::addConstruc
     if (op != EOpConstructStruct)
     {
         constructor->setPrecisionFromChildren();
         type.setPrecision(constructor->getPrecision());
     }
 
     constructor->setType(type);
 
-    TIntermTyped *constConstructor = intermediate.foldAggregateBuiltIn(constructor);
+    TIntermTyped *constConstructor = intermediate.foldAggregateBuiltIn(constructor, &mDiagnostics);
     if (constConstructor)
     {
         return constConstructor;
     }
 
     return constructor;
 }
 
-// This function returns vector field(s) being accessed from a constant vector.
-TIntermConstantUnion *TParseContext::foldVectorSwizzle(TVectorFields &fields,
-                                                       TIntermConstantUnion *baseNode,
-                                                       const TSourceLoc &location)
-{
-    const TConstantUnion *unionArray = baseNode->getUnionArrayPointer();
-    ASSERT(unionArray);
-
-    TConstantUnion *constArray = new TConstantUnion[fields.num];
-    const auto &type           = baseNode->getType();
-
-    for (int i = 0; i < fields.num; i++)
-    {
-        // Out-of-range indices should already be checked.
-        ASSERT(fields.offsets[i] < type.getNominalSize());
-        constArray[i] = unionArray[fields.offsets[i]];
-    }
-    return intermediate.addConstantUnion(constArray, type, location);
-}
-
-// This function returns the column vector being accessed from a constant matrix.
-TIntermConstantUnion *TParseContext::foldMatrixSubscript(int index,
-                                                         TIntermConstantUnion *baseNode,
-                                                         const TSourceLoc &location)
-{
-    ASSERT(index < baseNode->getType().getCols());
-
-    const TConstantUnion *unionArray = baseNode->getUnionArrayPointer();
-    int size                         = baseNode->getType().getRows();
-    return intermediate.addConstantUnion(&unionArray[size * index], baseNode->getType(), location);
-}
-
-// This function returns an element of an array accessed from a constant array.
-TIntermConstantUnion *TParseContext::foldArraySubscript(int index,
-                                                        TIntermConstantUnion *baseNode,
-                                                        const TSourceLoc &location)
-{
-    ASSERT(index < static_cast<int>(baseNode->getArraySize()));
-
-    TType arrayElementType = baseNode->getType();
-    arrayElementType.clearArrayness();
-    size_t arrayElementSize          = arrayElementType.getObjectSize();
-    const TConstantUnion *unionArray = baseNode->getUnionArrayPointer();
-    return intermediate.addConstantUnion(&unionArray[arrayElementSize * index], baseNode->getType(),
-                                         location);
-}
-
-//
-// This function returns the value of a particular field inside a constant structure from the symbol
-// table.
-// If there is an embedded/nested struct, it appropriately calls addConstStructNested or
-// addConstStructFromAggr function and returns the parse-tree with the values of the embedded/nested
-// struct.
-//
-TIntermTyped *TParseContext::addConstStruct(const TString &identifier,
-                                            TIntermTyped *node,
-                                            const TSourceLoc &line)
-{
-    const TFieldList &fields = node->getType().getStruct()->fields();
-    size_t instanceSize      = 0;
-
-    for (size_t index = 0; index < fields.size(); ++index)
-    {
-        if (fields[index]->name() == identifier)
-        {
-            break;
-        }
-        else
-        {
-            instanceSize += fields[index]->type()->getObjectSize();
-        }
-    }
-
-    TIntermTyped *typedNode;
-    TIntermConstantUnion *tempConstantNode = node->getAsConstantUnion();
-    if (tempConstantNode)
-    {
-        const TConstantUnion *constArray = tempConstantNode->getUnionArrayPointer();
-
-        // type will be changed in the calling function
-        typedNode = intermediate.addConstantUnion(constArray + instanceSize,
-                                                  tempConstantNode->getType(), line);
-    }
-    else
-    {
-        error(line, "Cannot offset into the structure", "Error");
-        return nullptr;
-    }
-
-    return typedNode;
-}
-
 //
 // Interface/uniform blocks
 //
-TIntermAggregate *TParseContext::addInterfaceBlock(const TPublicType &typeQualifier,
-                                                   const TSourceLoc &nameLine,
-                                                   const TString &blockName,
-                                                   TFieldList *fieldList,
-                                                   const TString *instanceName,
-                                                   const TSourceLoc &instanceLine,
-                                                   TIntermTyped *arrayIndex,
-                                                   const TSourceLoc &arrayIndexLine)
+TIntermAggregate *TParseContext::addInterfaceBlock(
+    const TTypeQualifierBuilder &typeQualifierBuilder,
+    const TSourceLoc &nameLine,
+    const TString &blockName,
+    TFieldList *fieldList,
+    const TString *instanceName,
+    const TSourceLoc &instanceLine,
+    TIntermTyped *arrayIndex,
+    const TSourceLoc &arrayIndexLine)
 {
     checkIsNotReserved(nameLine, blockName);
 
+    TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics);
+
     if (typeQualifier.qualifier != EvqUniform)
     {
         error(typeQualifier.line, "invalid qualifier:", getQualifierString(typeQualifier.qualifier),
               "interface blocks must be uniform");
     }
 
+    if (typeQualifier.invariant)
+    {
+        error(typeQualifier.line, "invalid qualifier on interface block member", "invariant");
+    }
+
     TLayoutQualifier blockLayoutQualifier = typeQualifier.layoutQualifier;
     checkLocationIsNotSpecified(typeQualifier.line, blockLayoutQualifier);
 
     if (blockLayoutQualifier.matrixPacking == EmpUnspecified)
     {
         blockLayoutQualifier.matrixPacking = mDefaultMatrixPacking;
     }
 
@@ -2482,16 +2452,21 @@ TIntermAggregate *TParseContext::addInte
             case EvqUniform:
                 break;
             default:
                 error(field->line(), "invalid qualifier on interface block member",
                       getQualifierString(qualifier));
                 break;
         }
 
+        if (fieldType->isInvariant())
+        {
+            error(field->line(), "invalid qualifier on interface block member", "invariant");
+        }
+
         // check layout qualifiers
         TLayoutQualifier fieldLayoutQualifier = fieldType->getLayoutQualifier();
         checkLocationIsNotSpecified(field->line(), fieldLayoutQualifier);
 
         if (fieldLayoutQualifier.blockStorage != EbsUnspecified)
         {
             error(field->line(), "invalid layout qualifier:",
                   getBlockStorageString(fieldLayoutQualifier.blockStorage), "cannot be used here");
@@ -2625,29 +2600,32 @@ void TParseContext::checkIsBelowStructNe
 
 //
 // Parse an array index expression
 //
 TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression,
                                                 const TSourceLoc &location,
                                                 TIntermTyped *indexExpression)
 {
-    TIntermTyped *indexedExpression = NULL;
-
     if (!baseExpression->isArray() && !baseExpression->isMatrix() && !baseExpression->isVector())
     {
         if (baseExpression->getAsSymbolNode())
         {
             error(location, " left of '[' is not of type array, matrix, or vector ",
                   baseExpression->getAsSymbolNode()->getSymbol().c_str());
         }
         else
         {
             error(location, " left of '[' is not of type array, matrix, or vector ", "expression");
         }
+
+        TConstantUnion *unionArray = new TConstantUnion[1];
+        unionArray->setFConst(0.0f);
+        return intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpHigh, EvqConst),
+                                             location);
     }
 
     TIntermConstantUnion *indexConstantUnion = indexExpression->getAsConstantUnion();
 
     // TODO(oetuaho@nvidia.com): Get rid of indexConstantUnion == nullptr below once ANGLE is able
     // to constant fold all constant expressions. Right now we don't allow indexing interface blocks
     // or fragment outputs with expressions that ANGLE is not able to constant fold, even if the
     // index is a constant expression.
@@ -2667,161 +2645,88 @@ TIntermTyped *TParseContext::addIndexExp
         else if (mShaderSpec == SH_WEBGL2_SPEC && baseExpression->getQualifier() == EvqFragData)
         {
             error(location, "", "[", "array index for gl_FragData must be constant zero");
         }
     }
 
     if (indexConstantUnion)
     {
-        // If the index is not qualified as constant, the behavior in the spec is undefined. This
-        // applies even if ANGLE has been able to constant fold it (ANGLE may constant fold
-        // expressions that are not constant expressions). The most compatible way to handle this
-        // case is to report a warning instead of an error and force the index to be in the
-        // correct range.
+        // If an out-of-range index is not qualified as constant, the behavior in the spec is
+        // undefined. This applies even if ANGLE has been able to constant fold it (ANGLE may
+        // constant fold expressions that are not constant expressions). The most compatible way to
+        // handle this case is to report a warning instead of an error and force the index to be in
+        // the correct range.
         bool outOfRangeIndexIsError = indexExpression->getQualifier() == EvqConst;
         int index = indexConstantUnion->getIConst(0);
-        if (!baseExpression->isArray())
+
+        int safeIndex = -1;
+
+        if (baseExpression->isArray())
         {
-            // Array checks are done later because a different error message might be generated
-            // based on the index in some cases.
-            if (baseExpression->isVector())
+            if (baseExpression->getQualifier() == EvqFragData && index > 0)
             {
-                index = checkIndexOutOfRange(outOfRangeIndexIsError, location, index,
-                                             baseExpression->getType().getNominalSize(),
-                                             "vector field selection out of range", "[]");
+                if (mShaderSpec == SH_WEBGL2_SPEC)
+                {
+                    // Error has been already generated if index is not const.
+                    if (indexExpression->getQualifier() == EvqConst)
+                    {
+                        error(location, "", "[",
+                              "array index for gl_FragData must be constant zero");
+                    }
+                    safeIndex = 0;
+                }
+                else if (!isExtensionEnabled("GL_EXT_draw_buffers"))
+                {
+                    outOfRangeError(outOfRangeIndexIsError, location, "", "[",
+                                    "array index for gl_FragData must be zero when "
+                                    "GL_EXT_draw_buffers is disabled");
+                    safeIndex = 0;
+                }
             }
-            else if (baseExpression->isMatrix())
+            // Only do generic out-of-range check if similar error hasn't already been reported.
+            if (safeIndex < 0)
             {
-                index = checkIndexOutOfRange(outOfRangeIndexIsError, location, index,
-                                             baseExpression->getType().getCols(),
-                                             "matrix field selection out of range", "[]");
+                safeIndex = checkIndexOutOfRange(outOfRangeIndexIsError, location, index,
+                                                 baseExpression->getArraySize(),
+                                                 "array index out of range", "[]");
             }
         }
-
-        TIntermConstantUnion *baseConstantUnion = baseExpression->getAsConstantUnion();
-        if (baseConstantUnion)
+        else if (baseExpression->isMatrix())
         {
-            if (baseExpression->isArray())
-            {
-                index = checkIndexOutOfRange(outOfRangeIndexIsError, location, index,
-                                             baseExpression->getArraySize(),
-                                             "array index out of range", "[]");
-                // Constant folding for array indexing.
-                indexedExpression = foldArraySubscript(index, baseConstantUnion, location);
-            }
-            else if (baseExpression->isVector())
-            {
-                // Constant folding for vector indexing - reusing vector swizzle folding.
-                TVectorFields fields;
-                fields.num = 1;
-                fields.offsets[0] = index;
-                indexedExpression = foldVectorSwizzle(fields, baseConstantUnion, location);
-            }
-            else if (baseExpression->isMatrix())
-            {
-                // Constant folding for matrix indexing.
-                indexedExpression = foldMatrixSubscript(index, baseConstantUnion, location);
-            }
+            safeIndex = checkIndexOutOfRange(outOfRangeIndexIsError, location, index,
+                                             baseExpression->getType().getCols(),
+                                             "matrix field selection out of range", "[]");
         }
-        else
+        else if (baseExpression->isVector())
         {
-            int safeIndex = -1;
-
-            if (baseExpression->isArray())
-            {
-                if (baseExpression->getQualifier() == EvqFragData && index > 0)
-                {
-                    if (mShaderSpec == SH_WEBGL2_SPEC)
-                    {
-                        // Error has been already generated if index is not const.
-                        if (indexExpression->getQualifier() == EvqConst)
-                        {
-                            error(location, "", "[",
-                                  "array index for gl_FragData must be constant zero");
-                        }
-                        safeIndex = 0;
-                    }
-                    else if (!isExtensionEnabled("GL_EXT_draw_buffers"))
-                    {
-                        outOfRangeError(outOfRangeIndexIsError, location, "", "[",
-                                        "array index for gl_FragData must be zero when "
-                                        "GL_EXT_draw_buffers is disabled");
-                        safeIndex = 0;
-                    }
-                }
-                // Only do generic out-of-range check if similar error hasn't already been reported.
-                if (safeIndex < 0)
-                {
-                    safeIndex = checkIndexOutOfRange(outOfRangeIndexIsError, location, index,
-                                                     baseExpression->getArraySize(),
-                                                     "array index out of range", "[]");
-                }
-            }
-
-            // Data of constant unions can't be changed, because it may be shared with other
-            // constant unions or even builtins, like gl_MaxDrawBuffers. Instead use a new
-            // sanitized object.
-            if (safeIndex != -1)
-            {
-                TConstantUnion *safeConstantUnion = new TConstantUnion();
-                safeConstantUnion->setIConst(safeIndex);
-                indexConstantUnion->replaceConstantUnion(safeConstantUnion);
-            }
-
-            indexedExpression =
-                intermediate.addIndex(EOpIndexDirect, baseExpression, indexExpression, location);
+            safeIndex = checkIndexOutOfRange(outOfRangeIndexIsError, location, index,
+                                             baseExpression->getType().getNominalSize(),
+                                             "vector field selection out of range", "[]");
         }
+
+        ASSERT(safeIndex >= 0);
+        // Data of constant unions can't be changed, because it may be shared with other
+        // constant unions or even builtins, like gl_MaxDrawBuffers. Instead use a new
+        // sanitized object.
+        if (safeIndex != index)
+        {
+            TConstantUnion *safeConstantUnion = new TConstantUnion();
+            safeConstantUnion->setIConst(safeIndex);
+            indexConstantUnion->replaceConstantUnion(safeConstantUnion);
+        }
+
+        return intermediate.addIndex(EOpIndexDirect, baseExpression, indexExpression, location,
+                                     &mDiagnostics);
     }
     else
     {
-        indexedExpression =
-            intermediate.addIndex(EOpIndexIndirect, baseExpression, indexExpression, location);
-    }
-
-    if (indexedExpression == 0)
-    {
-        TConstantUnion *unionArray = new TConstantUnion[1];
-        unionArray->setFConst(0.0f);
-        indexedExpression =
-            intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpHigh, EvqConst), location);
-    }
-    else if (baseExpression->isArray())
-    {
-        TType indexedType = baseExpression->getType();
-        indexedType.clearArrayness();
-        indexedExpression->setType(indexedType);
+        return intermediate.addIndex(EOpIndexIndirect, baseExpression, indexExpression, location,
+                                     &mDiagnostics);
     }
-    else if (baseExpression->isMatrix())
-    {
-        indexedExpression->setType(TType(baseExpression->getBasicType(),
-                                         baseExpression->getPrecision(), EvqTemporary,
-                                         static_cast<unsigned char>(baseExpression->getRows())));
-    }
-    else if (baseExpression->isVector())
-    {
-        indexedExpression->setType(
-            TType(baseExpression->getBasicType(), baseExpression->getPrecision(), EvqTemporary));
-    }
-    else
-    {
-        indexedExpression->setType(baseExpression->getType());
-    }
-
-    if (baseExpression->getType().getQualifier() == EvqConst &&
-        indexExpression->getType().getQualifier() == EvqConst)
-    {
-        indexedExpression->getTypePointer()->setQualifier(EvqConst);
-    }
-    else
-    {
-        indexedExpression->getTypePointer()->setQualifier(EvqTemporary);
-    }
-
-    return indexedExpression;
 }
 
 int TParseContext::checkIndexOutOfRange(bool outOfRangeIndexIsError,
                                         const TSourceLoc &location,
                                         int index,
                                         int arraySize,
                                         const char *reason,
                                         const char *token)
@@ -2844,143 +2749,101 @@ int TParseContext::checkIndexOutOfRange(
     return index;
 }
 
 TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpression,
                                                          const TSourceLoc &dotLocation,
                                                          const TString &fieldString,
                                                          const TSourceLoc &fieldLocation)
 {
-    TIntermTyped *indexedExpression = NULL;
-
     if (baseExpression->isArray())
     {
         error(fieldLocation, "cannot apply dot operator to an array", ".");
+        return baseExpression;
     }
 
     if (baseExpression->isVector())
     {
         TVectorFields fields;
         if (!parseVectorFields(fieldString, baseExpression->getNominalSize(), fields,
                                fieldLocation))
         {
             fields.num        = 1;
             fields.offsets[0] = 0;
         }
 
-        if (baseExpression->getAsConstantUnion())
-        {
-            // constant folding for vector fields
-            indexedExpression =
-                foldVectorSwizzle(fields, baseExpression->getAsConstantUnion(), fieldLocation);
-        }
-        else
-        {
-            TIntermTyped *index = intermediate.addSwizzle(fields, fieldLocation);
-            indexedExpression =
-                intermediate.addIndex(EOpVectorSwizzle, baseExpression, index, dotLocation);
-        }
-        if (indexedExpression == nullptr)
-        {
-            indexedExpression = baseExpression;
-        }
-        else
-        {
-            // Note that the qualifier set here will be corrected later.
-            indexedExpression->setType(TType(baseExpression->getBasicType(),
-                                             baseExpression->getPrecision(), EvqTemporary,
-                                             static_cast<unsigned char>(fields.num)));
-        }
+        TIntermTyped *index = intermediate.addSwizzle(fields, fieldLocation);
+        return intermediate.addIndex(EOpVectorSwizzle, baseExpression, index, dotLocation,
+                                     &mDiagnostics);
     }
     else if (baseExpression->getBasicType() == EbtStruct)
     {
-        bool fieldFound          = false;
         const TFieldList &fields = baseExpression->getType().getStruct()->fields();
         if (fields.empty())
         {
             error(dotLocation, "structure has no fields", "Internal Error");
-            indexedExpression = baseExpression;
+            return baseExpression;
         }
         else
         {
+            bool fieldFound = false;
             unsigned int i;
             for (i = 0; i < fields.size(); ++i)
             {
                 if (fields[i]->name() == fieldString)
                 {
                     fieldFound = true;
                     break;
                 }
             }
             if (fieldFound)
             {
-                if (baseExpression->getAsConstantUnion())
-                {
-                    indexedExpression = addConstStruct(fieldString, baseExpression, dotLocation);
-                    if (indexedExpression == 0)
-                    {
-                        indexedExpression = baseExpression;
-                    }
-                    else
-                    {
-                        indexedExpression->setType(*fields[i]->type());
-                    }
-                }
-                else
-                {
-                    TConstantUnion *unionArray = new TConstantUnion[1];
-                    unionArray->setIConst(i);
-                    TIntermTyped *index = intermediate.addConstantUnion(
-                        unionArray, *fields[i]->type(), fieldLocation);
-                    indexedExpression = intermediate.addIndex(EOpIndexDirectStruct, baseExpression,
-                                                              index, dotLocation);
-                    indexedExpression->setType(*fields[i]->type());
-                }
+                TIntermTyped *index = TIntermTyped::CreateIndexNode(i);
+                index->setLine(fieldLocation);
+                return intermediate.addIndex(EOpIndexDirectStruct, baseExpression, index,
+                                             dotLocation, &mDiagnostics);
             }
             else
             {
                 error(dotLocation, " no such field in structure", fieldString.c_str());
-                indexedExpression = baseExpression;
+                return baseExpression;
             }
         }
     }
     else if (baseExpression->isInterfaceBlock())
     {
-        bool fieldFound          = false;
         const TFieldList &fields = baseExpression->getType().getInterfaceBlock()->fields();
         if (fields.empty())
         {
             error(dotLocation, "interface block has no fields", "Internal Error");
-            indexedExpression = baseExpression;
+            return baseExpression;
         }
         else
         {
+            bool fieldFound = false;
             unsigned int i;
             for (i = 0; i < fields.size(); ++i)
             {
                 if (fields[i]->name() == fieldString)
                 {
                     fieldFound = true;
                     break;
                 }
             }
             if (fieldFound)
             {
-                TConstantUnion *unionArray = new TConstantUnion[1];
-                unionArray->setIConst(i);
-                TIntermTyped *index =
-                    intermediate.addConstantUnion(unionArray, *fields[i]->type(), fieldLocation);
-                indexedExpression = intermediate.addIndex(EOpIndexDirectInterfaceBlock,
-                                                          baseExpression, index, dotLocation);
-                indexedExpression->setType(*fields[i]->type());
+                TIntermTyped *index = TIntermTyped::CreateIndexNode(i);
+                index->setLine(fieldLocation);
+                return intermediate.addIndex(EOpIndexDirectInterfaceBlock, baseExpression, index,
+                                             dotLocation, &mDiagnostics);
             }
             else
             {
                 error(dotLocation, " no such field in interface block", fieldString.c_str());
-                indexedExpression = baseExpression;
+                return baseExpression;
             }
         }
     }
     else
     {
         if (mShaderVersion < 300)
         {
             error(dotLocation, " field selection requires structure or vector on left hand side",
@@ -2988,29 +2851,18 @@ TIntermTyped *TParseContext::addFieldSel
         }
         else
         {
             error(dotLocation,
                   " field selection requires structure, vector, or interface block on left hand "
                   "side",
                   fieldString.c_str());
         }
-        indexedExpression = baseExpression;
-    }
-
-    if (baseExpression->getQualifier() == EvqConst)
-    {
-        indexedExpression->getTypePointer()->setQualifier(EvqConst);
+        return baseExpression;
     }
-    else
-    {
-        indexedExpression->getTypePointer()->setQualifier(EvqTemporary);
-    }
-
-    return indexedExpression;
 }
 
 TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierType,
                                                      const TSourceLoc &qualifierTypeLine)
 {
     TLayoutQualifier qualifier = TLayoutQualifier::create();
 
     if (qualifierType == "shared")
@@ -3078,16 +2930,17 @@ TLayoutQualifier TParseContext::parseLay
         if (intValue < 0)
         {
             error(intValueLine, "out of range:", intValueString.c_str(),
                   "location must be non-negative");
         }
         else
         {
             qualifier.location = intValue;
+            qualifier.locationsSpecified = 1;
         }
     }
     else if (qualifierType == "local_size_x")
     {
         parseLocalSize(qualifierType, qualifierTypeLine, intValue, intValueLine, intValueString, 0u,
                        &qualifier.localSize);
     }
     else if (qualifierType == "local_size_y")
@@ -3103,159 +2956,100 @@ TLayoutQualifier TParseContext::parseLay
     else
     {
         error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str());
     }
 
     return qualifier;
 }
 
+TTypeQualifierBuilder *TParseContext::createTypeQualifierBuilder(const TSourceLoc &loc)
+{
+    return new TTypeQualifierBuilder(
+        new TStorageQualifierWrapper(symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary, loc),
+        mShaderVersion);
+}
+
 TLayoutQualifier TParseContext::joinLayoutQualifiers(TLayoutQualifier leftQualifier,
                                                      TLayoutQualifier rightQualifier,
                                                      const TSourceLoc &rightQualifierLocation)
 {
-    TLayoutQualifier joinedQualifier = leftQualifier;
-
-    if (rightQualifier.location != -1)
-    {
-        joinedQualifier.location = rightQualifier.location;
-    }
-    if (rightQualifier.matrixPacking != EmpUnspecified)
-    {
-        joinedQualifier.matrixPacking = rightQualifier.matrixPacking;
-    }
-    if (rightQualifier.blockStorage != EbsUnspecified)
-    {
-        joinedQualifier.blockStorage = rightQualifier.blockStorage;
-    }
-
-    for (size_t i = 0u; i < rightQualifier.localSize.size(); ++i)
-    {
-        if (rightQualifier.localSize[i] != -1)
-        {
-            if (joinedQualifier.localSize[i] != -1 &&
-                joinedQualifier.localSize[i] != rightQualifier.localSize[i])
-            {
-                error(rightQualifierLocation,
-                      "Cannot have multiple different work group size specifiers",
-                      getWorkGroupSizeString(i));
-            }
-            joinedQualifier.localSize[i] = rightQualifier.localSize[i];
-        }
-    }
-
-    return joinedQualifier;
+    return sh::JoinLayoutQualifiers(leftQualifier, rightQualifier, rightQualifierLocation,
+                                    &mDiagnostics);
 }
 
-TPublicType TParseContext::joinInterpolationQualifiers(const TSourceLoc &interpolationLoc,
-                                                       TQualifier interpolationQualifier,
-                                                       const TSourceLoc &storageLoc,
-                                                       TQualifier storageQualifier)
+TFieldList *TParseContext::addStructDeclaratorListWithQualifiers(
+    const TTypeQualifierBuilder &typeQualifierBuilder,
+    TPublicType *typeSpecifier,
+    TFieldList *fieldList)
 {
-    TQualifier mergedQualifier = EvqSmoothIn;
-
-    if (storageQualifier == EvqFragmentIn)
+    TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics);
+
+    typeSpecifier->qualifier       = typeQualifier.qualifier;
+    typeSpecifier->layoutQualifier = typeQualifier.layoutQualifier;
+    typeSpecifier->invariant       = typeQualifier.invariant;
+    if (typeQualifier.precision != EbpUndefined)
     {
-        if (interpolationQualifier == EvqSmooth)
-            mergedQualifier = EvqSmoothIn;
-        else if (interpolationQualifier == EvqFlat)
-            mergedQualifier = EvqFlatIn;
-        else
-            UNREACHABLE();
-    }
-    else if (storageQualifier == EvqCentroidIn)
-    {
-        if (interpolationQualifier == EvqSmooth)
-            mergedQualifier = EvqCentroidIn;
-        else if (interpolationQualifier == EvqFlat)
-            mergedQualifier = EvqFlatIn;
-        else
-            UNREACHABLE();
+        typeSpecifier->precision = typeQualifier.precision;
     }
-    else if (storageQualifier == EvqVertexOut)
-    {
-        if (interpolationQualifier == EvqSmooth)
-            mergedQualifier = EvqSmoothOut;
-        else if (interpolationQualifier == EvqFlat)
-            mergedQualifier = EvqFlatOut;
-        else
-            UNREACHABLE();
-    }
-    else if (storageQualifier == EvqCentroidOut)
-    {
-        if (interpolationQualifier == EvqSmooth)
-            mergedQualifier = EvqCentroidOut;
-        else if (interpolationQualifier == EvqFlat)
-            mergedQualifier = EvqFlatOut;
-        else
-            UNREACHABLE();
-    }
-    else
-    {
-        error(interpolationLoc,
-              "interpolation qualifier requires a fragment 'in' or vertex 'out' storage qualifier",
-              getInterpolationString(interpolationQualifier));
-
-        mergedQualifier = storageQualifier;
-    }
-
-    TPublicType type;
-    type.setBasic(EbtVoid, mergedQualifier, storageLoc);
-    return type;
+    return addStructDeclaratorList(*typeSpecifier, fieldList);
 }
 
 TFieldList *TParseContext::addStructDeclaratorList(const TPublicType &typeSpecifier,
                                                    TFieldList *fieldList)
 {
-    checkIsNonVoid(typeSpecifier.line, (*fieldList)[0]->name(), typeSpecifier.type);
-
-    checkWorkGroupSizeIsNotSpecified(typeSpecifier.line, typeSpecifier.layoutQualifier);
+    checkPrecisionSpecified(typeSpecifier.getLine(), typeSpecifier.precision,
+                            typeSpecifier.getBasicType());
+
+    checkIsNonVoid(typeSpecifier.getLine(), (*fieldList)[0]->name(), typeSpecifier.getBasicType());
+
+    checkWorkGroupSizeIsNotSpecified(typeSpecifier.getLine(), typeSpecifier.layoutQualifier);
 
     for (unsigned int i = 0; i < fieldList->size(); ++i)
     {
         //
         // Careful not to replace already known aspects of type, like array-ness
         //
         TType *type = (*fieldList)[i]->type();
-        type->setBasicType(typeSpecifier.type);
-        type->setPrimarySize(typeSpecifier.primarySize);
-        type->setSecondarySize(typeSpecifier.secondarySize);
+        type->setBasicType(typeSpecifier.getBasicType());
+        type->setPrimarySize(typeSpecifier.getPrimarySize());
+        type->setSecondarySize(typeSpecifier.getSecondarySize());
         type->setPrecision(typeSpecifier.precision);
         type->setQualifier(typeSpecifier.qualifier);
         type->setLayoutQualifier(typeSpecifier.layoutQualifier);
+        type->setInvariant(typeSpecifier.invariant);
 
         // don't allow arrays of arrays
         if (type->isArray())
         {
-            checkIsValidTypeForArray(typeSpecifier.line, typeSpecifier);
+            checkIsValidTypeForArray(typeSpecifier.getLine(), typeSpecifier);
         }
         if (typeSpecifier.array)
             type->setArraySize(static_cast<unsigned int>(typeSpecifier.arraySize));
-        if (typeSpecifier.userDef)
+        if (typeSpecifier.getUserDef())
         {
-            type->setStruct(typeSpecifier.userDef->getStruct());
+            type->setStruct(typeSpecifier.getUserDef()->getStruct());
         }
 
-        checkIsBelowStructNestingLimit(typeSpecifier.line, *(*fieldList)[i]);
+        checkIsBelowStructNestingLimit(typeSpecifier.getLine(), *(*fieldList)[i]);
     }
 
     return fieldList;
 }
 
-TPublicType TParseContext::addStructure(const TSourceLoc &structLine,
-                                        const TSourceLoc &nameLine,
-                                        const TString *structName,
-                                        TFieldList *fieldList)
+TTypeSpecifierNonArray TParseContext::addStructure(const TSourceLoc &structLine,
+                                                   const TSourceLoc &nameLine,
+                                                   const TString *structName,
+                                                   TFieldList *fieldList)
 {
     TStructure *structure = new TStructure(structName, fieldList);
     TType *structureType  = new TType(structure);
 
     // Store a bool in the struct if we're at global scope, to allow us to
     // skip the local struct scoping workaround in HLSL.
-    structure->setUniqueId(TSymbolTable::nextUniqueId());
     structure->setAtGlobalScope(symbolTable.atGlobalLevel());
 
     if (!structName->empty())
     {
         checkIsNotReserved(nameLine, *structName);
         TVariable *userTypeDef = new TVariable(structName, *structureType, true);
         if (!symbolTable.declare(userTypeDef))
         {
@@ -3273,25 +3067,31 @@ TPublicType TParseContext::addStructure(
             case EvqGlobal:
             case EvqTemporary:
                 break;
             default:
                 error(field.line(), "invalid qualifier on struct member",
                       getQualifierString(qualifier));
                 break;
         }
+        if (field.type()->isInvariant())
+        {
+            error(field.line(), "invalid qualifier on struct member", "invariant");
+        }
+
+        checkLocationIsNotSpecified(field.line(), field.type()->getLayoutQualifier());
     }
 
-    TPublicType publicType;
-    publicType.setBasic(EbtStruct, EvqTemporary, structLine);
-    publicType.userDef = structureType;
-    publicType.isStructSpecifier = true;
+    TTypeSpecifierNonArray typeSpecifierNonArray;
+    typeSpecifierNonArray.initialize(EbtStruct, structLine);
+    typeSpecifierNonArray.userDef           = structureType;
+    typeSpecifierNonArray.isStructSpecifier = true;
     exitStructDeclaration();
 
-    return publicType;
+    return typeSpecifierNonArray;
 }
 
 TIntermSwitch *TParseContext::addSwitch(TIntermTyped *init,
                                         TIntermAggregate *statementList,
                                         const TSourceLoc &loc)
 {
     TBasicType switchType = init->getBasicType();
     if ((switchType != EbtInt && switchType != EbtUInt) || init->isMatrix() || init->isArray() ||
@@ -3406,17 +3206,24 @@ TIntermTyped *TParseContext::createUnary
             {
                 return nullptr;
             }
         // Operators for built-ins are already type checked against their prototype.
         default:
             break;
     }
 
-    return intermediate.addUnaryMath(op, child, loc, funcReturnType);
+    TIntermUnary *node = new TIntermUnary(op, child);
+    node->setLine(loc);
+
+    TIntermTyped *foldedNode = node->fold(&mDiagnostics);
+    if (foldedNode)
+        return foldedNode;
+
+    return node;
 }
 
 TIntermTyped *TParseContext::addUnaryMath(TOperator op, TIntermTyped *child, const TSourceLoc &loc)
 {
     TIntermTyped *node = createUnaryMath(op, child, loc, nullptr);
     if (node == nullptr)
     {
         unaryOpError(loc, GetOperatorString(op), child->getCompleteString());
@@ -4035,17 +3842,18 @@ TIntermTyped *TParseContext::addFunction
                         aggregate->getTypePointer()->setQualifier(EvqConst);
                     }
 
                     // Some built-in functions have out parameters too.
                     functionCallLValueErrorCheck(fnCandidate, aggregate);
 
                     // See if we can constant fold a built-in. Note that this may be possible even
                     // if it is not const-qualified.
-                    TIntermTyped *foldedNode = intermediate.foldAggregateBuiltIn(aggregate);
+                    TIntermTyped *foldedNode =
+                        intermediate.foldAggregateBuiltIn(aggregate, &mDiagnostics);
                     if (foldedNode)
                     {
                         callNode = foldedNode;
                     }
                     else
                     {
                         callNode = aggregate;
                     }
@@ -4090,44 +3898,45 @@ TIntermTyped *TParseContext::addFunction
             callNode = intermediate.addConstantUnion(unionArray,
                                                      TType(EbtFloat, EbpUndefined, EvqConst), loc);
         }
     }
     return callNode;
 }
 
 TIntermTyped *TParseContext::addTernarySelection(TIntermTyped *cond,
-                                                 TIntermTyped *trueBlock,
-                                                 TIntermTyped *falseBlock,
+                                                 TIntermTyped *trueExpression,
+                                                 TIntermTyped *falseExpression,
                                                  const TSourceLoc &loc)
 {
     checkIsScalarBool(loc, cond);
 
-    if (trueBlock->getType() != falseBlock->getType())
+    if (trueExpression->getType() != falseExpression->getType())
     {
-        binaryOpError(loc, ":", trueBlock->getCompleteString(), falseBlock->getCompleteString());
-        return falseBlock;
+        binaryOpError(loc, ":", trueExpression->getCompleteString(),
+                      falseExpression->getCompleteString());
+        return falseExpression;
     }
     // ESSL1 sections 5.2 and 5.7:
     // ESSL3 section 5.7:
     // Ternary operator is not among the operators allowed for structures/arrays.
-    if (trueBlock->isArray() || trueBlock->getBasicType() == EbtStruct)
+    if (trueExpression->isArray() || trueExpression->getBasicType() == EbtStruct)
     {
         error(loc, "ternary operator is not allowed for structures or arrays", ":");
-        return falseBlock;
+        return falseExpression;
     }
     // WebGL2 section 5.26, the following results in an error:
     // "Ternary operator applied to void, arrays, or structs containing arrays"
-    if (mShaderSpec == SH_WEBGL2_SPEC && trueBlock->getBasicType() == EbtVoid)
+    if (mShaderSpec == SH_WEBGL2_SPEC && trueExpression->getBasicType() == EbtVoid)
     {
         error(loc, "ternary operator is not allowed for void", ":");
-        return falseBlock;
+        return falseExpression;
     }
 
-    return intermediate.addSelection(cond, trueBlock, falseBlock, loc);
+    return TIntermediate::AddTernarySelection(cond, trueExpression, falseExpression, loc);
 }
 
 //
 // Parse an array of strings using yyparse.
 //
 // Returns 0 for success.
 //
 int PaParseStrings(size_t count,
--- a/gfx/angle/src/compiler/translator/ParseContext.h
+++ b/gfx/angle/src/compiler/translator/ParseContext.h
@@ -6,16 +6,17 @@
 #ifndef COMPILER_TRANSLATOR_PARSECONTEXT_H_
 #define COMPILER_TRANSLATOR_PARSECONTEXT_H_
 
 #include "compiler/translator/Compiler.h"
 #include "compiler/translator/Diagnostics.h"
 #include "compiler/translator/DirectiveHandler.h"
 #include "compiler/translator/Intermediate.h"
 #include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/QualifierTypes.h"
 #include "compiler/preprocessor/Preprocessor.h"
 
 struct TMatrixFields
 {
     bool wholeRow;
     bool wholeCol;
     int row;
     int col;
@@ -25,24 +26,23 @@ struct TMatrixFields
 // The following are extra variables needed during parsing, grouped together so
 // they can be passed to the parser without needing a global.
 //
 class TParseContext : angle::NonCopyable
 {
   public:
     TParseContext(TSymbolTable &symt,
                   TExtensionBehavior &ext,
-                  TIntermediate &interm,
                   sh::GLenum type,
                   ShShaderSpec spec,
-                  int options,
+                  ShCompileOptions options,
                   bool checksPrecErrors,
                   TInfoSink &is,
                   const ShBuiltInResources &resources)
-        : intermediate(interm),
+        : intermediate(),
           symbolTable(symt),
           mDeferredSingleDeclarationErrorCheck(false),
           mShaderType(type),
           mShaderSpec(spec),
           mCompileOptions(options),
           mShaderVersion(100),
           mTreeRoot(nullptr),
           mLoopNestingLevel(0),
@@ -62,17 +62,18 @@ class TParseContext : angle::NonCopyable
                             resources.WEBGL_debug_shader_precision == 1),
           mPreprocessor(&mDiagnostics, &mDirectiveHandler),
           mScanner(nullptr),
           mUsesFragData(false),
           mUsesFragColor(false),
           mUsesSecondaryOutputs(false),
           mMinProgramTexelOffset(resources.MinProgramTexelOffset),
           mMaxProgramTexelOffset(resources.MaxProgramTexelOffset),
-          mComputeShaderLocalSizeDeclared(false)
+          mComputeShaderLocalSizeDeclared(false),
+          mDeclaringFunction(false)
     {
         mComputeShaderLocalSize.fill(-1);
     }
 
     const pp::Preprocessor &getPreprocessor() const { return mPreprocessor; }
     pp::Preprocessor &getPreprocessor() { return mPreprocessor; }
     void *getScanner() const { return mScanner; }
     void setScanner(void *scanner) { mScanner = scanner; }
@@ -114,16 +115,22 @@ class TParseContext : angle::NonCopyable
     void decrLoopNestingLevel() { --mLoopNestingLevel; }
 
     void incrSwitchNestingLevel() { ++mSwitchNestingLevel; }
     void decrSwitchNestingLevel() { --mSwitchNestingLevel; }
 
     bool isComputeShaderLocalSizeDeclared() const { return mComputeShaderLocalSizeDeclared; }
     sh::WorkGroupSize getComputeShaderLocalSize() const;
 
+    void enterFunctionDeclaration() { mDeclaringFunction = true; }
+
+    void exitFunctionDeclaration() { mDeclaringFunction = false; }
+
+    bool declaringFunction() const { return mDeclaringFunction; }
+
     // This method is guaranteed to succeed, even if no variable with 'name' exists.
     const TVariable *getNamedVariable(const TSourceLoc &location, const TString *name, const TSymbol *symbol);
     TIntermTyped *parseVariableIdentifier(const TSourceLoc &location,
                                           const TString *name,
                                           const TSymbol *symbol);
 
     bool parseVectorFields(const TString&, int vecSize, TVectorFields&, const TSourceLoc &line);
 
@@ -147,39 +154,41 @@ class TParseContext : angle::NonCopyable
 
     // Returns a sanitized array size to use (the size is at least 1).
     unsigned int checkIsValidArraySize(const TSourceLoc &line, TIntermTyped *expr);
     bool checkIsValidQualifierForArray(const TSourceLoc &line, const TPublicType &elementQualifier);
     bool checkIsValidTypeForArray(const TSourceLoc &line, const TPublicType &elementType);
     bool checkIsNonVoid(const TSourceLoc &line, const TString &identifier, const TBasicType &type);
     void checkIsScalarBool(const TSourceLoc &line, const TIntermTyped *type);
     void checkIsScalarBool(const TSourceLoc &line, const TPublicType &pType);
-    bool checkIsNotSampler(const TSourceLoc &line, const TPublicType &pType, const char *reason);
+    bool checkIsNotSampler(const TSourceLoc &line,
+                           const TTypeSpecifierNonArray &pType,
+                           const char *reason);
     void checkDeclaratorLocationIsNotSpecified(const TSourceLoc &line, const TPublicType &pType);
     void checkLocationIsNotSpecified(const TSourceLoc &location,
                                      const TLayoutQualifier &layoutQualifier);
     void checkOutParameterIsNotSampler(const TSourceLoc &line,
                                        TQualifier qualifier,
                                        const TType &type);
     void checkIsParameterQualifierValid(const TSourceLoc &line,
-                                        TQualifier qualifier,
-                                        TQualifier paramQualifier,
+                                        const TTypeQualifierBuilder &typeQualifierBuilder,