[resolver]: Begin constant value evaluation

Move the bulk of the constant evaulation logic out of transform::FoldConstants and into Resolver and sem::Expression.

transform::FoldConstants now replace TypeConstructor nodes that have a constant value on the expression.

This is ground work to:
* Cleaning up the HLSL uniform buffer indexing, which is `/` and `%` arithmatic heavy
* Prepares us to handle `constexpr` when it lands in the spec
* Provide a centralized place to do constant evaluation, instead of the
  having similar logic scattered around the codebase.

Change-Id: I3e2f542be692046a8d243b62a82556db519953e7
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/57426
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Reviewed-by: James Price <jrprice@google.com>
This commit is contained in:
Ben Clayton
2021-07-13 12:18:13 +00:00
parent c6bcab02fd
commit 71f619b6f1
22 changed files with 992 additions and 398 deletions

View File

@@ -2067,7 +2067,7 @@ bool Resolver::ArrayAccessor(ast::ArrayAccessorExpression* expr) {
ret = builder_->create<sem::Reference>(ret, ref->StorageClass(),
ref->Access());
}
SetType(expr, ret);
SetExprInfo(expr, ret);
return true;
}
@@ -2085,7 +2085,7 @@ bool Resolver::Bitcast(ast::BitcastExpression* expr) {
AddError("cannot cast to a pointer", expr->source());
return false;
}
SetType(expr, ty, expr->type()->FriendlyName(builder_->Symbols()));
SetExprInfo(expr, ty, expr->type()->FriendlyName(builder_->Symbols()));
return true;
}
@@ -2167,7 +2167,7 @@ bool Resolver::IntrinsicCall(ast::CallExpression* call,
builder_->Sem().Add(
call, builder_->create<sem::Call>(call, result, current_statement_));
SetType(call, result->ReturnType());
SetExprInfo(call, result->ReturnType());
current_function_->intrinsic_calls.emplace_back(
IntrinsicCallInfo{call, result});
@@ -2239,7 +2239,7 @@ bool Resolver::FunctionCall(const ast::CallExpression* call) {
function_calls_.emplace(call,
FunctionCallInfo{callee_func, current_statement_});
SetType(call, callee_func->return_type, callee_func->return_type_name);
SetExprInfo(call, callee_func->return_type, callee_func->return_type_name);
return true;
}
@@ -2258,7 +2258,7 @@ bool Resolver::Constructor(ast::ConstructorExpression* expr) {
return false;
}
SetType(expr, type, type_ctor->type()->FriendlyName(builder_->Symbols()));
auto type_name = type_ctor->type()->FriendlyName(builder_->Symbols());
// Now that the argument types have been determined, make sure that they
// obey the constructor type rules laid out in
@@ -2267,33 +2267,38 @@ bool Resolver::Constructor(ast::ConstructorExpression* expr) {
AddError("cannot cast to a pointer", expr->source());
return false;
}
bool ok = true;
if (auto* vec_type = type->As<sem::Vector>()) {
return ValidateVectorConstructor(type_ctor, vec_type);
ok = ValidateVectorConstructor(type_ctor, vec_type, type_name);
} else if (auto* mat_type = type->As<sem::Matrix>()) {
ok = ValidateMatrixConstructor(type_ctor, mat_type, type_name);
} else if (type->is_scalar()) {
ok = ValidateScalarConstructor(type_ctor, type, type_name);
} else if (auto* arr_type = type->As<sem::Array>()) {
ok = ValidateArrayConstructor(type_ctor, arr_type);
} else if (auto* struct_type = type->As<sem::Struct>()) {
ok = ValidateStructureConstructor(type_ctor, struct_type);
}
if (auto* mat_type = type->As<sem::Matrix>()) {
return ValidateMatrixConstructor(type_ctor, mat_type);
if (!ok) {
return false;
}
if (type->is_scalar()) {
return ValidateScalarConstructor(type_ctor, type);
}
if (auto* arr_type = type->As<sem::Array>()) {
return ValidateArrayConstructor(type_ctor, arr_type);
}
if (auto* struct_type = type->As<sem::Struct>()) {
return ValidateStructureConstructor(type_ctor, struct_type);
}
} else if (auto* scalar_ctor = expr->As<ast::ScalarConstructorExpression>()) {
SetExprInfo(expr, type, type_name);
return true;
}
if (auto* scalar_ctor = expr->As<ast::ScalarConstructorExpression>()) {
Mark(scalar_ctor->literal());
auto* type = TypeOf(scalar_ctor->literal());
if (!type) {
return false;
}
SetType(expr, type);
} else {
TINT_ICE(Resolver, diagnostics_)
<< "unexpected constructor expression type";
SetExprInfo(expr, type);
return true;
}
return true;
TINT_ICE(Resolver, diagnostics_) << "unexpected constructor expression type";
return false;
}
bool Resolver::ValidateStructureConstructor(
@@ -2366,7 +2371,8 @@ bool Resolver::ValidateArrayConstructor(
bool Resolver::ValidateVectorConstructor(
const ast::TypeConstructorExpression* ctor,
const sem::Vector* vec_type) {
const sem::Vector* vec_type,
const std::string& type_name) {
auto& values = ctor->values();
auto* elem_type = vec_type->type();
size_t value_cardinality_sum = 0;
@@ -2423,7 +2429,7 @@ bool Resolver::ValidateVectorConstructor(
}
const Source& values_start = values[0]->source();
const Source& values_end = values[values.size() - 1]->source();
AddError("attempted to construct '" + TypeNameOf(ctor) + "' with " +
AddError("attempted to construct '" + type_name + "' with " +
std::to_string(value_cardinality_sum) + " component(s)",
Source::Combine(values_start, values_end));
return false;
@@ -2450,7 +2456,8 @@ bool Resolver::ValidateMatrix(const sem::Matrix* ty, const Source& source) {
bool Resolver::ValidateMatrixConstructor(
const ast::TypeConstructorExpression* ctor,
const sem::Matrix* matrix_type) {
const sem::Matrix* matrix_type,
const std::string& type_name) {
auto& values = ctor->values();
// Zero Value expression
if (values.empty()) {
@@ -2467,8 +2474,8 @@ bool Resolver::ValidateMatrixConstructor(
const Source& values_end = values[values.size() - 1]->source();
AddError("expected " + std::to_string(matrix_type->columns()) + " '" +
VectorPretty(matrix_type->rows(), elem_type) +
"' arguments in '" + TypeNameOf(ctor) +
"' constructor, found " + std::to_string(values.size()),
"' arguments in '" + type_name + "' constructor, found " +
std::to_string(values.size()),
Source::Combine(values_start, values_end));
return false;
}
@@ -2481,8 +2488,8 @@ bool Resolver::ValidateMatrixConstructor(
elem_type != value_vec->type()) {
AddError("expected argument type '" +
VectorPretty(matrix_type->rows(), elem_type) + "' in '" +
TypeNameOf(ctor) + "' constructor, found '" +
TypeNameOf(value) + "'",
type_name + "' constructor, found '" + TypeNameOf(value) +
"'",
value->source());
return false;
}
@@ -2493,7 +2500,8 @@ bool Resolver::ValidateMatrixConstructor(
bool Resolver::ValidateScalarConstructor(
const ast::TypeConstructorExpression* ctor,
const sem::Type* type) {
const sem::Type* type,
const std::string& type_name) {
if (ctor->values().size() == 0) {
return true;
}
@@ -2519,8 +2527,8 @@ bool Resolver::ValidateScalarConstructor(
(type->Is<U32>() && value_type->IsAnyOf<I32, U32, F32>()) ||
(type->Is<F32>() && value_type->IsAnyOf<I32, U32, F32>());
if (!is_valid) {
AddError("cannot construct '" + TypeNameOf(ctor) +
"' with a value of type '" + TypeNameOf(value) + "'",
AddError("cannot construct '" + type_name + "' with a value of type '" +
TypeNameOf(value) + "'",
ctor->source());
return false;
@@ -2533,7 +2541,7 @@ bool Resolver::Identifier(ast::IdentifierExpression* expr) {
auto symbol = expr->symbol();
VariableInfo* var;
if (variable_stack_.get(symbol, &var)) {
SetType(expr, var->type, var->type_name);
SetExprInfo(expr, var->type, var->type_name);
var->users.push_back(expr);
set_referenced_from_function_if_needed(var, true);
@@ -2707,7 +2715,7 @@ bool Resolver::MemberAccessor(ast::MemberAccessorExpression* expr) {
return false;
}
SetType(expr, ret);
SetExprInfo(expr, ret);
return true;
}
@@ -2745,17 +2753,17 @@ bool Resolver::Binary(ast::BinaryExpression* expr) {
// Binary logical expressions
if (expr->IsLogicalAnd() || expr->IsLogicalOr()) {
if (matching_types && lhs_type->Is<Bool>()) {
SetType(expr, lhs_type);
SetExprInfo(expr, lhs_type);
return true;
}
}
if (expr->IsOr() || expr->IsAnd()) {
if (matching_types && lhs_type->Is<Bool>()) {
SetType(expr, lhs_type);
SetExprInfo(expr, lhs_type);
return true;
}
if (matching_types && lhs_vec_elem_type && lhs_vec_elem_type->Is<Bool>()) {
SetType(expr, lhs_type);
SetExprInfo(expr, lhs_type);
return true;
}
}
@@ -2764,14 +2772,14 @@ bool Resolver::Binary(ast::BinaryExpression* expr) {
if (expr->IsArithmetic()) {
// Binary arithmetic expressions over scalars
if (matching_types && lhs_type->is_numeric_scalar()) {
SetType(expr, lhs_type);
SetExprInfo(expr, lhs_type);
return true;
}
// Binary arithmetic expressions over vectors
if (matching_types && lhs_vec_elem_type &&
lhs_vec_elem_type->is_numeric_scalar()) {
SetType(expr, lhs_type);
SetExprInfo(expr, lhs_type);
return true;
}
@@ -2779,22 +2787,22 @@ bool Resolver::Binary(ast::BinaryExpression* expr) {
if (lhs_vec_elem_type && (lhs_vec_elem_type == rhs_type)) {
if (expr->IsModulo()) {
if (rhs_type->is_integer_scalar()) {
SetType(expr, lhs_type);
SetExprInfo(expr, lhs_type);
return true;
}
} else if (rhs_type->is_numeric_scalar()) {
SetType(expr, lhs_type);
SetExprInfo(expr, lhs_type);
return true;
}
}
if (rhs_vec_elem_type && (rhs_vec_elem_type == lhs_type)) {
if (expr->IsModulo()) {
if (lhs_type->is_integer_scalar()) {
SetType(expr, rhs_type);
SetExprInfo(expr, rhs_type);
return true;
}
} else if (lhs_type->is_numeric_scalar()) {
SetType(expr, rhs_type);
SetExprInfo(expr, rhs_type);
return true;
}
}
@@ -2811,19 +2819,19 @@ bool Resolver::Binary(ast::BinaryExpression* expr) {
rhs_mat_elem_type->Is<F32>() &&
(lhs_mat->columns() == rhs_mat->columns()) &&
(lhs_mat->rows() == rhs_mat->rows())) {
SetType(expr, rhs_type);
SetExprInfo(expr, rhs_type);
return true;
}
if (expr->IsMultiply()) {
// Multiplication of a matrix and a scalar
if (lhs_type->Is<F32>() && rhs_mat_elem_type &&
rhs_mat_elem_type->Is<F32>()) {
SetType(expr, rhs_type);
SetExprInfo(expr, rhs_type);
return true;
}
if (lhs_mat_elem_type && lhs_mat_elem_type->Is<F32>() &&
rhs_type->Is<F32>()) {
SetType(expr, lhs_type);
SetExprInfo(expr, lhs_type);
return true;
}
@@ -2831,8 +2839,8 @@ bool Resolver::Binary(ast::BinaryExpression* expr) {
if (lhs_vec_elem_type && lhs_vec_elem_type->Is<F32>() &&
rhs_mat_elem_type && rhs_mat_elem_type->Is<F32>() &&
(lhs_vec->size() == rhs_mat->rows())) {
SetType(expr, builder_->create<sem::Vector>(lhs_vec->type(),
rhs_mat->columns()));
SetExprInfo(expr, builder_->create<sem::Vector>(lhs_vec->type(),
rhs_mat->columns()));
return true;
}
@@ -2840,8 +2848,8 @@ bool Resolver::Binary(ast::BinaryExpression* expr) {
if (lhs_mat_elem_type && lhs_mat_elem_type->Is<F32>() &&
rhs_vec_elem_type && rhs_vec_elem_type->Is<F32>() &&
(lhs_mat->columns() == rhs_vec->size())) {
SetType(expr,
builder_->create<sem::Vector>(rhs_vec->type(), lhs_mat->rows()));
SetExprInfo(expr, builder_->create<sem::Vector>(rhs_vec->type(),
lhs_mat->rows()));
return true;
}
@@ -2849,10 +2857,10 @@ bool Resolver::Binary(ast::BinaryExpression* expr) {
if (lhs_mat_elem_type && lhs_mat_elem_type->Is<F32>() &&
rhs_mat_elem_type && rhs_mat_elem_type->Is<F32>() &&
(lhs_mat->columns() == rhs_mat->rows())) {
SetType(expr, builder_->create<sem::Matrix>(
builder_->create<sem::Vector>(lhs_mat_elem_type,
lhs_mat->rows()),
rhs_mat->columns()));
SetExprInfo(expr, builder_->create<sem::Matrix>(
builder_->create<sem::Vector>(lhs_mat_elem_type,
lhs_mat->rows()),
rhs_mat->columns()));
return true;
}
}
@@ -2862,13 +2870,13 @@ bool Resolver::Binary(ast::BinaryExpression* expr) {
if (matching_types) {
// Special case for bools: only == and !=
if (lhs_type->Is<Bool>() && (expr->IsEqual() || expr->IsNotEqual())) {
SetType(expr, builder_->create<sem::Bool>());
SetExprInfo(expr, builder_->create<sem::Bool>());
return true;
}
// For the rest, we can compare i32, u32, and f32
if (lhs_type->IsAnyOf<I32, U32, F32>()) {
SetType(expr, builder_->create<sem::Bool>());
SetExprInfo(expr, builder_->create<sem::Bool>());
return true;
}
}
@@ -2877,14 +2885,14 @@ bool Resolver::Binary(ast::BinaryExpression* expr) {
if (matching_vec_elem_types) {
if (lhs_vec_elem_type->Is<Bool>() &&
(expr->IsEqual() || expr->IsNotEqual())) {
SetType(expr, builder_->create<sem::Vector>(
builder_->create<sem::Bool>(), lhs_vec->size()));
SetExprInfo(expr, builder_->create<sem::Vector>(
builder_->create<sem::Bool>(), lhs_vec->size()));
return true;
}
if (lhs_vec_elem_type->is_numeric_scalar()) {
SetType(expr, builder_->create<sem::Vector>(
builder_->create<sem::Bool>(), lhs_vec->size()));
SetExprInfo(expr, builder_->create<sem::Vector>(
builder_->create<sem::Bool>(), lhs_vec->size()));
return true;
}
}
@@ -2893,7 +2901,7 @@ bool Resolver::Binary(ast::BinaryExpression* expr) {
// Binary bitwise operations
if (expr->IsBitwise()) {
if (matching_types && lhs_type->is_integer_scalar_or_vector()) {
SetType(expr, lhs_type);
SetExprInfo(expr, lhs_type);
return true;
}
}
@@ -2905,13 +2913,13 @@ bool Resolver::Binary(ast::BinaryExpression* expr) {
// logical depending on lhs type).
if (lhs_type->IsAnyOf<I32, U32>() && rhs_type->Is<U32>()) {
SetType(expr, lhs_type);
SetExprInfo(expr, lhs_type);
return true;
}
if (lhs_vec_elem_type && lhs_vec_elem_type->IsAnyOf<I32, U32>() &&
rhs_vec_elem_type && rhs_vec_elem_type->Is<U32>()) {
SetType(expr, lhs_type);
SetExprInfo(expr, lhs_type);
return true;
}
}
@@ -3002,7 +3010,7 @@ bool Resolver::UnaryOp(ast::UnaryOpExpression* unary) {
break;
}
SetType(unary, type);
SetExprInfo(unary, type);
return true;
}
@@ -3131,18 +3139,20 @@ sem::Type* Resolver::TypeOf(const ast::Literal* lit) {
return nullptr;
}
void Resolver::SetType(const ast::Expression* expr, const sem::Type* type) {
SetType(expr, type, type->FriendlyName(builder_->Symbols()));
}
void Resolver::SetType(const ast::Expression* expr,
const sem::Type* type,
const std::string& type_name) {
void Resolver::SetExprInfo(const ast::Expression* expr,
const sem::Type* type,
std::string type_name) {
if (expr_info_.count(expr)) {
TINT_ICE(Resolver, diagnostics_)
<< "SetType() called twice for the same expression";
<< "SetExprInfo() called twice for the same expression";
}
expr_info_.emplace(expr, ExpressionInfo{type, type_name, current_statement_});
if (type_name.empty()) {
type_name = type->FriendlyName(builder_->Symbols());
}
auto constant_value = EvaluateConstantValue(expr, type);
expr_info_.emplace(
expr, ExpressionInfo{type, std::move(type_name), current_statement_,
std::move(constant_value)});
}
bool Resolver::ValidatePipelineStages() {
@@ -3313,10 +3323,11 @@ void Resolver::CreateSemanticNodes() const {
// Create semantic node for the identifier expression if necessary
auto* sem_expr = sem.Get(user);
if (sem_expr == nullptr) {
auto* type = expr_info_.at(user).type;
auto* stmt = expr_info_.at(user).statement;
auto* sem_user =
builder_->create<sem::VariableUser>(user, type, stmt, sem_var);
auto& expr_info = expr_info_.at(user);
auto* type = expr_info.type;
auto* stmt = expr_info.statement;
auto* sem_user = builder_->create<sem::VariableUser>(
user, type, stmt, sem_var, expr_info.constant_value);
sem_var->AddUser(sem_user);
sem.Add(user, sem_user);
} else {
@@ -3372,9 +3383,9 @@ void Resolver::CreateSemanticNodes() const {
// Expression has already been assigned a semantic node
continue;
}
sem.Add(expr,
builder_->create<sem::Expression>(
const_cast<ast::Expression*>(expr), info.type, info.statement));
sem.Add(expr, builder_->create<sem::Expression>(
const_cast<ast::Expression*>(expr), info.type,
info.statement, info.constant_value));
}
}

View File

@@ -26,6 +26,7 @@
#include "src/scope_stack.h"
#include "src/sem/binding_point.h"
#include "src/sem/block_statement.h"
#include "src/sem/constant.h"
#include "src/sem/function.h"
#include "src/sem/struct.h"
#include "src/utils/unique_vector.h"
@@ -152,6 +153,7 @@ class Resolver {
sem::Type const* type;
std::string const type_name; // Declared type name
sem::Statement* statement;
sem::Constant constant_value;
};
/// Structure holding semantic information about a call expression to an
@@ -282,8 +284,6 @@ class Resolver {
bool ValidateInterpolateDecoration(const ast::InterpolateDecoration* deco,
const sem::Type* storage_type);
bool ValidateMatrix(const sem::Matrix* ty, const Source& source);
bool ValidateMatrixConstructor(const ast::TypeConstructorExpression* ctor,
const sem::Matrix* matrix_type);
bool ValidateFunctionParameter(const ast::Function* func,
const VariableInfo* info);
bool ValidateNoDuplicateDefinition(Symbol sym,
@@ -305,9 +305,14 @@ class Resolver {
const std::string& rhs_type_name);
bool ValidateVector(const sem::Vector* ty, const Source& source);
bool ValidateVectorConstructor(const ast::TypeConstructorExpression* ctor,
const sem::Vector* vec_type);
const sem::Vector* vec_type,
const std::string& type_name);
bool ValidateMatrixConstructor(const ast::TypeConstructorExpression* ctor,
const sem::Matrix* matrix_type,
const std::string& type_name);
bool ValidateScalarConstructor(const ast::TypeConstructorExpression* ctor,
const sem::Type* type);
const sem::Type* type,
const std::string& type_name);
bool ValidateArrayConstructor(const ast::TypeConstructorExpression* ctor,
const sem::Array* arr_type);
bool ValidateTypeDecl(const ast::TypeDecl* named_type) const;
@@ -380,21 +385,14 @@ class Resolver {
/// @param lit the literal
sem::Type* TypeOf(const ast::Literal* lit);
/// Creates a sem::Expression node with the resolved type `type`, and
/// assigns this semantic node to the expression `expr`.
/// @param expr the expression
/// @param type the resolved type
void SetType(const ast::Expression* expr, const sem::Type* type);
/// Creates a sem::Expression node with the resolved type `type`, the declared
/// type name `type_name` and assigns this semantic node to the expression
/// `expr`.
/// Records the semantic information for the expression node with the resolved
/// type `type` and optional declared type name `type_name`.
/// @param expr the expression
/// @param type the resolved type
/// @param type_name the declared type name
void SetType(const ast::Expression* expr,
const sem::Type* type,
const std::string& type_name);
void SetExprInfo(const ast::Expression* expr,
const sem::Type* type,
std::string type_name = "");
/// Resolve the value of a scalar const_expr.
/// @param expr the expression
@@ -435,6 +433,26 @@ class Resolver {
FunctionInfo* to,
CALLBACK&& callback) const;
//////////////////////////////////////////////////////////////////////////////
/// Constant value evaluation methods
//////////////////////////////////////////////////////////////////////////////
/// @return the Constant value of the given Expression
sem::Constant ConstantValueOf(const ast::Expression* expr);
/// Cast `Value` to `target_type`
/// @return the casted value
sem::Constant ConstantCast(const sem::Constant& value,
const sem::Type* target_elem_type);
sem::Constant EvaluateConstantValue(const ast::Expression* expr,
const sem::Type* type);
sem::Constant EvaluateConstantValue(
const ast::ScalarConstructorExpression* scalar_ctor,
const sem::Type* type);
sem::Constant EvaluateConstantValue(
const ast::TypeConstructorExpression* type_ctor,
const sem::Type* type);
ProgramBuilder* const builder_;
diag::List& diagnostics_;
std::unique_ptr<IntrinsicTable> const intrinsic_table_;

View File

@@ -0,0 +1,153 @@
// Copyright 2021 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/resolver/resolver.h"
#include "src/sem/constant.h"
namespace tint {
namespace resolver {
namespace {
using i32 = ProgramBuilder::i32;
using u32 = ProgramBuilder::u32;
using f32 = ProgramBuilder::f32;
} // namespace
sem::Constant Resolver::ConstantCast(const sem::Constant& value,
const sem::Type* target_elem_type) {
if (value.ElementType() == target_elem_type) {
return value;
}
sem::Constant::Scalars elems;
for (size_t i = 0; i < value.Elements().size(); ++i) {
if (target_elem_type->Is<sem::I32>()) {
elems.emplace_back(
value.WithScalarAt(i, [](auto&& s) { return static_cast<i32>(s); }));
} else if (target_elem_type->Is<sem::U32>()) {
elems.emplace_back(
value.WithScalarAt(i, [](auto&& s) { return static_cast<u32>(s); }));
} else if (target_elem_type->Is<sem::F32>()) {
elems.emplace_back(
value.WithScalarAt(i, [](auto&& s) { return static_cast<f32>(s); }));
} else if (target_elem_type->Is<sem::Bool>()) {
elems.emplace_back(
value.WithScalarAt(i, [](auto&& s) { return static_cast<bool>(s); }));
}
}
auto* target_type =
value.Type()->Is<sem::Vector>()
? builder_->create<sem::Vector>(target_elem_type,
static_cast<uint32_t>(elems.size()))
: target_elem_type;
return sem::Constant(target_type, elems);
}
sem::Constant Resolver::ConstantValueOf(const ast::Expression* expr) {
auto it = expr_info_.find(expr);
if (it != expr_info_.end()) {
return it->second.constant_value;
}
return {};
}
sem::Constant Resolver::EvaluateConstantValue(const ast::Expression* expr,
const sem::Type* type) {
if (auto* e = expr->As<ast::ScalarConstructorExpression>()) {
return EvaluateConstantValue(e, type);
}
if (auto* e = expr->As<ast::TypeConstructorExpression>()) {
return EvaluateConstantValue(e, type);
}
return {};
}
sem::Constant Resolver::EvaluateConstantValue(
const ast::ScalarConstructorExpression* scalar_ctor,
const sem::Type* type) {
auto* literal = scalar_ctor->literal();
if (auto* lit = literal->As<ast::SintLiteral>()) {
return {type, {lit->value_as_i32()}};
}
if (auto* lit = literal->As<ast::UintLiteral>()) {
return {type, {lit->value_as_u32()}};
}
if (auto* lit = literal->As<ast::FloatLiteral>()) {
return {type, {lit->value()}};
}
if (auto* lit = literal->As<ast::BoolLiteral>()) {
return {type, {lit->IsTrue()}};
}
TINT_UNREACHABLE(Resolver, builder_->Diagnostics());
return {};
}
sem::Constant Resolver::EvaluateConstantValue(
const ast::TypeConstructorExpression* type_ctor,
const sem::Type* type) {
auto& ctor_values = type_ctor->values();
auto* vec = type->As<sem::Vector>();
// For now, only fold scalars and vectors
if (!type->is_scalar() && !vec) {
return {};
}
auto* elem_type = vec ? vec->type() : type;
int result_size = vec ? static_cast<int>(vec->size()) : 1;
// For zero value init, return 0s
if (ctor_values.empty()) {
if (elem_type->Is<sem::I32>()) {
return sem::Constant(type, sem::Constant::Scalars(result_size, 0));
}
if (elem_type->Is<sem::U32>()) {
return sem::Constant(type, sem::Constant::Scalars(result_size, 0u));
}
if (elem_type->Is<sem::F32>()) {
return sem::Constant(type, sem::Constant::Scalars(result_size, 0.f));
}
if (elem_type->Is<sem::Bool>()) {
return sem::Constant(type, sem::Constant::Scalars(result_size, false));
}
}
// Build value for type_ctor from each child value by casting to
// type_ctor's type.
sem::Constant::Scalars elems;
for (auto* cv : ctor_values) {
auto value = ConstantValueOf(cv);
if (!value.IsValid()) {
return {};
}
auto cast = ConstantCast(value, elem_type);
elems.insert(elems.end(), cast.Elements().begin(), cast.Elements().end());
}
// Splat single-value initializers
if (elems.size() == 1) {
for (int i = 0; i < result_size - 1; ++i) {
elems.emplace_back(elems[0]);
}
}
return sem::Constant(type, std::move(elems));
}
} // namespace resolver
} // namespace tint

View File

@@ -0,0 +1,433 @@
// Copyright 2021 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/resolver/resolver.h"
#include "gtest/gtest.h"
#include "src/resolver/resolver_test_helper.h"
#include "src/sem/expression.h"
namespace tint {
namespace resolver {
namespace {
using Scalar = sem::Constant::Scalar;
using ResolverConstantsTest = ResolverTest;
TEST_F(ResolverConstantsTest, Scalar_i32) {
auto* expr = Expr(99);
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
EXPECT_NE(sem, nullptr);
EXPECT_TRUE(sem->Type()->Is<sem::I32>());
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
EXPECT_EQ(sem->ConstantValue().ElementType(), sem->Type());
ASSERT_EQ(sem->ConstantValue().Elements().size(), 1u);
EXPECT_EQ(sem->ConstantValue().Elements()[0].i32, 99);
}
TEST_F(ResolverConstantsTest, Scalar_u32) {
auto* expr = Expr(99u);
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
EXPECT_NE(sem, nullptr);
EXPECT_TRUE(sem->Type()->Is<sem::U32>());
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
EXPECT_EQ(sem->ConstantValue().ElementType(), sem->Type());
ASSERT_EQ(sem->ConstantValue().Elements().size(), 1u);
EXPECT_EQ(sem->ConstantValue().Elements()[0].u32, 99u);
}
TEST_F(ResolverConstantsTest, Scalar_f32) {
auto* expr = Expr(9.9f);
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
EXPECT_NE(sem, nullptr);
EXPECT_TRUE(sem->Type()->Is<sem::F32>());
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
EXPECT_EQ(sem->ConstantValue().ElementType(), sem->Type());
ASSERT_EQ(sem->ConstantValue().Elements().size(), 1u);
EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 9.9f);
}
TEST_F(ResolverConstantsTest, Scalar_bool) {
auto* expr = Expr(true);
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
EXPECT_NE(sem, nullptr);
EXPECT_TRUE(sem->Type()->Is<sem::Bool>());
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
EXPECT_EQ(sem->ConstantValue().ElementType(), sem->Type());
ASSERT_EQ(sem->ConstantValue().Elements().size(), 1u);
EXPECT_EQ(sem->ConstantValue().Elements()[0].bool_, true);
}
TEST_F(ResolverConstantsTest, Vec3_ZeroInit_i32) {
auto* expr = vec3<i32>();
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
EXPECT_NE(sem, nullptr);
ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
EXPECT_EQ(sem->Type()->As<sem::Vector>()->size(), 3u);
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
EXPECT_EQ(sem->ConstantValue().Elements()[0].i32, 0);
EXPECT_EQ(sem->ConstantValue().Elements()[1].i32, 0);
EXPECT_EQ(sem->ConstantValue().Elements()[2].i32, 0);
}
TEST_F(ResolverConstantsTest, Vec3_ZeroInit_u32) {
auto* expr = vec3<u32>();
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
EXPECT_NE(sem, nullptr);
ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::U32>());
EXPECT_EQ(sem->Type()->As<sem::Vector>()->size(), 3u);
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::U32>());
ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
EXPECT_EQ(sem->ConstantValue().Elements()[0].u32, 0u);
EXPECT_EQ(sem->ConstantValue().Elements()[1].u32, 0u);
EXPECT_EQ(sem->ConstantValue().Elements()[2].u32, 0u);
}
TEST_F(ResolverConstantsTest, Vec3_ZeroInit_f32) {
auto* expr = vec3<f32>();
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
EXPECT_NE(sem, nullptr);
ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
EXPECT_EQ(sem->Type()->As<sem::Vector>()->size(), 3u);
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 0u);
EXPECT_EQ(sem->ConstantValue().Elements()[1].f32, 0u);
EXPECT_EQ(sem->ConstantValue().Elements()[2].f32, 0u);
}
TEST_F(ResolverConstantsTest, Vec3_ZeroInit_bool) {
auto* expr = vec3<bool>();
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
EXPECT_NE(sem, nullptr);
ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::Bool>());
EXPECT_EQ(sem->Type()->As<sem::Vector>()->size(), 3u);
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::Bool>());
ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
EXPECT_EQ(sem->ConstantValue().Elements()[0].bool_, false);
EXPECT_EQ(sem->ConstantValue().Elements()[1].bool_, false);
EXPECT_EQ(sem->ConstantValue().Elements()[2].bool_, false);
}
TEST_F(ResolverConstantsTest, Vec3_Splat_i32) {
auto* expr = vec3<i32>(99);
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
EXPECT_NE(sem, nullptr);
ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
EXPECT_EQ(sem->Type()->As<sem::Vector>()->size(), 3u);
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
EXPECT_EQ(sem->ConstantValue().Elements()[0].i32, 99);
EXPECT_EQ(sem->ConstantValue().Elements()[1].i32, 99);
EXPECT_EQ(sem->ConstantValue().Elements()[2].i32, 99);
}
TEST_F(ResolverConstantsTest, Vec3_Splat_u32) {
auto* expr = vec3<u32>(99u);
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
EXPECT_NE(sem, nullptr);
ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::U32>());
EXPECT_EQ(sem->Type()->As<sem::Vector>()->size(), 3u);
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::U32>());
ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
EXPECT_EQ(sem->ConstantValue().Elements()[0].u32, 99u);
EXPECT_EQ(sem->ConstantValue().Elements()[1].u32, 99u);
EXPECT_EQ(sem->ConstantValue().Elements()[2].u32, 99u);
}
TEST_F(ResolverConstantsTest, Vec3_Splat_f32) {
auto* expr = vec3<f32>(9.9f);
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
EXPECT_NE(sem, nullptr);
ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
EXPECT_EQ(sem->Type()->As<sem::Vector>()->size(), 3u);
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 9.9f);
EXPECT_EQ(sem->ConstantValue().Elements()[1].f32, 9.9f);
EXPECT_EQ(sem->ConstantValue().Elements()[2].f32, 9.9f);
}
TEST_F(ResolverConstantsTest, Vec3_Splat_bool) {
auto* expr = vec3<bool>(true);
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
EXPECT_NE(sem, nullptr);
ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::Bool>());
EXPECT_EQ(sem->Type()->As<sem::Vector>()->size(), 3u);
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::Bool>());
ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
EXPECT_EQ(sem->ConstantValue().Elements()[0].bool_, true);
EXPECT_EQ(sem->ConstantValue().Elements()[1].bool_, true);
EXPECT_EQ(sem->ConstantValue().Elements()[2].bool_, true);
}
TEST_F(ResolverConstantsTest, Vec3_FullConstruct_i32) {
auto* expr = vec3<i32>(1, 2, 3);
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
EXPECT_NE(sem, nullptr);
ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
EXPECT_EQ(sem->Type()->As<sem::Vector>()->size(), 3u);
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
EXPECT_EQ(sem->ConstantValue().Elements()[0].i32, 1);
EXPECT_EQ(sem->ConstantValue().Elements()[1].i32, 2);
EXPECT_EQ(sem->ConstantValue().Elements()[2].i32, 3);
}
TEST_F(ResolverConstantsTest, Vec3_FullConstruct_u32) {
auto* expr = vec3<u32>(1u, 2u, 3u);
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
EXPECT_NE(sem, nullptr);
ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::U32>());
EXPECT_EQ(sem->Type()->As<sem::Vector>()->size(), 3u);
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::U32>());
ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
EXPECT_EQ(sem->ConstantValue().Elements()[0].u32, 1u);
EXPECT_EQ(sem->ConstantValue().Elements()[1].u32, 2u);
EXPECT_EQ(sem->ConstantValue().Elements()[2].u32, 3u);
}
TEST_F(ResolverConstantsTest, Vec3_FullConstruct_f32) {
auto* expr = vec3<f32>(1.f, 2.f, 3.f);
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
EXPECT_NE(sem, nullptr);
ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
EXPECT_EQ(sem->Type()->As<sem::Vector>()->size(), 3u);
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 1.f);
EXPECT_EQ(sem->ConstantValue().Elements()[1].f32, 2.f);
EXPECT_EQ(sem->ConstantValue().Elements()[2].f32, 3.f);
}
TEST_F(ResolverConstantsTest, Vec3_FullConstruct_bool) {
auto* expr = vec3<bool>(true, false, true);
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
EXPECT_NE(sem, nullptr);
ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::Bool>());
EXPECT_EQ(sem->Type()->As<sem::Vector>()->size(), 3u);
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::Bool>());
ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
EXPECT_EQ(sem->ConstantValue().Elements()[0].bool_, true);
EXPECT_EQ(sem->ConstantValue().Elements()[1].bool_, false);
EXPECT_EQ(sem->ConstantValue().Elements()[2].bool_, true);
}
TEST_F(ResolverConstantsTest, Vec3_MixConstruct_i32) {
auto* expr = vec3<i32>(1, vec2<i32>(2, 3));
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
EXPECT_NE(sem, nullptr);
ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
EXPECT_EQ(sem->Type()->As<sem::Vector>()->size(), 3u);
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
EXPECT_EQ(sem->ConstantValue().Elements()[0].i32, 1);
EXPECT_EQ(sem->ConstantValue().Elements()[1].i32, 2);
EXPECT_EQ(sem->ConstantValue().Elements()[2].i32, 3);
}
TEST_F(ResolverConstantsTest, Vec3_MixConstruct_u32) {
auto* expr = vec3<u32>(vec2<u32>(1u, 2u), 3u);
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
EXPECT_NE(sem, nullptr);
ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::U32>());
EXPECT_EQ(sem->Type()->As<sem::Vector>()->size(), 3u);
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::U32>());
ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
EXPECT_EQ(sem->ConstantValue().Elements()[0].u32, 1u);
EXPECT_EQ(sem->ConstantValue().Elements()[1].u32, 2u);
EXPECT_EQ(sem->ConstantValue().Elements()[2].u32, 3u);
}
TEST_F(ResolverConstantsTest, Vec3_MixConstruct_f32) {
auto* expr = vec3<f32>(1.f, vec2<f32>(2.f, 3.f));
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
EXPECT_NE(sem, nullptr);
ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
EXPECT_EQ(sem->Type()->As<sem::Vector>()->size(), 3u);
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 1.f);
EXPECT_EQ(sem->ConstantValue().Elements()[1].f32, 2.f);
EXPECT_EQ(sem->ConstantValue().Elements()[2].f32, 3.f);
}
TEST_F(ResolverConstantsTest, Vec3_MixConstruct_bool) {
auto* expr = vec3<bool>(vec2<bool>(true, false), true);
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
EXPECT_NE(sem, nullptr);
ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::Bool>());
EXPECT_EQ(sem->Type()->As<sem::Vector>()->size(), 3u);
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::Bool>());
ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
EXPECT_EQ(sem->ConstantValue().Elements()[0].bool_, true);
EXPECT_EQ(sem->ConstantValue().Elements()[1].bool_, false);
EXPECT_EQ(sem->ConstantValue().Elements()[2].bool_, true);
}
TEST_F(ResolverConstantsTest, Vec3_Cast_f32_to_32) {
auto* expr = vec3<i32>(vec3<f32>(1.1f, 2.2f, 3.3f));
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
EXPECT_NE(sem, nullptr);
ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
EXPECT_EQ(sem->Type()->As<sem::Vector>()->size(), 3u);
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
EXPECT_EQ(sem->ConstantValue().Elements()[0].i32, 1);
EXPECT_EQ(sem->ConstantValue().Elements()[1].i32, 2);
EXPECT_EQ(sem->ConstantValue().Elements()[2].i32, 3);
}
TEST_F(ResolverConstantsTest, Vec3_Cast_u32_to_f32) {
auto* expr = vec3<f32>(vec3<u32>(10u, 20u, 30u));
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
EXPECT_NE(sem, nullptr);
ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
EXPECT_EQ(sem->Type()->As<sem::Vector>()->size(), 3u);
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 10.f);
EXPECT_EQ(sem->ConstantValue().Elements()[1].f32, 20.f);
EXPECT_EQ(sem->ConstantValue().Elements()[2].f32, 30.f);
}
} // namespace
} // namespace resolver
} // namespace tint