John Stiles | e67bd13 | 2021-03-19 18:39:25 -0400 | [diff] [blame] | 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 | |
John Stiles | 2dda4b6 | 2021-09-16 13:18:10 -0400 | [diff] [blame^] | 8 | #include "src/sksl/ir/SkSLVarDeclarations.h" |
| 9 | |
| 10 | #include "include/sksl/SkSLErrorReporter.h" |
John Stiles | e67bd13 | 2021-03-19 18:39:25 -0400 | [diff] [blame] | 11 | #include "src/sksl/SkSLAnalysis.h" |
| 12 | #include "src/sksl/SkSLContext.h" |
| 13 | #include "src/sksl/SkSLProgramSettings.h" |
John Stiles | e67bd13 | 2021-03-19 18:39:25 -0400 | [diff] [blame] | 14 | |
| 15 | namespace SkSL { |
| 16 | |
| 17 | std::unique_ptr<Statement> VarDeclaration::clone() const { |
| 18 | return std::make_unique<VarDeclaration>(&this->var(), |
| 19 | &this->baseType(), |
| 20 | fArraySize, |
| 21 | this->value() ? this->value()->clone() : nullptr); |
| 22 | } |
| 23 | |
| 24 | String VarDeclaration::description() const { |
| 25 | String result = this->var().modifiers().description() + this->baseType().description() + " " + |
| 26 | this->var().name(); |
| 27 | if (this->arraySize() > 0) { |
| 28 | result.appendf("[%d]", this->arraySize()); |
John Stiles | e67bd13 | 2021-03-19 18:39:25 -0400 | [diff] [blame] | 29 | } |
| 30 | if (this->value()) { |
| 31 | result += " = " + this->value()->description(); |
| 32 | } |
| 33 | result += ";"; |
| 34 | return result; |
| 35 | } |
| 36 | |
| 37 | std::unique_ptr<Statement> VarDeclaration::Convert(const Context& context, |
| 38 | Variable* var, |
| 39 | std::unique_ptr<Expression> value) { |
John Stiles | 7bd3f1c | 2021-08-27 16:12:10 -0400 | [diff] [blame] | 40 | if (value) { |
| 41 | if (var->type().isOpaque()) { |
| 42 | context.fErrors->error(value->fOffset, "opaque type '" + var->type().name() + |
| 43 | "' cannot use initializer expressions"); |
| 44 | return nullptr; |
| 45 | } |
| 46 | if (var->modifiers().fFlags & Modifiers::kIn_Flag) { |
| 47 | context.fErrors->error(value->fOffset, |
| 48 | "'in' variables cannot use initializer expressions"); |
| 49 | return nullptr; |
| 50 | } |
| 51 | if (var->modifiers().fFlags & Modifiers::kUniform_Flag) { |
| 52 | context.fErrors->error(value->fOffset, |
| 53 | "'uniform' variables cannot use initializer expressions"); |
| 54 | return nullptr; |
| 55 | } |
| 56 | if (var->storage() == Variable::Storage::kInterfaceBlock) { |
| 57 | context.fErrors->error(value->fOffset, |
| 58 | "initializers are not permitted on interface block fields"); |
| 59 | return nullptr; |
| 60 | } |
| 61 | value = var->type().coerceExpression(std::move(value), context); |
| 62 | if (!value) { |
| 63 | return nullptr; |
| 64 | } |
| 65 | } |
John Stiles | e67bd13 | 2021-03-19 18:39:25 -0400 | [diff] [blame] | 66 | if (var->modifiers().fFlags & Modifiers::kConst_Flag) { |
| 67 | if (!value) { |
Ethan Nicholas | 39f6da4 | 2021-08-23 13:10:07 -0400 | [diff] [blame] | 68 | context.fErrors->error(var->fOffset, "'const' variables must be initialized"); |
John Stiles | e67bd13 | 2021-03-19 18:39:25 -0400 | [diff] [blame] | 69 | return nullptr; |
| 70 | } |
| 71 | if (!Analysis::IsConstantExpression(*value)) { |
Ethan Nicholas | 39f6da4 | 2021-08-23 13:10:07 -0400 | [diff] [blame] | 72 | context.fErrors->error(value->fOffset, |
Ethan Nicholas | 8d11654 | 2021-08-11 13:27:16 -0400 | [diff] [blame] | 73 | "'const' variable initializer must be a constant expression"); |
John Stiles | e67bd13 | 2021-03-19 18:39:25 -0400 | [diff] [blame] | 74 | return nullptr; |
| 75 | } |
| 76 | } |
John Stiles | 6efab71 | 2021-08-26 11:46:21 -0400 | [diff] [blame] | 77 | if (var->storage() == Variable::Storage::kInterfaceBlock) { |
| 78 | if (var->type().isOpaque()) { |
| 79 | context.fErrors->error(var->fOffset, "opaque type '" + var->type().name() + |
| 80 | "' is not permitted in an interface block"); |
| 81 | return nullptr; |
| 82 | } |
John Stiles | 6efab71 | 2021-08-26 11:46:21 -0400 | [diff] [blame] | 83 | } |
| 84 | if (var->storage() == Variable::Storage::kGlobal) { |
| 85 | if (value && !Analysis::IsConstantExpression(*value)) { |
Ethan Nicholas | 39f6da4 | 2021-08-23 13:10:07 -0400 | [diff] [blame] | 86 | context.fErrors->error(value->fOffset, |
Ethan Nicholas | 8d11654 | 2021-08-11 13:27:16 -0400 | [diff] [blame] | 87 | "global variable initializer must be a constant expression"); |
John Stiles | e67bd13 | 2021-03-19 18:39:25 -0400 | [diff] [blame] | 88 | return nullptr; |
| 89 | } |
John Stiles | 6efab71 | 2021-08-26 11:46:21 -0400 | [diff] [blame] | 90 | } |
John Stiles | e67bd13 | 2021-03-19 18:39:25 -0400 | [diff] [blame] | 91 | const Type* baseType = &var->type(); |
| 92 | int arraySize = 0; |
| 93 | if (baseType->isArray()) { |
| 94 | arraySize = baseType->columns(); |
| 95 | baseType = &baseType->componentType(); |
| 96 | } |
| 97 | return VarDeclaration::Make(context, var, baseType, arraySize, std::move(value)); |
| 98 | } |
| 99 | |
| 100 | std::unique_ptr<Statement> VarDeclaration::Make(const Context& context, |
| 101 | Variable* var, |
| 102 | const Type* baseType, |
| 103 | int arraySize, |
| 104 | std::unique_ptr<Expression> value) { |
| 105 | SkASSERT(!baseType->isArray()); |
John Stiles | 3b0d350 | 2021-08-26 14:33:46 -0400 | [diff] [blame] | 106 | // function parameters cannot have variable declarations |
| 107 | SkASSERT(var->storage() != Variable::Storage::kParameter); |
John Stiles | e67bd13 | 2021-03-19 18:39:25 -0400 | [diff] [blame] | 108 | // 'const' variables must be initialized |
| 109 | SkASSERT(!(var->modifiers().fFlags & Modifiers::kConst_Flag) || value); |
| 110 | // 'const' variable initializer must be a constant expression |
| 111 | SkASSERT(!(var->modifiers().fFlags & Modifiers::kConst_Flag) || |
| 112 | Analysis::IsConstantExpression(*value)); |
| 113 | // global variable initializer must be a constant expression |
| 114 | SkASSERT(!(value && var->storage() == Variable::Storage::kGlobal && |
John Stiles | e67bd13 | 2021-03-19 18:39:25 -0400 | [diff] [blame] | 115 | !Analysis::IsConstantExpression(*value))); |
John Stiles | 6efab71 | 2021-08-26 11:46:21 -0400 | [diff] [blame] | 116 | // opaque type not permitted on an interface block |
| 117 | SkASSERT(!(var->storage() == Variable::Storage::kInterfaceBlock && var->type().isOpaque())); |
| 118 | // initializers are not permitted on interface block fields |
| 119 | SkASSERT(!(var->storage() == Variable::Storage::kInterfaceBlock && value)); |
John Stiles | e67bd13 | 2021-03-19 18:39:25 -0400 | [diff] [blame] | 120 | // opaque type cannot use initializer expressions |
| 121 | SkASSERT(!(value && var->type().isOpaque())); |
| 122 | // 'in' variables cannot use initializer expressions |
| 123 | SkASSERT(!(value && (var->modifiers().fFlags & Modifiers::kIn_Flag))); |
| 124 | // 'uniform' variables cannot use initializer expressions |
| 125 | SkASSERT(!(value && (var->modifiers().fFlags & Modifiers::kUniform_Flag))); |
| 126 | |
John Stiles | b0d93cc | 2021-06-01 11:07:53 -0400 | [diff] [blame] | 127 | // Detect and report out-of-range initial-values for this variable. |
| 128 | if (value) { |
| 129 | var->type().checkForOutOfRangeLiteral(context, *value); |
| 130 | } |
| 131 | |
John Stiles | e67bd13 | 2021-03-19 18:39:25 -0400 | [diff] [blame] | 132 | auto result = std::make_unique<VarDeclaration>(var, baseType, arraySize, std::move(value)); |
| 133 | var->setDeclaration(result.get()); |
| 134 | return std::move(result); |
| 135 | } |
| 136 | |
| 137 | } // namespace SkSL |