]> git.tdb.fi Git - libs/gl.git/commitdiff
Properly resolve arithmetic assignment operators
authorMikko Rasa <tdb@tdb.fi>
Sun, 7 Mar 2021 09:13:23 +0000 (11:13 +0200)
committerMikko Rasa <tdb@tdb.fi>
Sun, 7 Mar 2021 09:35:52 +0000 (11:35 +0200)
After 50ab5ca operations like vec3 *= float were failing because the
validator required both operands to be of the same type.  Conversions
also need to be resolved for the right-hand operand.

source/glsl/debug.cpp
source/glsl/generate.cpp
source/glsl/generate.h
source/glsl/validate.cpp
tests/glsl/arithmetic_assignment.glsl [new file with mode: 0644]
tests/glsl/binary_operators.glsl
tests/glsl/invalid_expressions.glsl

index 8a76744a1a90bbeeeac5366489ab978c017c93ee..77b9025a8776cd8cce3ae83ba26dba54d3c8c0ad 100644 (file)
@@ -177,7 +177,7 @@ void DumpTree::visit(BinaryExpression &binary)
 
 void DumpTree::visit(Assignment &assign)
 {
-       append(format("Assignment: %s%s", assign.oper->token, (assign.self_referencing ? " (self-referencing)" : "")));
+       append(format("Assignment: %s%s -> %s", assign.oper->token, (assign.self_referencing ? " (self-referencing)" : ""), format_type(assign.type)));
        begin_sub();
        if(assign.target_declaration)
                append(format("Target: %%%d %s %s", get_label(*assign.target_declaration), assign.target_declaration->type, assign.target_declaration->name));
index b7ab9d3f7b9fc956c37752f18e7e5bb36df0e0d3..f29d65f58bfeeaf4fe837fa2222226473c6d3c66 100644 (file)
@@ -584,10 +584,8 @@ void ExpressionResolver::visit(UnaryExpression &unary)
        resolve(unary, basic, unary.expression->lvalue);
 }
 
-void ExpressionResolver::visit(BinaryExpression &binary)
+void ExpressionResolver::visit(BinaryExpression &binary, bool assign)
 {
-       TraversingVisitor::visit(binary);
-
        /* Binary operators are only defined for basic types (not for image or
        structure types). */
        BasicTypeDeclaration *basic_left = dynamic_cast<BasicTypeDeclaration *>(binary.left->type);
@@ -619,6 +617,8 @@ void ExpressionResolver::visit(BinaryExpression &binary)
        Compatibility elem_compat = get_compatibility(*elem_left, *elem_right);
        if(elem_compat==NOT_COMPATIBLE)
                return;
+       if(assign && (compat==LEFT_CONVERTIBLE || elem_compat==LEFT_CONVERTIBLE))
+               return;
 
        TypeDeclaration *type = 0;
        char oper2 = binary.oper->token[1];
@@ -708,6 +708,9 @@ void ExpressionResolver::visit(BinaryExpression &binary)
        else
                return;
 
+       if(assign && type!=basic_left)
+               return;
+
        bool converted = true;
        if(compat==LEFT_CONVERTIBLE)
                convert_to(binary.left, *basic_right);
@@ -721,12 +724,35 @@ void ExpressionResolver::visit(BinaryExpression &binary)
        if(!converted)
                type = 0;
 
-       resolve(binary, type, false);
+       resolve(binary, type, assign);
+}
+
+void ExpressionResolver::visit(BinaryExpression &binary)
+{
+       TraversingVisitor::visit(binary);
+       visit(binary, false);
 }
 
 void ExpressionResolver::visit(Assignment &assign)
 {
        TraversingVisitor::visit(assign);
+
+       if(assign.oper->token[0]!='=')
+               return visit(assign, true);
+       else if(assign.left->type!=assign.right->type)
+       {
+               BasicTypeDeclaration *basic_left = dynamic_cast<BasicTypeDeclaration *>(assign.left->type);
+               BasicTypeDeclaration *basic_right = dynamic_cast<BasicTypeDeclaration *>(assign.right->type);
+               if(!basic_left || !basic_right)
+                       return;
+
+               Compatibility compat = get_compatibility(*basic_left, *basic_right);
+               if(compat==RIGHT_CONVERTIBLE)
+                       convert_to(assign.right, *basic_left);
+               else if(compat!=SAME_TYPE)
+                       return;
+       }
+
        resolve(assign, assign.left->type, true);
 }
 
index 9365c6c3490fec2fb6290769d2083d1bec22bf8f..54e39f39aa0679088ea7d18dd98192c32645f07c 100644 (file)
@@ -150,6 +150,7 @@ private:
        virtual void visit(InterfaceBlockReference &);
        virtual void visit(MemberAccess &);
        virtual void visit(UnaryExpression &);
+       void visit(BinaryExpression &, bool);
        virtual void visit(BinaryExpression &);
        virtual void visit(Assignment &);
        virtual void visit(FunctionCall &);
index 676dd9e808792867458d8c59d03a62d47ceb8e88..2f02400ee675a5bc159362d9e85fec755a92c9d7 100644 (file)
@@ -1,3 +1,4 @@
+#include <cstring>
 #include <msp/core/raii.h>
 #include <msp/strings/format.h>
 #include "validate.h"
