Module: Mesa
Branch: master
Commit: ec6222ef014ca336e540e13fda423fd9f927feb1
URL:    
http://cgit.freedesktop.org/mesa/mesa/commit/?id=ec6222ef014ca336e540e13fda423fd9f927feb1

Author: Carl Worth <cwo...@cworth.org>
Date:   Fri Jun 13 14:54:46 2014 -0700

glsl/glcpp: Add short-circuiting for || and && in #if/#elif for OpenGL ES.

The GLSL ES Specification 3.00.4 says:

        #if, #ifdef, #ifndef, #else, #elif, and #endif are defined to operate
        as for C++ except for the following:
        ...
        • Undefined identifiers not consumed by the defined operator do not
          default to '0'. Use of such identifiers causes an error.

        [Page 11 (page 127 of the PDF file)]

as well as:

        The semantics of applying operators in the preprocessor match those
        standard in the C++ preprocessor with the following exceptions:

        • The 2nd operand in a logical and ('&&') operation is evaluated if
          and only if the 1st operand evaluates to non-zero.

        • The 2nd operand in a logical or ('||') operation is evaluated if
          and only if the 1st operand evaluates to zero.

        If an operand is not evaluated, the presence of undefined identifiers
        in the operand will not cause an error.

(Note that neither of these deviations from C++ preprocessor behavior apply to
non-ES GLSL, at least as of specfication version 4.30.6).

