Unify extension behavior checks

Some supportsExtension and isExtensionEnabled checks are now turned
into checkCanUseExtension checks. Using checkCanUseExtension is
preferable so that warnings are generated correctly when an extension
is used and a warn directive is present.

isExtensionEnabled is still used in some places where an error message
about the extension could be confusing, particularly when a core spec
version adds support for something that is also present in an
extension.

Also make it possible to disable ARB_texture_rectangle extension using
an extension directive. ARB_texture_rectangle extension functionality
is enabled by default in GLSL when the extension is supported.

BUG=angleproject:2238
TEST=angle_unittests

Change-Id: I7455293412ff469f54bc7da79df146e7bc127379
Reviewed-on: https://chromium-review.googlesource.com/760737
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/compiler/translator/ExtensionBehavior.cpp b/src/compiler/translator/ExtensionBehavior.cpp
index 608b8db..3f910b9 100644
--- a/src/compiler/translator/ExtensionBehavior.cpp
+++ b/src/compiler/translator/ExtensionBehavior.cpp
@@ -89,7 +89,8 @@
 {
     ASSERT(extension != TExtension::UNDEFINED);
     auto iter = extBehavior.find(extension);
-    return iter != extBehavior.end() && (iter->second == EBhEnable || iter->second == EBhRequire);
+    return iter != extBehavior.end() &&
+           (iter->second == EBhEnable || iter->second == EBhRequire || iter->second == EBhWarn);
 }
 
 }  // namespace sh
diff --git a/src/compiler/translator/Initialize.cpp b/src/compiler/translator/Initialize.cpp
index 054f2b9..aeb8d58 100644
--- a/src/compiler/translator/Initialize.cpp
+++ b/src/compiler/translator/Initialize.cpp
@@ -1050,7 +1050,9 @@
     }
     if (resources.ARB_texture_rectangle)
     {
-        extBehavior[TExtension::ARB_texture_rectangle] = EBhUndefined;
+        // Special: ARB_texture_rectangle extension does not follow the standard for #extension
+        // directives - it is enabled by default. An extension directive may still disable it.
+        extBehavior[TExtension::ARB_texture_rectangle] = EBhEnable;
     }
     if (resources.EXT_blend_func_extended)
     {
@@ -1097,9 +1099,16 @@
 
 void ResetExtensionBehavior(TExtensionBehavior &extBehavior)
 {
-    for (auto ext_iter = extBehavior.begin(); ext_iter != extBehavior.end(); ++ext_iter)
+    for (auto &ext : extBehavior)
     {
-        ext_iter->second = EBhUndefined;
+        if (ext.first == TExtension::ARB_texture_rectangle)
+        {
+            ext.second = EBhEnable;
+        }
+        else
+        {
+            ext.second = EBhUndefined;
+        }
     }
 }
 
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index 6b40262..7b4a742 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -1156,72 +1156,95 @@
     }
 }
 
