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 id30882
push userryanvm@gmail.com
push dateSat, 29 Oct 2016 13:12:06 +0000
treeherdermozilla-central@16cdd6273c48 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgilbert
bugs1303443
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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,