Add flags for all layout qualifiers, check for duplicates

Now, even if a qualifier has a default value, we will know that it
appeared in the text. We can use that to check for redundant qualifiers
(as is being done here), and in the IR generator to prevent any use of
certain qualifiers, depending on context. (eg, runtime effects, wrong
shader stage, on a parameter declaration, etc.)

Bug: skia:11301
Change-Id: I2cd6ad35c2b4c4d6f87ade97e80aea84dc16ee4b
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/374616
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
diff --git a/gn/sksl_tests.gni b/gn/sksl_tests.gni
index f195bcc..a62f633 100644
--- a/gn/sksl_tests.gni
+++ b/gn/sksl_tests.gni
@@ -125,6 +125,8 @@
   "/sksl/errors/InvalidOutParams.sksl",
   "/sksl/errors/InvalidToken.sksl",
   "/sksl/errors/InvalidUnary.sksl",
+  "/sksl/errors/LayoutMultiplePrimitiveTypes.sksl",
+  "/sksl/errors/LayoutRepeatedQualifiers.sksl",
   "/sksl/errors/MismatchedNumbers.sksl",
   "/sksl/errors/ModifiersInStruct.sksl",
   "/sksl/errors/OpaqueTypeAssignment.sksl",
diff --git a/resources/sksl/errors/LayoutMultiplePrimitiveTypes.sksl b/resources/sksl/errors/LayoutMultiplePrimitiveTypes.sksl
new file mode 100644
index 0000000..7b799fa
--- /dev/null
+++ b/resources/sksl/errors/LayoutMultiplePrimitiveTypes.sksl
@@ -0,0 +1,8 @@
+layout (
+    points,
+    lines,
+    line_strip,
+    lines_adjacency,
+    triangles,
+    triangle_strip
+) in;
diff --git a/resources/sksl/errors/LayoutRepeatedQualifiers.sksl b/resources/sksl/errors/LayoutRepeatedQualifiers.sksl
new file mode 100644
index 0000000..931278f
--- /dev/null
+++ b/resources/sksl/errors/LayoutRepeatedQualifiers.sksl
@@ -0,0 +1,41 @@
+layout (
+    origin_upper_left,
+    override_coverage,
+    push_constant,
+    blend_support_all_equations,
+    tracked,
+    srgb_unpremul,
+    key,
+    location = 1,
+    offset = 1,
+    binding = 1,
+    index = 1,
+    set = 1,
+    builtin = 1,
+    input_attachment_index = 1,
+    max_vertices = 1,
+    invocations = 1,
+    marker = one,
+    when = one,
+    ctype = int,
+
+    origin_upper_left,
+    override_coverage,
+    push_constant,
+    blend_support_all_equations,
+    tracked,
+    srgb_unpremul,
+    key,
+    location = 2,
+    offset = 2,
+    binding = 2,
+    index = 2,
+    set = 2,
+    builtin = 2,
+    input_attachment_index = 2,
+    max_vertices = 2,
+    invocations = 2,
+    marker = two,
+    when = two,
+    ctype = float
+) float x;
diff --git a/src/sksl/SkSLParser.cpp b/src/sksl/SkSLParser.cpp
index 13e134f..44f8aa7 100644
--- a/src/sksl/SkSLParser.cpp
+++ b/src/sksl/SkSLParser.cpp
@@ -891,93 +891,119 @@
         for (;;) {
             Token t = this->nextToken();
             String text = this->text(t);
+            auto setFlag = [&](Layout::Flag f) {
+                if (flags & f) {
+                    this->error(t, "layout qualifier '" + text + "' appears more than once");
+                }
+                flags |= f;
+            };
+            auto setPrimitive = [&](Layout::Primitive p) {
+                if (flags & Layout::kPrimitive_Flag) {
+                    this->error(t, "only one primitive-type layout qualifier is allowed");
+                }
+                flags |= Layout::kPrimitive_Flag;
+                primitive = p;
+            };
+
             auto found = layoutTokens->find(text);
             if (found != layoutTokens->end()) {
                 switch (found->second) {
+                    case LayoutToken::ORIGIN_UPPER_LEFT:
+                        setFlag(Layout::kOriginUpperLeft_Flag);
+                        break;
+                    case LayoutToken::OVERRIDE_COVERAGE:
+                        setFlag(Layout::kOverrideCoverage_Flag);
+                        break;
+                    case LayoutToken::PUSH_CONSTANT:
+                        setFlag(Layout::kPushConstant_Flag);
+                        break;
+                    case LayoutToken::BLEND_SUPPORT_ALL_EQUATIONS:
+                        setFlag(Layout::kBlendSupportAllEquations_Flag);
+                        break;
+                    case LayoutToken::TRACKED:
+                        setFlag(Layout::kTracked_Flag);
+                        break;
+                    case LayoutToken::SRGB_UNPREMUL:
+                        setFlag(Layout::kSRGBUnpremul_Flag);
+                        break;
+                    case LayoutToken::KEY:
+                        setFlag(Layout::kKey_Flag);
+                        break;
                     case LayoutToken::LOCATION:
+                        setFlag(Layout::kLocation_Flag);
                         location = this->layoutInt();
                         break;
                     case LayoutToken::OFFSET:
+                        setFlag(Layout::kOffset_Flag);
                         offset = this->layoutInt();
                         break;
                     case LayoutToken::BINDING:
+                        setFlag(Layout::kBinding_Flag);
                         binding = this->layoutInt();
                         break;
                     case LayoutToken::INDEX:
+                        setFlag(Layout::kIndex_Flag);
                         index = this->layoutInt();
                         break;
                     case LayoutToken::SET:
+                        setFlag(Layout::kSet_Flag);
                         set = this->layoutInt();
                         break;
                     case LayoutToken::BUILTIN:
+                        setFlag(Layout::kBuiltin_Flag);
                         builtin = this->layoutInt();
                         break;
                     case LayoutToken::INPUT_ATTACHMENT_INDEX:
+                        setFlag(Layout::kInputAttachmentIndex_Flag);
                         inputAttachmentIndex = this->layoutInt();
                         break;
-                    case LayoutToken::ORIGIN_UPPER_LEFT:
-                        flags |= Layout::kOriginUpperLeft_Flag;
-                        break;
-                    case LayoutToken::OVERRIDE_COVERAGE:
-                        flags |= Layout::kOverrideCoverage_Flag;
-                        break;
-                    case LayoutToken::BLEND_SUPPORT_ALL_EQUATIONS:
-                        flags |= Layout::kBlendSupportAllEquations_Flag;
-                        break;
-                    case LayoutToken::PUSH_CONSTANT:
-                        flags |= Layout::kPushConstant_Flag;
-                        break;
-                    case LayoutToken::TRACKED:
-                        flags |= Layout::kTracked_Flag;
-                        break;
-                    case LayoutToken::SRGB_UNPREMUL:
-                        flags |= Layout::kSRGBUnpremul_Flag;
-                        break;
-                    case LayoutToken::KEY:
-                        flags |= Layout::kKey_Flag;
-                        break;
                     case LayoutToken::POINTS:
-                        primitive = Layout::kPoints_Primitive;
+                        setPrimitive(Layout::kPoints_Primitive);
                         break;
                     case LayoutToken::LINES:
-                        primitive = Layout::kLines_Primitive;
+                        setPrimitive(Layout::kLines_Primitive);
                         break;
                     case LayoutToken::LINE_STRIP:
-                        primitive = Layout::kLineStrip_Primitive;
+                        setPrimitive(Layout::kLineStrip_Primitive);
                         break;
                     case LayoutToken::LINES_ADJACENCY:
-                        primitive = Layout::kLinesAdjacency_Primitive;
+                        setPrimitive(Layout::kLinesAdjacency_Primitive);
                         break;
                     case LayoutToken::TRIANGLES:
-                        primitive = Layout::kTriangles_Primitive;
+                        setPrimitive(Layout::kTriangles_Primitive);
                         break;
                     case LayoutToken::TRIANGLE_STRIP:
-                        primitive = Layout::kTriangleStrip_Primitive;
+                        setPrimitive(Layout::kTriangleStrip_Primitive);
                         break;
                     case LayoutToken::TRIANGLES_ADJACENCY:
-                        primitive = Layout::kTrianglesAdjacency_Primitive;
+                        setPrimitive(Layout::kTrianglesAdjacency_Primitive);
                         break;
                     case LayoutToken::MAX_VERTICES:
+                        setFlag(Layout::kMaxVertices_Flag);
                         maxVertices = this->layoutInt();
                         break;
                     case LayoutToken::INVOCATIONS:
+                        setFlag(Layout::kInvocations_Flag);
                         invocations = this->layoutInt();
                         break;
                     case LayoutToken::MARKER:
+                        setFlag(Layout::kMarker_Flag);
                         marker = this->layoutCode();
                         break;
                     case LayoutToken::WHEN:
+                        setFlag(Layout::kWhen_Flag);
                         when = this->layoutCode();
                         break;
                     case LayoutToken::CTYPE:
+                        setFlag(Layout::kCType_Flag);
                         ctype = this->layoutCType();
                         break;
                     default:
-                        this->error(t, ("'" + text + "' is not a valid layout qualifier").c_str());
+                        this->error(t, "'" + text + "' is not a valid layout qualifier");
                         break;
                 }
             } else {
-                this->error(t, ("'" + text + "' is not a valid layout qualifier").c_str());
+                this->error(t, "'" + text + "' is not a valid layout qualifier");
             }
             if (this->checkNext(Token::Kind::TK_RPAREN)) {
                 break;
diff --git a/src/sksl/generated/sksl_fp.dehydrated.sksl b/src/sksl/generated/sksl_fp.dehydrated.sksl
index 58bee99..2f8ecb3 100644
--- a/src/sksl/generated/sksl_fp.dehydrated.sksl
+++ b/src/sksl/generated/sksl_fp.dehydrated.sksl
@@ -1,6 +1,7 @@
-static uint8_t SKSL_INCLUDE_sksl_fp[] = {76,1,
+static uint8_t SKSL_INCLUDE_sksl_fp[] = {77,1,
 14,71,114,67,108,105,112,69,100,103,101,84,121,112,101,
 12,80,77,67,111,110,118,101,114,115,105,111,110,
+0,
 12,115,107,95,70,114,97,103,67,111,111,114,100,
 6,102,108,111,97,116,52,
 3,105,110,116,
@@ -34,93 +35,93 @@
 15,2,0,17,0,
 46,3,0,
 30,
-5,15,0,2,30,0,
-43,4,0,43,0,0,
+29,0,16,0,0,255,255,255,255,255,15,0,255,255,255,255,30,0,30,0,0,2,31,0,
+43,4,0,44,0,0,
 0,5,0,
-43,6,0,50,0,1,
+43,6,0,51,0,1,
 46,7,0,
 30,
-5,20,0,4,54,0,
+29,0,16,0,0,255,255,255,255,255,20,0,255,255,255,255,30,0,30,0,0,4,55,0,
 40,5,0,0,
 0,8,0,
 40,4,0,1,
 46,9,0,
 30,
-5,15,39,0,68,0,
+29,0,16,0,0,255,255,255,255,255,15,39,255,255,255,255,30,0,30,0,0,0,69,0,
 40,8,0,0,
 46,10,0,
 30,
-5,15,39,0,84,0,
-43,11,0,101,0,0,
+29,0,16,0,0,255,255,255,255,255,15,39,255,255,255,255,30,0,30,0,0,0,85,0,
+43,11,0,102,0,0,
 46,12,0,
 30,
-5,15,39,0,107,0,
+29,0,16,0,0,255,255,255,255,255,15,39,255,255,255,255,30,0,30,0,0,0,108,0,
 40,11,0,0,
 46,13,0,
 30,
-5,15,39,0,127,0,
+29,0,16,0,0,255,255,255,255,255,15,39,255,255,255,255,30,0,30,0,0,0,128,0,
 40,11,0,0,
 46,14,0,
 30,
-5,27,39,0,152,0,
-43,15,0,161,0,0,
+29,0,16,0,0,255,255,255,255,255,27,39,255,255,255,255,30,0,30,0,0,0,153,0,
+43,15,0,162,0,0,
 46,16,0,
 30,
-5,28,39,0,166,0,
+29,0,16,0,0,255,255,255,255,255,28,39,255,255,255,255,30,0,30,0,0,0,167,0,
 40,15,0,0,
 46,17,0,
-9,176,0,
-43,18,0,179,0,3,
+9,177,0,
+43,18,0,180,0,3,
 23,19,0,
-9,197,0,1,17,0,
+9,198,0,1,17,0,
 40,11,0,
 46,20,0,
-9,176,0,
+9,177,0,
 40,18,0,3,
 46,21,0,
-9,204,0,
-43,22,0,214,0,3,
+9,205,0,
+43,22,0,215,0,3,
 45,23,0,2,
 40,19,0,
 23,24,0,
-9,197,0,2,20,0,21,0,
+9,198,0,2,20,0,21,0,
 40,11,0,
 40,24,0,
 46,25,0,
-9,176,0,
+9,177,0,
 40,18,0,3,
 46,26,0,
-9,223,0,
-43,27,0,230,0,3,
+9,224,0,
+43,27,0,231,0,3,
 45,28,0,3,
 40,19,0,
 40,24,0,
 23,29,0,
-9,197,0,2,25,0,26,0,
+9,198,0,2,25,0,26,0,
 40,11,0,
 40,29,0,
 46,30,0,
-9,176,0,
+9,177,0,
 40,18,0,3,
 46,31,0,
-9,237,0,
+9,238,0,
 40,11,0,3,
 45,32,0,4,
 40,19,0,
 40,24,0,
 40,29,0,
 23,33,0,
-9,197,0,2,30,0,31,0,
+9,198,0,2,30,0,31,0,
 40,11,0,
 40,33,0,
 46,34,0,
-9,176,0,
+9,177,0,
 40,18,0,3,
 46,35,0,
-9,237,0,
+9,238,0,
 40,11,0,3,
 46,36,0,
-9,204,0,
+9,205,0,
 40,22,0,3,
 45,37,0,5,
 40,19,0,
@@ -128,17 +129,17 @@
 40,29,0,
 40,33,0,
 23,38,0,
-9,197,0,3,34,0,35,0,36,0,
+9,198,0,3,34,0,35,0,36,0,
 40,11,0,
 40,38,0,
 46,39,0,
-9,176,0,
+9,177,0,
 40,18,0,3,
 46,40,0,
-9,237,0,
+9,238,0,
 40,11,0,3,
 46,41,0,
-9,223,0,
+9,224,0,
 40,27,0,3,
 45,42,0,6,
 40,19,0,
@@ -147,7 +148,7 @@
 40,33,0,
 40,38,0,
 23,43,0,
-9,197,0,3,39,0,40,0,41,0,
+9,198,0,3,39,0,40,0,41,0,
 40,11,0,
 40,43,0,11,0,
 0,0,
@@ -166,23 +167,23 @@
 42,5,0,
 46,44,0,
 30,
-8,1,243,0,
+8,1,244,0,
 40,1,0,0,
 46,45,0,
 30,
-8,1,251,0,
+8,1,252,0,
 40,1,0,0,
 46,46,0,
 30,
-8,1,3,1,
+8,1,4,1,
 40,1,0,0,
 46,47,0,
 30,
-8,1,18,1,
+8,1,19,1,
 40,1,0,0,
 46,48,0,
 30,
-8,1,33,1,
+8,1,34,1,
 40,1,0,0,5,0,
 1,0,
 0,0,
@@ -193,15 +194,15 @@
 42,3,0,
 46,49,0,
 30,
-8,1,39,1,
+8,1,40,1,
 40,2,0,0,
 46,50,0,
 30,
-8,1,49,1,
+8,1,50,1,
 40,2,0,0,
 46,51,0,
 30,
-8,1,61,1,
+8,1,62,1,
 40,2,0,0,3,0,
 2,0,
 0,0,
diff --git a/src/sksl/generated/sksl_frag.dehydrated.sksl b/src/sksl/generated/sksl_frag.dehydrated.sksl
index a5671f9..fce9f6a 100644
--- a/src/sksl/generated/sksl_frag.dehydrated.sksl
+++ b/src/sksl/generated/sksl_frag.dehydrated.sksl
@@ -1,4 +1,5 @@
 static uint8_t SKSL_INCLUDE_sksl_frag[] = {142,0,
+0,
 12,115,107,95,70,114,97,103,67,111,111,114,100,
 6,102,108,111,97,116,52,
 12,115,107,95,67,108,111,99,107,119,105,115,101,
@@ -7,7 +8,6 @@
 13,115,107,95,83,97,109,112,108,101,77,97,115,107,
 24,103,108,95,83,101,99,111,110,100,97,114,121,70,114,97,103,67,111,108,111,114,69,88,84,
 5,104,97,108,102,52,
-0,
 12,115,107,95,70,114,97,103,67,111,108,111,114,
 16,115,107,95,76,97,115,116,70,114,97,103,67,111,108,111,114,
 8,115,107,95,87,105,100,116,104,
@@ -16,37 +16,37 @@
 42,9,0,
 46,1,0,
 30,
-5,15,0,2,2,0,
-43,2,0,15,0,0,
+29,0,16,0,0,255,255,255,255,255,15,0,255,255,255,255,2,0,2,0,0,2,3,0,
+43,2,0,16,0,0,
 46,3,0,
 30,
-5,17,0,2,22,0,
-43,4,0,35,0,0,
+29,0,16,0,0,255,255,255,255,255,17,0,255,255,255,255,2,0,2,0,0,2,23,0,
+43,4,0,36,0,0,
 0,5,0,
-43,6,0,40,0,1,
+43,6,0,41,0,1,
 46,7,0,
 30,
-5,20,0,4,44,0,
+29,0,16,0,0,255,255,255,255,255,20,0,255,255,255,255,2,0,2,0,0,4,45,0,
 40,5,0,0,
 46,8,0,
 30,
-5,15,39,4,58,0,
-43,9,0,83,0,0,
+29,0,16,0,0,255,255,255,255,255,15,39,255,255,255,255,2,0,2,0,0,4,59,0,
+43,9,0,84,0,0,
 46,10,0,
 30,
-29,0,0,0,0,0,255,255,0,255,17,39,255,255,255,255,89,0,89,0,0,4,90,0,
+29,128,20,0,0,0,255,255,0,255,17,39,255,255,255,255,2,0,2,0,0,4,90,0,
 40,9,0,0,
 46,11,0,
 30,
-5,24,39,0,103,0,
+29,0,16,0,0,255,255,255,255,255,24,39,255,255,255,255,2,0,2,0,0,0,103,0,
 40,9,0,0,
 46,12,0,
 30,
-5,27,39,0,120,0,
+29,0,16,0,0,255,255,255,255,255,27,39,255,255,255,255,2,0,2,0,0,0,120,0,
 43,13,0,129,0,0,
 46,14,0,
 30,
-5,28,39,0,134,0,
+29,0,16,0,0,255,255,255,255,255,28,39,255,255,255,255,2,0,2,0,0,0,134,0,
 40,13,0,0,8,0,
 4,0,
 1,0,
diff --git a/src/sksl/generated/sksl_geom.dehydrated.sksl b/src/sksl/generated/sksl_geom.dehydrated.sksl
index 15f3222..7a32178 100644
--- a/src/sksl/generated/sksl_geom.dehydrated.sksl
+++ b/src/sksl/generated/sksl_geom.dehydrated.sksl
@@ -1,5 +1,6 @@
 static uint8_t SKSL_INCLUDE_sksl_geom[] = {134,0,
 12,115,107,95,80,101,114,86,101,114,116,101,120,
+0,
 11,115,107,95,80,111,115,105,116,105,111,110,
 6,102,108,111,97,116,52,
 12,115,107,95,80,111,105,110,116,83,105,122,101,
@@ -12,54 +13,53 @@
 18,69,110,100,83,116,114,101,97,109,80,114,105,109,105,116,105,118,101,
 10,69,109,105,116,86,101,114,116,101,120,
 12,69,110,100,80,114,105,109,105,116,105,118,101,
-0,
 42,12,0,
 37,1,0,2,0,2,
 30,
-5,0,0,0,15,0,
-43,2,0,27,0,
+29,0,16,0,0,255,255,255,255,255,0,0,255,255,255,255,15,0,15,0,0,0,16,0,
+43,2,0,28,0,
 30,
-5,1,0,0,34,0,
-43,3,0,47,0,
+29,0,16,0,0,255,255,255,255,255,1,0,255,255,255,255,15,0,15,0,0,0,35,0,
+43,3,0,48,0,
 46,4,0,
 30,
-5,18,39,2,53,0,
+29,0,16,0,0,255,255,255,255,255,18,39,255,255,255,255,15,0,15,0,0,2,54,0,
 0,5,0,
 40,1,0,255,0,
 37,6,0,2,0,2,
 30,
-5,0,0,0,15,0,
+29,0,16,0,0,255,255,255,255,255,0,0,255,255,255,255,15,0,15,0,0,0,16,0,
 40,2,0,
 30,
-5,1,0,0,34,0,
+29,0,16,0,0,255,255,255,255,255,1,0,255,255,255,255,15,0,15,0,0,0,35,0,
 40,3,0,
 46,7,0,
 30,
-5,23,39,4,2,0,
+29,0,16,0,0,255,255,255,255,255,23,39,255,255,255,255,15,0,15,0,0,4,2,0,
 40,6,0,0,
 17,7,0,0,
 17,7,0,1,
 46,8,0,
-9,59,0,
-43,9,0,66,0,3,
+9,60,0,
+43,9,0,67,0,3,
 23,10,0,
 30,
-8,64,70,0,1,8,0,
-43,11,0,87,0,
+8,64,71,0,1,8,0,
+43,11,0,88,0,
 46,12,0,
-9,59,0,
+9,60,0,
 40,9,0,3,
 23,13,0,
 30,
-8,64,92,0,1,12,0,
+8,64,93,0,1,12,0,
 40,11,0,
 23,14,0,
 30,
-8,64,111,0,0,
+8,64,112,0,0,
 40,11,0,
 23,15,0,
 30,
-8,64,122,0,0,
+8,64,123,0,0,
 40,11,0,7,0,
 7,0,
 10,0,
@@ -70,8 +70,8 @@
 1,0,
 12,
 27,
-40,4,0,2,0,53,0,255,
+40,4,0,2,0,54,0,255,
 27,
-40,7,0,2,0,135,0,0,
+40,7,0,2,0,15,0,0,
 13,};
 static constexpr size_t SKSL_INCLUDE_sksl_geom_LENGTH = sizeof(SKSL_INCLUDE_sksl_geom);
diff --git a/src/sksl/generated/sksl_runtime.dehydrated.sksl b/src/sksl/generated/sksl_runtime.dehydrated.sksl
index 2c1b79e..00580c9 100644
--- a/src/sksl/generated/sksl_runtime.dehydrated.sksl
+++ b/src/sksl/generated/sksl_runtime.dehydrated.sksl
@@ -1,4 +1,5 @@
-static uint8_t SKSL_INCLUDE_sksl_runtime[] = {87,0,
+static uint8_t SKSL_INCLUDE_sksl_runtime[] = {88,0,
+0,
 12,115,107,95,70,114,97,103,67,111,111,114,100,
 6,102,108,111,97,116,52,
 2,102,112,
@@ -12,37 +13,37 @@
 42,11,0,
 46,1,0,
 30,
-5,15,0,0,2,0,
-43,2,0,15,0,0,
+29,0,16,0,0,255,255,255,255,255,15,0,255,255,255,255,2,0,2,0,0,0,3,0,
+43,2,0,16,0,0,
 46,3,0,
-9,22,0,
-43,4,0,25,0,3,
+9,23,0,
+43,4,0,26,0,3,
 23,5,0,
-9,43,0,1,3,0,
-43,6,0,50,0,
+9,44,0,1,3,0,
+43,6,0,51,0,
 46,7,0,
-9,22,0,
+9,23,0,
 40,4,0,3,
 46,8,0,
-9,56,0,
-43,9,0,63,0,3,
+9,57,0,
+43,9,0,64,0,3,
 45,10,0,2,
 40,5,0,
 23,11,0,
-9,43,0,2,7,0,8,0,
+9,44,0,2,7,0,8,0,
 40,6,0,
 40,11,0,
 46,12,0,
-9,22,0,
+9,23,0,
 40,4,0,3,
 46,13,0,
-9,70,0,
-43,14,0,80,0,3,
+9,71,0,
+43,14,0,81,0,3,
 45,15,0,3,
 40,5,0,
 40,11,0,
 23,16,0,
-9,43,0,2,12,0,13,0,
+9,44,0,2,12,0,13,0,
 40,6,0,
 40,16,0,2,0,
 9,0,
diff --git a/src/sksl/generated/sksl_vert.dehydrated.sksl b/src/sksl/generated/sksl_vert.dehydrated.sksl
index 16a5460..ecd5106 100644
--- a/src/sksl/generated/sksl_vert.dehydrated.sksl
+++ b/src/sksl/generated/sksl_vert.dehydrated.sksl
@@ -1,5 +1,6 @@
 static uint8_t SKSL_INCLUDE_sksl_vert[] = {82,0,
 12,115,107,95,80,101,114,86,101,114,116,101,120,
+0,
 11,115,107,95,80,111,115,105,116,105,111,110,
 6,102,108,111,97,116,52,
 12,115,107,95,80,111,105,110,116,83,105,122,101,
@@ -7,15 +8,14 @@
 11,115,107,95,86,101,114,116,101,120,73,68,
 3,105,110,116,
 13,115,107,95,73,110,115,116,97,110,99,101,73,68,
-0,
 42,6,0,
 37,1,0,2,0,2,
 30,
-5,0,0,0,15,0,
-43,2,0,27,0,
+29,0,16,0,0,255,255,255,255,255,0,0,255,255,255,255,15,0,15,0,0,0,16,0,
+43,2,0,28,0,
 30,
-5,1,0,0,34,0,
-43,3,0,47,0,
+29,0,16,0,0,255,255,255,255,255,1,0,255,255,255,255,15,0,15,0,0,0,35,0,
+43,3,0,48,0,
 46,4,0,
 30,
 8,4,2,0,
@@ -24,11 +24,11 @@
 17,4,0,1,
 46,5,0,
 30,
-5,42,0,2,53,0,
-43,6,0,65,0,0,
+29,0,16,0,0,255,255,255,255,255,42,0,255,255,255,255,15,0,15,0,0,2,54,0,
+43,6,0,66,0,0,
 46,7,0,
 30,
-5,43,0,2,69,0,
+29,0,16,0,0,255,255,255,255,255,43,0,255,255,255,255,15,0,15,0,0,2,70,0,
 40,6,0,0,4,0,
 5,0,
 3,0,
@@ -36,7 +36,7 @@
 4,0,
 12,
 27,
-40,4,0,2,0,83,0,0,
+40,4,0,2,0,15,0,0,
 48,
 47,5,0,
 40,6,0,0,
diff --git a/src/sksl/ir/SkSLLayout.h b/src/sksl/ir/SkSLLayout.h
index 1e6349d..59a646f 100644
--- a/src/sksl/ir/SkSLLayout.h
+++ b/src/sksl/ir/SkSLLayout.h
@@ -27,6 +27,21 @@
         kTracked_Flag                    = 1 <<  4,
         kSRGBUnpremul_Flag               = 1 <<  5,
         kKey_Flag                        = 1 <<  6,
+
+        // These flags indicate if the qualifier appeared, regardless of the accompanying value.
+        kLocation_Flag                   = 1 <<  7,
+        kOffset_Flag                     = 1 <<  8,
+        kBinding_Flag                    = 1 <<  9,
+        kIndex_Flag                      = 1 << 10,
+        kSet_Flag                        = 1 << 11,
+        kBuiltin_Flag                    = 1 << 12,
+        kInputAttachmentIndex_Flag       = 1 << 13,
+        kPrimitive_Flag                  = 1 << 14,
+        kMaxVertices_Flag                = 1 << 15,
+        kInvocations_Flag                = 1 << 16,
+        kMarker_Flag                     = 1 << 17,
+        kWhen_Flag                       = 1 << 18,
+        kCType_Flag                      = 1 << 19,
     };
 
     enum Primitive {
diff --git a/tests/sksl/errors/LayoutMultiplePrimitiveTypes.glsl b/tests/sksl/errors/LayoutMultiplePrimitiveTypes.glsl
new file mode 100644
index 0000000..cb32a4e
--- /dev/null
+++ b/tests/sksl/errors/LayoutMultiplePrimitiveTypes.glsl
@@ -0,0 +1,8 @@
+### Compilation failed:
+
+error: 3: only one primitive-type layout qualifier is allowed
+error: 4: only one primitive-type layout qualifier is allowed
+error: 5: only one primitive-type layout qualifier is allowed
+error: 6: only one primitive-type layout qualifier is allowed
+error: 7: only one primitive-type layout qualifier is allowed
+5 errors
diff --git a/tests/sksl/errors/LayoutRepeatedQualifiers.glsl b/tests/sksl/errors/LayoutRepeatedQualifiers.glsl
new file mode 100644
index 0000000..3211b68
--- /dev/null
+++ b/tests/sksl/errors/LayoutRepeatedQualifiers.glsl
@@ -0,0 +1,22 @@
+### Compilation failed:
+
+error: 22: layout qualifier 'origin_upper_left' appears more than once
+error: 23: layout qualifier 'override_coverage' appears more than once
+error: 24: layout qualifier 'push_constant' appears more than once
+error: 25: layout qualifier 'blend_support_all_equations' appears more than once
+error: 26: layout qualifier 'tracked' appears more than once
+error: 27: layout qualifier 'srgb_unpremul' appears more than once
+error: 28: layout qualifier 'key' appears more than once
+error: 29: layout qualifier 'location' appears more than once
+error: 30: layout qualifier 'offset' appears more than once
+error: 31: layout qualifier 'binding' appears more than once
+error: 32: layout qualifier 'index' appears more than once
+error: 33: layout qualifier 'set' appears more than once
+error: 34: layout qualifier 'builtin' appears more than once
+error: 35: layout qualifier 'input_attachment_index' appears more than once
+error: 36: layout qualifier 'max_vertices' appears more than once
+error: 37: layout qualifier 'invocations' appears more than once
+error: 38: layout qualifier 'marker' appears more than once
+error: 39: layout qualifier 'when' appears more than once
+error: 40: layout qualifier 'ctype' appears more than once
+19 errors