glsl: Check that interpolation quals only apply to vertex ins and fragment outs
[mesa.git] / src / glsl / ast_to_hir.cpp
index 1f4972cfca20a800e7513ebdc9355e7761697b7d..e5a21fd816810ccb5215f491e6161ccc223c26a5 100644 (file)
@@ -60,7 +60,7 @@ void
 _mesa_ast_to_hir(exec_list *instructions, struct _mesa_glsl_parse_state *state)
 {
    _mesa_glsl_initialize_variables(instructions, state);
-   _mesa_glsl_initialize_functions(instructions, state);
+   _mesa_glsl_initialize_functions(state);
 
    state->symbols->language_version = state->language_version;
 
@@ -598,17 +598,15 @@ ir_rvalue *
 validate_assignment(struct _mesa_glsl_parse_state *state,
                    const glsl_type *lhs_type, ir_rvalue *rhs)
 {
-   const glsl_type *rhs_type = rhs->type;
-
    /* If there is already some error in the RHS, just return it.  Anything
     * else will lead to an avalanche of error message back to the user.
     */
-   if (rhs_type->is_error())
+   if (rhs->type->is_error())
       return rhs;
 
    /* If the types are identical, the assignment can trivially proceed.
     */
-   if (rhs_type == lhs_type)
+   if (rhs->type == lhs_type)
       return rhs;
 
    /* If the array element types are the same and the size of the LHS is zero,
@@ -625,8 +623,7 @@ validate_assignment(struct _mesa_glsl_parse_state *state,
 
    /* Check for implicit conversion in GLSL 1.20 */
    if (apply_implicit_conversion(lhs_type, rhs, state)) {
-      rhs_type = rhs->type;
-      if (rhs_type == lhs_type)
+      if (rhs->type == lhs_type)
         return rhs;
    }
 
@@ -1567,18 +1564,38 @@ ast_expression::hir(exec_list *instructions,
         }
       }
 
-      /* From section 4.1.7 of the GLSL 1.30 spec:
+      /* From page 23 (29 of the PDF) of the GLSL 1.30 spec:
+       *
        *    "Samplers aggregated into arrays within a shader (using square
        *    brackets [ ]) can only be indexed with integral constant
        *    expressions [...]."
+       *
+       * This restriction was added in GLSL 1.30.  Shaders using earlier version
+       * of the language should not be rejected by the compiler front-end for
+       * using this construct.  This allows useful things such as using a loop
+       * counter as the index to an array of samplers.  If the loop in unrolled,
+       * the code should compile correctly.  Instead, emit a warning.
        */
       if (array->type->is_array() &&
           array->type->element_type()->is_sampler() &&
           const_index == NULL) {
 
-         _mesa_glsl_error(&loc, state, "sampler arrays can only be indexed "
-                          "with constant expressions");
-         error_emitted = true;
+        if (state->language_version == 100) {
+           _mesa_glsl_warning(&loc, state,
+                              "sampler arrays indexed with non-constant "
+                              "expressions is optional in GLSL ES 1.00");
+        } else if (state->language_version < 130) {
+           _mesa_glsl_warning(&loc, state,
+                              "sampler arrays indexed with non-constant "
+                              "expressions is forbidden in GLSL 1.30 and "
+                              "later");
+        } else {
+           _mesa_glsl_error(&loc, state,
+                            "sampler arrays indexed with non-constant "
+                            "expressions is forbidden in GLSL 1.30 and "
+                            "later");
+           error_emitted = true;
+        }
       }
 
       if (error_emitted)
@@ -1606,6 +1623,7 @@ ast_expression::hir(exec_list *instructions,
       result = new(ctx) ir_dereference_variable(var);
 
       if (var != NULL) {
+        var->used = true;
         type = result->type;
       } else {
         _mesa_glsl_error(& loc, state, "`%s' undeclared",
@@ -1780,8 +1798,16 @@ apply_type_qualifier_to_variable(const struct ast_type_qualifier *qual,
                                 struct _mesa_glsl_parse_state *state,
                                 YYLTYPE *loc)
 {
-   if (qual->flags.q.invariant)
-      var->invariant = 1;
+   if (qual->flags.q.invariant) {
+      if (var->used) {
+        _mesa_glsl_error(loc, state,
+                         "variable `%s' may not be redeclared "
+                         "`invariant' after being used",
+                         var->name);
+      } else {
+        var->invariant = 1;
+      }
+   }
 
    /* FINISHME: Mark 'in' variables at global scope as read-only. */
    if (qual->flags.q.constant || qual->flags.q.attribute
@@ -1835,6 +1861,23 @@ apply_type_qualifier_to_variable(const struct ast_type_qualifier *qual,
    else if (qual->flags.q.uniform)
       var->mode = ir_var_uniform;
 
+   if (state->all_invariant && (state->current_function == NULL)) {
+      switch (state->target) {
+      case vertex_shader:
+        if (var->mode == ir_var_out)
+           var->invariant = true;
+        break;
+      case geometry_shader:
+        if ((var->mode == ir_var_in) || (var->mode == ir_var_out))
+           var->invariant = true;
+        break;
+      case fragment_shader:
+        if (var->mode == ir_var_in)
+           var->invariant = true;
+        break;
+      }
+   }
+
    if (qual->flags.q.flat)
       var->interpolation = ir_var_flat;
    else if (qual->flags.q.noperspective)
@@ -1914,6 +1957,52 @@ apply_type_qualifier_to_variable(const struct ast_type_qualifier *qual,
       }
    }
 
+   /* Does the declaration use the 'layout' keyword?
+    */
+   const bool uses_layout = qual->flags.q.pixel_center_integer
+      || qual->flags.q.origin_upper_left
+      || qual->flags.q.explicit_location;
+
+   /* Does the declaration use the deprecated 'attribute' or 'varying'
+    * keywords?
+    */
+   const bool uses_deprecated_qualifier = qual->flags.q.attribute
+      || qual->flags.q.varying;
+
+   /* Is the 'layout' keyword used with parameters that allow relaxed checking.
+    * Many implementations of GL_ARB_fragment_coord_conventions_enable and some
+    * implementations (only Mesa?) GL_ARB_explicit_attrib_location_enable
+    * allowed the layout qualifier to be used with 'varying' and 'attribute'.
+    * These extensions and all following extensions that add the 'layout'
+    * keyword have been modified to require the use of 'in' or 'out'.
+    *
+    * The following extension do not allow the deprecated keywords:
+    *
+    *    GL_AMD_conservative_depth
+    *    GL_ARB_gpu_shader5
+    *    GL_ARB_separate_shader_objects
+    *    GL_ARB_tesselation_shader
+    *    GL_ARB_transform_feedback3
+    *    GL_ARB_uniform_buffer_object
+    *
+    * It is unknown whether GL_EXT_shader_image_load_store or GL_NV_gpu_shader5
+    * allow layout with the deprecated keywords.
+    */
+   const bool relaxed_layout_qualifier_checking =
+      state->ARB_fragment_coord_conventions_enable;
+
+   if (uses_layout && uses_deprecated_qualifier) {
+      if (relaxed_layout_qualifier_checking) {
+        _mesa_glsl_warning(loc, state,
+                           "`layout' qualifier may not be used with "
+                           "`attribute' or `varying'");
+      } else {
+        _mesa_glsl_error(loc, state,
+                         "`layout' qualifier may not be used with "
+                         "`attribute' or `varying'");
+      }
+   }
+
    if (var->type->is_array() && state->language_version != 110) {
       var->array_lvalue = true;
    }
@@ -1971,6 +2060,11 @@ ast_declarator_list::hir(exec_list *instructions,
            _mesa_glsl_error(& loc, state,
                             "`%s' cannot be marked invariant, fragment shader "
                             "inputs only\n", decl->identifier);
+        } else if (earlier->used) {
+           _mesa_glsl_error(& loc, state,
+                            "variable `%s' may not be redeclared "
+                            "`invariant' after being used",
+                            earlier->name);
         } else {
            earlier->invariant = true;
         }
@@ -2040,20 +2134,23 @@ ast_declarator_list::hir(exec_list *instructions,
        *
        *     Local variables can only use the qualifier const."
        *
-       * This is relaxed in GLSL 1.30.
+       * This is relaxed in GLSL 1.30.  It is also relaxed by any extension
+       * that adds the 'layout' keyword.
        */
-      if (state->language_version < 120) {
+      if ((state->language_version < 130)
+         && !state->ARB_explicit_attrib_location_enable
+         && !state->ARB_fragment_coord_conventions_enable) {
         if (this->type->qualifier.flags.q.out) {
            _mesa_glsl_error(& loc, state,
                             "`out' qualifier in declaration of `%s' "
-                            "only valid for function parameters in GLSL 1.10.",
-                            decl->identifier);
+                            "only valid for function parameters in %s.",
+                            decl->identifier, state->version_string);
         }
         if (this->type->qualifier.flags.q.in) {
            _mesa_glsl_error(& loc, state,
                             "`in' qualifier in declaration of `%s' "
-                            "only valid for function parameters in GLSL 1.10.",
-                            decl->identifier);
+                            "only valid for function parameters in %s.",
+                            decl->identifier, state->version_string);
         }
         /* FINISHME: Test for other invalid qualifiers. */
       }
@@ -2161,6 +2258,86 @@ ast_declarator_list::hir(exec_list *instructions,
         }
       }
 
+      /* Integer vertex outputs must be qualified with 'flat'.
+       *
+       * From section 4.3.6 of the GLSL 1.30 spec:
+       *    "If a vertex output is a signed or unsigned integer or integer
+       *    vector, then it must be qualified with the interpolation qualifier
+       *    flat."
+       */
+      if (state->language_version >= 130
+          && state->target == vertex_shader
+          && state->current_function == NULL
+          && var->type->is_integer()
+          && var->mode == ir_var_out
+          && var->interpolation != ir_var_flat) {
+
+         _mesa_glsl_error(&loc, state, "If a vertex output is an integer, "
+                          "then it must be qualified with 'flat'");
+      }
+
+
+      /* Interpolation qualifiers cannot be applied to 'centroid' and
+       * 'centroid varying'.
+       *
+       * From page 29 (page 35 of the PDF) of the GLSL 1.30 spec:
+       *    "interpolation qualifiers may only precede the qualifiers in,
+       *    centroid in, out, or centroid out in a declaration. They do not apply
+       *    to the deprecated storage qualifiers varying or centroid varying."
+       */
+      if (state->language_version >= 130
+          && this->type->qualifier.has_interpolation()
+          && this->type->qualifier.flags.q.varying) {
+
+         const char *i = this->type->qualifier.interpolation_string();
+         assert(i != NULL);
+         const char *s;
+         if (this->type->qualifier.flags.q.centroid)
+            s = "centroid varying";
+         else
+            s = "varying";
+
+         _mesa_glsl_error(&loc, state,
+                          "qualifier '%s' cannot be applied to the "
+                          "deprecated storage qualifier '%s'", i, s);
+      }
+
+
+      /* Interpolation qualifiers can only apply to vertex shader outputs and
+       * fragment shader inputs.
+       *
+       * From page 29 (page 35 of the PDF) of the GLSL 1.30 spec:
+       *    "Outputs from a vertex shader (out) and inputs to a fragment
+       *    shader (in) can be further qualified with one or more of these
+       *    interpolation qualifiers"
+       */
+      if (state->language_version >= 130
+          && this->type->qualifier.has_interpolation()) {
+
+         const char *i = this->type->qualifier.interpolation_string();
+         assert(i != NULL);
+
+         switch (state->target) {
+         case vertex_shader:
+            if (this->type->qualifier.flags.q.in) {
+               _mesa_glsl_error(&loc, state,
+                                "qualifier '%s' cannot be applied to vertex "
+                                "shader inputs", i);
+            }
+            break;
+         case fragment_shader:
+            if (this->type->qualifier.flags.q.out) {
+               _mesa_glsl_error(&loc, state,
+                                "qualifier '%s' cannot be applied to fragment "
+                                "shader outputs", i);
+            }
+            break;
+         default:
+            assert(0);
+         }
+      }
+
+
       /* Process the initializer and add its instructions to a temporary
        * list.  This list will be added to the instruction stream (below) after
        * the declaration is added.  This is done because in some cases (such as
@@ -2242,6 +2419,17 @@ ast_declarator_list::hir(exec_list *instructions,
            if (this->type->qualifier.flags.q.constant)
               var->read_only = false;
 
+           /* Never emit code to initialize a uniform.
+            */
+           const glsl_type *initializer_type;
+           if (!this->type->qualifier.flags.q.uniform) {
+              result = do_assignment(&initializer_instructions, state,
+                                     lhs, rhs,
+                                     this->get_location());
+              initializer_type = result->type;
+           } else
+              initializer_type = rhs->type;
+
            /* If the declared variable is an unsized array, it must inherrit
             * its full type from the initializer.  A declaration such as
             *
@@ -2256,16 +2444,14 @@ ast_declarator_list::hir(exec_list *instructions,
             *
             * If the declared variable is not an array, the types must
             * already match exactly.  As a result, the type assignment
-            * here can be done unconditionally.
+            * here can be done unconditionally.  For non-uniforms the call
+            * to do_assignment can change the type of the initializer (via
+            * the implicit conversion rules).  For uniforms the initializer
+            * must be a constant expression, and the type of that expression
+            * was validated above.
             */
-           var->type = rhs->type;
+           var->type = initializer_type;
 
-           /* Never emit code to initialize a uniform.
-            */
-           if (!this->type->qualifier.flags.q.uniform)
-              result = do_assignment(&initializer_instructions, state,
-                                     lhs, rhs,
-                                     this->get_location());
            var->read_only = temp;
         }
       }
@@ -2338,6 +2524,27 @@ ast_declarator_list::hir(exec_list *instructions,
             */
            earlier->origin_upper_left = var->origin_upper_left;
            earlier->pixel_center_integer = var->pixel_center_integer;
+
+        /* According to section 4.3.7 of the GLSL 1.30 spec,
+         * the following built-in varaibles can be redeclared with an
+         * interpolation qualifier:
+         *    * gl_FrontColor
+         *    * gl_BackColor
+         *    * gl_FrontSecondaryColor
+         *    * gl_BackSecondaryColor
+         *    * gl_Color
+         *    * gl_SecondaryColor
+         */
+        } else if (state->language_version >= 130
+                   && (strcmp(var->name, "gl_FrontColor") == 0
+                        || strcmp(var->name, "gl_BackColor") == 0
+                        || strcmp(var->name, "gl_FrontSecondaryColor") == 0
+                        || strcmp(var->name, "gl_BackSecondaryColor") == 0
+                        || strcmp(var->name, "gl_Color") == 0
+                        || strcmp(var->name, "gl_SecondaryColor") == 0)
+                   && earlier->type == var->type
+                   && earlier->mode == var->mode) {
+           earlier->interpolation = var->interpolation;
         } else {
            YYLTYPE loc = this->get_location();
            _mesa_glsl_error(&loc, state, "`%s' redeclared", decl->identifier);