blob: 725544ccc441d4738e424048924dcf7cee9ba154 [file] [log] [blame]
/*
* Copyright 2021 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/sksl/SkSLAnalysis.h"
#include "src/sksl/SkSLContext.h"
#include "src/sksl/SkSLProgramSettings.h"
#include "src/sksl/ir/SkSLVarDeclarations.h"
namespace SkSL {
std::unique_ptr<Statement> VarDeclaration::clone() const {
return std::make_unique<VarDeclaration>(&this->var(),
&this->baseType(),
fArraySize,
this->value() ? this->value()->clone() : nullptr);
}
String VarDeclaration::description() const {
String result = this->var().modifiers().description() + this->baseType().description() + " " +
this->var().name();
if (this->arraySize() > 0) {
result.appendf("[%d]", this->arraySize());
}
if (this->value()) {
result += " = " + this->value()->description();
}
result += ";";
return result;
}
std::unique_ptr<Statement> VarDeclaration::Convert(const Context& context,
Variable* var,
std::unique_ptr<Expression> value) {
if (value) {
if (var->type().isOpaque()) {
context.fErrors->error(value->fOffset, "opaque type '" + var->type().name() +
"' cannot use initializer expressions");
return nullptr;
}
if (var->modifiers().fFlags & Modifiers::kIn_Flag) {
context.fErrors->error(value->fOffset,
"'in' variables cannot use initializer expressions");
return nullptr;
}
if (var->modifiers().fFlags & Modifiers::kUniform_Flag) {
context.fErrors->error(value->fOffset,
"'uniform' variables cannot use initializer expressions");
return nullptr;
}
if (var->storage() == Variable::Storage::kInterfaceBlock) {
context.fErrors->error(value->fOffset,
"initializers are not permitted on interface block fields");
return nullptr;
}
value = var->type().coerceExpression(std::move(value), context);
if (!value) {
return nullptr;
}
}
if (var->modifiers().fFlags & Modifiers::kConst_Flag) {
if (!value) {
context.fErrors->error(var->fOffset, "'const' variables must be initialized");
return nullptr;
}
if (!Analysis::IsConstantExpression(*value)) {
context.fErrors->error(value->fOffset,
"'const' variable initializer must be a constant expression");
return nullptr;
}
}
if (var->storage() == Variable::Storage::kInterfaceBlock) {
if (var->type().isOpaque()) {
context.fErrors->error(var->fOffset, "opaque type '" + var->type().name() +
"' is not permitted in an interface block");
return nullptr;
}
}
if (var->storage() == Variable::Storage::kGlobal) {
if (value && !Analysis::IsConstantExpression(*value)) {
context.fErrors->error(value->fOffset,
"global variable initializer must be a constant expression");
return nullptr;
}
}
const Type* baseType = &var->type();
int arraySize = 0;
if (baseType->isArray()) {
arraySize = baseType->columns();
baseType = &baseType->componentType();
}
return VarDeclaration::Make(context, var, baseType, arraySize, std::move(value));
}
std::unique_ptr<Statement> VarDeclaration::Make(const Context& context,
Variable* var,
const Type* baseType,
int arraySize,
std::unique_ptr<Expression> value) {
SkASSERT(!baseType->isArray());
// function parameters cannot have variable declarations
SkASSERT(var->storage() != Variable::Storage::kParameter);
// 'const' variables must be initialized
SkASSERT(!(var->modifiers().fFlags & Modifiers::kConst_Flag) || value);
// 'const' variable initializer must be a constant expression
SkASSERT(!(var->modifiers().fFlags & Modifiers::kConst_Flag) ||
Analysis::IsConstantExpression(*value));
// global variable initializer must be a constant expression
SkASSERT(!(value && var->storage() == Variable::Storage::kGlobal &&
!Analysis::IsConstantExpression(*value)));
// opaque type not permitted on an interface block
SkASSERT(!(var->storage() == Variable::Storage::kInterfaceBlock && var->type().isOpaque()));
// initializers are not permitted on interface block fields
SkASSERT(!(var->storage() == Variable::Storage::kInterfaceBlock && value));
// opaque type cannot use initializer expressions
SkASSERT(!(value && var->type().isOpaque()));
// 'in' variables cannot use initializer expressions
SkASSERT(!(value && (var->modifiers().fFlags & Modifiers::kIn_Flag)));
// 'uniform' variables cannot use initializer expressions
SkASSERT(!(value && (var->modifiers().fFlags & Modifiers::kUniform_Flag)));
// Detect and report out-of-range initial-values for this variable.
if (value) {
var->type().checkForOutOfRangeLiteral(context, *value);
}
auto result = std::make_unique<VarDeclaration>(var, baseType, arraySize, std::move(value));
var->setDeclaration(result.get());
return std::move(result);
}
} // namespace SkSL