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/ir/SkSLVarDeclarations.h"
9
10#include "include/sksl/SkSLErrorReporter.h"
11#include "src/sksl/SkSLAnalysis.h"
12#include "src/sksl/SkSLCompiler.h"
13#include "src/sksl/SkSLContext.h"
14#include "src/sksl/SkSLProgramSettings.h"
15#include "src/sksl/SkSLThreadContext.h"
16
17namespace SkSL {
18
19std::unique_ptr<Statement> VarDeclaration::clone() const {
20    return std::make_unique<VarDeclaration>(&this->var(),
21                                            &this->baseType(),
22                                            fArraySize,
23                                            this->value() ? this->value()->clone() : nullptr);
24}
25
26String VarDeclaration::description() const {
27    String result = this->var().modifiers().description() + this->baseType().description() + " " +
28                    this->var().name();
29    if (this->arraySize() > 0) {
30        result.appendf("[%d]", this->arraySize());
31    }
32    if (this->value()) {
33        result += " = " + this->value()->description();
34    }
35    result += ";";
36    return result;
37}
38
39void VarDeclaration::ErrorCheck(const Context& context, int line, const Modifiers& modifiers,
40        const Type* baseType, Variable::Storage storage) {
41    if (*baseType == *context.fTypes.fInvalid) {
42        context.fErrors->error(line, "invalid type");
43        return;
44    }
45    if (context.fConfig->strictES2Mode() && baseType->isArray()) {
46        context.fErrors->error(line, "array size must appear after variable name");
47    }
48
49    if (baseType->componentType().isOpaque() && storage != Variable::Storage::kGlobal) {
50        context.fErrors->error(line,
51                "variables of type '" + baseType->displayName() + "' must be global");
52    }
53    if ((modifiers.fFlags & Modifiers::kIn_Flag) && baseType->isMatrix()) {
54        context.fErrors->error(line, "'in' variables may not have matrix type");
55    }
56    if ((modifiers.fFlags & Modifiers::kIn_Flag) && (modifiers.fFlags & Modifiers::kUniform_Flag)) {
57        context.fErrors->error(line, "'in uniform' variables not permitted");
58    }
59    if (ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
60        if (modifiers.fFlags & Modifiers::kIn_Flag) {
61            context.fErrors->error(line, "'in' variables not permitted in runtime effects");
62        }
63    }
64    if (baseType->isEffectChild() && !(modifiers.fFlags & Modifiers::kUniform_Flag)) {
65        context.fErrors->error(line,
66                "variables of type '" + baseType->displayName() + "' must be uniform");
67    }
68    if (modifiers.fLayout.fFlags & Layout::kSRGBUnpremul_Flag) {
69        if (!ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
70            context.fErrors->error(line, "'srgb_unpremul' is only permitted in runtime effects");
71        }
72        if (!(modifiers.fFlags & Modifiers::kUniform_Flag)) {
73            context.fErrors->error(line,
74                    "'srgb_unpremul' is only permitted on 'uniform' variables");
75        }
76        auto validColorXformType = [](const Type& t) {
77            return t.isVector() && t.componentType().isFloat() &&
78                   (t.columns() == 3 || t.columns() == 4);
79        };
80        if (!validColorXformType(*baseType) && !(baseType->isArray() &&
81                                                 validColorXformType(baseType->componentType()))) {
82            context.fErrors->error(line, "'srgb_unpremul' is only permitted on half3, half4, "
83                    "float3, or float4 variables");
84        }
85    }
86    int permitted = Modifiers::kConst_Flag | Modifiers::kHighp_Flag | Modifiers::kMediump_Flag |
87                    Modifiers::kLowp_Flag;
88    if (storage == Variable::Storage::kGlobal) {
89        permitted |= Modifiers::kIn_Flag | Modifiers::kOut_Flag | Modifiers::kUniform_Flag |
90                     Modifiers::kFlat_Flag | Modifiers::kNoPerspective_Flag;
91    }
92    // TODO(skbug.com/11301): Migrate above checks into building a mask of permitted layout flags
93    modifiers.checkPermitted(context, line, permitted, /*permittedLayoutFlags=*/~0);
94}
95
96bool VarDeclaration::ErrorCheckAndCoerce(const Context& context, const Variable& var,
97        std::unique_ptr<Expression>& value) {
98    const Type* baseType = &var.type();
99    if (baseType->isArray()) {
100        baseType = &baseType->componentType();
101    }
102    ErrorCheck(context, var.fLine, var.modifiers(), baseType, var.storage());
103    if (value) {
104        if (var.type().isOpaque()) {
105            context.fErrors->error(value->fLine,
106                    "opaque type '" + var.type().name() + "' cannot use initializer expressions");
107            return false;
108        }
109        if (var.modifiers().fFlags & Modifiers::kIn_Flag) {
110            context.fErrors->error(value->fLine,
111                    "'in' variables cannot use initializer expressions");
112            return false;
113        }
114        if (var.modifiers().fFlags & Modifiers::kUniform_Flag) {
115            context.fErrors->error(value->fLine,
116                    "'uniform' variables cannot use initializer expressions");
117            return false;
118        }
119        if (var.storage() == Variable::Storage::kInterfaceBlock) {
120            context.fErrors->error(value->fLine,
121                    "initializers are not permitted on interface block fields");
122            return false;
123        }
124        value = var.type().coerceExpression(std::move(value), context);
125        if (!value) {
126            return false;
127        }
128    }
129    if (var.modifiers().fFlags & Modifiers::kConst_Flag) {
130        if (!value) {
131            context.fErrors->error(var.fLine, "'const' variables must be initialized");
132            return false;
133        }
134        if (!Analysis::IsConstantExpression(*value)) {
135            context.fErrors->error(value->fLine,
136                    "'const' variable initializer must be a constant expression");
137            return false;
138        }
139    }
140    if (var.storage() == Variable::Storage::kInterfaceBlock) {
141        if (var.type().isOpaque()) {
142            context.fErrors->error(var.fLine, "opaque type '" + var.type().name() +
143                    "' is not permitted in an interface block");
144            return false;
145        }
146    }
147    if (var.storage() == Variable::Storage::kGlobal) {
148        if (value && !Analysis::IsConstantExpression(*value)) {
149            context.fErrors->error(value->fLine,
150                    "global variable initializer must be a constant expression");
151            return false;
152        }
153    }
154    return true;
155}
156
157std::unique_ptr<Statement> VarDeclaration::Convert(const Context& context,
158        std::unique_ptr<Variable> var, std::unique_ptr<Expression> value, bool addToSymbolTable) {
159    if (!ErrorCheckAndCoerce(context, *var, value)) {
160        return nullptr;
161    }
162    const Type* baseType = &var->type();
163    int arraySize = 0;
164    if (baseType->isArray()) {
165        arraySize = baseType->columns();
166        baseType = &baseType->componentType();
167    }
168    std::unique_ptr<Statement> varDecl = VarDeclaration::Make(context, var.get(), baseType,
169            arraySize, std::move(value));
170    if (!varDecl) {
171        return nullptr;
172    }
173
174    // Detect the declaration of magical variables.
175    if ((var->storage() == Variable::Storage::kGlobal) && var->name() == Compiler::FRAGCOLOR_NAME) {
176        // Silently ignore duplicate definitions of `sk_FragColor`.
177        const Symbol* symbol = (*ThreadContext::SymbolTable())[var->name()];
178        if (symbol) {
179            return nullptr;
180        }
181    } else if ((var->storage() == Variable::Storage::kGlobal ||
182                var->storage() == Variable::Storage::kInterfaceBlock) &&
183               var->name() == Compiler::RTADJUST_NAME) {
184        // `sk_RTAdjust` is special, and makes the IR generator emit position-fixup expressions.
185        if (ThreadContext::RTAdjustState().fVar || ThreadContext::RTAdjustState().fInterfaceBlock) {
186            context.fErrors->error(var->fLine, "duplicate definition of 'sk_RTAdjust'");
187            return nullptr;
188        }
189        if (var->type() != *context.fTypes.fFloat4) {
190            context.fErrors->error(var->fLine, "sk_RTAdjust must have type 'float4'");
191            return nullptr;
192        }
193        ThreadContext::RTAdjustState().fVar = var.get();
194    }
195
196    if (addToSymbolTable) {
197        ThreadContext::SymbolTable()->add(std::move(var));
198    } else {
199        ThreadContext::SymbolTable()->takeOwnershipOfSymbol(std::move(var));
200    }
201    return varDecl;
202}
203
204std::unique_ptr<Statement> VarDeclaration::Make(const Context& context, Variable* var,
205        const Type* baseType, int arraySize, std::unique_ptr<Expression> value) {
206    SkASSERT(!baseType->isArray());
207    // function parameters cannot have variable declarations
208    SkASSERT(var->storage() != Variable::Storage::kParameter);
209    // 'const' variables must be initialized
210    SkASSERT(!(var->modifiers().fFlags & Modifiers::kConst_Flag) || value);
211    // 'const' variable initializer must be a constant expression
212    SkASSERT(!(var->modifiers().fFlags & Modifiers::kConst_Flag) ||
213             Analysis::IsConstantExpression(*value));
214    // global variable initializer must be a constant expression
215    SkASSERT(!(value && var->storage() == Variable::Storage::kGlobal &&
216               !Analysis::IsConstantExpression(*value)));
217    // opaque type not permitted on an interface block
218    SkASSERT(!(var->storage() == Variable::Storage::kInterfaceBlock && var->type().isOpaque()));
219    // initializers are not permitted on interface block fields
220    SkASSERT(!(var->storage() == Variable::Storage::kInterfaceBlock && value));
221    // opaque type cannot use initializer expressions
222    SkASSERT(!(value && var->type().isOpaque()));
223    // 'in' variables cannot use initializer expressions
224    SkASSERT(!(value && (var->modifiers().fFlags & Modifiers::kIn_Flag)));
225    // 'uniform' variables cannot use initializer expressions
226    SkASSERT(!(value && (var->modifiers().fFlags & Modifiers::kUniform_Flag)));
227
228    auto result = std::make_unique<VarDeclaration>(var, baseType, arraySize, std::move(value));
229    var->setDeclaration(result.get());
230    return std::move(result);
231}
232
233}  // namespace SkSL
234