From: Mikko Rasa Date: Wed, 7 Apr 2021 20:49:31 +0000 (+0300) Subject: Validate GLSL flow control X-Git-Url: http://git.tdb.fi/?p=libs%2Fgl.git;a=commitdiff_plain;h=9d9e008291a447b5d192e724e9022180503c8c3e Validate GLSL flow control In particular, it's an error to reach the end of a non-void function without a return statement. --- diff --git a/source/glsl/compiler.cpp b/source/glsl/compiler.cpp index e2745e1b..7021c62d 100644 --- a/source/glsl/compiler.cpp +++ b/source/glsl/compiler.cpp @@ -323,6 +323,7 @@ void Compiler::validate(Stage &stage) IdentifierValidator().apply(stage); ReferenceValidator().apply(stage); ExpressionValidator().apply(stage); + FlowControlValidator().apply(stage); StageInterfaceValidator().apply(stage); } diff --git a/source/glsl/validate.cpp b/source/glsl/validate.cpp index 62f997bb..86afebed 100644 --- a/source/glsl/validate.cpp +++ b/source/glsl/validate.cpp @@ -699,6 +699,52 @@ void ExpressionValidator::visit(Return &ret) } +FlowControlValidator::FlowControlValidator(): + reachable(true) +{ } + +void FlowControlValidator::visit(Block &block) +{ + for(NodeList::const_iterator i=block.body.begin(); i!=block.body.end(); ++i) + { + if(!reachable) + { + diagnose(**i, Diagnostic::WARN, "Unreachable code detected"); + break; + } + (*i)->visit(*this); + } +} + +void FlowControlValidator::visit(FunctionDeclaration &func) +{ + func.body.visit(*this); + + if(func.definition==&func && func.return_type_declaration) + { + const BasicTypeDeclaration *basic_ret = dynamic_cast(func.return_type_declaration); + if(reachable && (!basic_ret || basic_ret->kind!=BasicTypeDeclaration::VOID)) + error(func, "Missing return statement at the end of a function not returning 'void'"); + } + reachable = true; +} + +void FlowControlValidator::visit(Conditional &cond) +{ + cond.body.visit(*this); + bool reachable_if_true = reachable; + reachable = true; + cond.else_body.visit(*this); + reachable |= reachable_if_true; +} + +void FlowControlValidator::visit(Iteration &iter) +{ + iter.body.visit(*this); + reachable = true; +} + + int StageInterfaceValidator::get_location(const Layout &layout) { for(vector::const_iterator i=layout.qualifiers.begin(); i!=layout.qualifiers.end(); ++i) diff --git a/source/glsl/validate.h b/source/glsl/validate.h index fe52ff5a..d3884901 100644 --- a/source/glsl/validate.h +++ b/source/glsl/validate.h @@ -140,6 +140,27 @@ private: virtual void visit(Return &); }; +/** Verifies flow control constructs. Functions returning non-void must have +return statements. Warnings are given about dead code. */ +class FlowControlValidator: private Validator +{ +private: + bool reachable; + +public: + FlowControlValidator(); + + void apply(Stage &s) { stage = &s; s.content.visit(*this); } + +private: + virtual void visit(Block &); + virtual void visit(FunctionDeclaration &); + virtual void visit(Conditional &); + virtual void visit(Iteration &); + virtual void visit(Return &) { reachable = false; } + virtual void visit(Jump &) { reachable = false; } +}; + /** Verifies that stage input and output interfaces are valid. Linked variables must have matching types and locations and there must not be any overlap in locations. */ diff --git a/tests/glsl/dead_code.glsl b/tests/glsl/dead_code.glsl index bdfd4503..4c15cc18 100644 --- a/tests/glsl/dead_code.glsl +++ b/tests/glsl/dead_code.glsl @@ -9,6 +9,10 @@ void main() gl_Position = position; } +/* Expected diagnostic: +:9: Unreachable code detected +*/ + /* Expected output: vertex layout(location=0) uniform mat4 mvp; layout(location=0) in vec4 position; diff --git a/tests/glsl/function_override.glsl b/tests/glsl/function_override.glsl index c7e64d52..746ea8d3 100644 --- a/tests/glsl/function_override.glsl +++ b/tests/glsl/function_override.glsl @@ -4,7 +4,7 @@ virtual float get_scale() { return 1.0; } -int main() +void main() { gl_Position = position*get_scale(); } @@ -15,7 +15,7 @@ float get_scale() override /* Expected output: vertex layout(location=0) in vec4 position; -int main() +void main() { gl_Position = position*2.0; } diff --git a/tests/glsl/invalid_subscript.glsl b/tests/glsl/invalid_subscript.glsl index 6826feff..5f959ffb 100644 --- a/tests/glsl/invalid_subscript.glsl +++ b/tests/glsl/invalid_subscript.glsl @@ -1,7 +1,7 @@ #pragma MSP stage(vertex) layout(location=0) in vec4 position; layout(location=1) in float scale; -int main() +void main() { gl_Position = position[scale]; gl_Position = scale[position]; diff --git a/tests/glsl/no_op_conversion.glsl b/tests/glsl/no_op_conversion.glsl index 2e3ff57b..f0f887e9 100644 --- a/tests/glsl/no_op_conversion.glsl +++ b/tests/glsl/no_op_conversion.glsl @@ -2,7 +2,7 @@ uniform mat4 mvp; #pragma MSP stage(vertex) layout(location=0) in vec4 position; -int main() +void main() { gl_Position = mat4(mvp)*vec4(position); } @@ -10,7 +10,7 @@ int main() /* Expected output: vertex layout(location=0) uniform mat4 mvp; layout(location=0) in vec4 position; -int main() +void main() { gl_Position = mat4(mvp)*vec4(position); } diff --git a/tests/glsl/unused_component_assignment.glsl b/tests/glsl/unused_component_assignment.glsl index 0febd561..af880f5d 100644 --- a/tests/glsl/unused_component_assignment.glsl +++ b/tests/glsl/unused_component_assignment.glsl @@ -2,7 +2,7 @@ uniform mat4 mvp; #pragma MSP stage(vertex) layout(location=0) in vec4 position; -int main() +void main() { vec4 c0 = mvp[0]*position; vec4 c1 = mvp[1]*position; @@ -19,7 +19,7 @@ int main() /* Expected output: vertex layout(location=0) uniform mat4 mvp; layout(location=0) in vec4 position; -int main() +void main() { vec4 c0 = mvp[0]*position; vec4 c1 = mvp[1]*position;