xref: /aosp_15_r20/external/skia/src/sksl/analysis/SkSLCanExitWithoutReturningValue.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2021 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/sksl/SkSLAnalysis.h"
9 #include "src/sksl/SkSLDefines.h"
10 #include "src/sksl/analysis/SkSLProgramVisitor.h"
11 #include "src/sksl/ir/SkSLDoStatement.h"
12 #include "src/sksl/ir/SkSLForStatement.h"
13 #include "src/sksl/ir/SkSLFunctionDeclaration.h"
14 #include "src/sksl/ir/SkSLIRNode.h"
15 #include "src/sksl/ir/SkSLIfStatement.h"
16 #include "src/sksl/ir/SkSLStatement.h"
17 #include "src/sksl/ir/SkSLSwitchCase.h"
18 #include "src/sksl/ir/SkSLSwitchStatement.h"
19 #include "src/sksl/ir/SkSLType.h"
20 
21 #include <memory>
22 
23 namespace SkSL {
24 class Expression;
25 namespace {
26 
27 class ReturnsOnAllPathsVisitor : public ProgramVisitor {
28 public:
visitExpression(const Expression & expr)29     bool visitExpression(const Expression& expr) override {
30         // We can avoid processing expressions entirely.
31         return false;
32     }
33 
visitStatement(const Statement & stmt)34     bool visitStatement(const Statement& stmt) override {
35         switch (stmt.kind()) {
36             // Returns, breaks, or continues will stop the scan, so only one of these should ever be
37             // true.
38             case Statement::Kind::kReturn:
39                 fFoundReturn = true;
40                 return true;
41 
42             case Statement::Kind::kBreak:
43                 fFoundBreak = true;
44                 return true;
45 
46             case Statement::Kind::kContinue:
47                 fFoundContinue = true;
48                 return true;
49 
50             case Statement::Kind::kIf: {
51                 const IfStatement& i = stmt.as<IfStatement>();
52                 ReturnsOnAllPathsVisitor trueVisitor;
53                 ReturnsOnAllPathsVisitor falseVisitor;
54                 trueVisitor.visitStatement(*i.ifTrue());
55                 if (i.ifFalse()) {
56                     falseVisitor.visitStatement(*i.ifFalse());
57                 }
58                 // If either branch leads to a break or continue, we report the entire if as
59                 // containing a break or continue, since we don't know which side will be reached.
60                 fFoundBreak    = (trueVisitor.fFoundBreak    || falseVisitor.fFoundBreak);
61                 fFoundContinue = (trueVisitor.fFoundContinue || falseVisitor.fFoundContinue);
62                 // On the other hand, we only want to report returns that definitely happen, so we
63                 // require those to be found on both sides.
64                 fFoundReturn   = (trueVisitor.fFoundReturn   && falseVisitor.fFoundReturn);
65                 return fFoundBreak || fFoundContinue || fFoundReturn;
66             }
67             case Statement::Kind::kFor: {
68                 const ForStatement& f = stmt.as<ForStatement>();
69                 // We assume a for/while loop runs for at least one iteration; this isn't strictly
70                 // guaranteed, but it's better to be slightly over-permissive here than to fail on
71                 // reasonable code.
72                 ReturnsOnAllPathsVisitor forVisitor;
73                 forVisitor.visitStatement(*f.statement());
74                 // A for loop that contains a break or continue is safe; it won't exit the entire
75                 // function, just the loop. So we disregard those signals.
76                 fFoundReturn = forVisitor.fFoundReturn;
77                 return fFoundReturn;
78             }
79             case Statement::Kind::kDo: {
80                 const DoStatement& d = stmt.as<DoStatement>();
81                 // Do-while blocks are always entered at least once.
82                 ReturnsOnAllPathsVisitor doVisitor;
83                 doVisitor.visitStatement(*d.statement());
84                 // A do-while loop that contains a break or continue is safe; it won't exit the
85                 // entire function, just the loop. So we disregard those signals.
86                 fFoundReturn = doVisitor.fFoundReturn;
87                 return fFoundReturn;
88             }
89             case Statement::Kind::kBlock:
90                 // Blocks are definitely entered and don't imply any additional control flow.
91                 // If the block contains a break, continue or return, we want to keep that.
92                 return INHERITED::visitStatement(stmt);
93 
94             case Statement::Kind::kSwitch: {
95                 // Switches are the most complex control flow we need to deal with; fortunately we
96                 // already have good primitives for dissecting them. We need to verify that:
97                 // - a default case exists, so that every possible input value is covered
98                 // - every switch-case either (a) returns unconditionally, or
99                 //                            (b) falls through to another case that does
100                 const SwitchStatement& s = stmt.as<SwitchStatement>();
101                 bool foundDefault = false;
102                 bool fellThrough = false;
103                 for (const std::unique_ptr<Statement>& switchStmt : s.cases()) {
104                     // The default case is indicated by a null value. A switch without a default
105                     // case cannot definitively return, as its value might not be in the cases list.
106                     const SwitchCase& sc = switchStmt->as<SwitchCase>();
107                     if (sc.isDefault()) {
108                         foundDefault = true;
109                     }
110                     // Scan this switch-case for any exit (break, continue or return).
111                     ReturnsOnAllPathsVisitor caseVisitor;
112                     caseVisitor.visitStatement(sc);
113 
114                     // If we found a break or continue, whether conditional or not, this switch case
115                     // can't be called an unconditional return. Switches absorb breaks but not
116                     // continues.
117                     if (caseVisitor.fFoundContinue) {
118                         fFoundContinue = true;
119                         return false;
120                     }
121                     if (caseVisitor.fFoundBreak) {
122                         return false;
123                     }
124                     // We just confirmed that there weren't any breaks or continues. If we didn't
125                     // find an unconditional return either, the switch is considered fallen-through.
126                     // (There might be a conditional return, but that doesn't count.)
127                     fellThrough = !caseVisitor.fFoundReturn;
128                 }
129 
130                 // If we didn't find a default case, or the very last case fell through, this switch
131                 // doesn't meet our criteria.
132                 if (fellThrough || !foundDefault) {
133                     return false;
134                 }
135 
136                 // We scanned the entire switch, found a default case, and every section either fell
137                 // through or contained an unconditional return.
138                 fFoundReturn = true;
139                 return true;
140             }
141 
142             case Statement::Kind::kSwitchCase:
143                 // Recurse into the switch-case.
144                 return INHERITED::visitStatement(stmt);
145 
146             case Statement::Kind::kDiscard:
147             case Statement::Kind::kExpression:
148             case Statement::Kind::kNop:
149             case Statement::Kind::kVarDeclaration:
150                 // None of these statements could contain a return.
151                 break;
152         }
153 
154         return false;
155     }
156 
157     bool fFoundReturn = false;
158     bool fFoundBreak = false;
159     bool fFoundContinue = false;
160 
161     using INHERITED = ProgramVisitor;
162 };
163 
164 }  // namespace
165 
CanExitWithoutReturningValue(const FunctionDeclaration & funcDecl,const Statement & body)166 bool Analysis::CanExitWithoutReturningValue(const FunctionDeclaration& funcDecl,
167                                             const Statement& body) {
168     if (funcDecl.returnType().isVoid()) {
169         return false;
170     }
171     ReturnsOnAllPathsVisitor visitor;
172     visitor.visitStatement(body);
173     return !visitor.fFoundReturn;
174 }
175 
176 }  // namespace SkSL
177