+template <size_t size>
+bool TParseContext::checkCanUseOneOfExtensions(const TSourceLoc &line,
+                                               const std::array<TExtension, size> &extensions)
+{
+    ASSERT(!extensions.empty());
+    const TExtensionBehavior &extBehavior = extensionBehavior();
+
+    bool canUseWithWarning    = false;
+    bool canUseWithoutWarning = false;
+
+    const char *errorMsgString   = "";
+    TExtension errorMsgExtension = TExtension::UNDEFINED;
+
+    for (TExtension extension : extensions)
+    {
+        auto extIter = extBehavior.find(extension);
+        if (canUseWithWarning)
+        {
+            // We already have an extension that we can use, but with a warning.
+            // See if we can use the alternative extension without a warning.
+            if (extIter == extBehavior.end())
+            {
+                continue;
+            }
+            if (extIter->second == EBhEnable || extIter->second == EBhRequire)
+            {
+                canUseWithoutWarning = true;
+                break;
+            }
+            continue;
+        }
+        if (extIter == extBehavior.end())
+        {
+            errorMsgString    = "extension is not supported";
+            errorMsgExtension = extension;
+        }
+        else if (extIter->second == EBhUndefined || extIter->second == EBhDisable)
+        {
+            errorMsgString    = "extension is disabled";
+            errorMsgExtension = extension;
+        }
+        else if (extIter->second == EBhWarn)
+        {
+            errorMsgExtension = extension;
+            canUseWithWarning = true;
+        }
+        else
+        {
+            ASSERT(extIter->second == EBhEnable || extIter->second == EBhRequire);
+            canUseWithoutWarning = true;
+            break;
+        }
+    }
+
+    if (canUseWithoutWarning)
+    {
+        return true;
+    }
+    if (canUseWithWarning)
+    {
+        warning(line, "extension is being used", GetExtensionNameString(errorMsgExtension));
+        return true;
+    }
+    error(line, errorMsgString, GetExtensionNameString(errorMsgExtension));
+    return false;
+}
+
+template bool TParseContext::checkCanUseOneOfExtensions(
+    const TSourceLoc &line,
+    const std::array<TExtension, 1> &extensions);
+template bool TParseContext::checkCanUseOneOfExtensions(
+    const TSourceLoc &line,
+    const std::array<TExtension, 2> &extensions);
+template bool TParseContext::checkCanUseOneOfExtensions(
+    const TSourceLoc &line,
+    const std::array<TExtension, 3> &extensions);
+
 bool TParseContext::checkCanUseExtension(const TSourceLoc &line, TExtension extension)
 {
     ASSERT(extension != TExtension::UNDEFINED);
     ASSERT(extension != TExtension::EXT_geometry_shader);
-    const TExtensionBehavior &extBehavior   = extensionBehavior();
-    TExtensionBehavior::const_iterator iter = extBehavior.find(extension);
-    if (iter == extBehavior.end())
+    if (extension == TExtension::OES_geometry_shader)
     {
-        error(line, "extension is not supported", GetExtensionNameString(extension));
-        return false;
+        // OES_geometry_shader and EXT_geometry_shader are always interchangeable.
+        constexpr std::array<TExtension, 2u> extensions{
+            {TExtension::EXT_geometry_shader, TExtension::OES_geometry_shader}};
+        return checkCanUseOneOfExtensions(line, extensions);
     }
-    // In GLSL ES, an extension's default behavior is "disable".
-    if (iter->second == EBhDisable || iter->second == EBhUndefined)
-    {
-        // We also need to check EXT_geometry_shader because internally we always use
-        // TExtension::OES_geometry_shader to represent both OES_geometry_shader and
-        // EXT_geometry_shader.
-        if (extension == TExtension::OES_geometry_shader)
-        {
-            TExtensionBehavior::const_iterator iterExt =
-                extBehavior.find(TExtension::EXT_geometry_shader);
-            ASSERT(iterExt != extBehavior.end());
-            if (iterExt->second == EBhUndefined)
-            {
-                error(line, "extension is disabled",
-                      GetExtensionNameString(TExtension::OES_geometry_shader));
-                return false;
-            }
-            if (iterExt->second == EBhDisable)
-            {
-                error(line, "extension is disabled",
-                      GetExtensionNameString(TExtension::EXT_geometry_shader));
-                return false;
-            }
-            if (iterExt->second == EBhWarn)
-            {
-                warning(line, "extension is being used",
-                        GetExtensionNameString(TExtension::EXT_geometry_shader));
-            }
-
-            return true;
-        }
-
-        error(line, "extension is disabled", GetExtensionNameString(extension));
-        return false;
-    }
-    if (iter->second == EBhWarn)
-    {
-        if (extension == TExtension::OES_geometry_shader)
-        {
-            TExtensionBehavior::const_iterator iterExt =
-                extBehavior.find(TExtension::EXT_geometry_shader);
-            ASSERT(iterExt != extBehavior.end());
-            // We should output no warnings when OES_geometry_shader is declared as "warn" and
-            // EXT_geometry_shader is declared as "require" or "enable".
-            if (iterExt->second == EBhRequire || iterExt->second == EBhEnable)
-            {
-                return true;
-            }
-        }
-
-        warning(line, "extension is being used", GetExtensionNameString(extension));
-        return true;
-    }
-
-    return true;
+    return checkCanUseOneOfExtensions(line, std::array<TExtension, 1u>{extension});
 }
 
 // ESSL 3.00.6 section 4.8 Empty Declarations: "The combinations of qualifiers that cause
@@ -1680,13 +1703,6 @@
     }
 }
 
