blob: 2a5a8bc5082554aaa16099f5f9b600433d191a65 [file] [log] [blame]
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/sksl/ir/SkSLType.h"
#include "src/sksl/SkSLConstantFolder.h"
#include "src/sksl/SkSLContext.h"
#include "src/sksl/ir/SkSLConstructor.h"
#include "src/sksl/ir/SkSLConstructorCompoundCast.h"
#include "src/sksl/ir/SkSLConstructorScalarCast.h"
#include "src/sksl/ir/SkSLFunctionReference.h"
#include "src/sksl/ir/SkSLSymbolTable.h"
#include "src/sksl/ir/SkSLType.h"
#include "src/sksl/ir/SkSLTypeReference.h"
namespace SkSL {
CoercionCost Type::coercionCost(const Type& other) const {
if (*this == other) {
return CoercionCost::Free();
}
if (this->isVector() && other.isVector()) {
if (this->columns() == other.columns()) {
return this->componentType().coercionCost(other.componentType());
}
return CoercionCost::Impossible();
}
if (this->isMatrix()) {
if (this->columns() == other.columns() && this->rows() == other.rows()) {
return this->componentType().coercionCost(other.componentType());
}
return CoercionCost::Impossible();
}
if (this->isNumber() && other.isNumber()) {
if (this->isLiteral() && this->isInteger()) {
return CoercionCost::Free();
} else if (this->numberKind() != other.numberKind()) {
return CoercionCost::Impossible();
} else if (other.priority() >= this->priority()) {
return CoercionCost::Normal(other.priority() - this->priority());
} else {
return CoercionCost::Narrowing(this->priority() - other.priority());
}
}
for (size_t i = 0; i < fCoercibleTypes.size(); i++) {
if (*fCoercibleTypes[i] == other) {
return CoercionCost::Normal((int) i + 1);
}
}
return CoercionCost::Impossible();
}
const Type& Type::toCompound(const Context& context, int columns, int rows) const {
SkASSERT(this->isScalar());
if (columns == 1 && rows == 1) {
return *this;
}
if (*this == *context.fTypes.fFloat || *this == *context.fTypes.fFloatLiteral) {
switch (rows) {
case 1:
switch (columns) {
case 1: return *context.fTypes.fFloat;
case 2: return *context.fTypes.fFloat2;
case 3: return *context.fTypes.fFloat3;
case 4: return *context.fTypes.fFloat4;
default: SK_ABORT("unsupported vector column count (%d)", columns);
}
case 2:
switch (columns) {
case 2: return *context.fTypes.fFloat2x2;
case 3: return *context.fTypes.fFloat3x2;
case 4: return *context.fTypes.fFloat4x2;
default: SK_ABORT("unsupported matrix column count (%d)", columns);
}
case 3:
switch (columns) {
case 2: return *context.fTypes.fFloat2x3;
case 3: return *context.fTypes.fFloat3x3;
case 4: return *context.fTypes.fFloat4x3;
default: SK_ABORT("unsupported matrix column count (%d)", columns);
}
case 4:
switch (columns) {
case 2: return *context.fTypes.fFloat2x4;
case 3: return *context.fTypes.fFloat3x4;
case 4: return *context.fTypes.fFloat4x4;
default: SK_ABORT("unsupported matrix column count (%d)", columns);
}
default: SK_ABORT("unsupported row count (%d)", rows);
}
} else if (*this == *context.fTypes.fHalf) {
switch (rows) {
case 1:
switch (columns) {
case 1: return *context.fTypes.fHalf;
case 2: return *context.fTypes.fHalf2;
case 3: return *context.fTypes.fHalf3;
case 4: return *context.fTypes.fHalf4;
default: SK_ABORT("unsupported vector column count (%d)", columns);
}
case 2:
switch (columns) {
case 2: return *context.fTypes.fHalf2x2;
case 3: return *context.fTypes.fHalf3x2;
case 4: return *context.fTypes.fHalf4x2;
default: SK_ABORT("unsupported matrix column count (%d)", columns);
}
case 3:
switch (columns) {
case 2: return *context.fTypes.fHalf2x3;
case 3: return *context.fTypes.fHalf3x3;
case 4: return *context.fTypes.fHalf4x3;
default: SK_ABORT("unsupported matrix column count (%d)", columns);
}
case 4:
switch (columns) {
case 2: return *context.fTypes.fHalf2x4;
case 3: return *context.fTypes.fHalf3x4;
case 4: return *context.fTypes.fHalf4x4;
default: SK_ABORT("unsupported matrix column count (%d)", columns);
}
default: SK_ABORT("unsupported row count (%d)", rows);
}
} else if (*this == *context.fTypes.fInt || *this == *context.fTypes.fIntLiteral) {
switch (rows) {
case 1:
switch (columns) {
case 1: return *context.fTypes.fInt;
case 2: return *context.fTypes.fInt2;
case 3: return *context.fTypes.fInt3;
case 4: return *context.fTypes.fInt4;
default: SK_ABORT("unsupported vector column count (%d)", columns);
}
default: SK_ABORT("unsupported row count (%d)", rows);
}
} else if (*this == *context.fTypes.fShort) {
switch (rows) {
case 1:
switch (columns) {
case 1: return *context.fTypes.fShort;
case 2: return *context.fTypes.fShort2;
case 3: return *context.fTypes.fShort3;
case 4: return *context.fTypes.fShort4;
default: SK_ABORT("unsupported vector column count (%d)", columns);
}
default: SK_ABORT("unsupported row count (%d)", rows);
}
} else if (*this == *context.fTypes.fUInt) {
switch (rows) {
case 1:
switch (columns) {
case 1: return *context.fTypes.fUInt;
case 2: return *context.fTypes.fUInt2;
case 3: return *context.fTypes.fUInt3;
case 4: return *context.fTypes.fUInt4;
default: SK_ABORT("unsupported vector column count (%d)", columns);
}
default: SK_ABORT("unsupported row count (%d)", rows);
}
} else if (*this == *context.fTypes.fUShort) {
switch (rows) {
case 1:
switch (columns) {
case 1: return *context.fTypes.fUShort;
case 2: return *context.fTypes.fUShort2;
case 3: return *context.fTypes.fUShort3;
case 4: return *context.fTypes.fUShort4;
default: SK_ABORT("unsupported vector column count (%d)", columns);
}
default: SK_ABORT("unsupported row count (%d)", rows);
}
} else if (*this == *context.fTypes.fBool) {
switch (rows) {
case 1:
switch (columns) {
case 1: return *context.fTypes.fBool;
case 2: return *context.fTypes.fBool2;
case 3: return *context.fTypes.fBool3;
case 4: return *context.fTypes.fBool4;
default: SK_ABORT("unsupported vector column count (%d)", columns);
}
default: SK_ABORT("unsupported row count (%d)", rows);
}
}
SkDEBUGFAILF("unsupported toCompound type %s", this->description().c_str());
return *context.fTypes.fVoid;
}
const Type* Type::clone(SymbolTable* symbolTable) const {
// Many types are built-ins, and exist in every SymbolTable by default.
if (this->isInBuiltinTypes()) {
return this;
}
// Even if the type isn't a built-in, it might already exist in the SymbolTable.
const Symbol* clonedSymbol = (*symbolTable)[this->name()];
if (clonedSymbol != nullptr) {
const Type& clonedType = clonedSymbol->as<Type>();
SkASSERT(clonedType.typeKind() == this->typeKind());
return &clonedType;
}
// This type actually needs to be cloned into the destination SymbolTable.
switch (this->typeKind()) {
case TypeKind::kArray:
return symbolTable->add(Type::MakeArrayType(String(this->name()), this->componentType(),
this->columns()));
case TypeKind::kStruct:
return symbolTable->add(Type::MakeStructType(this->fOffset, String(this->name()),
this->fields()));
case TypeKind::kEnum:
return symbolTable->add(Type::MakeEnumType(String(this->name())));
default:
SkDEBUGFAILF("don't know how to clone type '%s'", this->description().c_str());
return nullptr;
}
}
std::unique_ptr<Expression> Type::coerceExpression(std::unique_ptr<Expression> expr,
const Context& context) const {
if (!expr) {
return nullptr;
}
const int offset = expr->fOffset;
if (expr->is<FunctionReference>()) {
context.fErrors.error(offset, "expected '(' to begin function call");
return nullptr;
}
if (expr->is<TypeReference>()) {
context.fErrors.error(offset, "expected '(' to begin constructor invocation");
return nullptr;
}
if (expr->type() == *this) {
return expr;
}
const Program::Settings& settings = context.fConfig->fSettings;
if (!expr->coercionCost(*this).isPossible(settings.fAllowNarrowingConversions)) {
context.fErrors.error(offset, "expected '" + this->displayName() + "', but found '" +
expr->type().displayName() + "'");
return nullptr;
}
if (this->isScalar()) {
return ConstructorScalarCast::Make(context, offset, *this, std::move(expr));
}
if (this->isVector() || this->isMatrix()) {
return ConstructorCompoundCast::Make(context, offset, *this, std::move(expr));
}
context.fErrors.error(offset, "cannot construct '" + this->displayName() + "'");
return nullptr;
}
bool Type::isOrContainsArray() const {
if (this->isStruct()) {
for (const Field& f : this->fields()) {
if (f.fType->isOrContainsArray()) {
return true;
}
}
return false;
}
return this->isArray();
}
bool Type::checkForOutOfRangeLiteral(const Context& context, const Expression& expr) const {
bool foundError = false;
const Type& baseType = this->componentType();
if (baseType.isInteger()) {
// Replace constant expressions with their corresponding values.
const Expression* valueExpr = ConstantFolder::GetConstantValueForVariable(expr);
// Iterate over every constant subexpression in the value.
int numSlots = valueExpr->type().slotCount();
for (int slot = 0; slot < numSlots; ++slot) {
const Expression* subexpr = valueExpr->getConstantSubexpression(slot);
if (!subexpr || !subexpr->is<IntLiteral>()) {
continue;
}
// Look for an IntLiteral value that is out of range for the corresponding type.
SKSL_INT value = subexpr->as<IntLiteral>().value();
if (value < baseType.minimumValue() || value > baseType.maximumValue()) {
// We found a value that can't fit in the type. Flag it as an error.
context.fErrors.error(expr.fOffset,
String("integer is out of range for type '") +
this->displayName().c_str() + "': " + to_string(value));
foundError = true;
}
}
}
// We don't need range checks for floats or booleans; any matched-type value is acceptable.
return foundError;
}
} // namespace SkSL