diff options
-rw-r--r-- | src/glsl/glcpp/glcpp-parse.y | 106 | ||||
-rw-r--r-- | src/glsl/glcpp/glcpp.h | 7 | ||||
-rw-r--r-- | src/glsl/glcpp/tests/125-es-short-circuit-undefined.c | 27 | ||||
-rw-r--r-- | src/glsl/glcpp/tests/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 82e310270d6..68e4ace012e 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 b2654ffbed2..9783210b708 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 00000000000..4ee29f6d93f --- /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 00000000000..a52dae8f76a --- /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 + + + |