blob: 4fdaa2826fb913aa3828c323700ffd0cf1128970 [file] [log] [blame]
John Stiles8d130842021-08-27 12:42:45 -04001/*
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/SkSLContext.h"
10#include "src/sksl/SkSLProgramSettings.h"
11#include "src/sksl/ir/SkSLFunctionCall.h"
12#include "src/sksl/ir/SkSLFunctionDefinition.h"
13#include "src/sksl/ir/SkSLReturnStatement.h"
14
15namespace SkSL {
16
17void FunctionDefinition::FinalizeFunctionBody(const Context& context,
18 const FunctionDeclaration& function,
19 Statement* body,
20 IntrinsicSet* referencedIntrinsics) {
21 class Finalizer : public ProgramWriter {
22 public:
23 Finalizer(const Context& context, const FunctionDeclaration& function,
24 IntrinsicSet* referencedIntrinsics)
25 : fContext(context)
26 , fFunction(function)
27 , fReferencedIntrinsics(referencedIntrinsics) {}
28
29 ~Finalizer() override {
30 SkASSERT(!fBreakableLevel);
31 SkASSERT(!fContinuableLevel);
32 }
33
34 bool functionReturnsValue() const {
35 return !fFunction.returnType().isVoid();
36 }
37
38 bool visitExpression(Expression& expr) override {
39 if (expr.is<FunctionCall>()) {
40 const FunctionDeclaration& func = expr.as<FunctionCall>().function();
41 if (func.isBuiltin() && func.definition()) {
42 fReferencedIntrinsics->insert(&func);
43 }
44 }
45 return INHERITED::visitExpression(expr);
46 }
47
48 bool visitStatement(Statement& stmt) override {
49 switch (stmt.kind()) {
50 case Statement::Kind::kReturn: {
51 // Early returns from a vertex main() function will bypass sk_Position
52 // normalization, so SkASSERT that we aren't doing that. If this becomes an
53 // issue, we can add normalization before each return statement.
54 if (fContext.fConfig->fKind == ProgramKind::kVertex && fFunction.isMain()) {
55 fContext.fErrors->error(
56 stmt.fOffset,
57 "early returns from vertex programs are not supported");
58 }
59
60 // Verify that the return statement matches the function's return type.
61 ReturnStatement& returnStmt = stmt.as<ReturnStatement>();
62 if (returnStmt.expression()) {
63 if (this->functionReturnsValue()) {
64 // Coerce return expression to the function's return type.
65 returnStmt.setExpression(fFunction.returnType().coerceExpression(
66 std::move(returnStmt.expression()), fContext));
67 } else {
68 // Returning something from a function with a void return type.
69 returnStmt.setExpression(nullptr);
70 fContext.fErrors->error(returnStmt.fOffset,
71 "may not return a value from a void function");
72 }
73 } else {
74 if (this->functionReturnsValue()) {
75 // Returning nothing from a function with a non-void return type.
76 fContext.fErrors->error(returnStmt.fOffset,
77 "expected function to return '" +
78 fFunction.returnType().displayName() + "'");
79 }
80 }
81 break;
82 }
83 case Statement::Kind::kDo:
84 case Statement::Kind::kFor: {
85 ++fBreakableLevel;
86 ++fContinuableLevel;
87 bool result = INHERITED::visitStatement(stmt);
88 --fContinuableLevel;
89 --fBreakableLevel;
90 return result;
91 }
92 case Statement::Kind::kSwitch: {
93 ++fBreakableLevel;
94 bool result = INHERITED::visitStatement(stmt);
95 --fBreakableLevel;
96 return result;
97 }
98 case Statement::Kind::kBreak:
99 if (!fBreakableLevel) {
100 fContext.fErrors->error(stmt.fOffset,
101 "break statement must be inside a loop or switch");
102 }
103 break;
104 case Statement::Kind::kContinue:
105 if (!fContinuableLevel) {
106 fContext.fErrors->error(stmt.fOffset,
107 "continue statement must be inside a loop");
108 }
109 break;
110 default:
111 break;
112 }
113 return INHERITED::visitStatement(stmt);
114 }
115
116 private:
117 const Context& fContext;
118 const FunctionDeclaration& fFunction;
119 // which intrinsics have we encountered in this function
120 IntrinsicSet* fReferencedIntrinsics;
121 // how deeply nested we are in breakable constructs (for, do, switch).
122 int fBreakableLevel = 0;
123 // how deeply nested we are in continuable constructs (for, do).
124 int fContinuableLevel = 0;
125
126 using INHERITED = ProgramWriter;
127 };
128
129 Finalizer(context, function, referencedIntrinsics).visitStatement(*body);
130
131 if (Analysis::CanExitWithoutReturningValue(function, *body)) {
132 context.fErrors->error(function.fOffset, "function '" + function.name() +
133 "' can exit without returning a value");
134 }
135}
136
137} // namespace SkSL