-bool TParseContext::supportsExtension(TExtension extension)
-{
-    const TExtensionBehavior &extbehavior   = extensionBehavior();
-    TExtensionBehavior::const_iterator iter = extbehavior.find(extension);
-    return (iter != extbehavior.end());
-}
-
 bool TParseContext::isExtensionEnabled(TExtension extension) const
 {
     return IsExtensionEnabled(extensionBehavior(), extension);
@@ -4206,10 +4222,12 @@
         error(qualifierTypeLine, "invalid layout qualifier: location requires an argument",
               qualifierType.c_str());
     }
-    else if (qualifierType == "yuv" && isExtensionEnabled(TExtension::EXT_YUV_target) &&
-             mShaderType == GL_FRAGMENT_SHADER)
+    else if (qualifierType == "yuv" && mShaderType == GL_FRAGMENT_SHADER)
     {
-        qualifier.yuv = true;
+        if (checkCanUseExtension(qualifierTypeLine, TExtension::EXT_YUV_target))
+        {
+            qualifier.yuv = true;
+        }
     }
     else if (qualifierType == "rgba32f")
     {
@@ -4464,10 +4482,12 @@
         parseLocalSize(qualifierType, qualifierTypeLine, intValue, intValueLine, intValueString, 2u,
                        &qualifier.localSize);
     }
-    else if (qualifierType == "num_views" && isExtensionEnabled(TExtension::OVR_multiview) &&
-             mShaderType == GL_VERTEX_SHADER)
+    else if (qualifierType == "num_views" && mShaderType == GL_VERTEX_SHADER)
     {
-        parseNumViews(intValue, intValueLine, intValueString, &qualifier.numViews);
+        if (checkCanUseExtension(qualifierTypeLine, TExtension::OVR_multiview))
+        {
+            parseNumViews(intValue, intValueLine, intValueString, &qualifier.numViews);
+        }
     }
     else if (qualifierType == "invocations" && mShaderType == GL_GEOMETRY_SHADER_OES &&
              checkCanUseExtension(qualifierTypeLine, TExtension::OES_geometry_shader))
diff --git a/src/compiler/translator/ParseContext.h b/src/compiler/translator/ParseContext.h
index 12924d1..c611b31 100644
--- a/src/compiler/translator/ParseContext.h
+++ b/src/compiler/translator/ParseContext.h
@@ -141,6 +141,15 @@
     void checkIsParameterQualifierValid(const TSourceLoc &line,
                                         const TTypeQualifierBuilder &typeQualifierBuilder,
                                         TType *type);
+
+    // Check if at least one of the specified extensions can be used, and generate error/warning as
+    // appropriate according to the spec.
+    // This function is only needed for a few different small constant sizes of extension array, and
+    // we want to avoid unnecessary dynamic allocations. That's why checkCanUseOneOfExtensions is a
+    // template function rather than one taking a vector.
+    template <size_t size>
+    bool checkCanUseOneOfExtensions(const TSourceLoc &line,
+                                    const std::array<TExtension, size> &extensions);
     bool checkCanUseExtension(const TSourceLoc &line, TExtension extension);
 
     // Done for all declarations, whether empty or not.
@@ -171,7 +180,7 @@
     {
         return mDirectiveHandler.extensionBehavior();
     }