@@ -231,18 +232,30 @@ void ExpressionValidator::visit(BinaryExpression &binary)
 
 void ExpressionValidator::visit(Assignment &assign)
 {
-       if(assign.left->type && !assign.left->lvalue)
-               error(assign, "Target of assignment is not an lvalue");
-       if(assign.left->type && assign.right->type && assign.left->type!=assign.right->type)
-               error(assign, format("Assignment to variable of type '%s' from expression of type '%s'",
-                       assign.left->type->name, assign.right->type->name));
+       if(assign.left->type)
+       {
+               if(!assign.left->lvalue)
+                       error(assign, "Target of assignment is not an lvalue");
+               if(assign.right->type)
+               {
+                       if(assign.oper->token[0]!='=')
+                       {
+                               if(!assign.type)
+                                       error(assign, format("No matching operator '%s' found for '%s' and '%s'",
+                                               string(assign.oper->token, strlen(assign.oper->token)-1), assign.left->type->name, assign.right->type->name));
+                       }
+                       else if(assign.left->type!=assign.right->type)
+                               error(assign, format("Assignment to variable of type '%s' from expression of incompatible type '%s'",
+                                       assign.left->type->name, assign.right->type->name));
+               }
+       }
        TraversingVisitor::visit(assign);
 }
 
 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 type '%s'",
+               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);
 }
diff --git a/tests/glsl/arithmetic_assignment.glsl b/tests/glsl/arithmetic_assignment.glsl
new file mode 100644 (file)
index 0000000..adb2a0c
--- /dev/null
@@ -0,0 +1,58 @@
+uniform mat4 mvp;
+uniform sampler2D tex;
+uniform vec3 tint;
+uniform vec3 ambient;
+
+#pragma MSP stage(vertex)
+layout(location=0) in vec4 position;
+layout(location=1) in vec3 normal;
+layout(location=2) in vec2 texcoord;
+void main()
+{
+       out float light = normal.z;
+       passthrough;
+       gl_Position = mvp*position;
+}
+
+#pragma MSP stage(fragment)
+out vec4 frag_color;
+void main()
+{
+       vec3 color = texture(tex, texcoord);
+       color *= tint;
+       color *= light;
+       color += ambient;
+       frag_color = vec4(color, 1.0);
+}
+
+/* Expected output: vertex
+uniform mat4 mvp;
+layout(location=0) in vec4 position;
+layout(location=1) in vec3 normal;
+layout(location=2) in vec2 texcoord;
+out float light;
+out vec2 _vs_out_texcoord;
+void main()
+{
+  light = normal.z;
+  _vs_out_texcoord = texcoord;
+  gl_Position = mvp*position;
+}
+*/
+
+/* Expected output: fragment
+uniform sampler2D tex;
+uniform vec3 tint;
+uniform vec3 ambient;
+out vec4 frag_color;
+in vec2 _vs_out_texcoord;
+in float light;
+void main()
+{
+  vec3 color = texture(tex, _vs_out_texcoord);
+  color *= tint;
+  color *= light;
+  color += ambient;
+  frag_color = vec4(color, 1.0);
+}
+*/
index 06c367f24f71a745b0abab27e62bf0cc909f97f5..8e2520c83c7b9458584b6d92f1da51c0d2264ad2 100644 (file)
@@ -4,6 +4,7 @@ void main()
        int i = 0;
        i = i-3;
        float f = 0;
+       f = i+1;
        f = (f+i)*(f/i);
        bool b = i<f || i>=f;
        b = b && i==f;
@@ -31,7 +32,8 @@ void main()
 {
        int i = 0;
        i = i-3;
-       float f = float(0);
+       float f;
+       f = float(i+1);
        f = (f+float(i))*(f/float(i));
        bool b = float(i)<f||float(i)>=f;
        b = b&&float(i)==f;
index cf27c9a5f0d1a2a5d8a52de90aa462c15ecf0e99..996b0d355fecc09b2ce14d51f134672e12fa035f 100644 (file)
@@ -4,8 +4,10 @@ void main()
        int i = 1.0;
        bool b = 0;
        i = b;
+       i *= b;
        float f = b;
-       i+b;
+       f+b;
+       i /= f;
        mat3x2 m;
        vec3 v;
        m*m;
@@ -13,11 +15,13 @@ void main()
 }
 
 /* Expected error:
-<test>:4: Initializing a variable of type 'int' with an expression of type 'float'
-<test>:5: Initializing a variable of type 'bool' with an expression of type 'int'
-<test>:6: Assignment to variable of type 'int' from expression of type 'bool'
-<test>:7: Initializing a variable of type 'float' with an expression of type 'bool'
-<test>:8: No matching operator '+' found for 'int' and 'bool'
-<test>:11: No matching operator '*' found for 'mat3x2' and 'mat3x2'
-<test>:12: No matching operator '*' found for 'vec3' and 'mat3x2'
+<test>:4: Initializing a variable of type 'int' with an expression of incompatible type 'float'
+<test>:5: Initializing a variable of type 'bool' with an expression of incompatible type 'int'
+<test>:6: Assignment to variable of type 'int' from expression of incompatible type 'bool'
+<test>:7: No matching operator '*' found for 'int' and 'bool'
+<test>:8: Initializing a variable of type 'float' with an expression of incompatible type 'bool'
+<test>:9: No matching operator '+' found for 'float' and 'bool'
+<test>:10: No matching operator '/' found for 'int' and 'float'
+<test>:13: No matching operator '*' found for 'mat3x2' and 'mat3x2'
+<test>:14: No matching operator '*' found for 'vec3' and 'mat3x2'
 */