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