+ } else {
+ char *consumer_name = consumer_var->name;
+
+ if (nonarray_stage_to_array_stage &&
+ consumer_var->interface_type != NULL &&
+ consumer_var->interface_type->is_array() &&
+ !is_gl_identifier(consumer_var->name)) {
+ const size_t name_len = strlen(consumer_var->name);
+
+ if (name_len >= name_buffer_size) {
+ free(name_buffer);
+
+ name_buffer_size = name_len + 1;
+ name_buffer = malloc(name_buffer_size);
+ if (name_buffer == NULL) {
+ valid = false;
+ goto out;
+ }
+ }
+
+ consumer_name = (char *) name_buffer;
+
+ char *s = strchr(consumer_var->name, '[');
+ if (s == NULL) {
+ valid = false;
+ goto out;
+ }
+
+ char *t = strchr(s, ']');
+ if (t == NULL) {
+ valid = false;
+ goto out;
+ }
+
+ assert(t[1] == '.' || t[1] == '[');
+
+ const ptrdiff_t base_name_len = s - consumer_var->name;
+
+ memcpy(consumer_name, consumer_var->name, base_name_len);
+ strcpy(consumer_name + base_name_len, t + 1);
+ }
+
+ for (unsigned j = 0; j < num_outputs; j++) {
+ const gl_shader_variable *const var = outputs[j];
+
+ if (!var->explicit_location &&
+ strcmp(consumer_name, var->name) == 0) {
+ producer_var = var;
+ match_index = j;
+ break;
+ }
+ }
+ }
+
+ /* Section 7.4.1 (Shader Interface Matching) of the OpenGL ES 3.1 spec
+ * says:
+ *
+ * - An output variable is considered to match an input variable in
+ * the subsequent shader if:
+ *
+ * - the two variables match in name, type, and qualification; or
+ *
+ * - the two variables are declared with the same location
+ * qualifier and match in type and qualification.
+ */
+ if (producer_var == NULL) {
+ valid = false;
+ goto out;
+ }
+
+ /* An output cannot match more than one input, so remove the output from
+ * the set of possible outputs.
+ */
+ outputs[match_index] = NULL;
+ num_outputs--;
+ if (match_index < num_outputs)
+ outputs[match_index] = outputs[num_outputs];
+
+ /* Section 9.2.2 (Separable Programs) of the GLSL ES spec says:
+ *
+ * Qualifier Class| Qualifier |in/out
+ * ---------------+-------------+------
+ * Storage | in |
+ * | out | N/A
+ * | uniform |
+ * ---------------+-------------+------
+ * Auxiliary | centroid | No
+ * ---------------+-------------+------
+ * | location | Yes
+ * | Block layout| N/A
+ * | binding | N/A
+ * | offset | N/A
+ * | format | N/A
+ * ---------------+-------------+------
+ * Interpolation | smooth |
+ * | flat | Yes
+ * ---------------+-------------+------
+ * | lowp |
+ * Precision | mediump | Yes
+ * | highp |
+ * ---------------+-------------+------
+ * Variance | invariant | No
+ * ---------------+-------------+------
+ * Memory | all | N/A
+ *
+ * Note that location mismatches are detected by the loops above that
+ * find the producer variable that goes with the consumer variable.
+ */
+ if (nonarray_stage_to_array_stage) {
+ if (!consumer_var->type->is_array() ||
+ consumer_var->type->fields.array != producer_var->type) {
+ valid = false;
+ goto out;
+ }
+
+ if (consumer_var->interface_type != NULL) {
+ if (!consumer_var->interface_type->is_array() ||
+ consumer_var->interface_type->fields.array != producer_var->interface_type) {
+ valid = false;
+ goto out;
+ }
+ } else if (producer_var->interface_type != NULL) {
+ valid = false;
+ goto out;
+ }
+ } else {
+ if (producer_var->type != consumer_var->type) {
+ valid = false;
+ goto out;
+ }
+
+ if (producer_var->interface_type != consumer_var->interface_type) {
+ valid = false;
+ goto out;
+ }
+ }
+
+ if (producer_var->interpolation != consumer_var->interpolation) {
+ valid = false;
+ goto out;
+ }
+
+ if (producer_var->precision != consumer_var->precision) {
+ valid = false;
+ goto out;
+ }
+
+ if (producer_var->outermost_struct_type != consumer_var->outermost_struct_type) {
+ valid = false;
+ goto out;