Restored unsized array support to SkSL

This is a prerequisite for compute shaders. As of this CL, there isn't
yet a way to use unsized arrays, as it is a compute-only feature and
compute shaders are coming in a followup CL, but this adds the basic
framework and error tests.

Change-Id: I390c0961e324dd474474563bf9a8f6b34c9552a9
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/538900
Reviewed-by: John Stiles <johnstiles@google.com>
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
This commit is contained in:
Ethan Nicholas 2022-06-02 14:50:39 -04:00 committed by SkCQ
parent 1e5eab37e1
commit 9583759bbd
46 changed files with 268 additions and 34 deletions

View File

@ -20,13 +20,29 @@ sksl_error_tests = [
"/sksl/errors/ArraySplitDimensionsInFuncDecl.rts",
"/sksl/errors/ArraySplitDimensionsInStruct.rts",
"/sksl/errors/ArrayTooManyDimensions.rts",
"/sksl/errors/ArrayTooManyDimensionsUnsized1.rts",
"/sksl/errors/ArrayTooManyDimensionsUnsized2.rts",
"/sksl/errors/ArrayTooManyDimensionsInFuncBody.rts",
"/sksl/errors/ArrayTooManyDimensionsInFuncBodyUnsized1.rts",
"/sksl/errors/ArrayTooManyDimensionsInFuncBodyUnsized2.rts",
"/sksl/errors/ArrayTooManyDimensionsInFuncDecl.rts",
"/sksl/errors/ArrayTooManyDimensionsInFuncDeclUnsized1.rts",
"/sksl/errors/ArrayTooManyDimensionsInFuncDeclUnsized2.rts",
"/sksl/errors/ArrayTooManyDimensionsInStruct.rts",
"/sksl/errors/ArrayTooManyDimensionsInStructUnsized1.rts",
"/sksl/errors/ArrayTooManyDimensionsInStructUnsized2.rts",
"/sksl/errors/ArrayTypeTooManyDimensions.rts",
"/sksl/errors/ArrayTypeTooManyDimensionsUnsized1.rts",
"/sksl/errors/ArrayTypeTooManyDimensionsUnsized2.rts",
"/sksl/errors/ArrayTypeTooManyDimensionsInFuncBody.rts",
"/sksl/errors/ArrayTypeTooManyDimensionsInFuncBodyUnsized1.rts",
"/sksl/errors/ArrayTypeTooManyDimensionsInFuncBodyUnsized2.rts",
"/sksl/errors/ArrayTypeTooManyDimensionsInFuncDecl.rts",
"/sksl/errors/ArrayTypeTooManyDimensionsInFuncDeclUnsized1.rts",
"/sksl/errors/ArrayTypeTooManyDimensionsInFuncDeclUnsized2.rts",
"/sksl/errors/ArrayTypeTooManyDimensionsInStruct.rts",
"/sksl/errors/ArrayTypeTooManyDimensionsInStructUnsized1.rts",
"/sksl/errors/ArrayTypeTooManyDimensionsInStructUnsized2.rts",
"/sksl/errors/ArrayUnspecifiedDimensions.rts",
"/sksl/errors/AssignmentTypeMismatch.rts",
"/sksl/errors/BadCaps.sksl",

View File

@ -181,6 +181,7 @@ private:
friend DSLType Array(const DSLType& base, int count, Position pos);
friend DSLType Struct(std::string_view name, SkSpan<DSLField> fields, Position pos);
friend DSLType UnsizedArray(const DSLType& base, Position pos);
friend class DSLCore;
friend class DSLFunction;
friend class DSLVarBase;
@ -228,6 +229,8 @@ MATRIX_TYPE(Half)
DSLType Array(const DSLType& base, int count, Position pos = {});
DSLType UnsizedArray(const DSLType& base, Position pos = {});
class DSLField {
public:
DSLField(const DSLType type, std::string_view name,

View File

@ -43,7 +43,7 @@ array size out of bounds
array size must be an integer
array size must be an integer
array size must be an integer
expected array dimension
unsized arrays are not permitted here
integer is out of range for type 'int': 4000000000
integer is out of range for type 'int': 100000002004087734272
*%%*/

View File

@ -0,0 +1,5 @@
void func() { float x[][2]; }
/*%%*
unsized arrays are not permitted here
*%%*/

View File

@ -0,0 +1,5 @@
void func() { float x[2][]; }
/*%%*
unsized arrays are not permitted here
*%%*/

View File

@ -0,0 +1,5 @@
void func(float x[][2]) {}
/*%%*
unsized arrays are not permitted here
*%%*/

View File

@ -0,0 +1,5 @@
void func(float x[2][]) {}
/*%%*
unsized arrays are not permitted here
*%%*/

View File

@ -0,0 +1,5 @@
struct S { float x[][2]; };
/*%%*
unsized arrays are not permitted here
*%%*/

View File

@ -0,0 +1,5 @@
struct S { float x[2][]; };
/*%%*
unsized arrays are not permitted here
*%%*/

View File

@ -0,0 +1,5 @@
float x[][2];
/*%%*
unsized arrays are not permitted here
*%%*/

View File

@ -0,0 +1,5 @@
float x[2][];
/*%%*
unsized arrays are not permitted here
*%%*/

View File

@ -0,0 +1,5 @@
void func() { float[][2] x; }
/*%%*
unsized arrays are not permitted here
*%%*/

View File

@ -0,0 +1,5 @@
void func() { float[2][] x; }
/*%%*
unsized arrays are not permitted here
*%%*/

View File

@ -0,0 +1,5 @@
void func(float[][2] x) {}
/*%%*
unsized arrays are not permitted here
*%%*/

View File

@ -0,0 +1,5 @@
void func(float[2][] x) {}
/*%%*
unsized arrays are not permitted here
*%%*/

View File

@ -0,0 +1,5 @@
struct S { float[][2] x; };
/*%%*
unsized arrays are not permitted here
*%%*/

View File

@ -0,0 +1,5 @@
struct S { float[2][] x; };
/*%%*
unsized arrays are not permitted here
*%%*/

View File

@ -0,0 +1,5 @@
float[][2] x;
/*%%*
unsized arrays are not permitted here
*%%*/

View File

@ -0,0 +1,5 @@
float[2][] x;
/*%%*
unsized arrays are not permitted here
*%%*/

View File

@ -5,7 +5,7 @@ int arrBool[true];
int unsized_in_expression() { return int[](0)[0]; }
/*%%*
expected array dimension
unsized arrays are not permitted here
array size must be an integer
array size must be an integer
missing index in '[]'

View File

@ -28,6 +28,7 @@
#include "src/sksl/dsl/priv/DSL_priv.h"
#include "src/sksl/ir/SkSLExpression.h"
#include "src/sksl/ir/SkSLProgram.h"
#include "src/sksl/ir/SkSLType.h"
#include <algorithm>
#include <initializer_list>
@ -461,6 +462,11 @@ bool DSLParser::functionDeclarationEnd(Position start,
}
bool DSLParser::arraySize(SKSL_INT* outResult) {
Token next = this->peek();
if (next.fKind == Token::Kind::TK_RBRACKET) {
this->error(this->position(next), "unsized arrays are not permitted here");
return true;
}
DSLExpression sizeExpr = this->expression();
if (!sizeExpr.hasValue()) {
return false;
@ -492,7 +498,7 @@ bool DSLParser::parseArrayDimensions(Position pos, DSLType* type) {
Token next;
while (this->checkNext(Token::Kind::TK_LBRACKET, &next)) {
if (this->checkNext(Token::Kind::TK_RBRACKET)) {
this->error(this->rangeFrom(next), "expected array dimension");
this->error(this->rangeFrom(pos), "unsized arrays are not permitted here");
} else {
SKSL_INT size;
if (!this->arraySize(&size)) {
@ -967,17 +973,17 @@ DSLType DSLParser::type(DSLModifiers* modifiers) {
return DSLType(nullptr);
}
DSLType result(this->text(type), modifiers, this->position(type));
while (this->checkNext(Token::Kind::TK_LBRACKET)) {
if (this->peek().fKind != Token::Kind::TK_RBRACKET) {
Token bracket;
while (this->checkNext(Token::Kind::TK_LBRACKET, &bracket)) {
if (this->checkNext(Token::Kind::TK_RBRACKET)) {
this->error(this->rangeFrom(bracket), "unsized arrays are not permitted here");
} else {
SKSL_INT size;
if (!this->arraySize(&size)) {
return DSLType(nullptr);
}
this->expect(Token::Kind::TK_RBRACKET, "']'");
result = Array(result, size, this->rangeFrom(type));
} else {
this->error(this->peek(), "expected array dimension");
this->expect(Token::Kind::TK_RBRACKET, "']'");
}
}
return result;
@ -1023,7 +1029,7 @@ bool DSLParser::interfaceBlock(const dsl::DSLModifiers& modifiers) {
}
actualType = Array(std::move(actualType), size, this->position(typeName));
} else {
this->error(sizeToken, "unsized arrays are not permitted");
this->error(sizeToken, "unsized arrays are not permitted here");
}
this->expect(Token::Kind::TK_RBRACKET, "']'");
}

View File

@ -275,6 +275,11 @@ DSLType Array(const DSLType& base, int count, Position pos) {
return DSLType(ThreadContext::SymbolTable()->addArrayDimension(&base.skslType(), count), pos);
}
DSLType UnsizedArray(const DSLType& base, Position pos) {
return ThreadContext::SymbolTable()->addArrayDimension(&base.skslType(),
SkSL::Type::kUnsizedArray);
}
DSLType Struct(std::string_view name, SkSpan<DSLField> fields, Position pos) {
std::vector<SkSL::Type::Field> skslFields;
skslFields.reserve(fields.size());

View File

@ -20,10 +20,13 @@ namespace SkSL {
static bool index_out_of_range(const Context& context, Position pos, SKSL_INT index,
const Expression& base) {
if (index >= 0 && index < base.type().columns()) {
return false;
if (index >= 0) {
if (base.type().columns() == Type::kUnsizedArray) {
return false;
} else if (index < base.type().columns()) {
return false;
}
}
context.fErrors->error(pos, "index " + std::to_string(index) + " out of range for '" +
base.type().displayName() + "'");
return true;

View File

@ -126,8 +126,7 @@ public:
: INHERITED(name, abbrev, kTypeKind)
, fComponentType(componentType)
, fCount(count) {
// Only allow explicitly-sized arrays.
SkASSERT(count > 0);
SkASSERT(count > 0 || count == kUnsizedArray);
// Disallow multi-dimensional arrays.
SkASSERT(!componentType.is<ArrayType>());
}
@ -157,6 +156,7 @@ public:
}
size_t slotCount() const override {
SkASSERT(fCount != kUnsizedArray);
SkASSERT(fCount > 0);
return fCount * fComponentType.slotCount();
}
@ -522,6 +522,9 @@ private:
std::string Type::getArrayName(int arraySize) const {
std::string_view name = this->name();
if (arraySize == kUnsizedArray) {
return String::printf("%.*s[]", (int)name.size(), name.data());
}
return String::printf("%.*s[%d]", (int)name.size(), name.data(), arraySize);
}
@ -965,23 +968,30 @@ bool Type::checkForOutOfRangeLiteral(const Context& context, double value, Posit
return false;
}
bool Type::checkIfUsableInArray(const Context& context, Position arrayPos) const {
if (this->isArray()) {
context.fErrors->error(arrayPos, "multi-dimensional arrays are not supported");
return false;
}
if (this->isVoid()) {
context.fErrors->error(arrayPos, "type 'void' may not be used in an array");
return false;
}
if (this->isOpaque()) {
context.fErrors->error(arrayPos, "opaque type '" + std::string(this->name()) +
"' may not be used in an array");
return false;
}
return true;
}
SKSL_INT Type::convertArraySize(const Context& context, Position arrayPos,
std::unique_ptr<Expression> size) const {
size = context.fTypes.fInt->coerceExpression(std::move(size), context);
if (!size) {
return 0;
}
if (this->isArray()) {
context.fErrors->error(arrayPos, "multi-dimensional arrays are not supported");
return 0;
}
if (this->isVoid()) {
context.fErrors->error(arrayPos, "type 'void' may not be used in an array");
return 0;
}
if (this->isOpaque()) {
context.fErrors->error(arrayPos, "opaque type '" + std::string(this->name()) +
"' may not be used in an array");
if (!this->checkIfUsableInArray(context, arrayPos)) {
return 0;
}
SKSL_INT count;

View File

@ -58,7 +58,8 @@ class Type : public Symbol {
public:
inline static constexpr Kind kSymbolKind = Kind::kType;
inline static constexpr int kMaxAbbrevLength = 3;
// Represents unspecified array dimensions, as in `int[]`.
inline static constexpr int kUnsizedArray = -1;
struct Field {
Field(Position pos, Modifiers modifiers, std::string_view name, const Type* type)
: fPosition(pos)
@ -106,7 +107,7 @@ public:
Type(const Type& other) = delete;
/** Creates an array type. */
/** Creates an array type. `columns` may be kUnsizedArray. */
static std::unique_ptr<Type> MakeArrayType(std::string_view name, const Type& componentType,
int columns);
@ -526,9 +527,14 @@ public:
/** Checks if `value` can fit in this type. The type must be scalar. */
bool checkForOutOfRangeLiteral(const Context& context, double value, Position pos) const;
/**
* Reports errors and returns false if this type cannot be used as the base type for an array.
*/
bool checkIfUsableInArray(const Context& context, Position arrayPos) const;
/**
* Verifies that the expression is a valid constant array size for this type. Returns the array
* size, or zero if the expression isn't a valid literal value.
* size, or reports errors and returns zero if the expression isn't a valid literal value.
*/
SKSL_INT convertArraySize(const Context& context, Position arrayPos,
std::unique_ptr<Expression> size) const;

View File

@ -42,6 +42,9 @@ std::unique_ptr<Variable> Variable::Convert(const Context& context, Position pos
if (!context.fConfig->fIsBuiltinCode && skstd::starts_with(name, '$')) {
context.fErrors->error(namePos, "name '" + std::string(name) + "' is reserved");
}
if (baseType->isArray() && baseType->columns() == Type::kUnsizedArray) {
context.fErrors->error(pos, "unsized arrays are not permitted here");
}
return Make(context, pos, modifiersPos, modifiers, baseType, name, isArray,
std::move(arraySize), storage);

View File

@ -60,9 +60,9 @@ void g2() { float x[false]; }
error: 20: array size must be an integer
void h2() { float x[int2(2, 2)]; }
^^^^^^^^^^
error: 21: expected array dimension
error: 21: unsized arrays are not permitted here
void i2() { float x[]; }
^^
^^^^^^^^^
error: 22: integer is out of range for type 'int': 4000000000
void j2() { float x[int3(4000000000)]; }
^^^^^^^^^^

View File

@ -0,0 +1,6 @@
### Compilation failed:
error: 1: unsized arrays are not permitted here
void func() { float x[][2]; }
^^^^^^^^^
1 error

View File

@ -0,0 +1,6 @@
### Compilation failed:
error: 1: unsized arrays are not permitted here
void func() { float x[2][]; }
^^^^^^^^^^^^
1 error

View File

@ -0,0 +1,6 @@
### Compilation failed:
error: 1: unsized arrays are not permitted here
void func(float x[][2]) {}
^^^^^^^^^
1 error

View File

@ -0,0 +1,6 @@
### Compilation failed:
error: 1: unsized arrays are not permitted here
void func(float x[2][]) {}
^^^^^^^^^^^^
1 error

View File

@ -0,0 +1,9 @@
### Compilation failed:
error: 1: unsized arrays are not permitted here
struct S { float x[][2]; };
^
error: 1: multi-dimensional arrays are not supported
struct S { float x[][2]; };
^^^^^^^^^^^^
2 errors

View File

@ -0,0 +1,9 @@
### Compilation failed:
error: 1: unsized arrays are not permitted here
struct S { float x[2][]; };
^
error: 1: multi-dimensional arrays are not supported
struct S { float x[2][]; };
^^^^^^^^^^^^
2 errors

View File

@ -0,0 +1,6 @@
### Compilation failed:
error: 1: unsized arrays are not permitted here
float x[][2];
^^^^^^^^^
1 error

View File

@ -0,0 +1,6 @@
### Compilation failed:
error: 1: unsized arrays are not permitted here
float x[2][];
^^^^^^^^^^^^
1 error

View File

@ -0,0 +1,6 @@
### Compilation failed:
error: 1: unsized arrays are not permitted here
void func() { float[][2] x; }
^^
1 error

View File

@ -0,0 +1,6 @@
### Compilation failed:
error: 1: unsized arrays are not permitted here
void func() { float[2][] x; }
^^
1 error

View File

@ -0,0 +1,6 @@
### Compilation failed:
error: 1: unsized arrays are not permitted here
void func(float[][2] x) {}
^^
1 error

View File

@ -0,0 +1,6 @@
### Compilation failed:
error: 1: unsized arrays are not permitted here
void func(float[2][] x) {}
^^
1 error

View File

@ -0,0 +1,6 @@
### Compilation failed:
error: 1: unsized arrays are not permitted here
struct S { float[][2] x; };
^^
1 error

View File

@ -0,0 +1,6 @@
### Compilation failed:
error: 1: unsized arrays are not permitted here
struct S { float[2][] x; };
^^
1 error

View File

@ -0,0 +1,6 @@
### Compilation failed:
error: 1: unsized arrays are not permitted here
float[][2] x;
^^
1 error

View File

@ -0,0 +1,6 @@
### Compilation failed:
error: 1: unsized arrays are not permitted here
float[2][] x;
^^
1 error

View File

@ -1,8 +1,8 @@
### Compilation failed:
error: 1: expected array dimension
error: 1: unsized arrays are not permitted here
int arrUnsized[];
^^
^^^^^^^^^^^^^^^^
error: 2: array size must be an integer
int arrFloat[1.];
^^

View File

@ -1,6 +1,6 @@
### Compilation failed:
error: 1: expected expression, but found ']'
error: 1: unsized arrays are not permitted here
T { int x; } f[];
^
1 error

View File

@ -1,6 +1,6 @@
### Compilation failed:
error: 1: expected expression, but found ']'
error: 1: unsized arrays are not permitted here
k{int z;}m[];void main(){}
^
1 error