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