From: Mikko Rasa Date: Sun, 7 Mar 2021 09:13:23 +0000 (+0200) Subject: Properly resolve arithmetic assignment operators X-Git-Url: http://git.tdb.fi/?p=libs%2Fgl.git;a=commitdiff_plain;h=f639d088c478fe5d266f9f5779928735b5176976 Properly resolve arithmetic assignment operators 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. --- diff --git a/source/glsl/debug.cpp b/source/glsl/debug.cpp index 8a76744a..77b9025a 100644 --- a/source/glsl/debug.cpp +++ b/source/glsl/debug.cpp @@ -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)); diff --git a/source/glsl/generate.cpp b/source/glsl/generate.cpp index b7ab9d3f..f29d65f5 100644 --- a/source/glsl/generate.cpp +++ b/source/glsl/generate.cpp @@ -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(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(assign.left->type); + BasicTypeDeclaration *basic_right = dynamic_cast(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); } diff --git a/source/glsl/generate.h b/source/glsl/generate.h index 9365c6c3..54e39f39 100644 --- a/source/glsl/generate.h +++ b/source/glsl/generate.h @@ -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 &); diff --git a/source/glsl/validate.cpp b/source/glsl/validate.cpp index 676dd9e8..2f02400e 100644 --- a/source/glsl/validate.cpp +++ b/source/glsl/validate.cpp @@ -1,3 +1,4 @@ +#include #include #include #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 index 00000000..adb2a0cb --- /dev/null +++ b/tests/glsl/arithmetic_assignment.glsl @@ -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); +} +*/ diff --git a/tests/glsl/binary_operators.glsl b/tests/glsl/binary_operators.glsl index 06c367f2..8e2520c8 100644 --- a/tests/glsl/binary_operators.glsl +++ b/tests/glsl/binary_operators.glsl @@ -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; 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; b = b&&float(i)==f; diff --git a/tests/glsl/invalid_expressions.glsl b/tests/glsl/invalid_expressions.glsl index cf27c9a5..996b0d35 100644 --- a/tests/glsl/invalid_expressions.glsl +++ b/tests/glsl/invalid_expressions.glsl @@ -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: -:4: Initializing a variable of type 'int' with an expression of type 'float' -:5: Initializing a variable of type 'bool' with an expression of type 'int' -:6: Assignment to variable of type 'int' from expression of type 'bool' -:7: Initializing a variable of type 'float' with an expression of type 'bool' -:8: No matching operator '+' found for 'int' and 'bool' -:11: No matching operator '*' found for 'mat3x2' and 'mat3x2' -:12: No matching operator '*' found for 'vec3' and 'mat3x2' +:4: Initializing a variable of type 'int' with an expression of incompatible type 'float' +:5: Initializing a variable of type 'bool' with an expression of incompatible type 'int' +:6: Assignment to variable of type 'int' from expression of incompatible type 'bool' +:7: No matching operator '*' found for 'int' and 'bool' +:8: Initializing a variable of type 'float' with an expression of incompatible type 'bool' +:9: No matching operator '+' found for 'float' and 'bool' +:10: No matching operator '/' found for 'int' and 'float' +:13: No matching operator '*' found for 'mat3x2' and 'mat3x2' +:14: No matching operator '*' found for 'vec3' and 'mat3x2' */