-    bool supportsExtension(TExtension extension);
+
     bool isExtensionEnabled(TExtension extension) const;
     void handleExtensionDirective(const TSourceLoc &loc, const char *extName, const char *behavior);
     void handlePragmaDirective(const TSourceLoc &loc,
diff --git a/src/compiler/translator/glslang.y b/src/compiler/translator/glslang.y
index 4cc5a9e..706c552 100644
--- a/src/compiler/translator/glslang.y
+++ b/src/compiler/translator/glslang.y
@@ -282,7 +282,8 @@
         $$ = context->addScalarLiteral(unionArray, @1);
     }
     | YUVCSCSTANDARDEXTCONSTANT {
-        if (!context->isExtensionEnabled(TExtension::EXT_YUV_target)) {
+        if (!context->checkCanUseExtension(@1, TExtension::EXT_YUV_target))
+        {
            context->error(@1, "unsupported value", $1.string->c_str());
         }
         TConstantUnion *unionArray = new TConstantUnion[1];
@@ -1049,7 +1050,8 @@
         $$.setMatrix(4, 3);
     }
     | YUVCSCSTANDARDEXT {
-        if (!context->isExtensionEnabled(TExtension::EXT_YUV_target)) {
+        if (!context->checkCanUseExtension(@1, TExtension::EXT_YUV_target))
+        {
             context->error(@1, "unsupported type", "yuvCscStandardEXT");
         }
         $$.initialize(EbtYuvCscStandardEXT, @1);
@@ -1109,20 +1111,25 @@
         $$.initialize(EbtSampler2DArrayShadow, @1);
     }
     | SAMPLER_EXTERNAL_OES {
-        if (!context->supportsExtension(TExtension::OES_EGL_image_external) &&
-            !context->supportsExtension(TExtension::NV_EGL_stream_consumer_external)) {
+        constexpr std::array<TExtension, 3u> extensions{ { TExtension::NV_EGL_stream_consumer_external,
+                                                           TExtension::OES_EGL_image_external_essl3,
+                                                           TExtension::OES_EGL_image_external } };
+        if (!context->checkCanUseOneOfExtensions(@1, extensions))
+        {
             context->error(@1, "unsupported type", "samplerExternalOES");
         }
         $$.initialize(EbtSamplerExternalOES, @1);
     }
     | SAMPLEREXTERNAL2DY2YEXT {
-        if (!context->isExtensionEnabled(TExtension::EXT_YUV_target)) {
+        if (!context->checkCanUseExtension(@1, TExtension::EXT_YUV_target))
+        {
             context->error(@1, "unsupported type", "__samplerExternal2DY2YEXT");
         }
         $$.initialize(EbtSamplerExternal2DY2YEXT, @1);
     }
     | SAMPLER2DRECT {
-        if (!context->supportsExtension(TExtension::ARB_texture_rectangle)) {
+        if (!context->checkCanUseExtension(@1, TExtension::ARB_texture_rectangle))
+        {
             context->error(@1, "unsupported type", "sampler2DRect");
         }
         $$.initialize(EbtSampler2DRect, @1);
diff --git a/src/compiler/translator/glslang_tab.cpp b/src/compiler/translator/glslang_tab.cpp
index e6bfc75..ac8e202 100644
--- a/src/compiler/translator/glslang_tab.cpp
+++ b/src/compiler/translator/glslang_tab.cpp
@@ -743,35 +743,35 @@
 static const yytype_uint16 yyrline[] =
 {
        0,   247,   247,   248,   251,   261,   264,   269,   274,   279,
-     284,   292,   298,   301,   304,   307,   310,   313,   319,   326,
-     332,   336,   344,   347,   353,   357,   364,   369,   376,   384,
-     387,   390,   396,   399,   402,   405,   412,   413,   414,   415,
-     423,   424,   427,   430,   437,   438,   441,   447,   448,   452,
-     459,   460,   463,   466,   469,   475,   476,   479,   485,   486,
-     493,   494,   501,   502,   509,   510,   516,   517,   523,   524,
-     530,   531,   537,   538,   544,   545,   546,   547,   551,   552,
-     553,   557,   561,   565,   569,   576,   579,   585,   592,   599,
-     602,   605,   609,   613,   617,   621,   625,   632,   639,   642,
-     649,   657,   674,   684,   687,   693,   697,   701,   705,   712,
-     719,   722,   726,   730,   735,   742,   746,   750,   754,   759,
-     766,   770,   776,   779,   785,   789,   796,   802,   806,   810,
-     813,   816,   825,   830,   834,   837,   840,   843,   846,   850,
-     853,   857,   860,   863,   866,   869,   872,   879,   886,   889,
-     892,   898,   905,   908,   914,   917,   920,   923,   929,   932,
-     939,   943,   952,   955,   958,   961,   964,   967,   971,   975,
-     979,   983,   987,   991,   995,   999,  1003,  1007,  1011,  1015,
-    1019,  1023,  1027,  1031,  1035,  1039,  1043,  1047,  1051,  1057,
-    1060,  1063,  1066,  1069,  1072,  1075,  1078,  1081,  1084,  1087,
-    1090,  1093,  1096,  1099,  1102,  1105,  1108,  1111,  1118,  1124,
-    1130,  1133,  1136,  1139,  1142,  1145,  1148,  1151,  1154,  1157,
-    1160,  1163,  1166,  1169,  1172,  1180,  1180,  1183,  1183,  1189,
-    1192,  1198,  1201,  1208,  1212,  1218,  1221,  1227,  1231,  1235,
-    1236,  1242,  1243,  1244,  1245,  1246,  1247,  1248,  1252,  1256,
-    1256,  1256,  1263,  1264,  1268,  1268,  1269,  1269,  1274,  1278,
-    1285,  1289,  1296,  1297,  1301,  1307,  1311,  1320,  1320,  1327,
-    1330,  1336,  1340,  1346,  1346,  1351,  1351,  1355,  1355,  1363,
-    1366,  1372,  1375,  1381,  1385,  1392,  1395,  1398,  1401,  1404,
-    1412,  1418,  1424,  1427,  1433,  1433
+     284,   293,   299,   302,   305,   308,   311,   314,   320,   327,
+     333,   337,   345,   348,   354,   358,   365,   370,   377,   385,
+     388,   391,   397,   400,   403,   406,   413,   414,   415,   416,
+     424,   425,   428,   431,   438,   439,   442,   448,   449,   453,
+     460,   461,   464,   467,   470,   476,   477,   480,   486,   487,
+     494,   495,   502,   503,   510,   511,   517,   518,   524,   525,
+     531,   532,   538,   539,   545,   546,   547,   548,   552,   553,
+     554,   558,   562,   566,   570,   577,   580,   586,   593,   600,
+     603,   606,   610,   614,   618,   622,   626,   633,   640,   643,
+     650,   658,   675,   685,   688,   694,   698,   702,   706,   713,
+     720,   723,   727,   731,   736,   743,   747,   751,   755,   760,
+     767,   771,   777,   780,   786,   790,   797,   803,   807,   811,
+     814,   817,   826,   831,   835,   838,   841,   844,   847,   851,
+     854,   858,   861,   864,   867,   870,   873,   880,   887,   890,
+     893,   899,   906,   909,   915,   918,   921,   924,   930,   933,
+     940,   944,   953,   956,   959,   962,   965,   968,   972,   976,
+     980,   984,   988,   992,   996,  1000,  1004,  1008,  1012,  1016,
+    1020,  1024,  1028,  1032,  1036,  1040,  1044,  1048,  1052,  1059,
+    1062,  1065,  1068,  1071,  1074,  1077,  1080,  1083,  1086,  1089,
+    1092,  1095,  1098,  1101,  1104,  1107,  1110,  1113,  1123,  1130,
+    1137,  1140,  1143,  1146,  1149,  1152,  1155,  1158,  1161,  1164,
+    1167,  1170,  1173,  1176,  1179,  1187,  1187,  1190,  1190,  1196,
+    1199,  1205,  1208,  1215,  1219,  1225,  1228,  1234,  1238,  1242,
+    1243,  1249,  1250,  1251,  1252,  1253,  1254,  1255,  1259,  1263,
+    1263,  1263,  1270,  1271,  1275,  1275,  1276,  1276,  1281,  1285,
+    1292,  1296,  1303,  1304,  1308,  1314,  1318,  1327,  1327,  1334,
+    1337,  1343,  1347,  1353,  1353,  1358,  1358,  1362,  1362,  1370,
+    1373,  1379,  1382,  1388,  1392,  1399,  1402,  1405,  1408,  1411,
+    1419,  1425,  1431,  1434,  1440,  1440
 };
 #endif
 
@@ -2527,7 +2527,8 @@
   case 10:
 
     {
-        if (!context->isExtensionEnabled(TExtension::EXT_YUV_target)) {
+        if (!context->checkCanUseExtension((yylsp[0]), TExtension::EXT_YUV_target))
+        {
            context->error((yylsp[0]), "unsupported value", (yyvsp[0].lex).string->c_str());
         }
         TConstantUnion *unionArray = new TConstantUnion[1];
@@ -4019,7 +4020,8 @@
   case 188:
 
     {
-        if (!context->isExtensionEnabled(TExtension::EXT_YUV_target)) {
+        if (!context->checkCanUseExtension((yylsp[0]), TExtension::EXT_YUV_target))
+        {
             context->error((yylsp[0]), "unsupported type", "yuvCscStandardEXT");
         }
         (yyval.interm.typeSpecifierNonArray).initialize(EbtYuvCscStandardEXT, (yylsp[0]));
@@ -4174,8 +4176,11 @@
   case 207:
 
     {
-        if (!context->supportsExtension(TExtension::OES_EGL_image_external) &&
-            !context->supportsExtension(TExtension::NV_EGL_stream_consumer_external)) {
+        constexpr std::array<TExtension, 3u> extensions{ { TExtension::NV_EGL_stream_consumer_external,
+                                                           TExtension::OES_EGL_image_external_essl3,
+                                                           TExtension::OES_EGL_image_external } };
+        if (!context->checkCanUseOneOfExtensions((yylsp[0]), extensions))
+        {
             context->error((yylsp[0]), "unsupported type", "samplerExternalOES");
         }
         (yyval.interm.typeSpecifierNonArray).initialize(EbtSamplerExternalOES, (yylsp[0]));
@@ -4186,7 +4191,8 @@
   case 208:
 
     {
-        if (!context->isExtensionEnabled(TExtension::EXT_YUV_target)) {
+        if (!context->checkCanUseExtension((yylsp[0]), TExtension::EXT_YUV_target))
+        {
             context->error((yylsp[0]), "unsupported type", "__samplerExternal2DY2YEXT");
         }
         (yyval.interm.typeSpecifierNonArray).initialize(EbtSamplerExternal2DY2YEXT, (yylsp[0]));
@@ -4197,7 +4203,8 @@
   case 209:
 
     {
-        if (!context->supportsExtension(TExtension::ARB_texture_rectangle)) {
+        if (!context->checkCanUseExtension((yylsp[0]), TExtension::ARB_texture_rectangle))
+        {
             context->error((yylsp[0]), "unsupported type", "sampler2DRect");
         }
         (yyval.interm.typeSpecifierNonArray).initialize(EbtSampler2DRect, (yylsp[0]));
diff --git a/src/tests/angle_unittests.gypi b/src/tests/angle_unittests.gypi
index bc61dc3..1c94c4c 100644
--- a/src/tests/angle_unittests.gypi
+++ b/src/tests/angle_unittests.gypi
@@ -63,6 +63,7 @@
             '<(angle_path)/src/tests/compiler_tests/ExpressionLimit_test.cpp',
             '<(angle_path)/src/tests/compiler_tests/EXT_YUV_target_test.cpp',
             '<(angle_path)/src/tests/compiler_tests/EXT_blend_func_extended_test.cpp',
+            '<(angle_path)/src/tests/compiler_tests/ExtensionDirective_test.cpp',
             '<(angle_path)/src/tests/compiler_tests/FloatLex_test.cpp',
             '<(angle_path)/src/tests/compiler_tests/FragDepth_test.cpp',
             '<(angle_path)/src/tests/compiler_tests/GLSLCompatibilityOutput_test.cpp',
diff --git a/src/tests/compiler_tests/ARB_texture_rectangle_test.cpp b/src/tests/compiler_tests/ARB_texture_rectangle_test.cpp
index 9b88375..8a0052e 100644
--- a/src/tests/compiler_tests/ARB_texture_rectangle_test.cpp
+++ b/src/tests/compiler_tests/ARB_texture_rectangle_test.cpp
@@ -30,19 +30,6 @@
     }
 };
 
-// Check that new types and builtins are disallowed if the extension isn't present in the translator
-// resources
-TEST_F(ARBTextureRectangleTest, NewTypeAndBuiltinsWithoutTranslatorResourceExtension)
-{
-    // The new builtins require Sampler2DRect so we can't test them independently.
-    const std::string &shaderString =
-        "precision mediump float;\n"
-        "uniform sampler2DRect tex;\n"
-        "void main() {\n"
-        "}\n";
-    ASSERT_TRUE(compile(shaderString));
-}
-
 // Check that new types and builtins are usable even with the #extension directive
 // Issue #15 of ARB_texture_rectangle explains that the extension was specified before the
 // #extension mechanism was in place so it doesn't require explicit enabling.
@@ -56,7 +43,10 @@
         "    color = texture2DRectProj(tex, vec3(1.0));"
         "    color = texture2DRectProj(tex, vec4(1.0));"
         "}\n";
-    ASSERT_TRUE(compile(shaderString));
+    if (!compile(shaderString))
+    {
+        FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
+    }
 }
 
 // Test valid usage of the new types and builtins
@@ -71,7 +61,10 @@
         "    color = texture2DRectProj(tex, vec3(1.0));"
         "    color = texture2DRectProj(tex, vec4(1.0));"
         "}\n";
-    ASSERT_TRUE(compile(shaderString));
+    if (!compile(shaderString))
+    {
+        FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
+    }
 }
 
 // Check that it is not possible to pass a sampler2DRect where sampler2D is expected, and vice versa
@@ -84,7 +77,10 @@
         "void main() {\n"
         "    vec4 color = texture2D(tex, vec2(1.0));"
         "}\n";
-    ASSERT_FALSE(compile(shaderString1));
+    if (compile(shaderString1))
+    {
+        FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
+    }
 
     const std::string &shaderString2 =
         "#extension GL_ARB_texture_rectangle : require\n"
@@ -93,5 +89,28 @@
         "void main() {\n"
         "    vec4 color = texture2DRect(tex, vec2(1.0));"
         "}\n";
-    ASSERT_FALSE(compile(shaderString2));
+    if (compile(shaderString2))
+    {
+        FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
+    }
+}
+
+// Disabling ARB_texture_rectangle in GLSL should work, even if it is enabled by default.
+// See ARB_texture_rectangle spec: "a shader can still include all variations of #extension
+// GL_ARB_texture_rectangle in its source code"
+TEST_F(ARBTextureRectangleTest, DisableARBTextureRectangle)
+{
+    const std::string &shaderString =
+        R"(
+        #extension GL_ARB_texture_rectangle : disable
+
+        precision mediump float;
+
+        uniform sampler2DRect s;
+        void main()
+        {})";
+    if (compile(shaderString))
+    {
+        FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
+    }
 }
diff --git a/src/tests/compiler_tests/ExtensionDirective_test.cpp b/src/tests/compiler_tests/ExtensionDirective_test.cpp
new file mode 100644
index 0000000..7d6702e
--- /dev/null
+++ b/src/tests/compiler_tests/ExtensionDirective_test.cpp
@@ -0,0 +1,178 @@
+//
+// Copyright (c) 2017 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.
+//
+// ExtensionDirective_test.cpp:
+//   Miscellaneous tests for extension directives toggling functionality correctly.
+//
+
+#include "GLSLANG/ShaderLang.h"
+#include "angle_gl.h"
+#include "compiler/translator/ExtensionBehavior.h"
+#include "gtest/gtest.h"
+#include "tests/test_utils/ShaderCompileTreeTest.h"
+
+using namespace sh;
+
+class FragmentShaderExtensionDirectiveTest : public ShaderCompileTreeTest
+{
+  public:
+    FragmentShaderExtensionDirectiveTest() {}
+
+  protected:
+    ::GLenum getShaderType() const override { return GL_FRAGMENT_SHADER; }
+    ShShaderSpec getShaderSpec() const override { return SH_GLES3_1_SPEC; }
+
+    void testCompileNeedsExtensionDirective(const std::string &shader, const std::string &extension)
+    {
+        testCompileNeedsExtensionDirective(shader, extension, "");
+    }
+
+    void testCompileNeedsExtensionDirective(const std::string &shader,
+                                            const std::string &extension,
+                                            const std::string &versionDirective)
+    {
+        if (compile(versionDirective + shader))
+        {
+            FAIL()
+                << "Shader compilation without extension directive succeeded, expecting failure:\n"
+                << mInfoLog;
+        }
+        if (compile(versionDirective + getExtensionDirective(extension, sh::EBhDisable) + shader))
+        {
+            FAIL() << "Shader compilation with extension disable directive succeeded, expecting "
+                      "failure:\n"
+                   << mInfoLog;
+        }
+        if (!compile(versionDirective + getExtensionDirective(extension, sh::EBhEnable) + shader))
+        {
+            FAIL()
+                << "Shader compilation with extension enable directive failed, expecting success:\n"
+                << mInfoLog;
+        }
+
+        if (!compile(versionDirective + getExtensionDirective(extension, sh::EBhWarn) + shader))
+        {
+            FAIL()
+                << "Shader compilation with extension warn directive failed, expecting success:\n"
+                << mInfoLog;
+        }
+        else if (!hasWarning())
+        {
+            FAIL() << "Expected compilation to succeed with warning, but warning not present:\n"
+                   << mInfoLog;
+        }
+    }
+
+  private:
+    std::string getExtensionDirective(const std::string &extension, sh::TBehavior behavior)
+    {
+        std::string extensionDirective("#extension ");
+        extensionDirective += extension + " : ";
+        switch (behavior)
+        {
+            case EBhRequire:
+                extensionDirective += "require";
+                break;
+            case EBhEnable:
+                extensionDirective += "enable";
+                break;
+            case EBhWarn:
+                extensionDirective += "warn";
+                break;
+            case EBhDisable:
+                extensionDirective += "disable";
+                break;
+            default:
+                break;
+        }
+        extensionDirective += "\n";
+        return extensionDirective;
+    }
+};
+
+class OESEGLImageExternalTest : public FragmentShaderExtensionDirectiveTest
+{
+  public:
+    OESEGLImageExternalTest() {}
+
+  protected:
+    void initResources(ShBuiltInResources *resources) override
+    {
+        resources->OES_EGL_image_external = 1;
+    }
+};
+
+// OES_EGL_image_external needs to be enabled in GLSL to be able to use samplerExternalOES.
+TEST_F(OESEGLImageExternalTest, SamplerExternalOESUsageNeedsExtensionDirective)
+{
+    const std::string &shaderString =
+        R"(
+        precision mediump float;
+
+        uniform samplerExternalOES s;
+        void main()
+        {})";
+    testCompileNeedsExtensionDirective(shaderString, "GL_OES_EGL_image_external");
+}
+
+class NVEGLStreamConsumerExternalTest : public FragmentShaderExtensionDirectiveTest
+{
+  public:
+    NVEGLStreamConsumerExternalTest() {}
+
+  protected:
+    void initResources(ShBuiltInResources *resources) override
+    {
+        resources->NV_EGL_stream_consumer_external = 1;
+    }
+};
+
+// NV_EGL_stream_consumer_external needs to be enabled in GLSL to be able to use samplerExternalOES.
+TEST_F(NVEGLStreamConsumerExternalTest, SamplerExternalOESUsageNeedsExtensionDirective)
+{
+    const std::string &shaderString =
+        R"(
+        precision mediump float;
+
+        uniform samplerExternalOES s;
+        void main()
+        {})";
+    testCompileNeedsExtensionDirective(shaderString, "GL_NV_EGL_stream_consumer_external");
+}
+
+class EXTYUVTargetTest : public FragmentShaderExtensionDirectiveTest
+{
+  public:
+    EXTYUVTargetTest() {}
+
+  protected:
+    void initResources(ShBuiltInResources *resources) override { resources->EXT_YUV_target = 1; }
+};
+
+// GL_EXT_YUV_target needs to be enabled in GLSL to be able to use samplerExternal2DY2YEXT.
+TEST_F(EXTYUVTargetTest, SamplerExternal2DY2YEXTUsageNeedsExtensionDirective)
+{
+    const std::string &shaderString =
+        R"(
+        precision mediump float;
+
+        uniform __samplerExternal2DY2YEXT s;
+        void main()
+        {})";
+    testCompileNeedsExtensionDirective(shaderString, "GL_EXT_YUV_target", "#version 300 es\n");
+}
+
+// GL_EXT_YUV_target needs to be enabled in GLSL to be able to use samplerExternal2DY2YEXT.
+TEST_F(EXTYUVTargetTest, YUVLayoutNeedsExtensionDirective)
+{
+    const std::string &shaderString =
+        R"(
+        precision mediump float;
+
+        layout(yuv) out vec4 color;
+        void main()
+        {})";
+    testCompileNeedsExtensionDirective(shaderString, "GL_EXT_YUV_target", "#version 300 es\n");
+}
diff --git a/src/tests/gl_tests/GLSLTest.cpp b/src/tests/gl_tests/GLSLTest.cpp
index 8a95d81..88db5bd 100644
--- a/src/tests/gl_tests/GLSLTest.cpp
+++ b/src/tests/gl_tests/GLSLTest.cpp
@@ -2409,14 +2409,16 @@
     }
 
     const std::string fragmentShader =
-        "precision mediump float;\n"
-        "uniform samplerExternalOES tex0;\n"
-        "uniform sampler2D tex1;\n"
-        "void main(void)\n"
-        "{\n"
-        " vec2 uv = vec2(0.0, 0.0);"
-        " gl_FragColor = texture2D(tex0, uv) + texture2D(tex1, uv);\n"
-        "}\n";
+        R"(
+        #extension GL_OES_EGL_image_external : enable
+        precision mediump float;
+        uniform samplerExternalOES tex0;
+        uniform sampler2D tex1;
+        void main(void)
+        {
+            vec2 uv = vec2(0.0, 0.0);
+            gl_FragColor = texture2D(tex0, uv) + texture2D(tex1, uv);
+        })";
 
     ANGLE_GL_PROGRAM(program, mSimpleVSSource, fragmentShader);
 }