]> git.tdb.fi Git - libs/gl.git/commitdiff
Check that non-constant data is not accessed from constant expressions
authorMikko Rasa <tdb@tdb.fi>
Wed, 7 Apr 2021 12:28:24 +0000 (15:28 +0300)
committerMikko Rasa <tdb@tdb.fi>
Wed, 7 Apr 2021 15:53:50 +0000 (18:53 +0300)
source/glsl/validate.cpp
source/glsl/validate.h
tests/glsl/constant_expressions.glsl [new file with mode: 0644]

index 4a5ffe841801b064c04c7ea68d6d91eb53ea1c59..62f997bb5c29ad6d8a507620c3d78745118fc80f 100644 (file)
@@ -508,9 +508,22 @@ void ReferenceValidator::visit(FunctionDeclaration &func)
 
 
 ExpressionValidator::ExpressionValidator():
-       current_function(0)
+       current_function(0),
+       constant_expression(false)
 { }
 
+void ExpressionValidator::visit(VariableReference &var)
+{
+       if(var.declaration && constant_expression && !var.declaration->constant)
+               error(var, format("Reference to non-constant variable '%s' in a constant expression", var.name));
+}
+
+void ExpressionValidator::visit(InterfaceBlockReference &iface)
+{
+       if(constant_expression)
+               error(iface, format("Reference to interface block '%s' in a constant expression", iface.name));
+}
+
 void ExpressionValidator::visit(Swizzle &swizzle)
 {
        unsigned size = 0;
@@ -555,8 +568,13 @@ void ExpressionValidator::visit(UnaryExpression &unary)
        {
                if(!unary.type)
                        error(unary, format("No matching operator '%s' found for '%s'", unary.oper->token, unary.expression->type->name));
-               else if((unary.oper->token[1]=='+' || unary.oper->token[1]=='-') && !unary.expression->lvalue)
-                       error(unary, format("Operand of '%s' is not an lvalue", unary.oper->token));
+               else if(unary.oper->token[1]=='+' || unary.oper->token[1]=='-')
+               {
+                       if(constant_expression)
+                               error(unary, format("Use of '%s' in a constant expression", unary.oper->token));
+                       else if(!unary.expression->lvalue)
+                               error(unary, format("Operand of '%s' is not an lvalue", unary.oper->token));
+               }
        }
        TraversingVisitor::visit(unary);
 }
@@ -579,7 +597,9 @@ void ExpressionValidator::visit(Assignment &assign)
 {
        if(assign.left->type)
        {
-               if(!assign.left->lvalue)
+               if(constant_expression)
+                       error(assign, "Assignment in constant expression");
+               else if(!assign.left->lvalue)
                        error(assign, "Target of assignment is not an lvalue");
                if(assign.right->type)
                {
@@ -616,7 +636,19 @@ void ExpressionValidator::visit(VariableDeclaration &var)
        if(var.init_expression && var.init_expression->type && var.type_declaration && var.init_expression->type!=var.type_declaration)
                error(var, format("Initializing a variable of type '%s' with an expression of incompatible type '%s'",
                        var.type_declaration->name, var.init_expression->type->name));
-       TraversingVisitor::visit(var);
+
+       if(var.layout)
+               var.layout->visit(*this);
+       if(var.init_expression)
+       {
+               SetFlag set_const(constant_expression, var.constant);
+               TraversingVisitor::visit(var.init_expression);
+       }
+       if(var.array_size)
+       {
+               SetFlag set_const(constant_expression);
+               TraversingVisitor::visit(var.array_size);
+       }
 }
 
 void ExpressionValidator::visit(FunctionDeclaration &func)
index 1a05817069282873e27328f03683921b5945499e..fe52ff5aae1878aa0006c2dce8b4c60ca6b4b7af 100644 (file)
@@ -118,6 +118,7 @@ class ExpressionValidator: private Validator
 {
 private:
        FunctionDeclaration *current_function;
+       bool constant_expression;
 
 public:
        ExpressionValidator();
@@ -125,6 +126,8 @@ public:
        void apply(Stage &s) { stage = &s; s.content.visit(*this); }
 
 private:
+       virtual void visit(VariableReference &);
+       virtual void visit(InterfaceBlockReference &);
        virtual void visit(Swizzle &);
        virtual void visit(UnaryExpression &);
        virtual void visit(BinaryExpression &);
diff --git a/tests/glsl/constant_expressions.glsl b/tests/glsl/constant_expressions.glsl
new file mode 100644 (file)
index 0000000..90eab78
--- /dev/null
@@ -0,0 +1,15 @@
+uniform int n_lights;
+uniform vec3 light_dir[n_lights];
+
+#pragma MSP stage(vertex)
+layout(location=0) in vec4 position;
+const bool lower_half = position.y<0;
+void main()
+{
+       gl_Position = position;
+}
+
+/* Expected error:
+<test>:2: Reference to non-constant variable 'n_lights' in a constant expression
+<test>:6: Reference to non-constant variable 'position' in a constant expression
+*/