Improve DSL error reporting with invalid array sizes

This eliminates an extra "expected int literal" error when the
array size is an invalid expression.

Change-Id: Iaf5d15316df3ec5200d51d73c14d7e428ce17be9
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/443236
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
This commit is contained in:
Ethan Nicholas 2021-08-31 16:12:40 -04:00 committed by SkCQ
parent 966723594d
commit 51b4b86da6
9 changed files with 43 additions and 28 deletions

View File

@ -124,13 +124,18 @@ public:
/** /**
* Returns true if this object contains an expression. DSLExpressions which were created with * Returns true if this object contains an expression. DSLExpressions which were created with
* the empty constructor or which have already been release()ed are not valid. DSLExpressions * the empty constructor or which have already been release()ed do not have a value.
* created with errors are still considered valid (but contain a poison value). * DSLExpressions created with errors are still considered to have a value (but contain poison).
*/ */
bool valid() const { bool hasValue() const {
return fExpression != nullptr; return fExpression != nullptr;
} }
/**
* Returns true if this object contains an expression which is not poison.
*/
bool isValid() const;
void swap(DSLExpression& other); void swap(DSLExpression& other);
/** /**
@ -141,9 +146,9 @@ public:
private: private:
/** /**
* Calls release if this expression is valid, otherwise returns null. * Calls release if this expression has a value, otherwise returns null.
*/ */
std::unique_ptr<SkSL::Expression> releaseIfValid(); std::unique_ptr<SkSL::Expression> releaseIfPossible();
/** /**
* Invalidates this object and returns the SkSL expression it represents coerced to the * Invalidates this object and returns the SkSL expression it represents coerced to the

View File

@ -48,10 +48,10 @@ public:
DSLStatement& operator=(DSLStatement&& other) = default; DSLStatement& operator=(DSLStatement&& other) = default;
bool valid() { return fStatement != nullptr; } bool hasValue() { return fStatement != nullptr; }
std::unique_ptr<SkSL::Statement> release() { std::unique_ptr<SkSL::Statement> release() {
SkASSERT(this->valid()); SkASSERT(this->hasValue());
return std::move(fStatement); return std::move(fStatement);
} }
@ -60,7 +60,7 @@ private:
DSLStatement(std::unique_ptr<SkSL::Expression> expr); DSLStatement(std::unique_ptr<SkSL::Expression> expr);
std::unique_ptr<SkSL::Statement> releaseIfValid() { std::unique_ptr<SkSL::Statement> releaseIfPossible() {
return std::move(fStatement); return std::move(fStatement);
} }
@ -90,7 +90,7 @@ public:
~DSLPossibleStatement(); ~DSLPossibleStatement();
bool valid() { return fStatement != nullptr; } bool hasValue() { return fStatement != nullptr; }
std::unique_ptr<SkSL::Statement> release() { std::unique_ptr<SkSL::Statement> release() {
return DSLStatement(std::move(*this)).release(); return DSLStatement(std::move(*this)).release();

View File

@ -336,6 +336,10 @@ static skstd::optional<DSLStatement> declaration_statements(SkTArray<DSLVar> var
return Declare(vars); return Declare(vars);
} }
static bool is_valid(const skstd::optional<DSLWrapper<DSLExpression>>& expr) {
return expr && expr->get().isValid();
}
SKSL_INT DSLParser::arraySize() { SKSL_INT DSLParser::arraySize() {
Token next = this->peek(); Token next = this->peek();
if (next.fKind == Token::Kind::TK_INT_LITERAL) { if (next.fKind == Token::Kind::TK_INT_LITERAL) {
@ -357,8 +361,10 @@ SKSL_INT DSLParser::arraySize() {
this->error(next, "array size must be positive"); this->error(next, "array size must be positive");
return 1; return 1;
} else { } else {
this->expression(); skstd::optional<DSLWrapper<DSLExpression>> expr = this->expression();
if (is_valid(expr)) {
this->error(next, "expected int literal"); this->error(next, "expected int literal");
}
return 1; return 1;
} }
} }

View File

@ -185,15 +185,15 @@ public:
static DSLPossibleStatement For(DSLStatement initializer, DSLExpression test, static DSLPossibleStatement For(DSLStatement initializer, DSLExpression test,
DSLExpression next, DSLStatement stmt, PositionInfo pos) { DSLExpression next, DSLStatement stmt, PositionInfo pos) {
return ForStatement::Convert(DSLWriter::Context(), /*offset=*/-1, return ForStatement::Convert(DSLWriter::Context(), /*offset=*/-1,
initializer.releaseIfValid(), test.releaseIfValid(), initializer.releaseIfPossible(), test.releaseIfPossible(),
next.releaseIfValid(), stmt.release(), next.releaseIfPossible(), stmt.release(),
DSLWriter::SymbolTable()); DSLWriter::SymbolTable());
} }
static DSLPossibleStatement If(DSLExpression test, DSLStatement ifTrue, DSLStatement ifFalse, static DSLPossibleStatement If(DSLExpression test, DSLStatement ifTrue, DSLStatement ifFalse,
bool isStatic) { bool isStatic) {
return IfStatement::Convert(DSLWriter::Context(), /*offset=*/-1, isStatic, test.release(), return IfStatement::Convert(DSLWriter::Context(), /*offset=*/-1, isStatic, test.release(),
ifTrue.release(), ifFalse.releaseIfValid()); ifTrue.release(), ifFalse.releaseIfPossible());
} }
static DSLGlobalVar InterfaceBlock(const DSLModifiers& modifiers, skstd::string_view typeName, static DSLGlobalVar InterfaceBlock(const DSLModifiers& modifiers, skstd::string_view typeName,
@ -248,7 +248,7 @@ public:
// this point we do not know the function's return type. We therefore do not check for // this point we do not know the function's return type. We therefore do not check for
// errors, or coerce the value to the correct type, until the return statement is actually // errors, or coerce the value to the correct type, until the return statement is actually
// added to a function. (This is done in FunctionDefinition::Convert.) // added to a function. (This is done in FunctionDefinition::Convert.)
return SkSL::ReturnStatement::Make(/*offset=*/-1, value.releaseIfValid()); return SkSL::ReturnStatement::Make(/*offset=*/-1, value.releaseIfPossible());
} }
static DSLExpression Swizzle(DSLExpression base, SkSL::SwizzleComponent::Type a, static DSLExpression Swizzle(DSLExpression base, SkSL::SwizzleComponent::Type a,
@ -301,7 +301,7 @@ public:
SkTArray<StatementArray> statements; SkTArray<StatementArray> statements;
statements.reserve_back(cases.count()); statements.reserve_back(cases.count());
for (DSLCase& c : cases) { for (DSLCase& c : cases) {
values.push_back(c.fValue.releaseIfValid()); values.push_back(c.fValue.releaseIfPossible());
statements.push_back(std::move(c.fStatements)); statements.push_back(std::move(c.fStatements));
} }
return DSLWriter::ConvertSwitch(value.release(), std::move(values), std::move(statements), return DSLWriter::ConvertSwitch(value.release(), std::move(values), std::move(statements),

View File

@ -35,7 +35,7 @@ DSLExpression::DSLExpression(DSLExpression&& other)
DSLExpression::DSLExpression(std::unique_ptr<SkSL::Expression> expression) DSLExpression::DSLExpression(std::unique_ptr<SkSL::Expression> expression)
: fExpression(std::move(expression)) { : fExpression(std::move(expression)) {
SkASSERT(this->valid()); SkASSERT(this->hasValue());
DSLWriter::ReportErrors(); DSLWriter::ReportErrors();
} }
@ -106,21 +106,25 @@ DSLExpression::~DSLExpression() {
"ProgramSettings::fAssertDSLObjectsReleased)"); "ProgramSettings::fAssertDSLObjectsReleased)");
} }
bool DSLExpression::isValid() const {
return this->hasValue() && !fExpression->is<SkSL::Poison>();
}
void DSLExpression::swap(DSLExpression& other) { void DSLExpression::swap(DSLExpression& other) {
std::swap(fExpression, other.fExpression); std::swap(fExpression, other.fExpression);
} }
std::unique_ptr<SkSL::Expression> DSLExpression::release() { std::unique_ptr<SkSL::Expression> DSLExpression::release() {
SkASSERT(this->valid()); SkASSERT(this->hasValue());
return std::move(fExpression); return std::move(fExpression);
} }
std::unique_ptr<SkSL::Expression> DSLExpression::releaseIfValid() { std::unique_ptr<SkSL::Expression> DSLExpression::releaseIfPossible() {
return std::move(fExpression); return std::move(fExpression);
} }
DSLType DSLExpression::type() { DSLType DSLExpression::type() {
if (!this->valid()) { if (!this->hasValue()) {
return kVoid_Type; return kVoid_Type;
} }
return &fExpression->type(); return &fExpression->type();

View File

@ -39,7 +39,7 @@ void DSLFunction::init(DSLModifiers modifiers, const DSLType& returnType, skstd:
if (param->fDeclared) { if (param->fDeclared) {
DSLWriter::ReportError("parameter has already been used in another function"); DSLWriter::ReportError("parameter has already been used in another function");
} }
SkASSERT(!param->fInitialValue.valid()); SkASSERT(!param->fInitialValue.hasValue());
SkASSERT(!param->fDeclaration); SkASSERT(!param->fDeclaration);
param->fDeclared = true; param->fDeclared = true;
std::unique_ptr<SkSL::Variable> paramVar = DSLWriter::CreateParameterVar(*param); std::unique_ptr<SkSL::Variable> paramVar = DSLWriter::CreateParameterVar(*param);

View File

@ -37,12 +37,12 @@ DSLStatement::DSLStatement(DSLExpression expr) {
DSLStatement::DSLStatement(std::unique_ptr<SkSL::Expression> expr) DSLStatement::DSLStatement(std::unique_ptr<SkSL::Expression> expr)
: fStatement(SkSL::ExpressionStatement::Make(DSLWriter::Context(), std::move(expr))) { : fStatement(SkSL::ExpressionStatement::Make(DSLWriter::Context(), std::move(expr))) {
SkASSERT(this->valid()); SkASSERT(this->hasValue());
} }
DSLStatement::DSLStatement(std::unique_ptr<SkSL::Statement> stmt) DSLStatement::DSLStatement(std::unique_ptr<SkSL::Statement> stmt)
: fStatement(std::move(stmt)) { : fStatement(std::move(stmt)) {
SkASSERT(this->valid()); SkASSERT(this->hasValue());
} }
DSLStatement::DSLStatement(DSLPossibleExpression expr, PositionInfo pos) DSLStatement::DSLStatement(DSLPossibleExpression expr, PositionInfo pos)
@ -50,7 +50,7 @@ DSLStatement::DSLStatement(DSLPossibleExpression expr, PositionInfo pos)
DSLStatement::DSLStatement(DSLPossibleStatement stmt, PositionInfo pos) { DSLStatement::DSLStatement(DSLPossibleStatement stmt, PositionInfo pos) {
DSLWriter::ReportErrors(pos); DSLWriter::ReportErrors(pos);
if (stmt.valid()) { if (stmt.hasValue()) {
fStatement = std::move(stmt.fStatement); fStatement = std::move(stmt.fStatement);
} else { } else {
fStatement = SkSL::Nop::Make(); fStatement = SkSL::Nop::Make();

View File

@ -30,7 +30,7 @@ DSLGlobalVar sk_SampleCoord() {
} }
DSLExpression SampleChild(int index, DSLExpression sampleExpr) { DSLExpression SampleChild(int index, DSLExpression sampleExpr) {
std::unique_ptr<SkSL::Expression> expr = sampleExpr.releaseIfValid(); std::unique_ptr<SkSL::Expression> expr = sampleExpr.releaseIfPossible();
if (expr) { if (expr) {
SkASSERT(expr->type().isVector()); SkASSERT(expr->type().isVector());
SkASSERT(expr->type().componentType().isFloat()); SkASSERT(expr->type().componentType().isFloat());

View File

@ -160,7 +160,7 @@ DSLPossibleExpression DSLWriter::Construct(const SkSL::Type& type, SkSpan<DSLExp
args.reserve_back(rawArgs.size()); args.reserve_back(rawArgs.size());
for (DSLExpression& arg : rawArgs) { for (DSLExpression& arg : rawArgs) {
if (!arg.valid()) { if (!arg.hasValue()) {
return DSLPossibleExpression(nullptr); return DSLPossibleExpression(nullptr);
} }
args.push_back(arg.release()); args.push_back(arg.release());
@ -268,7 +268,7 @@ const SkSL::Variable* DSLWriter::Var(DSLVarBase& var) {
// of DSLParser we don't even need DSL variables to show up in the symbol table in the // of DSLParser we don't even need DSL variables to show up in the symbol table in the
// first place. // first place.
var.fDeclaration = DSLWriter::IRGenerator().convertVarDeclaration( var.fDeclaration = DSLWriter::IRGenerator().convertVarDeclaration(
std::move(skslvar), var.fInitialValue.releaseIfValid(), std::move(skslvar), var.fInitialValue.releaseIfPossible(),
/*addToSymbolTable=*/false); /*addToSymbolTable=*/false);
if (var.fDeclaration) { if (var.fDeclaration) {
var.fVar = varPtr; var.fVar = varPtr;
@ -294,7 +294,7 @@ std::unique_ptr<SkSL::Statement> DSLWriter::Declaration(DSLVarBase& var) {
if (!var.fDeclaration) { if (!var.fDeclaration) {
// We should have already reported an error before ending up here, just clean up the // We should have already reported an error before ending up here, just clean up the
// initial value so it doesn't assert and return a nop. // initial value so it doesn't assert and return a nop.
var.fInitialValue.releaseIfValid(); var.fInitialValue.releaseIfPossible();
return SkSL::Nop::Make(); return SkSL::Nop::Make();
} }
return std::move(var.fDeclaration); return std::move(var.fDeclaration);