2020-03-02 20:47:43 +00:00
|
|
|
|
// Copyright 2020 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.
|
|
|
|
|
|
2021-03-09 10:54:37 +00:00
|
|
|
|
#include "src/resolver/resolver.h"
|
2020-03-02 20:47:43 +00:00
|
|
|
|
|
2021-03-01 20:01:39 +00:00
|
|
|
|
#include <algorithm>
|
2021-07-15 19:09:25 +00:00
|
|
|
|
#include <cmath>
|
|
|
|
|
#include <iomanip>
|
2021-09-15 17:37:00 +00:00
|
|
|
|
#include <limits>
|
2021-01-26 16:57:10 +00:00
|
|
|
|
#include <utility>
|
2020-04-07 12:57:42 +00:00
|
|
|
|
|
2021-04-21 13:47:12 +00:00
|
|
|
|
#include "src/ast/alias.h"
|
|
|
|
|
#include "src/ast/array.h"
|
2020-04-07 12:47:23 +00:00
|
|
|
|
#include "src/ast/assignment_statement.h"
|
2020-09-22 22:07:13 +00:00
|
|
|
|
#include "src/ast/bitcast_expression.h"
|
2020-04-07 12:54:10 +00:00
|
|
|
|
#include "src/ast/break_statement.h"
|
2020-07-21 13:42:13 +00:00
|
|
|
|
#include "src/ast/call_statement.h"
|
2020-04-07 12:54:29 +00:00
|
|
|
|
#include "src/ast/continue_statement.h"
|
2021-04-21 13:47:12 +00:00
|
|
|
|
#include "src/ast/depth_texture.h"
|
2022-02-02 23:07:11 +00:00
|
|
|
|
#include "src/ast/disable_validation_attribute.h"
|
2020-11-30 23:30:58 +00:00
|
|
|
|
#include "src/ast/discard_statement.h"
|
|
|
|
|
#include "src/ast/fallthrough_statement.h"
|
2021-07-02 19:27:42 +00:00
|
|
|
|
#include "src/ast/for_loop_statement.h"
|
2020-04-07 12:55:25 +00:00
|
|
|
|
#include "src/ast/if_statement.h"
|
2022-02-02 23:07:11 +00:00
|
|
|
|
#include "src/ast/internal_attribute.h"
|
|
|
|
|
#include "src/ast/interpolate_attribute.h"
|
2020-04-07 12:55:51 +00:00
|
|
|
|
#include "src/ast/loop_statement.h"
|
2021-04-21 13:47:12 +00:00
|
|
|
|
#include "src/ast/matrix.h"
|
2022-02-02 23:07:11 +00:00
|
|
|
|
#include "src/ast/override_attribute.h"
|
2021-04-21 13:47:12 +00:00
|
|
|
|
#include "src/ast/pointer.h"
|
2020-04-07 12:56:24 +00:00
|
|
|
|
#include "src/ast/return_statement.h"
|
2021-04-21 13:47:12 +00:00
|
|
|
|
#include "src/ast/sampled_texture.h"
|
|
|
|
|
#include "src/ast/sampler.h"
|
|
|
|
|
#include "src/ast/storage_texture.h"
|
2020-04-07 12:56:45 +00:00
|
|
|
|
#include "src/ast/switch_statement.h"
|
2021-10-21 20:38:54 +00:00
|
|
|
|
#include "src/ast/traverse_expressions.h"
|
2021-05-05 09:09:41 +00:00
|
|
|
|
#include "src/ast/type_name.h"
|
2020-04-07 19:27:21 +00:00
|
|
|
|
#include "src/ast/unary_op_expression.h"
|
2020-04-07 12:57:12 +00:00
|
|
|
|
#include "src/ast/variable_decl_statement.h"
|
2021-04-21 13:47:12 +00:00
|
|
|
|
#include "src/ast/vector.h"
|
2022-02-02 23:07:11 +00:00
|
|
|
|
#include "src/ast/workgroup_attribute.h"
|
2021-04-16 19:07:51 +00:00
|
|
|
|
#include "src/sem/array.h"
|
2021-06-17 19:56:14 +00:00
|
|
|
|
#include "src/sem/atomic_type.h"
|
2021-04-16 19:07:51 +00:00
|
|
|
|
#include "src/sem/call.h"
|
2021-07-26 22:19:48 +00:00
|
|
|
|
#include "src/sem/depth_multisampled_texture_type.h"
|
2021-04-21 13:47:12 +00:00
|
|
|
|
#include "src/sem/depth_texture_type.h"
|
2021-07-14 09:44:41 +00:00
|
|
|
|
#include "src/sem/for_loop_statement.h"
|
2021-04-16 19:07:51 +00:00
|
|
|
|
#include "src/sem/function.h"
|
2021-07-14 09:44:41 +00:00
|
|
|
|
#include "src/sem/if_statement.h"
|
|
|
|
|
#include "src/sem/loop_statement.h"
|
2021-04-16 19:07:51 +00:00
|
|
|
|
#include "src/sem/member_accessor_expression.h"
|
2021-04-20 16:09:21 +00:00
|
|
|
|
#include "src/sem/multisampled_texture_type.h"
|
2021-04-21 13:47:12 +00:00
|
|
|
|
#include "src/sem/pointer_type.h"
|
2021-05-18 10:28:48 +00:00
|
|
|
|
#include "src/sem/reference_type.h"
|
2021-04-21 13:47:12 +00:00
|
|
|
|
#include "src/sem/sampled_texture_type.h"
|
|
|
|
|
#include "src/sem/sampler_type.h"
|
2021-04-16 19:07:51 +00:00
|
|
|
|
#include "src/sem/statement.h"
|
2021-04-20 20:47:51 +00:00
|
|
|
|
#include "src/sem/storage_texture_type.h"
|
2021-04-16 19:07:51 +00:00
|
|
|
|
#include "src/sem/struct.h"
|
2021-07-14 09:44:41 +00:00
|
|
|
|
#include "src/sem/switch_statement.h"
|
2021-11-15 20:45:50 +00:00
|
|
|
|
#include "src/sem/type_constructor.h"
|
|
|
|
|
#include "src/sem/type_conversion.h"
|
2021-04-16 19:07:51 +00:00
|
|
|
|
#include "src/sem/variable.h"
|
2021-07-08 21:23:33 +00:00
|
|
|
|
#include "src/utils/defer.h"
|
2021-03-15 13:37:41 +00:00
|
|
|
|
#include "src/utils/math.h"
|
2021-09-08 15:18:36 +00:00
|
|
|
|
#include "src/utils/reverse.h"
|
2021-05-19 16:11:04 +00:00
|
|
|
|
#include "src/utils/scoped_assignment.h"
|
2021-11-15 20:45:50 +00:00
|
|
|
|
#include "src/utils/transform.h"
|
2020-04-07 12:46:30 +00:00
|
|
|
|
|
2020-03-02 20:47:43 +00:00
|
|
|
|
namespace tint {
|
2021-03-09 15:08:48 +00:00
|
|
|
|
namespace resolver {
|
2020-03-02 20:47:43 +00:00
|
|
|
|
|
2021-03-09 10:54:37 +00:00
|
|
|
|
Resolver::Resolver(ProgramBuilder* builder)
|
2021-05-06 16:04:03 +00:00
|
|
|
|
: builder_(builder),
|
|
|
|
|
diagnostics_(builder->Diagnostics()),
|
2022-02-02 23:07:11 +00:00
|
|
|
|
builtin_table_(BuiltinTable::Create(*builder)) {}
|
2021-01-25 18:14:08 +00:00
|
|
|
|
|
2021-03-09 10:54:37 +00:00
|
|
|
|
Resolver::~Resolver() = default;
|
2020-03-02 20:47:43 +00:00
|
|
|
|
|
2021-03-09 10:54:37 +00:00
|
|
|
|
bool Resolver::Resolve() {
|
2021-05-05 09:09:41 +00:00
|
|
|
|
if (builder_->Diagnostics().contains_errors()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-22 11:44:57 +00:00
|
|
|
|
if (!DependencyGraph::Build(builder_->AST(), builder_->Symbols(),
|
|
|
|
|
builder_->Diagnostics(), dependencies_,
|
|
|
|
|
/* allow_out_of_order_decls*/ false)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-09 10:54:37 +00:00
|
|
|
|
bool result = ResolveInternal();
|
2021-02-03 17:51:09 +00:00
|
|
|
|
|
2021-05-06 16:04:03 +00:00
|
|
|
|
if (!result && !diagnostics_.contains_errors()) {
|
2021-06-24 11:27:36 +00:00
|
|
|
|
TINT_ICE(Resolver, diagnostics_)
|
|
|
|
|
<< "resolving failed, but no error was raised";
|
2021-05-05 09:09:41 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-03 17:51:09 +00:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-09 10:54:37 +00:00
|
|
|
|
bool Resolver::ResolveInternal() {
|
2021-04-19 19:16:12 +00:00
|
|
|
|
Mark(&builder_->AST());
|
|
|
|
|
|
2021-03-19 18:45:30 +00:00
|
|
|
|
// Process everything else in the order they appear in the module. This is
|
|
|
|
|
// necessary for validation of use-before-declaration.
|
|
|
|
|
for (auto* decl : builder_->AST().GlobalDeclarations()) {
|
2021-06-09 14:32:14 +00:00
|
|
|
|
if (auto* td = decl->As<ast::TypeDecl>()) {
|
|
|
|
|
Mark(td);
|
|
|
|
|
if (!TypeDecl(td)) {
|
2021-04-19 19:16:12 +00:00
|
|
|
|
return false;
|
2021-03-19 18:45:30 +00:00
|
|
|
|
}
|
|
|
|
|
} else if (auto* func = decl->As<ast::Function>()) {
|
2021-04-19 19:16:12 +00:00
|
|
|
|
Mark(func);
|
2021-03-19 18:45:30 +00:00
|
|
|
|
if (!Function(func)) {
|
2020-06-15 20:55:09 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-03-19 18:45:30 +00:00
|
|
|
|
} else if (auto* var = decl->As<ast::Variable>()) {
|
2021-04-19 19:16:12 +00:00
|
|
|
|
Mark(var);
|
2021-04-06 20:18:57 +00:00
|
|
|
|
if (!GlobalVariable(var)) {
|
2021-03-19 18:45:30 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-04-16 01:15:43 +00:00
|
|
|
|
} else {
|
2021-06-24 11:27:36 +00:00
|
|
|
|
TINT_UNREACHABLE(Resolver, diagnostics_)
|
2021-04-16 01:15:43 +00:00
|
|
|
|
<< "unhandled global declaration: " << decl->TypeInfo().name;
|
|
|
|
|
return false;
|
2020-04-06 21:07:41 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-19 18:45:30 +00:00
|
|
|
|
|
2021-09-30 17:29:50 +00:00
|
|
|
|
AllocateOverridableConstantIds();
|
|
|
|
|
|
2021-11-23 20:45:51 +00:00
|
|
|
|
SetShadows();
|
|
|
|
|
|
2021-06-03 16:07:34 +00:00
|
|
|
|
if (!ValidatePipelineStages()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-05 09:09:41 +00:00
|
|
|
|
bool result = true;
|
2021-04-19 19:16:12 +00:00
|
|
|
|
for (auto* node : builder_->ASTNodes().Objects()) {
|
|
|
|
|
if (marked_.count(node) == 0) {
|
2021-10-14 20:30:29 +00:00
|
|
|
|
TINT_ICE(Resolver, diagnostics_) << "AST node '" << node->TypeInfo().name
|
|
|
|
|
<< "' was not reached by the resolver\n"
|
2021-10-15 17:33:10 +00:00
|
|
|
|
<< "At: " << node->source << "\n"
|
2021-10-14 20:30:29 +00:00
|
|
|
|
<< "Pointer: " << node;
|
2021-05-05 09:09:41 +00:00
|
|
|
|
result = false;
|
2021-04-19 19:16:12 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-05 09:09:41 +00:00
|
|
|
|
return result;
|
2021-04-19 19:16:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-07 14:49:34 +00:00
|
|
|
|
sem::Type* Resolver::Type(const ast::Type* ty) {
|
2021-04-21 13:47:12 +00:00
|
|
|
|
Mark(ty);
|
2021-05-07 14:49:34 +00:00
|
|
|
|
auto* s = [&]() -> sem::Type* {
|
2021-04-30 20:20:19 +00:00
|
|
|
|
if (ty->Is<ast::Void>()) {
|
|
|
|
|
return builder_->create<sem::Void>();
|
|
|
|
|
}
|
|
|
|
|
if (ty->Is<ast::Bool>()) {
|
|
|
|
|
return builder_->create<sem::Bool>();
|
|
|
|
|
}
|
|
|
|
|
if (ty->Is<ast::I32>()) {
|
|
|
|
|
return builder_->create<sem::I32>();
|
|
|
|
|
}
|
|
|
|
|
if (ty->Is<ast::U32>()) {
|
|
|
|
|
return builder_->create<sem::U32>();
|
|
|
|
|
}
|
|
|
|
|
if (ty->Is<ast::F32>()) {
|
|
|
|
|
return builder_->create<sem::F32>();
|
|
|
|
|
}
|
|
|
|
|
if (auto* t = ty->As<ast::Vector>()) {
|
2021-12-09 14:37:37 +00:00
|
|
|
|
if (!t->type) {
|
|
|
|
|
AddError("missing vector element type", t->source.End());
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2021-10-15 17:33:10 +00:00
|
|
|
|
if (auto* el = Type(t->type)) {
|
2021-10-19 18:38:54 +00:00
|
|
|
|
if (auto* vector = builder_->create<sem::Vector>(el, t->width)) {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
if (ValidateVector(vector, t->source)) {
|
2021-07-05 20:21:35 +00:00
|
|
|
|
return vector;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-30 20:20:19 +00:00
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
if (auto* t = ty->As<ast::Matrix>()) {
|
2021-12-09 14:37:37 +00:00
|
|
|
|
if (!t->type) {
|
|
|
|
|
AddError("missing matrix element type", t->source.End());
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2021-10-15 17:33:10 +00:00
|
|
|
|
if (auto* el = Type(t->type)) {
|
2021-10-19 18:38:54 +00:00
|
|
|
|
if (auto* column_type = builder_->create<sem::Vector>(el, t->rows)) {
|
2021-07-05 20:21:35 +00:00
|
|
|
|
if (auto* matrix =
|
2021-10-15 17:33:10 +00:00
|
|
|
|
builder_->create<sem::Matrix>(column_type, t->columns)) {
|
|
|
|
|
if (ValidateMatrix(matrix, t->source)) {
|
2021-07-05 20:21:35 +00:00
|
|
|
|
return matrix;
|
2021-06-21 17:08:05 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-30 20:20:19 +00:00
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
if (auto* t = ty->As<ast::Array>()) {
|
2021-05-07 20:58:34 +00:00
|
|
|
|
return Array(t);
|
2021-04-30 20:20:19 +00:00
|
|
|
|
}
|
2021-06-17 19:56:14 +00:00
|
|
|
|
if (auto* t = ty->As<ast::Atomic>()) {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
if (auto* el = Type(t->type)) {
|
2021-10-19 18:38:54 +00:00
|
|
|
|
auto* a = builder_->create<sem::Atomic>(el);
|
2021-06-17 19:56:14 +00:00
|
|
|
|
if (!ValidateAtomic(t, a)) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
return a;
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2021-04-30 20:20:19 +00:00
|
|
|
|
if (auto* t = ty->As<ast::Pointer>()) {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
if (auto* el = Type(t->type)) {
|
|
|
|
|
auto access = t->access;
|
2021-06-04 22:17:37 +00:00
|
|
|
|
if (access == ast::kUndefined) {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
access = DefaultAccessForStorageClass(t->storage_class);
|
2021-06-04 22:17:37 +00:00
|
|
|
|
}
|
2021-10-19 18:38:54 +00:00
|
|
|
|
return builder_->create<sem::Pointer>(el, t->storage_class, access);
|
2021-04-30 20:20:19 +00:00
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
if (auto* t = ty->As<ast::Sampler>()) {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
return builder_->create<sem::Sampler>(t->kind);
|
2021-04-30 20:20:19 +00:00
|
|
|
|
}
|
|
|
|
|
if (auto* t = ty->As<ast::SampledTexture>()) {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
if (auto* el = Type(t->type)) {
|
2021-10-19 18:38:54 +00:00
|
|
|
|
return builder_->create<sem::SampledTexture>(t->dim, el);
|
2021-04-30 20:20:19 +00:00
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
if (auto* t = ty->As<ast::MultisampledTexture>()) {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
if (auto* el = Type(t->type)) {
|
2021-10-19 18:38:54 +00:00
|
|
|
|
return builder_->create<sem::MultisampledTexture>(t->dim, el);
|
2021-04-30 20:20:19 +00:00
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
if (auto* t = ty->As<ast::DepthTexture>()) {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
return builder_->create<sem::DepthTexture>(t->dim);
|
2021-04-30 20:20:19 +00:00
|
|
|
|
}
|
2021-07-26 22:19:48 +00:00
|
|
|
|
if (auto* t = ty->As<ast::DepthMultisampledTexture>()) {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
return builder_->create<sem::DepthMultisampledTexture>(t->dim);
|
2021-07-26 22:19:48 +00:00
|
|
|
|
}
|
2021-04-30 20:20:19 +00:00
|
|
|
|
if (auto* t = ty->As<ast::StorageTexture>()) {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
if (auto* el = Type(t->type)) {
|
2021-06-04 20:41:47 +00:00
|
|
|
|
if (!ValidateStorageTexture(t)) {
|
2021-05-21 14:32:38 +00:00
|
|
|
|
return nullptr;
|
2021-05-14 17:51:13 +00:00
|
|
|
|
}
|
2021-10-19 18:38:54 +00:00
|
|
|
|
return builder_->create<sem::StorageTexture>(t->dim, t->format,
|
|
|
|
|
t->access, el);
|
2021-04-30 20:20:19 +00:00
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2021-05-06 08:20:52 +00:00
|
|
|
|
if (ty->As<ast::ExternalTexture>()) {
|
2021-05-05 18:00:52 +00:00
|
|
|
|
return builder_->create<sem::ExternalTexture>();
|
|
|
|
|
}
|
2021-11-23 20:45:51 +00:00
|
|
|
|
if (auto* type = ResolvedSymbol<sem::Type>(ty)) {
|
|
|
|
|
return type;
|
2021-05-05 09:09:41 +00:00
|
|
|
|
}
|
2021-06-24 11:27:36 +00:00
|
|
|
|
TINT_UNREACHABLE(Resolver, diagnostics_)
|
2021-04-30 20:20:19 +00:00
|
|
|
|
<< "Unhandled ast::Type: " << ty->TypeInfo().name;
|
|
|
|
|
return nullptr;
|
|
|
|
|
}();
|
|
|
|
|
|
2021-05-07 14:49:34 +00:00
|
|
|
|
if (s) {
|
|
|
|
|
builder_->Sem().Add(ty, s);
|
2021-04-21 13:47:12 +00:00
|
|
|
|
}
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
sem::Variable* Resolver::Variable(const ast::Variable* var,
|
|
|
|
|
VariableKind kind,
|
|
|
|
|
uint32_t index /* = 0 */) {
|
|
|
|
|
const sem::Type* storage_ty = nullptr;
|
2021-05-18 10:28:48 +00:00
|
|
|
|
|
|
|
|
|
// If the variable has a declared type, resolve it.
|
2021-10-15 17:33:10 +00:00
|
|
|
|
if (auto* ty = var->type) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
storage_ty = Type(ty);
|
|
|
|
|
if (!storage_ty) {
|
2021-05-12 10:41:21 +00:00
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2021-05-05 09:09:41 +00:00
|
|
|
|
}
|
2021-05-12 10:41:21 +00:00
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
const sem::Expression* rhs = nullptr;
|
2021-05-18 10:28:48 +00:00
|
|
|
|
|
2021-05-12 10:41:21 +00:00
|
|
|
|
// Does the variable have a constructor?
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (var->constructor) {
|
|
|
|
|
rhs = Expression(var->constructor);
|
|
|
|
|
if (!rhs) {
|
2021-05-12 10:41:21 +00:00
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the variable has no declared type, infer it from the RHS
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (!storage_ty) {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
if (!var->is_const && kind == VariableKind::kGlobal) {
|
|
|
|
|
AddError("global var declaration must specify a type", var->source);
|
2021-06-04 15:28:47 +00:00
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
storage_ty = rhs->Type()->UnwrapRef(); // Implicit load of RHS
|
2021-05-12 10:41:21 +00:00
|
|
|
|
}
|
2021-10-15 17:33:10 +00:00
|
|
|
|
} else if (var->is_const && kind != VariableKind::kParameter &&
|
2022-02-02 23:07:11 +00:00
|
|
|
|
!ast::HasAttribute<ast::OverrideAttribute>(var->attributes)) {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
AddError("let declaration must have an initializer", var->source);
|
2021-07-19 19:50:52 +00:00
|
|
|
|
return nullptr;
|
2021-10-15 17:33:10 +00:00
|
|
|
|
} else if (!var->type) {
|
2021-07-19 19:50:52 +00:00
|
|
|
|
AddError(
|
|
|
|
|
(kind == VariableKind::kGlobal)
|
|
|
|
|
? "module scope var declaration requires a type and initializer"
|
|
|
|
|
: "function scope var declaration requires a type or initializer",
|
2021-10-15 17:33:10 +00:00
|
|
|
|
var->source);
|
2021-05-05 09:09:41 +00:00
|
|
|
|
return nullptr;
|
2021-04-28 13:50:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (!storage_ty) {
|
2021-06-24 11:27:36 +00:00
|
|
|
|
TINT_ICE(Resolver, diagnostics_)
|
2021-05-18 10:28:48 +00:00
|
|
|
|
<< "failed to determine storage type for variable '" +
|
2021-10-15 17:33:10 +00:00
|
|
|
|
builder_->Symbols().NameFor(var->symbol) + "'\n"
|
|
|
|
|
<< "Source: " << var->source;
|
2021-05-18 10:28:48 +00:00
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-15 17:33:10 +00:00
|
|
|
|
auto storage_class = var->declared_storage_class;
|
|
|
|
|
if (storage_class == ast::StorageClass::kNone && !var->is_const) {
|
2021-08-27 14:54:47 +00:00
|
|
|
|
// No declared storage class. Infer from usage / type.
|
|
|
|
|
if (kind == VariableKind::kLocal) {
|
|
|
|
|
storage_class = ast::StorageClass::kFunction;
|
2021-11-05 16:51:38 +00:00
|
|
|
|
} else if (storage_ty->UnwrapRef()->is_handle()) {
|
2021-05-18 10:28:48 +00:00
|
|
|
|
// https://gpuweb.github.io/gpuweb/wgsl/#module-scope-variables
|
|
|
|
|
// If the store type is a texture type or a sampler type, then the
|
2022-02-02 23:07:11 +00:00
|
|
|
|
// variable declaration must not have a storage class attribute. The
|
2021-05-18 10:28:48 +00:00
|
|
|
|
// storage class will always be handle.
|
|
|
|
|
storage_class = ast::StorageClass::kUniformConstant;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (kind == VariableKind::kLocal && !var->is_const &&
|
|
|
|
|
storage_class != ast::StorageClass::kFunction &&
|
2022-02-02 23:07:11 +00:00
|
|
|
|
IsValidationEnabled(var->attributes,
|
2021-11-05 16:51:38 +00:00
|
|
|
|
ast::DisabledValidation::kIgnoreStorageClass)) {
|
|
|
|
|
AddError("function variable has a non-function storage class", var->source);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-15 17:33:10 +00:00
|
|
|
|
auto access = var->declared_access;
|
2021-06-04 22:17:37 +00:00
|
|
|
|
if (access == ast::Access::kUndefined) {
|
|
|
|
|
access = DefaultAccessForStorageClass(storage_class);
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto* var_ty = storage_ty;
|
2021-10-15 17:33:10 +00:00
|
|
|
|
if (!var->is_const) {
|
2021-05-18 10:28:48 +00:00
|
|
|
|
// Variable declaration. Unlike `let`, `var` has storage.
|
|
|
|
|
// Variables are always of a reference type to the declared storage type.
|
2021-11-05 16:51:38 +00:00
|
|
|
|
var_ty =
|
|
|
|
|
builder_->create<sem::Reference>(storage_ty, storage_class, access);
|
2021-05-18 10:28:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-15 20:45:50 +00:00
|
|
|
|
if (rhs && !ValidateVariableConstructorOrCast(var, storage_class, storage_ty,
|
|
|
|
|
rhs->Type())) {
|
2021-05-18 10:28:48 +00:00
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (!ApplyStorageClassUsageToType(
|
|
|
|
|
storage_class, const_cast<sem::Type*>(var_ty), var->source)) {
|
|
|
|
|
AddNote(
|
|
|
|
|
std::string("while instantiating ") +
|
|
|
|
|
((kind == VariableKind::kParameter) ? "parameter " : "variable ") +
|
|
|
|
|
builder_->Symbols().NameFor(var->symbol),
|
|
|
|
|
var->source);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2021-04-16 01:15:43 +00:00
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (kind == VariableKind::kParameter) {
|
|
|
|
|
if (auto* ptr = var_ty->As<sem::Pointer>()) {
|
|
|
|
|
// For MSL, we push module-scope variables into the entry point as pointer
|
|
|
|
|
// parameters, so we also need to handle their store type.
|
|
|
|
|
if (!ApplyStorageClassUsageToType(
|
|
|
|
|
ptr->StorageClass(), const_cast<sem::Type*>(ptr->StoreType()),
|
|
|
|
|
var->source)) {
|
|
|
|
|
AddNote("while instantiating parameter " +
|
|
|
|
|
builder_->Symbols().NameFor(var->symbol),
|
|
|
|
|
var->source);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case VariableKind::kGlobal: {
|
|
|
|
|
sem::BindingPoint binding_point;
|
|
|
|
|
if (auto bp = var->BindingPoint()) {
|
|
|
|
|
binding_point = {bp.group->value, bp.binding->value};
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-11 19:12:36 +00:00
|
|
|
|
auto* override =
|
2022-02-02 23:07:11 +00:00
|
|
|
|
ast::GetAttribute<ast::OverrideAttribute>(var->attributes);
|
2021-11-11 19:12:36 +00:00
|
|
|
|
bool has_const_val = rhs && var->is_const && !override;
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto* global = builder_->create<sem::GlobalVariable>(
|
|
|
|
|
var, var_ty, storage_class, access,
|
2021-11-11 19:12:36 +00:00
|
|
|
|
has_const_val ? rhs->ConstantValue() : sem::Constant{},
|
2021-11-05 16:51:38 +00:00
|
|
|
|
binding_point);
|
|
|
|
|
|
2021-11-11 19:12:36 +00:00
|
|
|
|
if (override) {
|
|
|
|
|
global->SetIsOverridable();
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (override->has_value) {
|
|
|
|
|
global->SetConstantId(static_cast<uint16_t>(override->value));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-03 11:42:43 +00:00
|
|
|
|
global->SetConstructor(rhs);
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
builder_->Sem().Add(var, global);
|
|
|
|
|
return global;
|
|
|
|
|
}
|
|
|
|
|
case VariableKind::kLocal: {
|
|
|
|
|
auto* local = builder_->create<sem::LocalVariable>(
|
2021-11-23 20:45:51 +00:00
|
|
|
|
var, var_ty, storage_class, access, current_statement_,
|
2021-11-05 16:51:38 +00:00
|
|
|
|
(rhs && var->is_const) ? rhs->ConstantValue() : sem::Constant{});
|
|
|
|
|
builder_->Sem().Add(var, local);
|
2021-12-03 11:42:43 +00:00
|
|
|
|
local->SetConstructor(rhs);
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return local;
|
|
|
|
|
}
|
|
|
|
|
case VariableKind::kParameter: {
|
|
|
|
|
auto* param = builder_->create<sem::Parameter>(var, index, var_ty,
|
|
|
|
|
storage_class, access);
|
|
|
|
|
builder_->Sem().Add(var, param);
|
|
|
|
|
return param;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TINT_UNREACHABLE(Resolver, diagnostics_)
|
|
|
|
|
<< "unhandled VariableKind " << static_cast<int>(kind);
|
|
|
|
|
return nullptr;
|
2021-04-16 01:15:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-07-06 10:20:19 +00:00
|
|
|
|
ast::Access Resolver::DefaultAccessForStorageClass(
|
2021-06-04 22:17:37 +00:00
|
|
|
|
ast::StorageClass storage_class) {
|
|
|
|
|
// https://gpuweb.github.io/gpuweb/wgsl/#storage-class
|
|
|
|
|
switch (storage_class) {
|
|
|
|
|
case ast::StorageClass::kStorage:
|
|
|
|
|
case ast::StorageClass::kUniform:
|
|
|
|
|
case ast::StorageClass::kUniformConstant:
|
|
|
|
|
return ast::Access::kRead;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return ast::Access::kReadWrite;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-30 17:29:50 +00:00
|
|
|
|
void Resolver::AllocateOverridableConstantIds() {
|
|
|
|
|
// The next pipeline constant ID to try to allocate.
|
|
|
|
|
uint16_t next_constant_id = 0;
|
|
|
|
|
|
|
|
|
|
// Allocate constant IDs in global declaration order, so that they are
|
|
|
|
|
// deterministic.
|
|
|
|
|
// TODO(crbug.com/tint/1192): If a transform changes the order or removes an
|
|
|
|
|
// unused constant, the allocation may change on the next Resolver pass.
|
|
|
|
|
for (auto* decl : builder_->AST().GlobalDeclarations()) {
|
|
|
|
|
auto* var = decl->As<ast::Variable>();
|
|
|
|
|
if (!var) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2022-02-02 23:07:11 +00:00
|
|
|
|
auto* override_attr =
|
|
|
|
|
ast::GetAttribute<ast::OverrideAttribute>(var->attributes);
|
|
|
|
|
if (!override_attr) {
|
2021-09-30 17:29:50 +00:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint16_t constant_id;
|
2022-02-02 23:07:11 +00:00
|
|
|
|
if (override_attr->has_value) {
|
|
|
|
|
constant_id = static_cast<uint16_t>(override_attr->value);
|
2021-09-30 17:29:50 +00:00
|
|
|
|
} else {
|
|
|
|
|
// No ID was specified, so allocate the next available ID.
|
|
|
|
|
constant_id = next_constant_id;
|
|
|
|
|
while (constant_ids_.count(constant_id)) {
|
|
|
|
|
if (constant_id == UINT16_MAX) {
|
|
|
|
|
TINT_ICE(Resolver, builder_->Diagnostics())
|
|
|
|
|
<< "no more pipeline constant IDs available";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
constant_id++;
|
|
|
|
|
}
|
|
|
|
|
next_constant_id = constant_id + 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto* sem = Sem<sem::GlobalVariable>(var);
|
|
|
|
|
const_cast<sem::GlobalVariable*>(sem)->SetConstantId(constant_id);
|
2021-09-30 17:29:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-23 20:45:51 +00:00
|
|
|
|
void Resolver::SetShadows() {
|
|
|
|
|
for (auto it : dependencies_.shadows) {
|
|
|
|
|
auto* var = Sem(it.first);
|
|
|
|
|
if (auto* local = var->As<sem::LocalVariable>()) {
|
|
|
|
|
local->SetShadows(Sem(it.second));
|
|
|
|
|
}
|
|
|
|
|
if (auto* param = var->As<sem::Parameter>()) {
|
|
|
|
|
param->SetShadows(Sem(it.second));
|
|
|
|
|
}
|
2021-04-06 20:18:57 +00:00
|
|
|
|
}
|
2021-11-23 20:45:51 +00:00
|
|
|
|
} // namespace resolver
|
2021-04-06 20:18:57 +00:00
|
|
|
|
|
2021-11-23 20:45:51 +00:00
|
|
|
|
bool Resolver::GlobalVariable(const ast::Variable* var) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto* sem = Variable(var, VariableKind::kGlobal);
|
|
|
|
|
if (!sem) {
|
2021-04-16 01:15:43 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-04-06 20:18:57 +00:00
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto storage_class = sem->StorageClass();
|
|
|
|
|
if (!var->is_const && storage_class == ast::StorageClass::kNone) {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
AddError("global variables must have a storage class", var->source);
|
2021-04-06 20:18:57 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (var->is_const && storage_class != ast::StorageClass::kNone) {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
AddError("global constants shouldn't have a storage class", var->source);
|
2021-04-06 20:18:57 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-02 23:07:11 +00:00
|
|
|
|
for (auto* attr : var->attributes) {
|
|
|
|
|
Mark(attr);
|
2021-05-13 20:32:32 +00:00
|
|
|
|
|
2022-02-02 23:07:11 +00:00
|
|
|
|
if (auto* override_attr = attr->As<ast::OverrideAttribute>()) {
|
2021-05-13 20:32:32 +00:00
|
|
|
|
// Track the constant IDs that are specified in the shader.
|
2022-02-02 23:07:11 +00:00
|
|
|
|
if (override_attr->has_value) {
|
|
|
|
|
constant_ids_.emplace(override_attr->value, sem);
|
2021-05-13 20:32:32 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-05-10 19:16:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-02-02 23:07:11 +00:00
|
|
|
|
if (!ValidateNoDuplicateAttributes(var->attributes)) {
|
2021-06-09 18:53:57 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (!ValidateGlobalVariable(sem)) {
|
2021-04-06 20:18:57 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-15 19:09:25 +00:00
|
|
|
|
// TODO(bclayton): Call this at the end of resolve on all uniform and storage
|
|
|
|
|
// referenced structs
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (!ValidateStorageClassLayout(sem)) {
|
2021-07-15 19:09:25 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-18 17:11:56 +00:00
|
|
|
|
sem::Function* Resolver::Function(const ast::Function* decl) {
|
|
|
|
|
uint32_t parameter_index = 0;
|
|
|
|
|
std::unordered_map<Symbol, Source> parameter_names;
|
|
|
|
|
std::vector<sem::Parameter*> parameters;
|
2021-07-15 19:09:25 +00:00
|
|
|
|
|
2021-11-18 17:11:56 +00:00
|
|
|
|
// Resolve all the parameters
|
|
|
|
|
for (auto* param : decl->params) {
|
|
|
|
|
Mark(param);
|
2021-07-15 19:09:25 +00:00
|
|
|
|
|
2021-11-18 17:11:56 +00:00
|
|
|
|
{ // Check the parameter name is unique for the function
|
|
|
|
|
auto emplaced = parameter_names.emplace(param->symbol, param->source);
|
|
|
|
|
if (!emplaced.second) {
|
|
|
|
|
auto name = builder_->Symbols().NameFor(param->symbol);
|
|
|
|
|
AddError("redefinition of parameter '" + name + "'", param->source);
|
|
|
|
|
AddNote("previous definition is here", emplaced.first->second);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2021-07-15 19:09:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-18 17:11:56 +00:00
|
|
|
|
auto* var = As<sem::Parameter>(
|
|
|
|
|
Variable(param, VariableKind::kParameter, parameter_index++));
|
|
|
|
|
if (!var) {
|
|
|
|
|
return nullptr;
|
2021-07-15 19:09:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-02-02 23:07:11 +00:00
|
|
|
|
for (auto* attr : param->attributes) {
|
|
|
|
|
Mark(attr);
|
2021-11-18 17:11:56 +00:00
|
|
|
|
}
|
2022-02-02 23:07:11 +00:00
|
|
|
|
if (!ValidateNoDuplicateAttributes(param->attributes)) {
|
2021-11-18 17:11:56 +00:00
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2021-07-15 19:09:25 +00:00
|
|
|
|
|
2021-11-18 17:11:56 +00:00
|
|
|
|
parameters.emplace_back(var);
|
2021-07-15 19:09:25 +00:00
|
|
|
|
|
2021-11-18 17:11:56 +00:00
|
|
|
|
auto* var_ty = const_cast<sem::Type*>(var->Type());
|
|
|
|
|
if (auto* str = var_ty->As<sem::Struct>()) {
|
|
|
|
|
switch (decl->PipelineStage()) {
|
|
|
|
|
case ast::PipelineStage::kVertex:
|
|
|
|
|
str->AddUsage(sem::PipelineStageUsage::kVertexInput);
|
|
|
|
|
break;
|
|
|
|
|
case ast::PipelineStage::kFragment:
|
|
|
|
|
str->AddUsage(sem::PipelineStageUsage::kFragmentInput);
|
|
|
|
|
break;
|
|
|
|
|
case ast::PipelineStage::kCompute:
|
|
|
|
|
str->AddUsage(sem::PipelineStageUsage::kComputeInput);
|
|
|
|
|
break;
|
|
|
|
|
case ast::PipelineStage::kNone:
|
|
|
|
|
break;
|
2021-07-15 19:09:25 +00:00
|
|
|
|
}
|
2021-11-18 17:11:56 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-07-15 19:09:25 +00:00
|
|
|
|
|
2021-11-18 17:11:56 +00:00
|
|
|
|
// Resolve the return type
|
|
|
|
|
sem::Type* return_type = nullptr;
|
|
|
|
|
if (auto* ty = decl->return_type) {
|
|
|
|
|
return_type = Type(ty);
|
|
|
|
|
if (!return_type) {
|
|
|
|
|
return nullptr;
|
2021-07-15 19:09:25 +00:00
|
|
|
|
}
|
2021-11-18 17:11:56 +00:00
|
|
|
|
} else {
|
|
|
|
|
return_type = builder_->create<sem::Void>();
|
|
|
|
|
}
|
2021-07-15 19:09:25 +00:00
|
|
|
|
|
2021-11-18 17:11:56 +00:00
|
|
|
|
if (auto* str = return_type->As<sem::Struct>()) {
|
|
|
|
|
if (!ApplyStorageClassUsageToType(ast::StorageClass::kNone, str,
|
|
|
|
|
decl->source)) {
|
|
|
|
|
AddNote("while instantiating return type for " +
|
|
|
|
|
builder_->Symbols().NameFor(decl->symbol),
|
|
|
|
|
decl->source);
|
|
|
|
|
return nullptr;
|
2021-07-15 19:09:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-18 17:11:56 +00:00
|
|
|
|
switch (decl->PipelineStage()) {
|
|
|
|
|
case ast::PipelineStage::kVertex:
|
|
|
|
|
str->AddUsage(sem::PipelineStageUsage::kVertexOutput);
|
|
|
|
|
break;
|
|
|
|
|
case ast::PipelineStage::kFragment:
|
|
|
|
|
str->AddUsage(sem::PipelineStageUsage::kFragmentOutput);
|
|
|
|
|
break;
|
|
|
|
|
case ast::PipelineStage::kCompute:
|
|
|
|
|
str->AddUsage(sem::PipelineStageUsage::kComputeOutput);
|
|
|
|
|
break;
|
|
|
|
|
case ast::PipelineStage::kNone:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-07-15 19:09:25 +00:00
|
|
|
|
|
2021-11-23 21:46:48 +00:00
|
|
|
|
auto* func = builder_->create<sem::Function>(decl, return_type, parameters);
|
|
|
|
|
builder_->Sem().Add(decl, func);
|
|
|
|
|
|
|
|
|
|
TINT_SCOPED_ASSIGNMENT(current_function_, func);
|
|
|
|
|
|
|
|
|
|
if (!WorkgroupSize(decl)) {
|
2021-11-18 17:11:56 +00:00
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2021-07-15 19:09:25 +00:00
|
|
|
|
|
2021-11-18 17:11:56 +00:00
|
|
|
|
if (decl->IsEntryPoint()) {
|
|
|
|
|
entry_points_.emplace_back(func);
|
2021-07-15 19:09:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-18 17:11:56 +00:00
|
|
|
|
if (decl->body) {
|
|
|
|
|
Mark(decl->body);
|
|
|
|
|
if (current_compound_statement_) {
|
|
|
|
|
TINT_ICE(Resolver, diagnostics_)
|
|
|
|
|
<< "Resolver::Function() called with a current compound statement";
|
|
|
|
|
return nullptr;
|
2021-07-15 19:09:25 +00:00
|
|
|
|
}
|
2021-12-03 15:49:34 +00:00
|
|
|
|
auto* body = StatementScope(
|
|
|
|
|
decl->body, builder_->create<sem::FunctionBlockStatement>(func),
|
|
|
|
|
[&] { return Statements(decl->body->statements); });
|
|
|
|
|
if (!body) {
|
2021-11-18 17:11:56 +00:00
|
|
|
|
return nullptr;
|
2021-07-15 19:09:25 +00:00
|
|
|
|
}
|
2021-12-03 15:49:34 +00:00
|
|
|
|
func->Behaviors() = body->Behaviors();
|
|
|
|
|
if (func->Behaviors().Contains(sem::Behavior::kReturn)) {
|
|
|
|
|
// https://www.w3.org/TR/WGSL/#behaviors-rules
|
|
|
|
|
// We assign a behavior to each function: it is its body’s behavior
|
|
|
|
|
// (treating the body as a regular statement), with any "Return" replaced
|
|
|
|
|
// by "Next".
|
|
|
|
|
func->Behaviors().Remove(sem::Behavior::kReturn);
|
|
|
|
|
func->Behaviors().Add(sem::Behavior::kNext);
|
|
|
|
|
}
|
2021-11-18 17:11:56 +00:00
|
|
|
|
}
|
2021-07-15 19:09:25 +00:00
|
|
|
|
|
2022-02-02 23:07:11 +00:00
|
|
|
|
for (auto* attr : decl->attributes) {
|
|
|
|
|
Mark(attr);
|
2021-11-18 17:11:56 +00:00
|
|
|
|
}
|
2022-02-02 23:07:11 +00:00
|
|
|
|
if (!ValidateNoDuplicateAttributes(decl->attributes)) {
|
2021-11-18 17:11:56 +00:00
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2021-07-15 19:09:25 +00:00
|
|
|
|
|
2022-02-02 23:07:11 +00:00
|
|
|
|
for (auto* attr : decl->return_type_attributes) {
|
|
|
|
|
Mark(attr);
|
2021-11-18 17:11:56 +00:00
|
|
|
|
}
|
2022-02-02 23:07:11 +00:00
|
|
|
|
if (!ValidateNoDuplicateAttributes(decl->return_type_attributes)) {
|
2021-11-18 17:11:56 +00:00
|
|
|
|
return nullptr;
|
2021-07-15 19:09:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-18 17:11:56 +00:00
|
|
|
|
if (!ValidateFunction(func)) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2021-07-15 19:09:25 +00:00
|
|
|
|
|
2021-11-18 17:11:56 +00:00
|
|
|
|
// If this is an entry point, mark all transitively called functions as being
|
|
|
|
|
// used by this entry point.
|
|
|
|
|
if (decl->IsEntryPoint()) {
|
|
|
|
|
for (auto* f : func->TransitivelyCalledFunctions()) {
|
|
|
|
|
const_cast<sem::Function*>(f)->AddAncestorEntryPoint(func);
|
2021-07-15 19:09:25 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-18 17:11:56 +00:00
|
|
|
|
return func;
|
2021-11-05 16:51:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-23 21:46:48 +00:00
|
|
|
|
bool Resolver::WorkgroupSize(const ast::Function* func) {
|
2021-05-19 08:15:18 +00:00
|
|
|
|
// Set work-group size defaults.
|
2021-11-23 21:46:48 +00:00
|
|
|
|
sem::WorkgroupSize ws;
|
2021-05-19 08:15:18 +00:00
|
|
|
|
for (int i = 0; i < 3; i++) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
ws[i].value = 1;
|
|
|
|
|
ws[i].overridable_const = nullptr;
|
2021-05-19 08:15:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-02-02 23:07:11 +00:00
|
|
|
|
auto* attr = ast::GetAttribute<ast::WorkgroupAttribute>(func->attributes);
|
|
|
|
|
if (!attr) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2021-05-19 13:40:08 +00:00
|
|
|
|
|
2022-02-02 23:07:11 +00:00
|
|
|
|
auto values = attr->Values();
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto any_i32 = false;
|
|
|
|
|
auto any_u32 = false;
|
|
|
|
|
for (int i = 0; i < 3; i++) {
|
2022-02-02 23:07:11 +00:00
|
|
|
|
// Each argument to this attribute can either be a literal, an
|
2021-11-05 16:51:38 +00:00
|
|
|
|
// identifier for a module-scope constants, or nullptr if not specified.
|
2021-05-19 13:40:08 +00:00
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto* expr = values[i];
|
|
|
|
|
if (!expr) {
|
|
|
|
|
// Not specified, just use the default.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2021-05-19 13:40:08 +00:00
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto* expr_sem = Expression(expr);
|
|
|
|
|
if (!expr_sem) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-05-19 13:40:08 +00:00
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
constexpr const char* kErrBadType =
|
|
|
|
|
"workgroup_size argument must be either literal or module-scope "
|
|
|
|
|
"constant of type i32 or u32";
|
|
|
|
|
constexpr const char* kErrInconsistentType =
|
|
|
|
|
"workgroup_size arguments must be of the same type, either i32 "
|
|
|
|
|
"or u32";
|
2021-07-28 21:24:06 +00:00
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto* ty = TypeOf(expr);
|
|
|
|
|
bool is_i32 = ty->UnwrapRef()->Is<sem::I32>();
|
|
|
|
|
bool is_u32 = ty->UnwrapRef()->Is<sem::U32>();
|
|
|
|
|
if (!is_i32 && !is_u32) {
|
|
|
|
|
AddError(kErrBadType, expr->source);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-05-19 13:40:08 +00:00
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
any_i32 = any_i32 || is_i32;
|
|
|
|
|
any_u32 = any_u32 || is_u32;
|
|
|
|
|
if (any_i32 && any_u32) {
|
|
|
|
|
AddError(kErrInconsistentType, expr->source);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-05-19 13:40:08 +00:00
|
|
|
|
|
2021-11-11 19:12:36 +00:00
|
|
|
|
sem::Constant value;
|
|
|
|
|
|
|
|
|
|
if (auto* user = Sem(expr)->As<sem::VariableUser>()) {
|
|
|
|
|
// We have an variable of a module-scope constant.
|
|
|
|
|
auto* decl = user->Variable()->Declaration();
|
|
|
|
|
if (!decl->is_const) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
AddError(kErrBadType, expr->source);
|
2021-09-02 10:05:19 +00:00
|
|
|
|
return false;
|
2021-05-19 13:40:08 +00:00
|
|
|
|
}
|
2021-11-05 16:51:38 +00:00
|
|
|
|
// Capture the constant if an [[override]] attribute is present.
|
2022-02-02 23:07:11 +00:00
|
|
|
|
if (ast::HasAttribute<ast::OverrideAttribute>(decl->attributes)) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
ws[i].overridable_const = decl;
|
2021-07-28 21:24:06 +00:00
|
|
|
|
}
|
2021-11-05 16:51:38 +00:00
|
|
|
|
|
2021-11-11 19:12:36 +00:00
|
|
|
|
if (decl->constructor) {
|
|
|
|
|
value = Sem(decl->constructor)->ConstantValue();
|
|
|
|
|
} else {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
// No constructor means this value must be overriden by the user.
|
|
|
|
|
ws[i].value = 0;
|
|
|
|
|
continue;
|
2021-05-19 13:40:08 +00:00
|
|
|
|
}
|
2021-11-11 19:12:36 +00:00
|
|
|
|
} else if (expr->Is<ast::LiteralExpression>()) {
|
|
|
|
|
value = Sem(expr)->ConstantValue();
|
|
|
|
|
} else {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
AddError(
|
|
|
|
|
"workgroup_size argument must be either a literal or a "
|
|
|
|
|
"module-scope constant",
|
|
|
|
|
values[i]->source);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-07-28 21:24:06 +00:00
|
|
|
|
|
2021-11-11 19:12:36 +00:00
|
|
|
|
if (!value) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
TINT_ICE(Resolver, diagnostics_)
|
|
|
|
|
<< "could not resolve constant workgroup_size constant value";
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// Validate and set the default value for this dimension.
|
2021-11-11 19:12:36 +00:00
|
|
|
|
if (is_i32 ? value.Elements()[0].i32 < 1 : value.Elements()[0].u32 < 1) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
AddError("workgroup_size argument must be at least 1", values[i]->source);
|
|
|
|
|
return false;
|
2021-05-19 13:40:08 +00:00
|
|
|
|
}
|
2021-05-19 08:15:18 +00:00
|
|
|
|
|
2021-11-11 19:12:36 +00:00
|
|
|
|
ws[i].value = is_i32 ? static_cast<uint32_t>(value.Elements()[0].i32)
|
|
|
|
|
: value.Elements()[0].u32;
|
2021-03-26 12:47:58 +00:00
|
|
|
|
}
|
2021-11-23 21:46:48 +00:00
|
|
|
|
|
|
|
|
|
current_function_->SetWorkgroupSize(std::move(ws));
|
2020-03-02 20:47:43 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-09 10:54:37 +00:00
|
|
|
|
bool Resolver::Statements(const ast::StatementList& stmts) {
|
2021-12-03 15:49:34 +00:00
|
|
|
|
sem::Behaviors behaviors{sem::Behavior::kNext};
|
|
|
|
|
|
2021-12-03 17:51:48 +00:00
|
|
|
|
bool reachable = true;
|
2021-03-09 10:26:57 +00:00
|
|
|
|
for (auto* stmt : stmts) {
|
2021-04-19 19:16:12 +00:00
|
|
|
|
Mark(stmt);
|
2021-11-26 16:26:42 +00:00
|
|
|
|
auto* sem = Statement(stmt);
|
|
|
|
|
if (!sem) {
|
2020-04-06 21:07:41 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-12-03 15:49:34 +00:00
|
|
|
|
// s1 s2:(B1∖{Next}) ∪ B2
|
2021-12-03 17:51:48 +00:00
|
|
|
|
sem->SetIsReachable(reachable);
|
|
|
|
|
if (reachable) {
|
|
|
|
|
behaviors = (behaviors - sem::Behavior::kNext) + sem->Behaviors();
|
|
|
|
|
}
|
|
|
|
|
reachable = reachable && sem->Behaviors().Contains(sem::Behavior::kNext);
|
2020-04-06 21:07:41 +00:00
|
|
|
|
}
|
2021-12-03 15:49:34 +00:00
|
|
|
|
|
|
|
|
|
current_statement_->Behaviors() = behaviors;
|
|
|
|
|
|
2021-06-21 17:53:56 +00:00
|
|
|
|
if (!ValidateStatements(stmts)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-26 16:26:42 +00:00
|
|
|
|
sem::Statement* Resolver::Statement(const ast::Statement* stmt) {
|
2021-07-14 09:44:41 +00:00
|
|
|
|
if (stmt->Is<ast::CaseStatement>()) {
|
|
|
|
|
AddError("case statement can only be used inside a switch statement",
|
2021-10-15 17:33:10 +00:00
|
|
|
|
stmt->source);
|
2021-11-26 16:26:42 +00:00
|
|
|
|
return nullptr;
|
2021-05-15 14:48:46 +00:00
|
|
|
|
}
|
|
|
|
|
if (stmt->Is<ast::ElseStatement>()) {
|
2021-06-24 11:27:36 +00:00
|
|
|
|
TINT_ICE(Resolver, diagnostics_)
|
2021-05-15 14:48:46 +00:00
|
|
|
|
<< "Resolver::Statement() encountered an Else statement. Else "
|
|
|
|
|
"statements are embedded in If statements, so should never be "
|
|
|
|
|
"encountered as top-level statements";
|
2021-11-26 16:26:42 +00:00
|
|
|
|
return nullptr;
|
2021-05-15 14:48:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-07-14 09:44:41 +00:00
|
|
|
|
// Compound statements. These create their own sem::CompoundStatement
|
|
|
|
|
// bindings.
|
|
|
|
|
if (auto* b = stmt->As<ast::BlockStatement>()) {
|
|
|
|
|
return BlockStatement(b);
|
|
|
|
|
}
|
|
|
|
|
if (auto* l = stmt->As<ast::ForLoopStatement>()) {
|
|
|
|
|
return ForLoopStatement(l);
|
|
|
|
|
}
|
|
|
|
|
if (auto* l = stmt->As<ast::LoopStatement>()) {
|
|
|
|
|
return LoopStatement(l);
|
|
|
|
|
}
|
|
|
|
|
if (auto* i = stmt->As<ast::IfStatement>()) {
|
|
|
|
|
return IfStatement(i);
|
|
|
|
|
}
|
|
|
|
|
if (auto* s = stmt->As<ast::SwitchStatement>()) {
|
|
|
|
|
return SwitchStatement(s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Non-Compound statements
|
2020-11-30 23:30:58 +00:00
|
|
|
|
if (auto* a = stmt->As<ast::AssignmentStatement>()) {
|
2021-11-26 16:26:42 +00:00
|
|
|
|
return AssignmentStatement(a);
|
2020-04-07 12:47:23 +00:00
|
|
|
|
}
|
2021-11-26 16:26:42 +00:00
|
|
|
|
if (auto* b = stmt->As<ast::BreakStatement>()) {
|
|
|
|
|
return BreakStatement(b);
|
2020-04-07 12:54:10 +00:00
|
|
|
|
}
|
2020-11-30 23:30:58 +00:00
|
|
|
|
if (auto* c = stmt->As<ast::CallStatement>()) {
|
2021-11-26 16:26:42 +00:00
|
|
|
|
return CallStatement(c);
|
2020-07-21 13:42:13 +00:00
|
|
|
|
}
|
2021-09-30 17:29:50 +00:00
|
|
|
|
if (auto* c = stmt->As<ast::ContinueStatement>()) {
|
2021-11-26 16:26:42 +00:00
|
|
|
|
return ContinueStatement(c);
|
2020-04-07 12:54:29 +00:00
|
|
|
|
}
|
2021-11-26 16:26:42 +00:00
|
|
|
|
if (auto* d = stmt->As<ast::DiscardStatement>()) {
|
|
|
|
|
return DiscardStatement(d);
|
2020-07-25 14:33:50 +00:00
|
|
|
|
}
|
2021-11-26 16:26:42 +00:00
|
|
|
|
if (auto* f = stmt->As<ast::FallthroughStatement>()) {
|
|
|
|
|
return FallthroughStatement(f);
|
2020-04-07 12:54:59 +00:00
|
|
|
|
}
|
2020-11-30 23:30:58 +00:00
|
|
|
|
if (auto* r = stmt->As<ast::ReturnStatement>()) {
|
2021-11-26 16:26:42 +00:00
|
|
|
|
return ReturnStatement(r);
|
2020-04-07 12:56:24 +00:00
|
|
|
|
}
|
2020-11-30 23:30:58 +00:00
|
|
|
|
if (auto* v = stmt->As<ast::VariableDeclStatement>()) {
|
2021-03-17 22:47:33 +00:00
|
|
|
|
return VariableDeclStatement(v);
|
2020-04-07 12:57:12 +00:00
|
|
|
|
}
|
2020-04-07 12:47:23 +00:00
|
|
|
|
|
2021-11-22 11:44:57 +00:00
|
|
|
|
AddError("unknown statement type: " + std::string(stmt->TypeInfo().name),
|
2021-10-15 17:33:10 +00:00
|
|
|
|
stmt->source);
|
2021-11-26 16:26:42 +00:00
|
|
|
|
return nullptr;
|
2020-04-06 21:07:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-12-03 15:23:52 +00:00
|
|
|
|
sem::CaseStatement* Resolver::CaseStatement(const ast::CaseStatement* stmt) {
|
|
|
|
|
auto* sem = builder_->create<sem::CaseStatement>(
|
|
|
|
|
stmt, current_compound_statement_, current_function_);
|
2021-11-26 16:26:42 +00:00
|
|
|
|
return StatementScope(stmt, sem, [&] {
|
|
|
|
|
for (auto* sel : stmt->selectors) {
|
|
|
|
|
Mark(sel);
|
|
|
|
|
}
|
2021-12-03 15:23:52 +00:00
|
|
|
|
Mark(stmt->body);
|
|
|
|
|
auto* body = BlockStatement(stmt->body);
|
|
|
|
|
if (!body) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
sem->SetBlock(body);
|
2021-12-03 15:49:34 +00:00
|
|
|
|
sem->Behaviors() = body->Behaviors();
|
2021-12-03 15:23:52 +00:00
|
|
|
|
return true;
|
2021-11-26 16:26:42 +00:00
|
|
|
|
});
|
2021-03-09 15:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-26 16:26:42 +00:00
|
|
|
|
sem::IfStatement* Resolver::IfStatement(const ast::IfStatement* stmt) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto* sem = builder_->create<sem::IfStatement>(
|
|
|
|
|
stmt, current_compound_statement_, current_function_);
|
2021-11-26 16:26:42 +00:00
|
|
|
|
return StatementScope(stmt, sem, [&] {
|
|
|
|
|
auto* cond = Expression(stmt->condition);
|
|
|
|
|
if (!cond) {
|
2021-07-14 09:44:41 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-11-26 16:26:42 +00:00
|
|
|
|
sem->SetCondition(cond);
|
2021-12-03 15:49:34 +00:00
|
|
|
|
sem->Behaviors() = cond->Behaviors();
|
|
|
|
|
sem->Behaviors().Remove(sem::Behavior::kNext);
|
2021-03-09 15:17:28 +00:00
|
|
|
|
|
2021-10-15 17:33:10 +00:00
|
|
|
|
Mark(stmt->body);
|
2021-07-14 09:44:41 +00:00
|
|
|
|
auto* body = builder_->create<sem::BlockStatement>(
|
2021-11-05 16:51:38 +00:00
|
|
|
|
stmt->body, current_compound_statement_, current_function_);
|
2021-11-26 16:26:42 +00:00
|
|
|
|
if (!StatementScope(stmt->body, body,
|
|
|
|
|
[&] { return Statements(stmt->body->statements); })) {
|
2021-05-15 14:48:46 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-12-03 15:49:34 +00:00
|
|
|
|
sem->Behaviors().Add(body->Behaviors());
|
2021-03-09 15:17:28 +00:00
|
|
|
|
|
2021-10-15 17:33:10 +00:00
|
|
|
|
for (auto* else_stmt : stmt->else_statements) {
|
2021-07-14 09:44:41 +00:00
|
|
|
|
Mark(else_stmt);
|
2021-12-03 15:49:34 +00:00
|
|
|
|
auto* else_sem = ElseStatement(else_stmt);
|
|
|
|
|
if (!else_sem) {
|
2021-07-14 09:44:41 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-12-03 15:49:34 +00:00
|
|
|
|
sem->Behaviors().Add(else_sem->Behaviors());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (stmt->else_statements.empty() ||
|
|
|
|
|
stmt->else_statements.back()->condition != nullptr) {
|
|
|
|
|
// https://www.w3.org/TR/WGSL/#behaviors-rules
|
|
|
|
|
// if statements without an else branch are treated as if they had an
|
|
|
|
|
// empty else branch (which adds Next to their behavior)
|
|
|
|
|
sem->Behaviors().Add(sem::Behavior::kNext);
|
2021-07-14 09:44:41 +00:00
|
|
|
|
}
|
2021-11-26 16:26:42 +00:00
|
|
|
|
|
|
|
|
|
return ValidateIfStatement(sem);
|
2021-07-14 09:44:41 +00:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-26 16:26:42 +00:00
|
|
|
|
sem::ElseStatement* Resolver::ElseStatement(const ast::ElseStatement* stmt) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto* sem = builder_->create<sem::ElseStatement>(
|
2022-02-03 00:12:52 +00:00
|
|
|
|
stmt, current_compound_statement_->As<sem::IfStatement>(),
|
|
|
|
|
current_function_);
|
2021-11-26 16:26:42 +00:00
|
|
|
|
return StatementScope(stmt, sem, [&] {
|
|
|
|
|
if (auto* cond_expr = stmt->condition) {
|
|
|
|
|
auto* cond = Expression(cond_expr);
|
|
|
|
|
if (!cond) {
|
2021-05-10 13:46:06 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-11-26 16:26:42 +00:00
|
|
|
|
sem->SetCondition(cond);
|
2021-12-03 15:49:34 +00:00
|
|
|
|
// https://www.w3.org/TR/WGSL/#behaviors-rules
|
|
|
|
|
// if statements with else if branches are treated as if they were nested
|
|
|
|
|
// simple if/else statements
|
|
|
|
|
sem->Behaviors() = cond->Behaviors();
|
2021-03-31 20:35:46 +00:00
|
|
|
|
}
|
2021-12-03 15:49:34 +00:00
|
|
|
|
sem->Behaviors().Remove(sem::Behavior::kNext);
|
2021-07-14 09:44:41 +00:00
|
|
|
|
|
2021-10-15 17:33:10 +00:00
|
|
|
|
Mark(stmt->body);
|
2021-07-14 09:44:41 +00:00
|
|
|
|
auto* body = builder_->create<sem::BlockStatement>(
|
2021-11-05 16:51:38 +00:00
|
|
|
|
stmt->body, current_compound_statement_, current_function_);
|
2021-11-26 16:26:42 +00:00
|
|
|
|
if (!StatementScope(stmt->body, body,
|
|
|
|
|
[&] { return Statements(stmt->body->statements); })) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-12-03 15:49:34 +00:00
|
|
|
|
sem->Behaviors().Add(body->Behaviors());
|
2021-11-26 16:26:42 +00:00
|
|
|
|
|
|
|
|
|
return ValidateElseStatement(sem);
|
2021-07-14 09:44:41 +00:00
|
|
|
|
});
|
2021-03-09 15:06:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-26 16:26:42 +00:00
|
|
|
|
sem::BlockStatement* Resolver::BlockStatement(const ast::BlockStatement* stmt) {
|
2021-07-14 09:44:41 +00:00
|
|
|
|
auto* sem = builder_->create<sem::BlockStatement>(
|
2021-11-05 16:51:38 +00:00
|
|
|
|
stmt->As<ast::BlockStatement>(), current_compound_statement_,
|
|
|
|
|
current_function_);
|
2021-11-26 16:26:42 +00:00
|
|
|
|
return StatementScope(stmt, sem,
|
|
|
|
|
[&] { return Statements(stmt->statements); });
|
2021-07-14 09:44:41 +00:00
|
|
|
|
}
|
2021-05-15 14:48:46 +00:00
|
|
|
|
|
2021-11-26 16:26:42 +00:00
|
|
|
|
sem::LoopStatement* Resolver::LoopStatement(const ast::LoopStatement* stmt) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto* sem = builder_->create<sem::LoopStatement>(
|
|
|
|
|
stmt, current_compound_statement_, current_function_);
|
2021-11-26 16:26:42 +00:00
|
|
|
|
return StatementScope(stmt, sem, [&] {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
Mark(stmt->body);
|
2021-07-14 09:44:41 +00:00
|
|
|
|
|
|
|
|
|
auto* body = builder_->create<sem::LoopBlockStatement>(
|
2021-11-05 16:51:38 +00:00
|
|
|
|
stmt->body, current_compound_statement_, current_function_);
|
2021-11-26 16:26:42 +00:00
|
|
|
|
return StatementScope(stmt->body, body, [&] {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
if (!Statements(stmt->body->statements)) {
|
2021-05-15 14:48:46 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-12-03 15:49:34 +00:00
|
|
|
|
auto& behaviors = sem->Behaviors();
|
|
|
|
|
behaviors = body->Behaviors();
|
2021-11-26 16:26:42 +00:00
|
|
|
|
|
2021-10-15 17:33:10 +00:00
|
|
|
|
if (stmt->continuing) {
|
|
|
|
|
Mark(stmt->continuing);
|
|
|
|
|
if (!stmt->continuing->Empty()) {
|
2021-12-03 15:49:34 +00:00
|
|
|
|
auto* continuing = StatementScope(
|
|
|
|
|
stmt->continuing,
|
2021-10-15 17:33:10 +00:00
|
|
|
|
builder_->create<sem::LoopContinuingBlockStatement>(
|
2021-11-05 16:51:38 +00:00
|
|
|
|
stmt->continuing, current_compound_statement_,
|
2021-12-03 15:49:34 +00:00
|
|
|
|
current_function_),
|
|
|
|
|
[&] { return Statements(stmt->continuing->statements); });
|
|
|
|
|
if (!continuing) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
behaviors.Add(continuing->Behaviors());
|
2021-07-14 09:44:41 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-11-26 16:26:42 +00:00
|
|
|
|
|
2021-12-03 15:49:34 +00:00
|
|
|
|
if (behaviors.Contains(sem::Behavior::kBreak)) { // Does the loop exit?
|
|
|
|
|
behaviors.Add(sem::Behavior::kNext);
|
|
|
|
|
} else {
|
|
|
|
|
behaviors.Remove(sem::Behavior::kNext);
|
|
|
|
|
}
|
|
|
|
|
behaviors.Remove(sem::Behavior::kBreak, sem::Behavior::kContinue);
|
|
|
|
|
|
2022-01-14 17:16:32 +00:00
|
|
|
|
return ValidateLoopStatement(sem);
|
2021-07-14 09:44:41 +00:00
|
|
|
|
});
|
2021-05-15 14:48:46 +00:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-26 16:26:42 +00:00
|
|
|
|
sem::ForLoopStatement* Resolver::ForLoopStatement(
|
|
|
|
|
const ast::ForLoopStatement* stmt) {
|
2021-07-14 09:44:41 +00:00
|
|
|
|
auto* sem = builder_->create<sem::ForLoopStatement>(
|
2021-11-05 16:51:38 +00:00
|
|
|
|
stmt, current_compound_statement_, current_function_);
|
2021-11-26 16:26:42 +00:00
|
|
|
|
return StatementScope(stmt, sem, [&] {
|
2021-12-03 15:49:34 +00:00
|
|
|
|
auto& behaviors = sem->Behaviors();
|
2021-10-15 17:33:10 +00:00
|
|
|
|
if (auto* initializer = stmt->initializer) {
|
2021-07-14 09:44:41 +00:00
|
|
|
|
Mark(initializer);
|
2021-12-03 15:49:34 +00:00
|
|
|
|
auto* init = Statement(initializer);
|
|
|
|
|
if (!init) {
|
2021-07-14 09:44:41 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-12-03 15:49:34 +00:00
|
|
|
|
behaviors.Add(init->Behaviors());
|
2021-07-02 19:27:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-26 16:26:42 +00:00
|
|
|
|
if (auto* cond_expr = stmt->condition) {
|
|
|
|
|
auto* cond = Expression(cond_expr);
|
|
|
|
|
if (!cond) {
|
2021-07-14 09:44:41 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-11-26 16:26:42 +00:00
|
|
|
|
sem->SetCondition(cond);
|
2021-12-03 15:49:34 +00:00
|
|
|
|
behaviors.Add(cond->Behaviors());
|
2021-07-02 19:27:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-15 17:33:10 +00:00
|
|
|
|
if (auto* continuing = stmt->continuing) {
|
2021-07-14 09:44:41 +00:00
|
|
|
|
Mark(continuing);
|
2021-12-03 15:49:34 +00:00
|
|
|
|
auto* cont = Statement(continuing);
|
|
|
|
|
if (!cont) {
|
2021-07-14 09:44:41 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-12-03 15:49:34 +00:00
|
|
|
|
behaviors.Add(cont->Behaviors());
|
2021-07-02 19:27:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-15 17:33:10 +00:00
|
|
|
|
Mark(stmt->body);
|
2021-07-14 09:44:41 +00:00
|
|
|
|
|
|
|
|
|
auto* body = builder_->create<sem::LoopBlockStatement>(
|
2021-11-05 16:51:38 +00:00
|
|
|
|
stmt->body, current_compound_statement_, current_function_);
|
2021-11-26 16:26:42 +00:00
|
|
|
|
if (!StatementScope(stmt->body, body,
|
|
|
|
|
[&] { return Statements(stmt->body->statements); })) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-03 15:49:34 +00:00
|
|
|
|
behaviors.Add(body->Behaviors());
|
|
|
|
|
if (stmt->condition ||
|
|
|
|
|
behaviors.Contains(sem::Behavior::kBreak)) { // Does the loop exit?
|
|
|
|
|
behaviors.Add(sem::Behavior::kNext);
|
|
|
|
|
} else {
|
|
|
|
|
behaviors.Remove(sem::Behavior::kNext);
|
|
|
|
|
}
|
|
|
|
|
behaviors.Remove(sem::Behavior::kBreak, sem::Behavior::kContinue);
|
|
|
|
|
|
2021-11-26 16:26:42 +00:00
|
|
|
|
return ValidateForLoopStatement(sem);
|
2021-07-14 09:44:41 +00:00
|
|
|
|
});
|
2021-07-02 19:27:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
sem::Expression* Resolver::Expression(const ast::Expression* root) {
|
2021-10-19 18:38:54 +00:00
|
|
|
|
std::vector<const ast::Expression*> sorted;
|
2021-11-22 11:44:57 +00:00
|
|
|
|
bool mark_failed = false;
|
2021-10-21 20:38:54 +00:00
|
|
|
|
if (!ast::TraverseExpressions<ast::TraverseOrder::RightToLeft>(
|
|
|
|
|
root, diagnostics_, [&](const ast::Expression* expr) {
|
2021-11-22 11:44:57 +00:00
|
|
|
|
if (!Mark(expr)) {
|
|
|
|
|
mark_failed = true;
|
|
|
|
|
return ast::TraverseAction::Stop;
|
|
|
|
|
}
|
2021-10-21 20:38:54 +00:00
|
|
|
|
sorted.emplace_back(expr);
|
|
|
|
|
return ast::TraverseAction::Descend;
|
|
|
|
|
})) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return nullptr;
|
2021-09-08 15:18:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-22 11:44:57 +00:00
|
|
|
|
if (mark_failed) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-08 15:18:36 +00:00
|
|
|
|
for (auto* expr : utils::Reverse(sorted)) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
sem::Expression* sem_expr = nullptr;
|
2021-11-09 09:35:00 +00:00
|
|
|
|
if (auto* array = expr->As<ast::IndexAccessorExpression>()) {
|
|
|
|
|
sem_expr = IndexAccessor(array);
|
2021-09-08 15:18:36 +00:00
|
|
|
|
} else if (auto* bin_op = expr->As<ast::BinaryExpression>()) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
sem_expr = Binary(bin_op);
|
2021-09-08 15:18:36 +00:00
|
|
|
|
} else if (auto* bitcast = expr->As<ast::BitcastExpression>()) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
sem_expr = Bitcast(bitcast);
|
2021-09-08 15:18:36 +00:00
|
|
|
|
} else if (auto* call = expr->As<ast::CallExpression>()) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
sem_expr = Call(call);
|
2021-09-08 15:18:36 +00:00
|
|
|
|
} else if (auto* ident = expr->As<ast::IdentifierExpression>()) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
sem_expr = Identifier(ident);
|
2021-11-10 19:23:07 +00:00
|
|
|
|
} else if (auto* literal = expr->As<ast::LiteralExpression>()) {
|
2021-11-09 09:35:00 +00:00
|
|
|
|
sem_expr = Literal(literal);
|
2021-09-08 15:18:36 +00:00
|
|
|
|
} else if (auto* member = expr->As<ast::MemberAccessorExpression>()) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
sem_expr = MemberAccessor(member);
|
2021-09-08 15:18:36 +00:00
|
|
|
|
} else if (auto* unary = expr->As<ast::UnaryOpExpression>()) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
sem_expr = UnaryOp(unary);
|
2021-10-21 23:04:44 +00:00
|
|
|
|
} else if (expr->Is<ast::PhonyExpression>()) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
sem_expr = builder_->create<sem::Expression>(
|
|
|
|
|
expr, builder_->create<sem::Void>(), current_statement_,
|
|
|
|
|
sem::Constant{});
|
2021-09-08 15:18:36 +00:00
|
|
|
|
} else {
|
|
|
|
|
TINT_ICE(Resolver, diagnostics_)
|
|
|
|
|
<< "unhandled expression type: " << expr->TypeInfo().name;
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return nullptr;
|
2021-09-08 15:18:36 +00:00
|
|
|
|
}
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (!sem_expr) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2021-12-03 15:49:34 +00:00
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
builder_->Sem().Add(expr, sem_expr);
|
|
|
|
|
if (expr == root) {
|
|
|
|
|
return sem_expr;
|
2021-09-08 15:18:36 +00:00
|
|
|
|
}
|
2020-04-07 16:41:33 +00:00
|
|
|
|
}
|
2021-06-17 19:56:14 +00:00
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
TINT_ICE(Resolver, diagnostics_) << "Expression() did not find root node";
|
|
|
|
|
return nullptr;
|
2020-04-06 21:07:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-09 09:35:00 +00:00
|
|
|
|
sem::Expression* Resolver::IndexAccessor(
|
|
|
|
|
const ast::IndexAccessorExpression* expr) {
|
2021-12-03 15:49:34 +00:00
|
|
|
|
auto* idx = Sem(expr->index);
|
|
|
|
|
auto* obj = Sem(expr->object);
|
|
|
|
|
auto* obj_raw_ty = obj->Type();
|
|
|
|
|
auto* obj_ty = obj_raw_ty->UnwrapRef();
|
2021-11-05 16:51:38 +00:00
|
|
|
|
const sem::Type* ty = nullptr;
|
2021-12-03 15:49:34 +00:00
|
|
|
|
if (auto* arr = obj_ty->As<sem::Array>()) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
ty = arr->ElemType();
|
2021-12-03 15:49:34 +00:00
|
|
|
|
} else if (auto* vec = obj_ty->As<sem::Vector>()) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
ty = vec->type();
|
2021-12-03 15:49:34 +00:00
|
|
|
|
} else if (auto* mat = obj_ty->As<sem::Matrix>()) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
ty = builder_->create<sem::Vector>(mat->type(), mat->rows());
|
2020-04-07 12:57:42 +00:00
|
|
|
|
} else {
|
2021-12-03 15:49:34 +00:00
|
|
|
|
AddError("cannot index type '" + TypeNameOf(obj_ty) + "'", expr->source);
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return nullptr;
|
2020-04-07 12:57:42 +00:00
|
|
|
|
}
|
2020-04-23 22:26:52 +00:00
|
|
|
|
|
2021-12-03 15:49:34 +00:00
|
|
|
|
auto* idx_ty = idx->Type()->UnwrapRef();
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (!idx_ty->IsAnyOf<sem::I32, sem::U32>()) {
|
2021-06-24 11:27:36 +00:00
|
|
|
|
AddError("index must be of type 'i32' or 'u32', found: '" +
|
2021-11-05 16:51:38 +00:00
|
|
|
|
TypeNameOf(idx_ty) + "'",
|
2021-12-03 15:49:34 +00:00
|
|
|
|
idx->Declaration()->source);
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return nullptr;
|
2021-06-16 18:50:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-18 10:28:48 +00:00
|
|
|
|
// If we're extracting from a reference, we return a reference.
|
2021-12-03 15:49:34 +00:00
|
|
|
|
if (auto* ref = obj_raw_ty->As<sem::Reference>()) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
ty = builder_->create<sem::Reference>(ty, ref->StorageClass(),
|
|
|
|
|
ref->Access());
|
2020-04-23 22:26:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto val = EvaluateConstantValue(expr, ty);
|
2021-12-03 15:49:34 +00:00
|
|
|
|
auto* sem =
|
|
|
|
|
builder_->create<sem::Expression>(expr, ty, current_statement_, val);
|
|
|
|
|
sem->Behaviors() = idx->Behaviors() + obj->Behaviors();
|
|
|
|
|
return sem;
|
2020-04-07 12:57:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
sem::Expression* Resolver::Bitcast(const ast::BitcastExpression* expr) {
|
2021-12-03 15:49:34 +00:00
|
|
|
|
auto* inner = Sem(expr->expr);
|
2021-10-15 17:33:10 +00:00
|
|
|
|
auto* ty = Type(expr->type);
|
2021-06-03 08:17:44 +00:00
|
|
|
|
if (!ty) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return nullptr;
|
2021-06-03 08:17:44 +00:00
|
|
|
|
}
|
2021-11-05 16:51:38 +00:00
|
|
|
|
|
|
|
|
|
auto val = EvaluateConstantValue(expr, ty);
|
2021-12-03 15:49:34 +00:00
|
|
|
|
auto* sem =
|
|
|
|
|
builder_->create<sem::Expression>(expr, ty, current_statement_, val);
|
2021-12-03 21:29:13 +00:00
|
|
|
|
|
2021-12-03 15:49:34 +00:00
|
|
|
|
sem->Behaviors() = inner->Behaviors();
|
2021-12-03 21:29:13 +00:00
|
|
|
|
|
|
|
|
|
if (!ValidateBitcast(expr, ty)) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-03 15:49:34 +00:00
|
|
|
|
return sem;
|
2020-04-07 12:57:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-15 20:45:50 +00:00
|
|
|
|
sem::Call* Resolver::Call(const ast::CallExpression* expr) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
std::vector<const sem::Expression*> args(expr->args.size());
|
2021-11-15 20:45:50 +00:00
|
|
|
|
std::vector<const sem::Type*> arg_tys(args.size());
|
2021-12-03 15:49:34 +00:00
|
|
|
|
sem::Behaviors arg_behaviors;
|
|
|
|
|
|
2021-12-09 14:37:37 +00:00
|
|
|
|
// The element type of all the arguments. Nullptr if argument types are
|
|
|
|
|
// different.
|
|
|
|
|
const sem::Type* arg_el_ty = nullptr;
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
for (size_t i = 0; i < expr->args.size(); i++) {
|
|
|
|
|
auto* arg = Sem(expr->args[i]);
|
|
|
|
|
if (!arg) {
|
|
|
|
|
return nullptr;
|
2021-06-11 13:22:27 +00:00
|
|
|
|
}
|
2021-11-05 16:51:38 +00:00
|
|
|
|
args[i] = arg;
|
2021-11-15 20:45:50 +00:00
|
|
|
|
arg_tys[i] = args[i]->Type();
|
2021-12-03 15:49:34 +00:00
|
|
|
|
arg_behaviors.Add(arg->Behaviors());
|
2021-12-09 14:37:37 +00:00
|
|
|
|
|
|
|
|
|
// Determine the common argument element type
|
|
|
|
|
auto* el_ty = arg_tys[i]->UnwrapRef();
|
|
|
|
|
if (auto* vec = el_ty->As<sem::Vector>()) {
|
|
|
|
|
el_ty = vec->type();
|
|
|
|
|
} else if (auto* mat = el_ty->As<sem::Matrix>()) {
|
|
|
|
|
el_ty = mat->type();
|
|
|
|
|
}
|
|
|
|
|
if (i == 0) {
|
|
|
|
|
arg_el_ty = el_ty;
|
|
|
|
|
} else if (arg_el_ty != el_ty) {
|
|
|
|
|
arg_el_ty = nullptr;
|
|
|
|
|
}
|
2021-11-15 20:45:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-12-03 15:49:34 +00:00
|
|
|
|
arg_behaviors.Remove(sem::Behavior::kNext);
|
|
|
|
|
|
2021-11-15 20:45:50 +00:00
|
|
|
|
auto type_ctor_or_conv = [&](const sem::Type* ty) -> sem::Call* {
|
|
|
|
|
// The call has resolved to a type constructor or cast.
|
|
|
|
|
if (args.size() == 1) {
|
|
|
|
|
auto* target = ty;
|
|
|
|
|
auto* source = args[0]->Type()->UnwrapRef();
|
|
|
|
|
if ((source != target) && //
|
|
|
|
|
((source->is_scalar() && target->is_scalar()) ||
|
|
|
|
|
(source->Is<sem::Vector>() && target->Is<sem::Vector>()) ||
|
|
|
|
|
(source->Is<sem::Matrix>() && target->Is<sem::Matrix>()))) {
|
|
|
|
|
// Note: Matrix types currently cannot be converted (the element type
|
|
|
|
|
// must only be f32). We implement this for the day we support other
|
|
|
|
|
// matrix element types.
|
|
|
|
|
return TypeConversion(expr, ty, args[0], arg_tys[0]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return TypeConstructor(expr, ty, std::move(args), std::move(arg_tys));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Resolve the target of the CallExpression to determine whether this is a
|
|
|
|
|
// function call, cast or type constructor expression.
|
|
|
|
|
if (expr->target.type) {
|
2021-12-09 14:37:37 +00:00
|
|
|
|
const sem::Type* ty = nullptr;
|
|
|
|
|
|
|
|
|
|
auto err_cannot_infer_el_ty = [&](std::string name) {
|
|
|
|
|
AddError(
|
|
|
|
|
"cannot infer " + name +
|
|
|
|
|
" element type, as constructor arguments have different types",
|
|
|
|
|
expr->source);
|
|
|
|
|
for (size_t i = 0; i < args.size(); i++) {
|
|
|
|
|
auto* arg = args[i];
|
|
|
|
|
AddNote("argument " + std::to_string(i) + " has type " +
|
|
|
|
|
arg->Type()->FriendlyName(builder_->Symbols()),
|
|
|
|
|
arg->Declaration()->source);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (!expr->args.empty()) {
|
|
|
|
|
// vecN() without explicit element type?
|
|
|
|
|
// Try to infer element type from args
|
|
|
|
|
if (auto* vec = expr->target.type->As<ast::Vector>()) {
|
|
|
|
|
if (!vec->type) {
|
|
|
|
|
if (!arg_el_ty) {
|
|
|
|
|
err_cannot_infer_el_ty("vector");
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Mark(vec);
|
|
|
|
|
auto* v = builder_->create<sem::Vector>(
|
|
|
|
|
arg_el_ty, static_cast<uint32_t>(vec->width));
|
|
|
|
|
if (!ValidateVector(v, vec->source)) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
builder_->Sem().Add(vec, v);
|
|
|
|
|
ty = v;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// matNxM() without explicit element type?
|
|
|
|
|
// Try to infer element type from args
|
|
|
|
|
if (auto* mat = expr->target.type->As<ast::Matrix>()) {
|
|
|
|
|
if (!mat->type) {
|
|
|
|
|
if (!arg_el_ty) {
|
|
|
|
|
err_cannot_infer_el_ty("matrix");
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Mark(mat);
|
|
|
|
|
auto* column_type =
|
|
|
|
|
builder_->create<sem::Vector>(arg_el_ty, mat->rows);
|
|
|
|
|
auto* m = builder_->create<sem::Matrix>(column_type, mat->columns);
|
|
|
|
|
if (!ValidateMatrix(m, mat->source)) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
builder_->Sem().Add(mat, m);
|
|
|
|
|
ty = m;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-11-15 20:45:50 +00:00
|
|
|
|
}
|
2021-12-09 14:37:37 +00:00
|
|
|
|
|
|
|
|
|
if (ty == nullptr) {
|
|
|
|
|
ty = Type(expr->target.type);
|
|
|
|
|
if (!ty) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-15 20:45:50 +00:00
|
|
|
|
return type_ctor_or_conv(ty);
|
2021-06-11 13:22:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-15 20:45:50 +00:00
|
|
|
|
auto* ident = expr->target.name;
|
|
|
|
|
Mark(ident);
|
|
|
|
|
|
2021-11-23 20:45:51 +00:00
|
|
|
|
auto* resolved = ResolvedSymbol(ident);
|
|
|
|
|
if (auto* ty = As<sem::Type>(resolved)) {
|
|
|
|
|
return type_ctor_or_conv(ty);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (auto* fn = As<sem::Function>(resolved)) {
|
2021-12-03 15:49:34 +00:00
|
|
|
|
return FunctionCall(expr, fn, std::move(args), arg_behaviors);
|
2021-11-15 20:45:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto name = builder_->Symbols().NameFor(ident->symbol);
|
2022-02-02 23:07:11 +00:00
|
|
|
|
auto builtin_type = sem::ParseBuiltinType(name);
|
|
|
|
|
if (builtin_type != sem::BuiltinType::kNone) {
|
|
|
|
|
return BuiltinCall(expr, builtin_type, std::move(args), std::move(arg_tys));
|
2021-11-23 20:45:51 +00:00
|
|
|
|
}
|
2021-11-15 20:45:50 +00:00
|
|
|
|
|
2021-11-23 20:45:51 +00:00
|
|
|
|
TINT_ICE(Resolver, diagnostics_)
|
|
|
|
|
<< expr->source << " unresolved CallExpression target:\n"
|
|
|
|
|
<< "resolved: " << (resolved ? resolved->TypeInfo().name : "<null>")
|
|
|
|
|
<< "\n"
|
|
|
|
|
<< "name: " << builder_->Symbols().NameFor(ident->symbol);
|
|
|
|
|
return nullptr;
|
2021-11-15 20:45:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-02-02 23:07:11 +00:00
|
|
|
|
sem::Call* Resolver::BuiltinCall(const ast::CallExpression* expr,
|
|
|
|
|
sem::BuiltinType builtin_type,
|
|
|
|
|
const std::vector<const sem::Expression*> args,
|
|
|
|
|
const std::vector<const sem::Type*> arg_tys) {
|
|
|
|
|
auto* builtin =
|
|
|
|
|
builtin_table_->Lookup(builtin_type, std::move(arg_tys), expr->source);
|
|
|
|
|
if (!builtin) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return nullptr;
|
2020-07-21 17:44:44 +00:00
|
|
|
|
}
|
2020-06-01 13:43:22 +00:00
|
|
|
|
|
2022-02-02 23:07:11 +00:00
|
|
|
|
if (builtin->IsDeprecated()) {
|
|
|
|
|
AddWarning("use of deprecated builtin", expr->source);
|
2021-06-10 17:31:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-02-02 23:07:11 +00:00
|
|
|
|
auto* call = builder_->create<sem::Call>(expr, builtin, std::move(args),
|
2021-11-12 13:53:49 +00:00
|
|
|
|
current_statement_, sem::Constant{});
|
2021-06-03 16:07:34 +00:00
|
|
|
|
|
2022-02-02 23:07:11 +00:00
|
|
|
|
current_function_->AddDirectlyCalledBuiltin(builtin);
|
2021-06-03 16:07:34 +00:00
|
|
|
|
|
2022-02-02 23:07:11 +00:00
|
|
|
|
if (IsTextureBuiltin(builtin_type)) {
|
|
|
|
|
if (!ValidateTextureBuiltinFunction(call)) {
|
2022-01-10 20:46:35 +00:00
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2022-02-02 23:07:11 +00:00
|
|
|
|
// Collect a texture/sampler pair for this builtin.
|
|
|
|
|
const auto& signature = builtin->Signature();
|
2022-01-10 20:46:35 +00:00
|
|
|
|
int texture_index = signature.IndexOf(sem::ParameterUsage::kTexture);
|
|
|
|
|
if (texture_index == -1) {
|
|
|
|
|
TINT_ICE(Resolver, diagnostics_)
|
2022-02-02 23:07:11 +00:00
|
|
|
|
<< "texture builtin without texture parameter";
|
2022-01-10 20:46:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto* texture = args[texture_index]->As<sem::VariableUser>()->Variable();
|
GLSL: fix textureLoad() and textureStore(), depth textures, and more.
The CombineSamplers transform was incorrectly flagging StorageTexture
(which in GLSL ends up as image2D) as needing to be combined with a
sampler, or at least renamed. This is incorrect: StorageTexture never
has an associated sampler, so don't try to pair it up and just output
it as image* in GLSL.
In GLSL, textureLoad (aka texelFetch) of depth textures is not allowed.
The fix is to bind the depth texture as the corresponding f32 texture
instead (e.g., texture_depth_2d -> texture_2d<f32>,
texture_depth_cube -> texture_cube<f32>, etc). This requires changing
both the uniform globals and function parameter types. We're now going
to receive a vec4 instead of a float from texelFetch, so add a ".x"
member accessor to retrieve the first component. (Note that we don't
do this inside a CallStatement since this gives the CloneContext
indigestion, and CallStatement is going to ignore the result of the
call anyway.)
We were failing to find the dummy samplers that Dawn creates for the
calls that actually do require a dummy sampler, since the old Inspector
implementation of GetSamplerTextureUses() does not find them. The fix
is to implement a new Inspector call to return the texture/sampler
pairs the Resolver found during resolution. This will include the
dummy sampler as a null variable pointer.
In order to identify the placeholder sampler, we pass in a BindingPair
to represent it. When we discover a null sampler in the variable pair,
we return the passed-in placeholder binding point to the caller (Dawn).
(Dawn will use a group of kMaxBindGroups, to ensure that it never
collides with an existing sampler.)
Bug: tint:1298
Change-Id: I82e142c2b4318608c27a9fa9521c27f15a6214cd
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/78820
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Stephen White <senorblanco@chromium.org>
2022-02-03 22:39:13 +00:00
|
|
|
|
if (!texture->Type()->UnwrapRef()->Is<sem::StorageTexture>()) {
|
|
|
|
|
int sampler_index = signature.IndexOf(sem::ParameterUsage::kSampler);
|
|
|
|
|
const sem::Variable* sampler =
|
|
|
|
|
sampler_index != -1
|
|
|
|
|
? args[sampler_index]->As<sem::VariableUser>()->Variable()
|
|
|
|
|
: nullptr;
|
|
|
|
|
current_function_->AddTextureSamplerPair(texture, sampler);
|
|
|
|
|
}
|
2021-07-16 13:38:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-02-02 23:07:11 +00:00
|
|
|
|
if (!ValidateBuiltinCall(call)) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return nullptr;
|
2021-07-16 13:38:05 +00:00
|
|
|
|
}
|
2021-11-05 16:51:38 +00:00
|
|
|
|
|
2021-11-23 20:45:51 +00:00
|
|
|
|
current_function_->AddDirectCall(call);
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return call;
|
2020-06-01 13:43:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-15 20:45:50 +00:00
|
|
|
|
sem::Call* Resolver::FunctionCall(
|
|
|
|
|
const ast::CallExpression* expr,
|
2021-11-23 20:45:51 +00:00
|
|
|
|
sem::Function* target,
|
2021-12-03 15:49:34 +00:00
|
|
|
|
const std::vector<const sem::Expression*> args,
|
|
|
|
|
sem::Behaviors arg_behaviors) {
|
2021-11-15 20:45:50 +00:00
|
|
|
|
auto sym = expr->target.name->symbol;
|
|
|
|
|
auto name = builder_->Symbols().NameFor(sym);
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto* call = builder_->create<sem::Call>(expr, target, std::move(args),
|
2021-11-12 13:53:49 +00:00
|
|
|
|
current_statement_, sem::Constant{});
|
2021-06-09 20:17:59 +00:00
|
|
|
|
|
|
|
|
|
if (current_function_) {
|
|
|
|
|
// Note: Requires called functions to be resolved first.
|
|
|
|
|
// This is currently guaranteed as functions must be declared before
|
|
|
|
|
// use.
|
2021-11-05 16:51:38 +00:00
|
|
|
|
current_function_->AddTransitivelyCalledFunction(target);
|
2021-11-23 20:45:51 +00:00
|
|
|
|
current_function_->AddDirectCall(call);
|
2021-11-05 16:51:38 +00:00
|
|
|
|
for (auto* transitive_call : target->TransitivelyCalledFunctions()) {
|
|
|
|
|
current_function_->AddTransitivelyCalledFunction(transitive_call);
|
2021-06-09 20:17:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We inherit any referenced variables from the callee.
|
2021-11-05 16:51:38 +00:00
|
|
|
|
for (auto* var : target->TransitivelyReferencedGlobals()) {
|
|
|
|
|
current_function_->AddTransitivelyReferencedGlobal(var);
|
2021-06-09 20:17:59 +00:00
|
|
|
|
}
|
2022-01-10 20:46:35 +00:00
|
|
|
|
|
|
|
|
|
// Map all texture/sampler pairs from the target function to the
|
|
|
|
|
// current function. These can only be global or parameter
|
|
|
|
|
// variables. Resolve any parameter variables to the corresponding
|
|
|
|
|
// argument passed to the current function. Leave global variables
|
|
|
|
|
// as-is. Then add the mapped pair to the current function's list of
|
|
|
|
|
// texture/sampler pairs.
|
|
|
|
|
for (sem::VariablePair pair : target->TextureSamplerPairs()) {
|
|
|
|
|
const sem::Variable* texture = pair.first;
|
|
|
|
|
const sem::Variable* sampler = pair.second;
|
|
|
|
|
if (auto* param = texture->As<sem::Parameter>()) {
|
|
|
|
|
texture = args[param->Index()]->As<sem::VariableUser>()->Variable();
|
|
|
|
|
}
|
|
|
|
|
if (sampler) {
|
|
|
|
|
if (auto* param = sampler->As<sem::Parameter>()) {
|
|
|
|
|
sampler = args[param->Index()]->As<sem::VariableUser>()->Variable();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
current_function_->AddTextureSamplerPair(texture, sampler);
|
|
|
|
|
}
|
2021-06-09 20:17:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-23 20:45:51 +00:00
|
|
|
|
target->AddCallSite(call);
|
|
|
|
|
|
2021-12-03 15:49:34 +00:00
|
|
|
|
call->Behaviors() = arg_behaviors + target->Behaviors();
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (!ValidateFunctionCall(call)) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return call;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-15 20:45:50 +00:00
|
|
|
|
sem::Call* Resolver::TypeConversion(const ast::CallExpression* expr,
|
|
|
|
|
const sem::Type* target,
|
|
|
|
|
const sem::Expression* arg,
|
|
|
|
|
const sem::Type* source) {
|
|
|
|
|
// It is not valid to have a type-cast call expression inside a call
|
|
|
|
|
// statement.
|
2021-12-03 15:49:34 +00:00
|
|
|
|
if (IsCallStatement(expr)) {
|
|
|
|
|
AddError("type cast evaluated but not used", expr->source);
|
|
|
|
|
return nullptr;
|
2021-11-09 09:35:00 +00:00
|
|
|
|
}
|
2021-11-05 16:51:38 +00:00
|
|
|
|
|
2021-11-15 20:45:50 +00:00
|
|
|
|
auto* call_target = utils::GetOrCreate(
|
|
|
|
|
type_conversions_, TypeConversionSig{target, source},
|
|
|
|
|
[&]() -> sem::TypeConversion* {
|
2021-12-09 14:37:37 +00:00
|
|
|
|
// Now that the argument types have been determined, make sure that
|
|
|
|
|
// they obey the conversion rules laid out in
|
2021-11-15 20:45:50 +00:00
|
|
|
|
// https://gpuweb.github.io/gpuweb/wgsl/#conversion-expr.
|
|
|
|
|
bool ok = true;
|
|
|
|
|
if (auto* vec_type = target->As<sem::Vector>()) {
|
|
|
|
|
ok = ValidateVectorConstructorOrCast(expr, vec_type);
|
|
|
|
|
} else if (auto* mat_type = target->As<sem::Matrix>()) {
|
2021-12-09 14:37:37 +00:00
|
|
|
|
// Note: Matrix types currently cannot be converted (the element
|
|
|
|
|
// type must only be f32). We implement this for the day we support
|
|
|
|
|
// other matrix element types.
|
2021-11-15 20:45:50 +00:00
|
|
|
|
ok = ValidateMatrixConstructorOrCast(expr, mat_type);
|
|
|
|
|
} else if (target->is_scalar()) {
|
|
|
|
|
ok = ValidateScalarConstructorOrCast(expr, target);
|
|
|
|
|
} else if (auto* arr_type = target->As<sem::Array>()) {
|
|
|
|
|
ok = ValidateArrayConstructorOrCast(expr, arr_type);
|
|
|
|
|
} else if (auto* struct_type = target->As<sem::Struct>()) {
|
|
|
|
|
ok = ValidateStructureConstructorOrCast(expr, struct_type);
|
|
|
|
|
} else {
|
|
|
|
|
AddError("type is not constructible", expr->source);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
if (!ok) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto* param = builder_->create<sem::Parameter>(
|
|
|
|
|
nullptr, // declaration
|
|
|
|
|
0, // index
|
|
|
|
|
source->UnwrapRef(), // type
|
|
|
|
|
ast::StorageClass::kNone, // storage_class
|
|
|
|
|
ast::Access::kUndefined); // access
|
|
|
|
|
return builder_->create<sem::TypeConversion>(target, param);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!call_target) {
|
2021-11-09 09:35:00 +00:00
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2021-11-15 20:45:50 +00:00
|
|
|
|
|
|
|
|
|
auto val = EvaluateConstantValue(expr, target);
|
|
|
|
|
return builder_->create<sem::Call>(expr, call_target,
|
|
|
|
|
std::vector<const sem::Expression*>{arg},
|
|
|
|
|
current_statement_, val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sem::Call* Resolver::TypeConstructor(
|
|
|
|
|
const ast::CallExpression* expr,
|
|
|
|
|
const sem::Type* ty,
|
|
|
|
|
const std::vector<const sem::Expression*> args,
|
|
|
|
|
const std::vector<const sem::Type*> arg_tys) {
|
|
|
|
|
// It is not valid to have a type-constructor call expression as a call
|
|
|
|
|
// statement.
|
2021-12-03 15:49:34 +00:00
|
|
|
|
if (IsCallStatement(expr)) {
|
|
|
|
|
AddError("type constructor evaluated but not used", expr->source);
|
|
|
|
|
return nullptr;
|
2021-11-15 20:45:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto* call_target = utils::GetOrCreate(
|
|
|
|
|
type_ctors_, TypeConstructorSig{ty, arg_tys},
|
|
|
|
|
[&]() -> sem::TypeConstructor* {
|
2021-12-09 14:37:37 +00:00
|
|
|
|
// Now that the argument types have been determined, make sure that
|
|
|
|
|
// they obey the constructor type rules laid out in
|
2021-11-15 20:45:50 +00:00
|
|
|
|
// https://gpuweb.github.io/gpuweb/wgsl/#type-constructor-expr.
|
|
|
|
|
bool ok = true;
|
|
|
|
|
if (auto* vec_type = ty->As<sem::Vector>()) {
|
|
|
|
|
ok = ValidateVectorConstructorOrCast(expr, vec_type);
|
|
|
|
|
} else if (auto* mat_type = ty->As<sem::Matrix>()) {
|
|
|
|
|
ok = ValidateMatrixConstructorOrCast(expr, mat_type);
|
|
|
|
|
} else if (ty->is_scalar()) {
|
|
|
|
|
ok = ValidateScalarConstructorOrCast(expr, ty);
|
|
|
|
|
} else if (auto* arr_type = ty->As<sem::Array>()) {
|
|
|
|
|
ok = ValidateArrayConstructorOrCast(expr, arr_type);
|
|
|
|
|
} else if (auto* struct_type = ty->As<sem::Struct>()) {
|
|
|
|
|
ok = ValidateStructureConstructorOrCast(expr, struct_type);
|
|
|
|
|
} else {
|
|
|
|
|
AddError("type is not constructible", expr->source);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
if (!ok) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return builder_->create<sem::TypeConstructor>(
|
|
|
|
|
ty, utils::Transform(
|
|
|
|
|
arg_tys,
|
|
|
|
|
[&](const sem::Type* t, size_t i) -> const sem::Parameter* {
|
|
|
|
|
return builder_->create<sem::Parameter>(
|
|
|
|
|
nullptr, // declaration
|
2022-01-04 15:31:24 +00:00
|
|
|
|
static_cast<uint32_t>(i), // index
|
2021-11-15 20:45:50 +00:00
|
|
|
|
t->UnwrapRef(), // type
|
|
|
|
|
ast::StorageClass::kNone, // storage_class
|
|
|
|
|
ast::Access::kUndefined); // access
|
|
|
|
|
}));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!call_target) {
|
2021-11-09 09:35:00 +00:00
|
|
|
|
return nullptr;
|
2021-07-13 12:18:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-09 09:35:00 +00:00
|
|
|
|
auto val = EvaluateConstantValue(expr, ty);
|
2021-11-15 20:45:50 +00:00
|
|
|
|
return builder_->create<sem::Call>(expr, call_target, std::move(args),
|
|
|
|
|
current_statement_, val);
|
2021-11-09 09:35:00 +00:00
|
|
|
|
}
|
2021-11-05 16:51:38 +00:00
|
|
|
|
|
2021-11-10 19:23:07 +00:00
|
|
|
|
sem::Expression* Resolver::Literal(const ast::LiteralExpression* literal) {
|
2021-11-09 09:35:00 +00:00
|
|
|
|
auto* ty = TypeOf(literal);
|
|
|
|
|
if (!ty) {
|
|
|
|
|
return nullptr;
|
2021-03-15 21:21:33 +00:00
|
|
|
|
}
|
2021-07-13 12:18:13 +00:00
|
|
|
|
|
2021-11-09 09:35:00 +00:00
|
|
|
|
auto val = EvaluateConstantValue(literal, ty);
|
|
|
|
|
return builder_->create<sem::Expression>(literal, ty, current_statement_,
|
|
|
|
|
val);
|
2021-03-15 21:21:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
sem::Expression* Resolver::Identifier(const ast::IdentifierExpression* expr) {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
auto symbol = expr->symbol;
|
2021-11-23 20:45:51 +00:00
|
|
|
|
auto* resolved = ResolvedSymbol(expr);
|
|
|
|
|
if (auto* var = As<sem::Variable>(resolved)) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto* user =
|
|
|
|
|
builder_->create<sem::VariableUser>(expr, current_statement_, var);
|
2021-03-09 10:26:57 +00:00
|
|
|
|
|
2021-09-30 17:29:50 +00:00
|
|
|
|
if (current_statement_) {
|
2021-05-18 10:28:48 +00:00
|
|
|
|
// If identifier is part of a loop continuing block, make sure it
|
|
|
|
|
// doesn't refer to a variable that is bypassed by a continue statement
|
|
|
|
|
// in the loop's body block.
|
2021-05-19 21:22:07 +00:00
|
|
|
|
if (auto* continuing_block =
|
2021-09-30 17:29:50 +00:00
|
|
|
|
current_statement_
|
2021-05-19 21:22:07 +00:00
|
|
|
|
->FindFirstParent<sem::LoopContinuingBlockStatement>()) {
|
2021-03-22 17:25:56 +00:00
|
|
|
|
auto* loop_block =
|
2021-05-19 21:22:07 +00:00
|
|
|
|
continuing_block->FindFirstParent<sem::LoopBlockStatement>();
|
2021-09-30 17:29:50 +00:00
|
|
|
|
if (loop_block->FirstContinue()) {
|
2021-05-15 14:48:46 +00:00
|
|
|
|
auto& decls = loop_block->Decls();
|
2021-03-22 17:25:56 +00:00
|
|
|
|
// If our identifier is in loop_block->decls, make sure its index is
|
|
|
|
|
// less than first_continue
|
2021-10-15 17:33:10 +00:00
|
|
|
|
auto iter =
|
|
|
|
|
std::find_if(decls.begin(), decls.end(),
|
|
|
|
|
[&symbol](auto* v) { return v->symbol == symbol; });
|
2021-03-22 17:25:56 +00:00
|
|
|
|
if (iter != decls.end()) {
|
|
|
|
|
auto var_decl_index =
|
|
|
|
|
static_cast<size_t>(std::distance(decls.begin(), iter));
|
2021-09-30 17:29:50 +00:00
|
|
|
|
if (var_decl_index >= loop_block->NumDeclsAtFirstContinue()) {
|
2021-06-24 11:27:36 +00:00
|
|
|
|
AddError("continue statement bypasses declaration of '" +
|
2021-09-30 17:29:50 +00:00
|
|
|
|
builder_->Symbols().NameFor(symbol) + "'",
|
2021-10-15 17:33:10 +00:00
|
|
|
|
loop_block->FirstContinue()->source);
|
2021-09-30 17:29:50 +00:00
|
|
|
|
AddNote("identifier '" + builder_->Symbols().NameFor(symbol) +
|
|
|
|
|
"' declared here",
|
2021-10-15 17:33:10 +00:00
|
|
|
|
(*iter)->source);
|
2021-09-30 17:29:50 +00:00
|
|
|
|
AddNote("identifier '" + builder_->Symbols().NameFor(symbol) +
|
|
|
|
|
"' referenced in continuing block here",
|
2021-10-15 17:33:10 +00:00
|
|
|
|
expr->source);
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return nullptr;
|
2021-03-22 17:25:56 +00:00
|
|
|
|
}
|
2021-03-09 10:26:57 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (current_function_) {
|
|
|
|
|
if (auto* global = var->As<sem::GlobalVariable>()) {
|
|
|
|
|
current_function_->AddDirectlyReferencedGlobal(global);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var->AddUser(user);
|
|
|
|
|
return user;
|
2020-04-07 12:57:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-23 20:45:51 +00:00
|
|
|
|
if (Is<sem::Function>(resolved)) {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
AddError("missing '(' for function call", expr->source.End());
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return nullptr;
|
2020-04-07 12:57:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-02-02 23:07:11 +00:00
|
|
|
|
if (IsBuiltin(symbol)) {
|
|
|
|
|
AddError("missing '(' for builtin call", expr->source.End());
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return nullptr;
|
2020-10-14 18:26:31 +00:00
|
|
|
|
}
|
2021-02-03 21:02:25 +00:00
|
|
|
|
|
2021-11-23 20:45:51 +00:00
|
|
|
|
if (resolved->Is<sem::Type>()) {
|
|
|
|
|
AddError("missing '(' for type constructor or cast", expr->source.End());
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TINT_ICE(Resolver, diagnostics_)
|
|
|
|
|
<< expr->source << " unresolved identifier:\n"
|
|
|
|
|
<< "resolved: " << (resolved ? resolved->TypeInfo().name : "<null>")
|
|
|
|
|
<< "\n"
|
|
|
|
|
<< "name: " << builder_->Symbols().NameFor(symbol);
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return nullptr;
|
2020-04-07 16:41:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
sem::Expression* Resolver::MemberAccessor(
|
|
|
|
|
const ast::MemberAccessorExpression* expr) {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
auto* structure = TypeOf(expr->structure);
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto* storage_ty = structure->UnwrapRef();
|
2020-04-24 00:40:45 +00:00
|
|
|
|
|
2021-10-19 18:38:54 +00:00
|
|
|
|
const sem::Type* ret = nullptr;
|
2021-02-24 14:15:02 +00:00
|
|
|
|
std::vector<uint32_t> swizzle;
|
2021-02-03 23:55:56 +00:00
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (auto* str = storage_ty->As<sem::Struct>()) {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
Mark(expr->member);
|
|
|
|
|
auto symbol = expr->member->symbol;
|
2020-04-07 16:41:33 +00:00
|
|
|
|
|
2021-04-16 19:07:51 +00:00
|
|
|
|
const sem::StructMember* member = nullptr;
|
2021-05-07 14:49:34 +00:00
|
|
|
|
for (auto* m : str->Members()) {
|
2021-07-23 16:43:01 +00:00
|
|
|
|
if (m->Name() == symbol) {
|
2021-05-05 09:09:41 +00:00
|
|
|
|
ret = m->Type();
|
2021-04-09 13:56:08 +00:00
|
|
|
|
member = m;
|
2020-04-23 22:26:52 +00:00
|
|
|
|
break;
|
2020-04-07 16:41:33 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-23 22:26:52 +00:00
|
|
|
|
if (ret == nullptr) {
|
2021-06-24 11:27:36 +00:00
|
|
|
|
AddError(
|
2021-02-17 20:13:34 +00:00
|
|
|
|
"struct member " + builder_->Symbols().NameFor(symbol) + " not found",
|
2021-10-15 17:33:10 +00:00
|
|
|
|
expr->source);
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return nullptr;
|
2020-04-23 22:26:52 +00:00
|
|
|
|
}
|
2020-05-01 16:17:03 +00:00
|
|
|
|
|
2021-05-18 10:28:48 +00:00
|
|
|
|
// If we're extracting from a reference, we return a reference.
|
|
|
|
|
if (auto* ref = structure->As<sem::Reference>()) {
|
2021-06-04 22:17:37 +00:00
|
|
|
|
ret = builder_->create<sem::Reference>(ret, ref->StorageClass(),
|
|
|
|
|
ref->Access());
|
2020-05-01 16:17:03 +00:00
|
|
|
|
}
|
2021-04-09 13:56:08 +00:00
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return builder_->create<sem::StructMemberAccess>(
|
|
|
|
|
expr, ret, current_statement_, member);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (auto* vec = storage_ty->As<sem::Vector>()) {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
Mark(expr->member);
|
|
|
|
|
std::string s = builder_->Symbols().NameFor(expr->member->symbol);
|
2021-05-07 14:49:34 +00:00
|
|
|
|
auto size = s.size();
|
|
|
|
|
swizzle.reserve(s.size());
|
2021-02-24 14:15:02 +00:00
|
|
|
|
|
2021-05-07 14:49:34 +00:00
|
|
|
|
for (auto c : s) {
|
2021-02-24 14:15:02 +00:00
|
|
|
|
switch (c) {
|
|
|
|
|
case 'x':
|
|
|
|
|
case 'r':
|
|
|
|
|
swizzle.emplace_back(0);
|
|
|
|
|
break;
|
|
|
|
|
case 'y':
|
|
|
|
|
case 'g':
|
|
|
|
|
swizzle.emplace_back(1);
|
|
|
|
|
break;
|
|
|
|
|
case 'z':
|
|
|
|
|
case 'b':
|
|
|
|
|
swizzle.emplace_back(2);
|
|
|
|
|
break;
|
|
|
|
|
case 'w':
|
|
|
|
|
case 'a':
|
|
|
|
|
swizzle.emplace_back(3);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2021-06-24 11:27:36 +00:00
|
|
|
|
AddError("invalid vector swizzle character",
|
2021-10-15 17:33:10 +00:00
|
|
|
|
expr->member->source.Begin() + swizzle.size());
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return nullptr;
|
2021-02-24 14:15:02 +00:00
|
|
|
|
}
|
2021-06-01 00:37:40 +00:00
|
|
|
|
|
2021-07-22 18:31:34 +00:00
|
|
|
|
if (swizzle.back() >= vec->Width()) {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
AddError("invalid vector swizzle member", expr->member->source);
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return nullptr;
|
2021-06-01 00:37:40 +00:00
|
|
|
|
}
|
2021-02-24 14:15:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (size < 1 || size > 4) {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
AddError("invalid vector swizzle size", expr->member->source);
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return nullptr;
|
2021-02-24 14:15:02 +00:00
|
|
|
|
}
|
2021-01-11 16:24:32 +00:00
|
|
|
|
|
2021-03-01 20:01:39 +00:00
|
|
|
|
// All characters are valid, check if they're being mixed
|
|
|
|
|
auto is_rgba = [](char c) {
|
|
|
|
|
return c == 'r' || c == 'g' || c == 'b' || c == 'a';
|
|
|
|
|
};
|
|
|
|
|
auto is_xyzw = [](char c) {
|
|
|
|
|
return c == 'x' || c == 'y' || c == 'z' || c == 'w';
|
|
|
|
|
};
|
2021-05-07 14:49:34 +00:00
|
|
|
|
if (!std::all_of(s.begin(), s.end(), is_rgba) &&
|
|
|
|
|
!std::all_of(s.begin(), s.end(), is_xyzw)) {
|
2021-06-24 11:27:36 +00:00
|
|
|
|
AddError("invalid mixing of vector swizzle characters rgba with xyzw",
|
2021-10-15 17:33:10 +00:00
|
|
|
|
expr->member->source);
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return nullptr;
|
2021-03-01 20:01:39 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-21 13:05:34 +00:00
|
|
|
|
if (size == 1) {
|
|
|
|
|
// A single element swizzle is just the type of the vector.
|
2020-04-23 22:26:52 +00:00
|
|
|
|
ret = vec->type();
|
2021-05-18 10:28:48 +00:00
|
|
|
|
// If we're extracting from a reference, we return a reference.
|
|
|
|
|
if (auto* ref = structure->As<sem::Reference>()) {
|
2021-06-04 22:17:37 +00:00
|
|
|
|
ret = builder_->create<sem::Reference>(ret, ref->StorageClass(),
|
|
|
|
|
ref->Access());
|
2020-05-01 16:17:03 +00:00
|
|
|
|
}
|
2020-04-21 13:05:34 +00:00
|
|
|
|
} else {
|
2021-02-24 14:15:02 +00:00
|
|
|
|
// The vector will have a number of components equal to the length of
|
2021-04-08 14:08:47 +00:00
|
|
|
|
// the swizzle.
|
2021-04-19 22:51:23 +00:00
|
|
|
|
ret = builder_->create<sem::Vector>(vec->type(),
|
|
|
|
|
static_cast<uint32_t>(size));
|
2020-04-21 13:05:34 +00:00
|
|
|
|
}
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return builder_->create<sem::Swizzle>(expr, ret, current_statement_,
|
|
|
|
|
std::move(swizzle));
|
2020-04-07 16:41:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
AddError(
|
|
|
|
|
"invalid member accessor expression. Expected vector or struct, got '" +
|
|
|
|
|
TypeNameOf(storage_ty) + "'",
|
|
|
|
|
expr->structure->source);
|
|
|
|
|
return nullptr;
|
2020-04-07 12:57:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
sem::Expression* Resolver::Binary(const ast::BinaryExpression* expr) {
|
2021-04-19 22:51:23 +00:00
|
|
|
|
using Bool = sem::Bool;
|
|
|
|
|
using F32 = sem::F32;
|
|
|
|
|
using I32 = sem::I32;
|
|
|
|
|
using U32 = sem::U32;
|
|
|
|
|
using Matrix = sem::Matrix;
|
|
|
|
|
using Vector = sem::Vector;
|
2021-03-16 13:26:03 +00:00
|
|
|
|
|
2021-12-03 15:49:34 +00:00
|
|
|
|
auto* lhs = Sem(expr->lhs);
|
|
|
|
|
auto* rhs = Sem(expr->rhs);
|
|
|
|
|
|
|
|
|
|
auto* lhs_ty = lhs->Type()->UnwrapRef();
|
|
|
|
|
auto* rhs_ty = rhs->Type()->UnwrapRef();
|
2021-03-16 13:26:03 +00:00
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto* lhs_vec = lhs_ty->As<Vector>();
|
2021-04-13 13:32:33 +00:00
|
|
|
|
auto* lhs_vec_elem_type = lhs_vec ? lhs_vec->type() : nullptr;
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto* rhs_vec = rhs_ty->As<Vector>();
|
2021-04-13 13:32:33 +00:00
|
|
|
|
auto* rhs_vec_elem_type = rhs_vec ? rhs_vec->type() : nullptr;
|
2021-03-16 13:26:03 +00:00
|
|
|
|
|
2021-04-01 19:58:37 +00:00
|
|
|
|
const bool matching_vec_elem_types =
|
|
|
|
|
lhs_vec_elem_type && rhs_vec_elem_type &&
|
|
|
|
|
(lhs_vec_elem_type == rhs_vec_elem_type) &&
|
2021-07-22 18:31:34 +00:00
|
|
|
|
(lhs_vec->Width() == rhs_vec->Width());
|
2021-03-16 13:26:03 +00:00
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
const bool matching_types = matching_vec_elem_types || (lhs_ty == rhs_ty);
|
|
|
|
|
|
|
|
|
|
auto build = [&](const sem::Type* ty) {
|
|
|
|
|
auto val = EvaluateConstantValue(expr, ty);
|
2021-12-03 15:49:34 +00:00
|
|
|
|
auto* sem =
|
|
|
|
|
builder_->create<sem::Expression>(expr, ty, current_statement_, val);
|
|
|
|
|
sem->Behaviors() = lhs->Behaviors() + rhs->Behaviors();
|
|
|
|
|
return sem;
|
2021-11-05 16:51:38 +00:00
|
|
|
|
};
|
2021-04-01 14:59:27 +00:00
|
|
|
|
|
2021-03-16 13:26:03 +00:00
|
|
|
|
// Binary logical expressions
|
|
|
|
|
if (expr->IsLogicalAnd() || expr->IsLogicalOr()) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (matching_types && lhs_ty->Is<Bool>()) {
|
|
|
|
|
return build(lhs_ty);
|
2021-03-16 13:26:03 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (expr->IsOr() || expr->IsAnd()) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (matching_types && lhs_ty->Is<Bool>()) {
|
|
|
|
|
return build(lhs_ty);
|
2021-03-16 13:26:03 +00:00
|
|
|
|
}
|
|
|
|
|
if (matching_types && lhs_vec_elem_type && lhs_vec_elem_type->Is<Bool>()) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return build(lhs_ty);
|
2021-03-16 13:26:03 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Arithmetic expressions
|
|
|
|
|
if (expr->IsArithmetic()) {
|
|
|
|
|
// Binary arithmetic expressions over scalars
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (matching_types && lhs_ty->is_numeric_scalar()) {
|
|
|
|
|
return build(lhs_ty);
|
2021-03-16 13:26:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Binary arithmetic expressions over vectors
|
|
|
|
|
if (matching_types && lhs_vec_elem_type &&
|
2021-05-27 18:25:06 +00:00
|
|
|
|
lhs_vec_elem_type->is_numeric_scalar()) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return build(lhs_ty);
|
2021-03-16 13:26:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-27 21:07:56 +00:00
|
|
|
|
// Binary arithmetic expressions with mixed scalar and vector operands
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (lhs_vec_elem_type && (lhs_vec_elem_type == rhs_ty)) {
|
2021-05-27 21:07:56 +00:00
|
|
|
|
if (expr->IsModulo()) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (rhs_ty->is_integer_scalar()) {
|
|
|
|
|
return build(lhs_ty);
|
2021-05-27 21:07:56 +00:00
|
|
|
|
}
|
2021-11-05 16:51:38 +00:00
|
|
|
|
} else if (rhs_ty->is_numeric_scalar()) {
|
|
|
|
|
return build(lhs_ty);
|
2021-05-27 21:07:56 +00:00
|
|
|
|
}
|
2021-03-16 13:26:03 +00:00
|
|
|
|
}
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (rhs_vec_elem_type && (rhs_vec_elem_type == lhs_ty)) {
|
2021-05-27 21:07:56 +00:00
|
|
|
|
if (expr->IsModulo()) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (lhs_ty->is_integer_scalar()) {
|
|
|
|
|
return build(rhs_ty);
|
2021-05-27 21:07:56 +00:00
|
|
|
|
}
|
2021-11-05 16:51:38 +00:00
|
|
|
|
} else if (lhs_ty->is_numeric_scalar()) {
|
|
|
|
|
return build(rhs_ty);
|
2021-05-27 21:07:56 +00:00
|
|
|
|
}
|
2021-03-16 13:26:03 +00:00
|
|
|
|
}
|
2021-05-27 21:07:56 +00:00
|
|
|
|
}
|
2021-03-16 13:26:03 +00:00
|
|
|
|
|
2021-05-27 21:07:56 +00:00
|
|
|
|
// Matrix arithmetic
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto* lhs_mat = lhs_ty->As<Matrix>();
|
2021-05-31 15:53:20 +00:00
|
|
|
|
auto* lhs_mat_elem_type = lhs_mat ? lhs_mat->type() : nullptr;
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto* rhs_mat = rhs_ty->As<Matrix>();
|
2021-05-31 15:53:20 +00:00
|
|
|
|
auto* rhs_mat_elem_type = rhs_mat ? rhs_mat->type() : nullptr;
|
|
|
|
|
// Addition and subtraction of float matrices
|
|
|
|
|
if ((expr->IsAdd() || expr->IsSubtract()) && 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->columns()) &&
|
|
|
|
|
(lhs_mat->rows() == rhs_mat->rows())) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return build(rhs_ty);
|
2021-05-31 15:53:20 +00:00
|
|
|
|
}
|
2021-05-27 21:07:56 +00:00
|
|
|
|
if (expr->IsMultiply()) {
|
2021-03-16 13:26:03 +00:00
|
|
|
|
// Multiplication of a matrix and a scalar
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (lhs_ty->Is<F32>() && rhs_mat_elem_type &&
|
2021-03-16 13:26:03 +00:00
|
|
|
|
rhs_mat_elem_type->Is<F32>()) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return build(rhs_ty);
|
2021-03-16 13:26:03 +00:00
|
|
|
|
}
|
|
|
|
|
if (lhs_mat_elem_type && lhs_mat_elem_type->Is<F32>() &&
|
2021-11-05 16:51:38 +00:00
|
|
|
|
rhs_ty->Is<F32>()) {
|
|
|
|
|
return build(lhs_ty);
|
2021-03-16 13:26:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Vector times matrix
|
|
|
|
|
if (lhs_vec_elem_type && lhs_vec_elem_type->Is<F32>() &&
|
2021-04-01 19:58:37 +00:00
|
|
|
|
rhs_mat_elem_type && rhs_mat_elem_type->Is<F32>() &&
|
2021-07-22 18:31:34 +00:00
|
|
|
|
(lhs_vec->Width() == rhs_mat->rows())) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return build(
|
|
|
|
|
builder_->create<sem::Vector>(lhs_vec->type(), rhs_mat->columns()));
|
2021-03-16 13:26:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Matrix times vector
|
|
|
|
|
if (lhs_mat_elem_type && lhs_mat_elem_type->Is<F32>() &&
|
2021-04-01 19:58:37 +00:00
|
|
|
|
rhs_vec_elem_type && rhs_vec_elem_type->Is<F32>() &&
|
2021-07-22 18:31:34 +00:00
|
|
|
|
(lhs_mat->columns() == rhs_vec->Width())) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return build(
|
|
|
|
|
builder_->create<sem::Vector>(rhs_vec->type(), lhs_mat->rows()));
|
2021-03-16 13:26:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Matrix times matrix
|
|
|
|
|
if (lhs_mat_elem_type && lhs_mat_elem_type->Is<F32>() &&
|
2021-04-01 19:58:37 +00:00
|
|
|
|
rhs_mat_elem_type && rhs_mat_elem_type->Is<F32>() &&
|
|
|
|
|
(lhs_mat->columns() == rhs_mat->rows())) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return build(builder_->create<sem::Matrix>(
|
|
|
|
|
builder_->create<sem::Vector>(lhs_mat_elem_type, lhs_mat->rows()),
|
|
|
|
|
rhs_mat->columns()));
|
2021-03-16 13:26:03 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Comparison expressions
|
|
|
|
|
if (expr->IsComparison()) {
|
|
|
|
|
if (matching_types) {
|
|
|
|
|
// Special case for bools: only == and !=
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (lhs_ty->Is<Bool>() && (expr->IsEqual() || expr->IsNotEqual())) {
|
|
|
|
|
return build(builder_->create<sem::Bool>());
|
2021-03-16 13:26:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// For the rest, we can compare i32, u32, and f32
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (lhs_ty->IsAnyOf<I32, U32, F32>()) {
|
|
|
|
|
return build(builder_->create<sem::Bool>());
|
2021-03-16 13:26:03 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Same for vectors
|
|
|
|
|
if (matching_vec_elem_types) {
|
|
|
|
|
if (lhs_vec_elem_type->Is<Bool>() &&
|
|
|
|
|
(expr->IsEqual() || expr->IsNotEqual())) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return build(builder_->create<sem::Vector>(
|
|
|
|
|
builder_->create<sem::Bool>(), lhs_vec->Width()));
|
2021-03-16 13:26:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-27 18:25:06 +00:00
|
|
|
|
if (lhs_vec_elem_type->is_numeric_scalar()) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return build(builder_->create<sem::Vector>(
|
|
|
|
|
builder_->create<sem::Bool>(), lhs_vec->Width()));
|
2021-03-16 13:26:03 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Binary bitwise operations
|
|
|
|
|
if (expr->IsBitwise()) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (matching_types && lhs_ty->is_integer_scalar_or_vector()) {
|
|
|
|
|
return build(lhs_ty);
|
2021-03-16 13:26:03 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Bit shift expressions
|
|
|
|
|
if (expr->IsBitshift()) {
|
|
|
|
|
// Type validation rules are the same for left or right shift, despite
|
|
|
|
|
// differences in computation rules (i.e. right shift can be arithmetic or
|
|
|
|
|
// logical depending on lhs type).
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (lhs_ty->IsAnyOf<I32, U32>() && rhs_ty->Is<U32>()) {
|
|
|
|
|
return build(lhs_ty);
|
2021-03-16 13:26:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (lhs_vec_elem_type && lhs_vec_elem_type->IsAnyOf<I32, U32>() &&
|
|
|
|
|
rhs_vec_elem_type && rhs_vec_elem_type->Is<U32>()) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return build(lhs_ty);
|
2021-03-16 13:26:03 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-24 11:27:36 +00:00
|
|
|
|
AddError("Binary expression operand types are invalid for this operation: " +
|
2021-11-05 16:51:38 +00:00
|
|
|
|
TypeNameOf(lhs_ty) + " " + FriendlyName(expr->op) + " " +
|
|
|
|
|
TypeNameOf(rhs_ty),
|
2021-10-15 17:33:10 +00:00
|
|
|
|
expr->source);
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return nullptr;
|
2021-03-16 13:26:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
sem::Expression* Resolver::UnaryOp(const ast::UnaryOpExpression* unary) {
|
2021-12-03 15:49:34 +00:00
|
|
|
|
auto* expr = Sem(unary->expr);
|
|
|
|
|
auto* expr_ty = expr->Type();
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (!expr_ty) {
|
|
|
|
|
return nullptr;
|
2020-04-07 19:27:21 +00:00
|
|
|
|
}
|
2021-01-29 16:43:41 +00:00
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
const sem::Type* ty = nullptr;
|
2021-05-18 10:28:48 +00:00
|
|
|
|
|
2021-10-15 17:33:10 +00:00
|
|
|
|
switch (unary->op) {
|
2021-06-28 14:09:33 +00:00
|
|
|
|
case ast::UnaryOp::kNot:
|
|
|
|
|
// Result type matches the deref'd inner type.
|
2021-11-05 16:51:38 +00:00
|
|
|
|
ty = expr_ty->UnwrapRef();
|
|
|
|
|
if (!ty->Is<sem::Bool>() && !ty->is_bool_vector()) {
|
|
|
|
|
AddError(
|
|
|
|
|
"cannot logical negate expression of type '" + TypeNameOf(expr_ty),
|
|
|
|
|
unary->expr->source);
|
|
|
|
|
return nullptr;
|
2021-06-28 14:09:33 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2021-06-17 08:35:54 +00:00
|
|
|
|
case ast::UnaryOp::kComplement:
|
2021-06-28 14:09:33 +00:00
|
|
|
|
// Result type matches the deref'd inner type.
|
2021-11-05 16:51:38 +00:00
|
|
|
|
ty = expr_ty->UnwrapRef();
|
|
|
|
|
if (!ty->is_integer_scalar_or_vector()) {
|
2021-06-28 14:09:33 +00:00
|
|
|
|
AddError("cannot bitwise complement expression of type '" +
|
2021-11-05 16:51:38 +00:00
|
|
|
|
TypeNameOf(expr_ty),
|
2021-10-15 17:33:10 +00:00
|
|
|
|
unary->expr->source);
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return nullptr;
|
2021-06-28 14:09:33 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2021-05-18 10:28:48 +00:00
|
|
|
|
case ast::UnaryOp::kNegation:
|
|
|
|
|
// Result type matches the deref'd inner type.
|
2021-11-05 16:51:38 +00:00
|
|
|
|
ty = expr_ty->UnwrapRef();
|
|
|
|
|
if (!(ty->IsAnyOf<sem::F32, sem::I32>() ||
|
|
|
|
|
ty->is_signed_integer_vector() || ty->is_float_vector())) {
|
|
|
|
|
AddError("cannot negate expression of type '" + TypeNameOf(expr_ty),
|
2021-10-15 17:33:10 +00:00
|
|
|
|
unary->expr->source);
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return nullptr;
|
2021-06-28 14:09:33 +00:00
|
|
|
|
}
|
2021-05-18 10:28:48 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ast::UnaryOp::kAddressOf:
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (auto* ref = expr_ty->As<sem::Reference>()) {
|
2021-07-28 03:57:22 +00:00
|
|
|
|
if (ref->StoreType()->UnwrapRef()->is_handle()) {
|
|
|
|
|
AddError(
|
|
|
|
|
"cannot take the address of expression in handle storage class",
|
2021-10-15 17:33:10 +00:00
|
|
|
|
unary->expr->source);
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return nullptr;
|
2021-07-28 03:57:22 +00:00
|
|
|
|
}
|
2021-11-04 19:55:57 +00:00
|
|
|
|
|
2021-11-09 09:35:00 +00:00
|
|
|
|
auto* array = unary->expr->As<ast::IndexAccessorExpression>();
|
2021-11-04 19:55:57 +00:00
|
|
|
|
auto* member = unary->expr->As<ast::MemberAccessorExpression>();
|
2021-11-10 19:23:07 +00:00
|
|
|
|
if ((array && TypeOf(array->object)->UnwrapRef()->Is<sem::Vector>()) ||
|
2021-11-04 19:55:57 +00:00
|
|
|
|
(member &&
|
|
|
|
|
TypeOf(member->structure)->UnwrapRef()->Is<sem::Vector>())) {
|
|
|
|
|
AddError("cannot take the address of a vector component",
|
|
|
|
|
unary->expr->source);
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return nullptr;
|
2021-11-04 19:55:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
ty = builder_->create<sem::Pointer>(ref->StoreType(),
|
|
|
|
|
ref->StorageClass(), ref->Access());
|
2021-05-18 10:28:48 +00:00
|
|
|
|
} else {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
AddError("cannot take the address of expression", unary->expr->source);
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return nullptr;
|
2021-05-18 10:28:48 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ast::UnaryOp::kIndirection:
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (auto* ptr = expr_ty->As<sem::Pointer>()) {
|
|
|
|
|
ty = builder_->create<sem::Reference>(
|
2021-06-04 22:17:37 +00:00
|
|
|
|
ptr->StoreType(), ptr->StorageClass(), ptr->Access());
|
2021-05-18 10:28:48 +00:00
|
|
|
|
} else {
|
2021-06-24 11:27:36 +00:00
|
|
|
|
AddError("cannot dereference expression of type '" +
|
2021-11-05 16:51:38 +00:00
|
|
|
|
TypeNameOf(expr_ty) + "'",
|
2021-10-15 17:33:10 +00:00
|
|
|
|
unary->expr->source);
|
2021-11-05 16:51:38 +00:00
|
|
|
|
return nullptr;
|
2021-05-18 10:28:48 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto val = EvaluateConstantValue(unary, ty);
|
2021-12-03 15:49:34 +00:00
|
|
|
|
auto* sem =
|
|
|
|
|
builder_->create<sem::Expression>(unary, ty, current_statement_, val);
|
|
|
|
|
sem->Behaviors() = expr->Behaviors();
|
|
|
|
|
return sem;
|
2020-04-07 19:27:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-06-09 14:32:14 +00:00
|
|
|
|
sem::Type* Resolver::TypeDecl(const ast::TypeDecl* named_type) {
|
2021-05-20 08:44:57 +00:00
|
|
|
|
sem::Type* result = nullptr;
|
|
|
|
|
if (auto* alias = named_type->As<ast::Alias>()) {
|
2021-11-26 09:56:19 +00:00
|
|
|
|
result = Alias(alias);
|
2021-05-20 08:44:57 +00:00
|
|
|
|
} else if (auto* str = named_type->As<ast::Struct>()) {
|
|
|
|
|
result = Structure(str);
|
|
|
|
|
} else {
|
2021-06-24 11:27:36 +00:00
|
|
|
|
TINT_UNREACHABLE(Resolver, diagnostics_) << "Unhandled TypeDecl";
|
2021-05-20 08:44:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!result) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
builder_->Sem().Add(named_type, result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-07 14:49:34 +00:00
|
|
|
|
sem::Type* Resolver::TypeOf(const ast::Expression* expr) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto* sem = Sem(expr);
|
|
|
|
|
return sem ? const_cast<sem::Type*>(sem->Type()) : nullptr;
|
2021-02-09 17:38:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
std::string Resolver::TypeNameOf(const sem::Type* ty) {
|
|
|
|
|
return RawTypeNameOf(ty->UnwrapRef());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string Resolver::RawTypeNameOf(const sem::Type* ty) {
|
|
|
|
|
return ty->FriendlyName(builder_->Symbols());
|
2021-04-28 13:50:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-10 19:23:07 +00:00
|
|
|
|
sem::Type* Resolver::TypeOf(const ast::LiteralExpression* lit) {
|
|
|
|
|
if (lit->Is<ast::SintLiteralExpression>()) {
|
2021-04-28 13:50:43 +00:00
|
|
|
|
return builder_->create<sem::I32>();
|
|
|
|
|
}
|
2021-11-10 19:23:07 +00:00
|
|
|
|
if (lit->Is<ast::UintLiteralExpression>()) {
|
2021-04-28 13:50:43 +00:00
|
|
|
|
return builder_->create<sem::U32>();
|
|
|
|
|
}
|
2021-11-10 19:23:07 +00:00
|
|
|
|
if (lit->Is<ast::FloatLiteralExpression>()) {
|
2021-04-28 13:50:43 +00:00
|
|
|
|
return builder_->create<sem::F32>();
|
|
|
|
|
}
|
2021-11-10 19:23:07 +00:00
|
|
|
|
if (lit->Is<ast::BoolLiteralExpression>()) {
|
2021-04-28 13:50:43 +00:00
|
|
|
|
return builder_->create<sem::Bool>();
|
|
|
|
|
}
|
2021-11-18 17:11:56 +00:00
|
|
|
|
TINT_UNREACHABLE(Resolver, diagnostics_)
|
|
|
|
|
<< "Unhandled literal type: " << lit->TypeInfo().name;
|
|
|
|
|
return nullptr;
|
2021-06-03 16:07:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-07 20:58:34 +00:00
|
|
|
|
sem::Array* Resolver::Array(const ast::Array* arr) {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
auto source = arr->source;
|
2021-03-15 10:43:11 +00:00
|
|
|
|
|
2021-10-15 17:33:10 +00:00
|
|
|
|
auto* elem_type = Type(arr->type);
|
2021-08-05 15:18:29 +00:00
|
|
|
|
if (!elem_type) {
|
2021-03-15 10:43:11 +00:00
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-05 15:18:29 +00:00
|
|
|
|
if (!IsPlain(elem_type)) { // Check must come before GetDefaultAlignAndSize()
|
2021-11-05 16:51:38 +00:00
|
|
|
|
AddError(TypeNameOf(elem_type) +
|
2021-06-24 11:27:36 +00:00
|
|
|
|
" cannot be used as an element type of an array",
|
|
|
|
|
source);
|
2021-06-03 08:24:55 +00:00
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-05 15:18:29 +00:00
|
|
|
|
uint32_t el_align = elem_type->Align();
|
|
|
|
|
uint32_t el_size = elem_type->Size();
|
2021-03-15 10:43:11 +00:00
|
|
|
|
|
2022-02-02 23:07:11 +00:00
|
|
|
|
if (!ValidateNoDuplicateAttributes(arr->attributes)) {
|
2021-06-09 18:53:57 +00:00
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-02 23:07:11 +00:00
|
|
|
|
// Look for explicit stride via @stride(n) attribute
|
2021-04-19 19:16:12 +00:00
|
|
|
|
uint32_t explicit_stride = 0;
|
2022-02-02 23:07:11 +00:00
|
|
|
|
for (auto* attr : arr->attributes) {
|
|
|
|
|
Mark(attr);
|
|
|
|
|
if (auto* sd = attr->As<ast::StrideAttribute>()) {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
explicit_stride = sd->stride;
|
2022-02-02 23:07:11 +00:00
|
|
|
|
if (!ValidateArrayStrideAttribute(sd, el_size, el_align, source)) {
|
2021-04-16 02:36:44 +00:00
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2021-05-07 20:58:34 +00:00
|
|
|
|
continue;
|
2021-03-15 10:43:11 +00:00
|
|
|
|
}
|
2021-05-07 20:58:34 +00:00
|
|
|
|
|
2022-02-02 23:07:11 +00:00
|
|
|
|
AddError("attribute is not valid for array types", attr->source);
|
2021-05-07 20:58:34 +00:00
|
|
|
|
return nullptr;
|
2021-04-19 19:16:12 +00:00
|
|
|
|
}
|
2021-03-15 10:43:11 +00:00
|
|
|
|
|
|
|
|
|
// Calculate implicit stride
|
2021-09-15 17:37:00 +00:00
|
|
|
|
uint64_t implicit_stride = utils::RoundUp<uint64_t>(el_align, el_size);
|
2021-05-07 20:58:34 +00:00
|
|
|
|
|
2021-09-15 17:37:00 +00:00
|
|
|
|
uint64_t stride = explicit_stride ? explicit_stride : implicit_stride;
|
2021-05-07 20:58:34 +00:00
|
|
|
|
|
2021-09-02 13:49:59 +00:00
|
|
|
|
// Evaluate the constant array size expression.
|
|
|
|
|
// sem::Array uses a size of 0 for a runtime-sized array.
|
|
|
|
|
uint32_t count = 0;
|
2021-10-15 17:33:10 +00:00
|
|
|
|
if (auto* count_expr = arr->count) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto* count_sem = Expression(count_expr);
|
|
|
|
|
if (!count_sem) {
|
2021-09-02 13:49:59 +00:00
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-15 17:33:10 +00:00
|
|
|
|
auto size_source = count_expr->source;
|
2021-09-02 13:49:59 +00:00
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto* ty = count_sem->Type()->UnwrapRef();
|
2021-09-02 13:49:59 +00:00
|
|
|
|
if (!ty->is_integer_scalar()) {
|
|
|
|
|
AddError("array size must be integer scalar", size_source);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (auto* ident = count_expr->As<ast::IdentifierExpression>()) {
|
|
|
|
|
// Make sure the identifier is a non-overridable module-scope constant.
|
2021-11-23 20:45:51 +00:00
|
|
|
|
auto* var = ResolvedSymbol<sem::Variable>(ident);
|
2021-11-05 16:51:38 +00:00
|
|
|
|
if (!var || !var->Is<sem::GlobalVariable>() ||
|
|
|
|
|
!var->Declaration()->is_const) {
|
2021-09-02 13:49:59 +00:00
|
|
|
|
AddError("array size identifier must be a module-scope constant",
|
|
|
|
|
size_source);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2022-02-02 23:07:11 +00:00
|
|
|
|
if (ast::HasAttribute<ast::OverrideAttribute>(
|
|
|
|
|
var->Declaration()->attributes)) {
|
2021-09-02 13:49:59 +00:00
|
|
|
|
AddError("array size expression must not be pipeline-overridable",
|
|
|
|
|
size_source);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
count_expr = var->Declaration()->constructor;
|
2021-11-10 19:23:07 +00:00
|
|
|
|
} else if (!count_expr->Is<ast::LiteralExpression>()) {
|
2021-09-02 13:49:59 +00:00
|
|
|
|
AddError(
|
|
|
|
|
"array size expression must be either a literal or a module-scope "
|
|
|
|
|
"constant",
|
|
|
|
|
size_source);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto count_val = count_sem->ConstantValue();
|
2021-09-02 13:49:59 +00:00
|
|
|
|
if (!count_val) {
|
|
|
|
|
TINT_ICE(Resolver, diagnostics_)
|
|
|
|
|
<< "could not resolve array size expression";
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ty->is_signed_integer_scalar() ? count_val.Elements()[0].i32 < 1
|
|
|
|
|
: count_val.Elements()[0].u32 < 1u) {
|
|
|
|
|
AddError("array size must be at least 1", size_source);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
count = count_val.Elements()[0].u32;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-15 17:37:00 +00:00
|
|
|
|
auto size = std::max<uint64_t>(count, 1) * stride;
|
|
|
|
|
if (size > std::numeric_limits<uint32_t>::max()) {
|
|
|
|
|
std::stringstream msg;
|
|
|
|
|
msg << "array size in bytes must not exceed 0x" << std::hex
|
|
|
|
|
<< std::numeric_limits<uint32_t>::max() << ", but is 0x" << std::hex
|
|
|
|
|
<< size;
|
2021-10-15 17:33:10 +00:00
|
|
|
|
AddError(msg.str(), arr->source);
|
2021-09-15 17:37:00 +00:00
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
if (stride > std::numeric_limits<uint32_t>::max() ||
|
|
|
|
|
implicit_stride > std::numeric_limits<uint32_t>::max()) {
|
|
|
|
|
TINT_ICE(Resolver, diagnostics_)
|
|
|
|
|
<< "calculated array stride exceeds uint32";
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
auto* out = builder_->create<sem::Array>(
|
|
|
|
|
elem_type, count, el_align, static_cast<uint32_t>(size),
|
|
|
|
|
static_cast<uint32_t>(stride), static_cast<uint32_t>(implicit_stride));
|
2021-05-07 20:58:34 +00:00
|
|
|
|
|
2021-08-05 15:18:29 +00:00
|
|
|
|
if (!ValidateArray(out, source)) {
|
2021-05-07 20:58:34 +00:00
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-05 15:18:29 +00:00
|
|
|
|
if (elem_type->Is<sem::Atomic>()) {
|
2021-10-15 17:33:10 +00:00
|
|
|
|
atomic_composite_info_.emplace(out, arr->type->source);
|
2021-08-05 15:18:29 +00:00
|
|
|
|
} else {
|
|
|
|
|
auto found = atomic_composite_info_.find(elem_type);
|
|
|
|
|
if (found != atomic_composite_info_.end()) {
|
|
|
|
|
atomic_composite_info_.emplace(out, found->second);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return out;
|
2021-03-15 10:43:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-26 09:56:19 +00:00
|
|
|
|
sem::Type* Resolver::Alias(const ast::Alias* alias) {
|
|
|
|
|
auto* ty = Type(alias->type);
|
|
|
|
|
if (!ty) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
if (!ValidateAlias(alias)) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
return ty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sem::Struct* Resolver::Structure(const ast::Struct* str) {
|
2022-02-02 23:07:11 +00:00
|
|
|
|
if (!ValidateNoDuplicateAttributes(str->attributes)) {
|
2021-11-26 09:56:19 +00:00
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2022-02-02 23:07:11 +00:00
|
|
|
|
for (auto* attr : str->attributes) {
|
|
|
|
|
Mark(attr);
|
2021-11-26 09:56:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sem::StructMemberList sem_members;
|
|
|
|
|
sem_members.reserve(str->members.size());
|
|
|
|
|
|
|
|
|
|
// Calculate the effective size and alignment of each field, and the overall
|
|
|
|
|
// size of the structure.
|
|
|
|
|
// For size, use the size attribute if provided, otherwise use the default
|
|
|
|
|
// size for the type.
|
|
|
|
|
// For alignment, use the alignment attribute if provided, otherwise use the
|
|
|
|
|
// default alignment for the member type.
|
|
|
|
|
// Diagnostic errors are raised if a basic rule is violated.
|
|
|
|
|
// Validation of storage-class rules requires analysing the actual variable
|
|
|
|
|
// usage of the structure, and so is performed as part of the variable
|
|
|
|
|
// validation.
|
|
|
|
|
uint64_t struct_size = 0;
|
|
|
|
|
uint64_t struct_align = 1;
|
|
|
|
|
std::unordered_map<Symbol, const ast::StructMember*> member_map;
|
|
|
|
|
|
|
|
|
|
for (auto* member : str->members) {
|
|
|
|
|
Mark(member);
|
|
|
|
|
auto result = member_map.emplace(member->symbol, member);
|
|
|
|
|
if (!result.second) {
|
|
|
|
|
AddError("redefinition of '" +
|
|
|
|
|
builder_->Symbols().NameFor(member->symbol) + "'",
|
|
|
|
|
member->source);
|
|
|
|
|
AddNote("previous definition is here", result.first->second->source);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Resolve member type
|
|
|
|
|
auto* type = Type(member->type);
|
|
|
|
|
if (!type) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validate member type
|
|
|
|
|
if (!IsPlain(type)) {
|
|
|
|
|
AddError(TypeNameOf(type) +
|
|
|
|
|
" cannot be used as the type of a structure member",
|
|
|
|
|
member->source);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint64_t offset = struct_size;
|
|
|
|
|
uint64_t align = type->Align();
|
|
|
|
|
uint64_t size = type->Size();
|
|
|
|
|
|
2022-02-02 23:07:11 +00:00
|
|
|
|
if (!ValidateNoDuplicateAttributes(member->attributes)) {
|
2021-11-26 09:56:19 +00:00
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-02 23:07:11 +00:00
|
|
|
|
bool has_offset_attr = false;
|
|
|
|
|
bool has_align_attr = false;
|
|
|
|
|
bool has_size_attr = false;
|
|
|
|
|
for (auto* attr : member->attributes) {
|
|
|
|
|
Mark(attr);
|
|
|
|
|
if (auto* o = attr->As<ast::StructMemberOffsetAttribute>()) {
|
|
|
|
|
// Offset attributes are not part of the WGSL spec, but are emitted
|
2021-11-26 09:56:19 +00:00
|
|
|
|
// by the SPIR-V reader.
|
|
|
|
|
if (o->offset < struct_size) {
|
|
|
|
|
AddError("offsets must be in ascending order", o->source);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
offset = o->offset;
|
|
|
|
|
align = 1;
|
2022-02-02 23:07:11 +00:00
|
|
|
|
has_offset_attr = true;
|
|
|
|
|
} else if (auto* a = attr->As<ast::StructMemberAlignAttribute>()) {
|
2021-11-26 09:56:19 +00:00
|
|
|
|
if (a->align <= 0 || !utils::IsPowerOfTwo(a->align)) {
|
|
|
|
|
AddError("align value must be a positive, power-of-two integer",
|
|
|
|
|
a->source);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
align = a->align;
|
2022-02-02 23:07:11 +00:00
|
|
|
|
has_align_attr = true;
|
|
|
|
|
} else if (auto* s = attr->As<ast::StructMemberSizeAttribute>()) {
|
2021-11-26 09:56:19 +00:00
|
|
|
|
if (s->size < size) {
|
|
|
|
|
AddError("size must be at least as big as the type's size (" +
|
|
|
|
|
std::to_string(size) + ")",
|
|
|
|
|
s->source);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
size = s->size;
|
2022-02-02 23:07:11 +00:00
|
|
|
|
has_size_attr = true;
|
2021-11-26 09:56:19 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-02 23:07:11 +00:00
|
|
|
|
if (has_offset_attr && (has_align_attr || has_size_attr)) {
|
|
|
|
|
AddError("offset attributes cannot be used with align or size attributes",
|
|
|
|
|
member->source);
|
2021-11-26 09:56:19 +00:00
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
offset = utils::RoundUp(align, offset);
|
|
|
|
|
if (offset > std::numeric_limits<uint32_t>::max()) {
|
|
|
|
|
std::stringstream msg;
|
|
|
|
|
msg << "struct member has byte offset 0x" << std::hex << offset
|
|
|
|
|
<< ", but must not exceed 0x" << std::hex
|
|
|
|
|
<< std::numeric_limits<uint32_t>::max();
|
|
|
|
|
AddError(msg.str(), member->source);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto* sem_member = builder_->create<sem::StructMember>(
|
|
|
|
|
member, member->symbol, type, static_cast<uint32_t>(sem_members.size()),
|
|
|
|
|
static_cast<uint32_t>(offset), static_cast<uint32_t>(align),
|
|
|
|
|
static_cast<uint32_t>(size));
|
|
|
|
|
builder_->Sem().Add(member, sem_member);
|
|
|
|
|
sem_members.emplace_back(sem_member);
|
|
|
|
|
|
|
|
|
|
struct_size = offset + size;
|
|
|
|
|
struct_align = std::max(struct_align, align);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint64_t size_no_padding = struct_size;
|
|
|
|
|
struct_size = utils::RoundUp(struct_align, struct_size);
|
|
|
|
|
|
|
|
|
|
if (struct_size > std::numeric_limits<uint32_t>::max()) {
|
|
|
|
|
std::stringstream msg;
|
|
|
|
|
msg << "struct size in bytes must not exceed 0x" << std::hex
|
|
|
|
|
<< std::numeric_limits<uint32_t>::max() << ", but is 0x" << std::hex
|
|
|
|
|
<< struct_size;
|
|
|
|
|
AddError(msg.str(), str->source);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
if (struct_align > std::numeric_limits<uint32_t>::max()) {
|
|
|
|
|
TINT_ICE(Resolver, diagnostics_)
|
|
|
|
|
<< "calculated struct stride exceeds uint32";
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto* out = builder_->create<sem::Struct>(
|
|
|
|
|
str, str->name, sem_members, static_cast<uint32_t>(struct_align),
|
|
|
|
|
static_cast<uint32_t>(struct_size),
|
|
|
|
|
static_cast<uint32_t>(size_no_padding));
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < sem_members.size(); i++) {
|
|
|
|
|
auto* mem_type = sem_members[i]->Type();
|
|
|
|
|
if (mem_type->Is<sem::Atomic>()) {
|
|
|
|
|
atomic_composite_info_.emplace(out,
|
|
|
|
|
sem_members[i]->Declaration()->source);
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
|
|
|
|
auto found = atomic_composite_info_.find(mem_type);
|
|
|
|
|
if (found != atomic_composite_info_.end()) {
|
|
|
|
|
atomic_composite_info_.emplace(out, found->second);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ValidateStructure(out)) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-26 16:26:42 +00:00
|
|
|
|
sem::Statement* Resolver::ReturnStatement(const ast::ReturnStatement* stmt) {
|
|
|
|
|
auto* sem = builder_->create<sem::Statement>(
|
|
|
|
|
stmt, current_compound_statement_, current_function_);
|
|
|
|
|
return StatementScope(stmt, sem, [&] {
|
2021-12-03 15:49:34 +00:00
|
|
|
|
auto& behaviors = current_statement_->Behaviors();
|
|
|
|
|
behaviors = sem::Behavior::kReturn;
|
|
|
|
|
|
2021-11-26 16:26:42 +00:00
|
|
|
|
if (auto* value = stmt->value) {
|
2021-12-03 15:49:34 +00:00
|
|
|
|
auto* expr = Expression(value);
|
|
|
|
|
if (!expr) {
|
2021-11-26 16:26:42 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-12-03 15:49:34 +00:00
|
|
|
|
behaviors.Add(expr->Behaviors() - sem::Behavior::kNext);
|
2021-06-15 15:00:57 +00:00
|
|
|
|
}
|
2021-03-22 23:20:17 +00:00
|
|
|
|
|
2021-12-09 14:37:37 +00:00
|
|
|
|
// Validate after processing the return value expression so that its type
|
|
|
|
|
// is available for validation.
|
2021-11-26 16:26:42 +00:00
|
|
|
|
return ValidateReturn(stmt);
|
|
|
|
|
});
|
2021-03-22 23:20:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-26 16:26:42 +00:00
|
|
|
|
sem::SwitchStatement* Resolver::SwitchStatement(
|
|
|
|
|
const ast::SwitchStatement* stmt) {
|
2021-11-05 16:51:38 +00:00
|
|
|
|
auto* sem = builder_->create<sem::SwitchStatement>(
|
|
|
|
|
stmt, current_compound_statement_, current_function_);
|
2021-11-26 16:26:42 +00:00
|
|
|
|
return StatementScope(stmt, sem, [&] {
|
2021-12-03 15:49:34 +00:00
|
|
|
|
auto& behaviors = sem->Behaviors();
|
|
|
|
|
|
|
|
|
|
auto* cond = Expression(stmt->condition);
|
|
|
|
|
if (!cond) {
|
2021-03-25 12:55:27 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-12-03 15:49:34 +00:00
|
|
|
|
behaviors = cond->Behaviors() - sem::Behavior::kNext;
|
2021-11-26 16:26:42 +00:00
|
|
|
|
|
2021-10-15 17:33:10 +00:00
|
|
|
|
for (auto* case_stmt : stmt->body) {
|
2021-07-14 09:44:41 +00:00
|
|
|
|
Mark(case_stmt);
|
2021-12-03 15:49:34 +00:00
|
|
|
|
auto* c = CaseStatement(case_stmt);
|
|
|
|
|
if (!c) {
|
2021-07-14 09:44:41 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-12-03 15:49:34 +00:00
|
|
|
|
behaviors.Add(c->Behaviors());
|
2021-07-14 09:44:41 +00:00
|
|
|
|
}
|
2021-11-26 16:26:42 +00:00
|
|
|
|
|
2021-12-03 15:49:34 +00:00
|
|
|
|
if (behaviors.Contains(sem::Behavior::kBreak)) {
|
|
|
|
|
behaviors.Add(sem::Behavior::kNext);
|
|
|
|
|
}
|
|
|
|
|
behaviors.Remove(sem::Behavior::kBreak, sem::Behavior::kFallthrough);
|
|
|
|
|
|
2021-11-26 16:26:42 +00:00
|
|
|
|
return ValidateSwitch(stmt);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sem::Statement* Resolver::VariableDeclStatement(
|
|
|
|
|
const ast::VariableDeclStatement* stmt) {
|
|
|
|
|
auto* sem = builder_->create<sem::Statement>(
|
|
|
|
|
stmt, current_compound_statement_, current_function_);
|
|
|
|
|
return StatementScope(stmt, sem, [&] {
|
|
|
|
|
Mark(stmt->variable);
|
|
|
|
|
|
|
|
|
|
auto* var = Variable(stmt->variable, VariableKind::kLocal);
|
|
|
|
|
if (!var) {
|
2021-07-14 09:44:41 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-11-26 16:26:42 +00:00
|
|
|
|
|
2022-02-02 23:07:11 +00:00
|
|
|
|
for (auto* attr : stmt->variable->attributes) {
|
|
|
|
|
Mark(attr);
|
|
|
|
|
if (!attr->Is<ast::InternalAttribute>()) {
|
|
|
|
|
AddError("attributes are not valid on local variables", attr->source);
|
2021-11-26 16:26:42 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (current_block_) { // Not all statements are inside a block
|
|
|
|
|
current_block_->AddDecl(stmt->variable);
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-03 15:49:34 +00:00
|
|
|
|
if (auto* ctor = var->Constructor()) {
|
|
|
|
|
sem->Behaviors() = ctor->Behaviors();
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-26 16:26:42 +00:00
|
|
|
|
return ValidateVariable(var);
|
2021-07-14 09:44:41 +00:00
|
|
|
|
});
|
2021-03-25 12:55:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-26 16:26:42 +00:00
|
|
|
|
sem::Statement* Resolver::AssignmentStatement(
|
|
|
|
|
const ast::AssignmentStatement* stmt) {
|
|
|
|
|
auto* sem = builder_->create<sem::Statement>(
|
|
|
|
|
stmt, current_compound_statement_, current_function_);
|
|
|
|
|
return StatementScope(stmt, sem, [&] {
|
2021-12-03 15:49:34 +00:00
|
|
|
|
auto* lhs = Expression(stmt->lhs);
|
|
|
|
|
if (!lhs) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto* rhs = Expression(stmt->rhs);
|
|
|
|
|
if (!rhs) {
|
2021-11-26 16:26:42 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-03 15:49:34 +00:00
|
|
|
|
auto& behaviors = sem->Behaviors();
|
|
|
|
|
behaviors = rhs->Behaviors();
|
|
|
|
|
if (!stmt->lhs->Is<ast::PhonyExpression>()) {
|
|
|
|
|
behaviors.Add(lhs->Behaviors());
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-26 16:26:42 +00:00
|
|
|
|
return ValidateAssignment(stmt);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sem::Statement* Resolver::BreakStatement(const ast::BreakStatement* stmt) {
|
|
|
|
|
auto* sem = builder_->create<sem::Statement>(
|
|
|
|
|
stmt, current_compound_statement_, current_function_);
|
2021-12-03 15:49:34 +00:00
|
|
|
|
return StatementScope(stmt, sem, [&] {
|
|
|
|
|
sem->Behaviors() = sem::Behavior::kBreak;
|
|
|
|
|
|
|
|
|
|
return ValidateBreakStatement(sem);
|
|
|
|
|
});
|
2021-11-26 16:26:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sem::Statement* Resolver::CallStatement(const ast::CallStatement* stmt) {
|
|
|
|
|
auto* sem = builder_->create<sem::Statement>(
|
|
|
|
|
stmt, current_compound_statement_, current_function_);
|
2021-12-03 15:49:34 +00:00
|
|
|
|
return StatementScope(stmt, sem, [&] {
|
|
|
|
|
if (auto* expr = Expression(stmt->expr)) {
|
|
|
|
|
sem->Behaviors() = expr->Behaviors();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
});
|
2021-11-26 16:26:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sem::Statement* Resolver::ContinueStatement(
|
|
|
|
|
const ast::ContinueStatement* stmt) {
|
|
|
|
|
auto* sem = builder_->create<sem::Statement>(
|
|
|
|
|
stmt, current_compound_statement_, current_function_);
|
|
|
|
|
return StatementScope(stmt, sem, [&] {
|
2021-12-03 15:49:34 +00:00
|
|
|
|
sem->Behaviors() = sem::Behavior::kContinue;
|
|
|
|
|
|
2021-11-26 16:26:42 +00:00
|
|
|
|
// Set if we've hit the first continue statement in our parent loop
|
|
|
|
|
if (auto* block = sem->FindFirstParent<sem::LoopBlockStatement>()) {
|
|
|
|
|
if (!block->FirstContinue()) {
|
|
|
|
|
const_cast<sem::LoopBlockStatement*>(block)->SetFirstContinue(
|
|
|
|
|
stmt, block->Decls().size());
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-10-21 23:04:44 +00:00
|
|
|
|
|
2021-11-26 16:26:42 +00:00
|
|
|
|
return ValidateContinueStatement(sem);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sem::Statement* Resolver::DiscardStatement(const ast::DiscardStatement* stmt) {
|
|
|
|
|
auto* sem = builder_->create<sem::Statement>(
|
|
|
|
|
stmt, current_compound_statement_, current_function_);
|
|
|
|
|
return StatementScope(stmt, sem, [&] {
|
2021-12-03 15:49:34 +00:00
|
|
|
|
sem->Behaviors() = sem::Behavior::kDiscard;
|
2021-11-26 16:26:42 +00:00
|
|
|
|
current_function_->SetHasDiscard();
|
|
|
|
|
|
|
|
|
|
return ValidateDiscardStatement(sem);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sem::Statement* Resolver::FallthroughStatement(
|
|
|
|
|
const ast::FallthroughStatement* stmt) {
|
|
|
|
|
auto* sem = builder_->create<sem::Statement>(
|
|
|
|
|
stmt, current_compound_statement_, current_function_);
|
2021-12-03 15:23:52 +00:00
|
|
|
|
return StatementScope(stmt, sem, [&] {
|
2021-12-03 15:49:34 +00:00
|
|
|
|
sem->Behaviors() = sem::Behavior::kFallthrough;
|
|
|
|
|
|
2021-12-03 15:23:52 +00:00
|
|
|
|
return ValidateFallthroughStatement(sem);
|
|
|
|
|
});
|
2021-05-18 10:28:48 +00:00
|
|
|
|
}
|
2021-03-31 13:26:43 +00:00
|
|
|
|
|
2021-03-17 22:47:33 +00:00
|
|
|
|
bool Resolver::ApplyStorageClassUsageToType(ast::StorageClass sc,
|
2021-05-07 14:49:34 +00:00
|
|
|
|
sem::Type* ty,
|
2021-04-16 01:15:43 +00:00
|
|
|
|
const Source& usage) {
|
2021-05-18 10:28:48 +00:00
|
|
|
|
ty = const_cast<sem::Type*>(ty->UnwrapRef());
|
2021-03-17 22:47:33 +00:00
|
|
|
|
|
2021-05-07 14:49:34 +00:00
|
|
|
|
if (auto* str = ty->As<sem::Struct>()) {
|
|
|
|
|
if (str->StorageClassUsage().count(sc)) {
|
2021-03-17 22:47:33 +00:00
|
|
|
|
return true; // Already applied
|
|
|
|
|
}
|
2021-05-07 14:49:34 +00:00
|
|
|
|
|
|
|
|
|
str->AddUsage(sc);
|
|
|
|
|
|
|
|
|
|
for (auto* member : str->Members()) {
|
2021-05-05 09:09:41 +00:00
|
|
|
|
if (!ApplyStorageClassUsageToType(sc, member->Type(), usage)) {
|
2021-03-17 22:47:33 +00:00
|
|
|
|
std::stringstream err;
|
2021-11-05 16:51:38 +00:00
|
|
|
|
err << "while analysing structure member " << TypeNameOf(str) << "."
|
2021-10-15 17:33:10 +00:00
|
|
|
|
<< builder_->Symbols().NameFor(member->Declaration()->symbol);
|
|
|
|
|
AddNote(err.str(), member->Declaration()->source);
|
2021-03-17 22:47:33 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-18 20:46:24 +00:00
|
|
|
|
return true;
|
2021-03-17 22:47:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-07 20:58:34 +00:00
|
|
|
|
if (auto* arr = ty->As<sem::Array>()) {
|
2022-01-12 20:51:36 +00:00
|
|
|
|
if (arr->IsRuntimeSized() && sc != ast::StorageClass::kStorage) {
|
|
|
|
|
AddError(
|
|
|
|
|
"runtime-sized arrays can only be used in the <storage> storage "
|
|
|
|
|
"class",
|
|
|
|
|
usage);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-07 20:58:34 +00:00
|
|
|
|
return ApplyStorageClassUsageToType(
|
|
|
|
|
sc, const_cast<sem::Type*>(arr->ElemType()), usage);
|
2021-03-18 20:46:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-18 21:03:24 +00:00
|
|
|
|
if (ast::IsHostShareable(sc) && !IsHostShareable(ty)) {
|
2021-03-18 20:46:24 +00:00
|
|
|
|
std::stringstream err;
|
2021-11-05 16:51:38 +00:00
|
|
|
|
err << "Type '" << TypeNameOf(ty) << "' cannot be used in storage class '"
|
|
|
|
|
<< sc << "' as it is non-host-shareable";
|
2021-06-24 11:27:36 +00:00
|
|
|
|
AddError(err.str(), usage);
|
2021-03-18 20:46:24 +00:00
|
|
|
|
return false;
|
2021-03-17 22:47:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-26 16:26:42 +00:00
|
|
|
|
template <typename SEM, typename F>
|
|
|
|
|
SEM* Resolver::StatementScope(const ast::Statement* ast,
|
|
|
|
|
SEM* sem,
|
|
|
|
|
F&& callback) {
|
|
|
|
|
builder_->Sem().Add(ast, sem);
|
|
|
|
|
|
|
|
|
|
auto* as_compound =
|
|
|
|
|
As<sem::CompoundStatement, CastFlags::kDontErrorOnImpossibleCast>(sem);
|
|
|
|
|
auto* as_block =
|
|
|
|
|
As<sem::BlockStatement, CastFlags::kDontErrorOnImpossibleCast>(sem);
|
|
|
|
|
|
|
|
|
|
TINT_SCOPED_ASSIGNMENT(current_statement_, sem);
|
|
|
|
|
TINT_SCOPED_ASSIGNMENT(
|
|
|
|
|
current_compound_statement_,
|
|
|
|
|
as_compound ? as_compound : current_compound_statement_);
|
|
|
|
|
TINT_SCOPED_ASSIGNMENT(current_block_, as_block ? as_block : current_block_);
|
|
|
|
|
|
|
|
|
|
if (!callback()) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2021-07-14 09:44:41 +00:00
|
|
|
|
|
2021-11-26 16:26:42 +00:00
|
|
|
|
return sem;
|
2021-03-09 15:06:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-07 20:58:34 +00:00
|
|
|
|
std::string Resolver::VectorPretty(uint32_t size,
|
|
|
|
|
const sem::Type* element_type) {
|
2021-04-19 22:51:23 +00:00
|
|
|
|
sem::Vector vec_type(element_type, size);
|
2021-03-18 15:43:14 +00:00
|
|
|
|
return vec_type.FriendlyName(builder_->Symbols());
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-22 11:44:57 +00:00
|
|
|
|
bool Resolver::Mark(const ast::Node* node) {
|
2021-04-19 19:16:12 +00:00
|
|
|
|
if (node == nullptr) {
|
2021-06-24 11:27:36 +00:00
|
|
|
|
TINT_ICE(Resolver, diagnostics_) << "Resolver::Mark() called with nullptr";
|
2021-11-22 11:44:57 +00:00
|
|
|
|
return false;
|
2021-04-19 19:16:12 +00:00
|
|
|
|
}
|
|
|
|
|
if (marked_.emplace(node).second) {
|
2021-11-22 11:44:57 +00:00
|
|
|
|
return true;
|
2021-04-19 19:16:12 +00:00
|
|
|
|
}
|
2021-06-24 11:27:36 +00:00
|
|
|
|
TINT_ICE(Resolver, diagnostics_)
|
2021-04-19 19:16:12 +00:00
|
|
|
|
<< "AST node '" << node->TypeInfo().name
|
|
|
|
|
<< "' was encountered twice in the same AST of a Program\n"
|
2021-10-15 17:33:10 +00:00
|
|
|
|
<< "At: " << node->source << "\n"
|
2021-04-29 20:57:55 +00:00
|
|
|
|
<< "Pointer: " << node;
|
2021-11-22 11:44:57 +00:00
|
|
|
|
return false;
|
2021-04-19 19:16:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-06-24 11:27:36 +00:00
|
|
|
|
void Resolver::AddError(const std::string& msg, const Source& source) const {
|
|
|
|
|
diagnostics_.add_error(diag::System::Resolver, msg, source);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Resolver::AddWarning(const std::string& msg, const Source& source) const {
|
|
|
|
|
diagnostics_.add_warning(diag::System::Resolver, msg, source);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Resolver::AddNote(const std::string& msg, const Source& source) const {
|
|
|
|
|
diagnostics_.add_note(diag::System::Resolver, msg, source);
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-18 17:11:56 +00:00
|
|
|
|
// https://gpuweb.github.io/gpuweb/wgsl/#plain-types-section
|
|
|
|
|
bool Resolver::IsPlain(const sem::Type* type) const {
|
|
|
|
|
return type->is_scalar() ||
|
|
|
|
|
type->IsAnyOf<sem::Atomic, sem::Vector, sem::Matrix, sem::Array,
|
|
|
|
|
sem::Struct>();
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-09 15:45:03 +00:00
|
|
|
|
// https://gpuweb.github.io/gpuweb/wgsl/#fixed-footprint-types
|
|
|
|
|
bool Resolver::IsFixedFootprint(const sem::Type* type) const {
|
|
|
|
|
if (type->is_scalar()) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (type->Is<sem::Vector>()) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (type->Is<sem::Matrix>()) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (type->Is<sem::Atomic>()) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (auto* arr = type->As<sem::Array>()) {
|
|
|
|
|
return !arr->IsRuntimeSized() && IsFixedFootprint(arr->ElemType());
|
|
|
|
|
}
|
|
|
|
|
if (auto* str = type->As<sem::Struct>()) {
|
|
|
|
|
for (auto* member : str->Members()) {
|
|
|
|
|
if (!IsFixedFootprint(member->Type())) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-18 17:11:56 +00:00
|
|
|
|
// https://gpuweb.github.io/gpuweb/wgsl.html#storable-types
|
|
|
|
|
bool Resolver::IsStorable(const sem::Type* type) const {
|
|
|
|
|
return IsPlain(type) || type->IsAnyOf<sem::Texture, sem::Sampler>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// https://gpuweb.github.io/gpuweb/wgsl.html#host-shareable-types
|
|
|
|
|
bool Resolver::IsHostShareable(const sem::Type* type) const {
|
|
|
|
|
if (type->IsAnyOf<sem::I32, sem::U32, sem::F32>()) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (auto* vec = type->As<sem::Vector>()) {
|
|
|
|
|
return IsHostShareable(vec->type());
|
|
|
|
|
}
|
|
|
|
|
if (auto* mat = type->As<sem::Matrix>()) {
|
|
|
|
|
return IsHostShareable(mat->type());
|
|
|
|
|
}
|
|
|
|
|
if (auto* arr = type->As<sem::Array>()) {
|
|
|
|
|
return IsHostShareable(arr->ElemType());
|
|
|
|
|
}
|
|
|
|
|
if (auto* str = type->As<sem::Struct>()) {
|
|
|
|
|
for (auto* member : str->Members()) {
|
|
|
|
|
if (!IsHostShareable(member->Type())) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
2021-11-05 16:51:38 +00:00
|
|
|
|
}
|
2021-11-18 17:11:56 +00:00
|
|
|
|
if (auto* atomic = type->As<sem::Atomic>()) {
|
|
|
|
|
return IsHostShareable(atomic->Type());
|
|
|
|
|
}
|
|
|
|
|
return false;
|
2021-11-05 16:51:38 +00:00
|
|
|
|
}
|
2021-02-03 16:43:20 +00:00
|
|
|
|
|
2022-02-02 23:07:11 +00:00
|
|
|
|
bool Resolver::IsBuiltin(Symbol symbol) const {
|
2021-11-23 20:45:51 +00:00
|
|
|
|
std::string name = builder_->Symbols().NameFor(symbol);
|
2022-02-02 23:07:11 +00:00
|
|
|
|
return sem::ParseBuiltinType(name) != sem::BuiltinType::kNone;
|
2021-11-23 20:45:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-12-03 15:49:34 +00:00
|
|
|
|
bool Resolver::IsCallStatement(const ast::Expression* expr) const {
|
|
|
|
|
return current_statement_ &&
|
|
|
|
|
Is<ast::CallStatement>(current_statement_->Declaration(),
|
|
|
|
|
[&](auto* stmt) { return stmt->expr == expr; });
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-03 17:51:48 +00:00
|
|
|
|
const ast::Statement* Resolver::ClosestContinuing(bool stop_at_loop) const {
|
|
|
|
|
for (const auto* s = current_statement_; s != nullptr; s = s->Parent()) {
|
|
|
|
|
if (stop_at_loop && s->Is<sem::LoopStatement>()) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (s->Is<sem::LoopContinuingBlockStatement>()) {
|
|
|
|
|
return s->Declaration();
|
|
|
|
|
}
|
|
|
|
|
if (auto* f = As<sem::ForLoopStatement>(s->Parent())) {
|
|
|
|
|
if (f->Declaration()->continuing == s->Declaration()) {
|
|
|
|
|
return s->Declaration();
|
|
|
|
|
}
|
|
|
|
|
if (stop_at_loop) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-15 20:45:50 +00:00
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Resolver::TypeConversionSig
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
bool Resolver::TypeConversionSig::operator==(
|
|
|
|
|
const TypeConversionSig& rhs) const {
|
|
|
|
|
return target == rhs.target && source == rhs.source;
|
|
|
|
|
}
|
|
|
|
|
std::size_t Resolver::TypeConversionSig::Hasher::operator()(
|
|
|
|
|
const TypeConversionSig& sig) const {
|
|
|
|
|
return utils::Hash(sig.target, sig.source);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Resolver::TypeConstructorSig
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
Resolver::TypeConstructorSig::TypeConstructorSig(
|
|
|
|
|
const sem::Type* ty,
|
|
|
|
|
const std::vector<const sem::Type*> params)
|
|
|
|
|
: type(ty), parameters(params) {}
|
|
|
|
|
Resolver::TypeConstructorSig::TypeConstructorSig(const TypeConstructorSig&) =
|
|
|
|
|
default;
|
|
|
|
|
Resolver::TypeConstructorSig::~TypeConstructorSig() = default;
|
|
|
|
|
|
|
|
|
|
bool Resolver::TypeConstructorSig::operator==(
|
|
|
|
|
const TypeConstructorSig& rhs) const {
|
|
|
|
|
return type == rhs.type && parameters == rhs.parameters;
|
|
|
|
|
}
|
|
|
|
|
std::size_t Resolver::TypeConstructorSig::Hasher::operator()(
|
|
|
|
|
const TypeConstructorSig& sig) const {
|
|
|
|
|
return utils::Hash(sig.type, sig.parameters);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-09 15:08:48 +00:00
|
|
|
|
} // namespace resolver
|
2020-03-02 20:47:43 +00:00
|
|
|
|
} // namespace tint
|