The first portion of this, (generating an error for an undefined macro in an
(short-circuiting to squelch errors), was not implemented previously, but is
implemented in this commit.

A test is added for "make check" to ensure this behavior.

Note: The change as implemented does make the error message a bit less
precise, (it just states that an undefined macro was encountered, but not the
name of the macro).

This commit fixes the following Khronos GLES3 conformance test:

        undefined_identifiers.valid_undefined_identifier_1_vertex
        undefined_identifiers.valid_undefined_identifier_1_fragment
        undefined_identifiers.valid_undefined_identifier_2_vertex
        undefined_identifiers.valid_undefined_identifier_2_fragment

Reviewed-by: Kenneth Graunke <kenn...@whitecape.org>

---

 src/glsl/glcpp/glcpp-parse.y                       |  106 ++++++++++++++------
 src/glsl/glcpp/glcpp.h                             |    7 ++
 .../glcpp/tests/125-es-short-circuit-undefined.c   |   27 +++++
 .../125-es-short-circuit-undefined.c.expected      |   30 ++++++
 4 files changed, 140 insertions(+), 30 deletions(-)

diff --git a/src/glsl/glcpp/glcpp-parse.y b/src/glsl/glcpp/glcpp-parse.y
index 82e3102..68e4ace 100644
--- a/src/glsl/glcpp/glcpp-parse.y
+++ b/src/glsl/glcpp/glcpp-parse.y
@@ -166,7 +166,8 @@ add_builtin_define(glcpp_parser_t *parser, const char 
*name, int value);
 %expect 0
 %token COMMA_FINAL DEFINED ELIF_EXPANDED HASH HASH_DEFINE FUNC_IDENTIFIER 
OBJ_IDENTIFIER HASH_ELIF HASH_ELSE HASH_ENDIF HASH_IF HASH_IFDEF HASH_IFNDEF 
HASH_LINE HASH_UNDEF HASH_VERSION IDENTIFIER IF_EXPANDED INTEGER INTEGER_STRING 
LINE_EXPANDED NEWLINE OTHER PLACEHOLDER SPACE
 %token PASTE
-%type <ival> expression INTEGER operator SPACE integer_constant
+%type <ival> INTEGER operator SPACE integer_constant
+%type <expression_value> expression
 %type <str> IDENTIFIER FUNC_IDENTIFIER OBJ_IDENTIFIER INTEGER_STRING OTHER
 %type <string_list> identifier_list
 %type <token> preprocessing_token conditional_token
@@ -216,10 +217,14 @@ line:
 
 expanded_line:
        IF_EXPANDED expression NEWLINE {
-               _glcpp_parser_skip_stack_push_if (parser, & @1, $2);
+               if (parser->is_gles && $2.has_undefined)
+                       glcpp_error(& @1, parser, "undefined macro in 
expression (illegal in GLES)");
+               _glcpp_parser_skip_stack_push_if (parser, & @1, $2.value);
        }
 |      ELIF_EXPANDED expression NEWLINE {
-               _glcpp_parser_skip_stack_change_if (parser, & @1, "elif", $2);
+               if (parser->is_gles && $2.has_undefined)
+                       glcpp_error(& @1, parser, "undefined macro in 
expression (illegal in GLES)");
+               _glcpp_parser_skip_stack_change_if (parser, & @1, "elif", 
$2.value);
        }
 |      LINE_EXPANDED integer_constant NEWLINE {
                parser->has_new_line_number = 1;
@@ -412,87 +417,128 @@ integer_constant:
        }
 
 expression:
-       integer_constant
+       integer_constant {
+               $$.value = $1;
+               $$.has_undefined = false;
+       }
 |      IDENTIFIER {
+               $$.value = 0;
                if (parser->is_gles)
-                       glcpp_error(& @1, parser, "undefined macro %s in 
expression (illegal in GLES)", $1);
-               $$ = 0;
+                       $$.has_undefined = true;
+               else
+                       $$.has_undefined = false;
        }
 |      expression OR expression {
-               $$ = $1 || $3;
+               $$.value = $1.value || $3.value;
+
+               /* Short-circuit: Only flag undefined from right side
+                * if left side evaluates to false.
+                */
+               if ($1.value)
+                       $$.has_undefined = $1.has_undefined;
+               else
+                       $$.has_undefined = $1.has_undefined || $3.has_undefined;
        }
 |      expression AND expression {
-               $$ = $1 && $3;
+               $$.value = $1.value && $3.value;
+
+               /* Short-circuit: Only flag undefined from right-side
+                * if left side evaluates to true.
+                */
+               if ($1.value)
+                       $$.has_undefined = $1.has_undefined || $3.has_undefined;
+               else
+                       $$.has_undefined = $1.has_undefined;
        }
 |      expression '|' expression {
-               $$ = $1 | $3;
+               $$.value = $1.value | $3.value;
+               $$.has_undefined = $1.has_undefined || $3.has_undefined;
        }
 |      expression '^' expression {
-               $$ = $1 ^ $3;
+               $$.value = $1.value ^ $3.value;
+               $$.has_undefined = $1.has_undefined || $3.has_undefined;
        }
 |      expression '&' expression {
-               $$ = $1 & $3;
+               $$.value = $1.value & $3.value;
+               $$.has_undefined = $1.has_undefined || $3.has_undefined;
        }
 |      expression NOT_EQUAL expression {
-               $$ = $1 != $3;
+               $$.value = $1.value != $3.value;
+               $$.has_undefined = $1.has_undefined || $3.has_undefined;
        }
 |      expression EQUAL expression {
-               $$ = $1 == $3;
+               $$.value = $1.value == $3.value;
+               $$.has_undefined = $1.has_undefined || $3.has_undefined;
        }
 |      expression GREATER_OR_EQUAL expression {
-               $$ = $1 >= $3;
+               $$.value = $1.value >= $3.value;
+               $$.has_undefined = $1.has_undefined || $3.has_undefined;
        }
 |      expression LESS_OR_EQUAL expression {
-               $$ = $1 <= $3;
+               $$.value = $1.value <= $3.value;
+               $$.has_undefined = $1.has_undefined || $3.has_undefined;
        }
 |      expression '>' expression {
-               $$ = $1 > $3;
+               $$.value = $1.value > $3.value;
+               $$.has_undefined = $1.has_undefined || $3.has_undefined;
        }
 |      expression '<' expression {
-               $$ = $1 < $3;
+               $$.value = $1.value < $3.value;
+               $$.has_undefined = $1.has_undefined || $3.has_undefined;
        }
 |      expression RIGHT_SHIFT expression {
-               $$ = $1 >> $3;
+               $$.value = $1.value >> $3.value;
+               $$.has_undefined = $1.has_undefined || $3.has_undefined;
        }
 |      expression LEFT_SHIFT expression {
-               $$ = $1 << $3;
+               $$.value = $1.value << $3.value;
+               $$.has_undefined = $1.has_undefined || $3.has_undefined;
        }
 |      expression '-' expression {
-               $$ = $1 - $3;
+               $$.value = $1.value - $3.value;
+               $$.has_undefined = $1.has_undefined || $3.has_undefined;
        }
 |      expression '+' expression {
-               $$ = $1 + $3;
+               $$.value = $1.value + $3.value;
+               $$.has_undefined = $1.has_undefined || $3.has_undefined;
        }
 |      expression '%' expression {
-               if ($3 == 0) {
+               if ($3.value == 0) {
                        yyerror (& @1, parser,
                                 "zero modulus in preprocessor directive");
                } else {
-                       $$ = $1 % $3;
+                       $$.value = $1.value % $3.value;
                }
+               $$.has_undefined = $1.has_undefined || $3.has_undefined;
        }
 |      expression '/' expression {
-               if ($3 == 0) {
+               if ($3.value == 0) {
                        yyerror (& @1, parser,
                                 "division by 0 in preprocessor directive");
                } else {
-                       $$ = $1 / $3;
+                       $$.value = $1.value / $3.value;
                }
+               $$.has_undefined = $1.has_undefined || $3.has_undefined;
        }
 |      expression '*' expression {
-               $$ = $1 * $3;
+               $$.value = $1.value * $3.value;
+               $$.has_undefined = $1.has_undefined || $3.has_undefined;
        }
 |      '!' expression %prec UNARY {
-               $$ = ! $2;
+               $$.value = ! $2.value;
+               $$.has_undefined = $2.has_undefined;
        }
 |      '~' expression %prec UNARY {
-               $$ = ~ $2;
+               $$.value = ~ $2.value;
+               $$.has_undefined = $2.has_undefined;
        }
 |      '-' expression %prec UNARY {
-               $$ = - $2;
+               $$.value = - $2.value;
+               $$.has_undefined = $2.has_undefined;
        }
 |      '+' expression %prec UNARY {
-               $$ = + $2;
+               $$.value = + $2.value;
+               $$.has_undefined = $2.has_undefined;
        }
 |      '(' expression ')' {
                $$ = $2;
diff --git a/src/glsl/glcpp/glcpp.h b/src/glsl/glcpp/glcpp.h
index b2654ff..9783210 100644
--- a/src/glsl/glcpp/glcpp.h
+++ b/src/glsl/glcpp/glcpp.h
@@ -37,6 +37,12 @@
 
 /* Some data types used for parser values. */
 
+typedef struct expression_value {
+       intmax_t value;
+       bool has_undefined;
+} expression_value_t;
+   
+
 typedef struct string_node {
        const char *str;
        struct string_node *next;
@@ -53,6 +59,7 @@ typedef struct token_list token_list_t;
 typedef union YYSTYPE
 {
        intmax_t ival;
+       expression_value_t expression_value;
        char *str;
        string_list_t *string_list;
        token_t *token;
diff --git a/src/glsl/glcpp/tests/125-es-short-circuit-undefined.c 
b/src/glsl/glcpp/tests/125-es-short-circuit-undefined.c
new file mode 100644
index 0000000..4ee29f6
--- /dev/null
+++ b/src/glsl/glcpp/tests/125-es-short-circuit-undefined.c
@@ -0,0 +1,27 @@
+/* For GLSL in OpenGL ES, an undefined macro appearing in an #if or #elif
+ * expression, (other than as an argument to defined) is an error.
+ *
+ * Except in the case of a short-circuiting && or || operator, where the
+ * specification explicitly mandates that there be no error.
+ */
+#version 300 es
+
+/* These yield errors */
+#if NOT_DEFINED
+#endif
+
+#if 0
+#elif ALSO_NOT_DEFINED
+#endif
+
+/* But these yield no errors */
+#if 1 || STILL_NOT_DEFINED
+Success
+#endif
+
+#if 0
+#elif 0 && WILL_ANYONE_DEFINE_ANYTHING
+#else
+More success
+#endif
+
diff --git a/src/glsl/glcpp/tests/125-es-short-circuit-undefined.c.expected 
b/src/glsl/glcpp/tests/125-es-short-circuit-undefined.c.expected
new file mode 100644
index 0000000..a52dae8
--- /dev/null
+++ b/src/glsl/glcpp/tests/125-es-short-circuit-undefined.c.expected
@@ -0,0 +1,30 @@
+0:10(16): preprocessor error: undefined macro in expression (illegal in GLES)
+0:14(23): preprocessor error: undefined macro in expression (illegal in GLES)
+ 
+
+
+
+
+
+#version 300 es
+
+
+
+
+
+
+
+
+
+
+
+Success
+
+
+
+
+
+More success
+
+
+

_______________________________________________
mesa-commit mailing list
mesa-commit@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/mesa-commit

Reply via email to