mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-05-15 11:51:22 +00:00
Add TINT_UNREACHABLE() and TINT_ICE() helper macros
Appends an error message with the tint compiler source location to the provided diagnositic list, and then calls the global error handler if one is set. Tests and the sample app now register an error handler to print the diagnostic list to stderr and abort when NDEBUG is not defined. All uses of assert(false) have been fixed up to use these macros. Change-Id: I2f63e51ed86ac23883301d280070bd1a357c6cb2 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/41620 Commit-Queue: dan sinclair <dsinclair@chromium.org> Reviewed-by: dan sinclair <dsinclair@chromium.org>
This commit is contained in:
parent
c7ca7668cc
commit
6b4924fb0e
5
BUILD.gn
5
BUILD.gn
@ -353,6 +353,8 @@ source_set("libtint_core_src") {
|
|||||||
"src/castable.h",
|
"src/castable.h",
|
||||||
"src/clone_context.cc",
|
"src/clone_context.cc",
|
||||||
"src/clone_context.h",
|
"src/clone_context.h",
|
||||||
|
"src/debug.cc",
|
||||||
|
"src/debug.h",
|
||||||
"src/demangler.cc",
|
"src/demangler.cc",
|
||||||
"src/demangler.h",
|
"src/demangler.h",
|
||||||
"src/diagnostic/diagnostic.cc",
|
"src/diagnostic/diagnostic.cc",
|
||||||
@ -728,7 +730,6 @@ if (!build_with_chromium) {
|
|||||||
testonly = true
|
testonly = true
|
||||||
sources = [
|
sources = [
|
||||||
"${tint_googletest_dir}/googlemock/src/gmock-all.cc",
|
"${tint_googletest_dir}/googlemock/src/gmock-all.cc",
|
||||||
"${tint_googletest_dir}/googlemock/src/gmock_main.cc",
|
|
||||||
]
|
]
|
||||||
public_configs = [ ":gmock_config" ]
|
public_configs = [ ":gmock_config" ]
|
||||||
}
|
}
|
||||||
@ -833,6 +834,7 @@ source_set("tint_unittests_core_src") {
|
|||||||
"src/block_allocator_test.cc",
|
"src/block_allocator_test.cc",
|
||||||
"src/castable_test.cc",
|
"src/castable_test.cc",
|
||||||
"src/clone_context_test.cc",
|
"src/clone_context_test.cc",
|
||||||
|
"src/debug_test.cc",
|
||||||
"src/demangler_test.cc",
|
"src/demangler_test.cc",
|
||||||
"src/diagnostic/formatter_test.cc",
|
"src/diagnostic/formatter_test.cc",
|
||||||
"src/diagnostic/printer_test.cc",
|
"src/diagnostic/printer_test.cc",
|
||||||
@ -844,6 +846,7 @@ source_set("tint_unittests_core_src") {
|
|||||||
"src/scope_stack_test.cc",
|
"src/scope_stack_test.cc",
|
||||||
"src/symbol_table_test.cc",
|
"src/symbol_table_test.cc",
|
||||||
"src/symbol_test.cc",
|
"src/symbol_test.cc",
|
||||||
|
"src/test_main.cc",
|
||||||
"src/traits_test.cc",
|
"src/traits_test.cc",
|
||||||
"src/transform/bound_array_accessors_test.cc",
|
"src/transform/bound_array_accessors_test.cc",
|
||||||
"src/transform/emit_vertex_point_size_test.cc",
|
"src/transform/emit_vertex_point_size_test.cc",
|
||||||
|
@ -28,6 +28,13 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
[[noreturn]]
|
||||||
|
void TintInternalCompilerErrorReporter(const tint::diag::List& diagnostics) {
|
||||||
|
auto printer = tint::diag::Printer::create(stderr, true);
|
||||||
|
tint::diag::Formatter{}.format(diagnostics, printer.get());
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
enum class Format {
|
enum class Format {
|
||||||
kNone = -1,
|
kNone = -1,
|
||||||
kSpirv,
|
kSpirv,
|
||||||
@ -419,6 +426,8 @@ int main(int argc, const char** argv) {
|
|||||||
std::vector<std::string> args(argv, argv + argc);
|
std::vector<std::string> args(argv, argv + argc);
|
||||||
Options options;
|
Options options;
|
||||||
|
|
||||||
|
tint::SetInternalCompilerErrorReporter(&TintInternalCompilerErrorReporter);
|
||||||
|
|
||||||
if (!ParseArgs(args, &options)) {
|
if (!ParseArgs(args, &options)) {
|
||||||
std::cerr << "Failed to parse arguments." << std::endl;
|
std::cerr << "Failed to parse arguments." << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -167,8 +167,10 @@ set(TINT_LIB_SRCS
|
|||||||
castable.h
|
castable.h
|
||||||
clone_context.cc
|
clone_context.cc
|
||||||
clone_context.h
|
clone_context.h
|
||||||
|
debug.cc
|
||||||
|
debug.h
|
||||||
demangler.cc
|
demangler.cc
|
||||||
demangler.h;
|
demangler.h
|
||||||
intrinsic_table.cc
|
intrinsic_table.cc
|
||||||
intrinsic_table.h
|
intrinsic_table.h
|
||||||
diagnostic/diagnostic.cc
|
diagnostic/diagnostic.cc
|
||||||
@ -463,6 +465,7 @@ if(${TINT_BUILD_TESTS})
|
|||||||
block_allocator_test.cc
|
block_allocator_test.cc
|
||||||
castable_test.cc
|
castable_test.cc
|
||||||
clone_context_test.cc
|
clone_context_test.cc
|
||||||
|
debug_test.cc
|
||||||
demangler_test.cc
|
demangler_test.cc
|
||||||
diagnostic/formatter_test.cc
|
diagnostic/formatter_test.cc
|
||||||
diagnostic/printer_test.cc
|
diagnostic/printer_test.cc
|
||||||
@ -474,6 +477,7 @@ if(${TINT_BUILD_TESTS})
|
|||||||
symbol_table_test.cc
|
symbol_table_test.cc
|
||||||
symbol_test.cc
|
symbol_test.cc
|
||||||
traits_test.cc
|
traits_test.cc
|
||||||
|
test_main.cc
|
||||||
type_determiner_test.cc
|
type_determiner_test.cc
|
||||||
type/access_control_type_test.cc
|
type/access_control_type_test.cc
|
||||||
type/alias_type_test.cc
|
type/alias_type_test.cc
|
||||||
@ -782,7 +786,7 @@ if(${TINT_BUILD_TESTS})
|
|||||||
## Test executable
|
## Test executable
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
tint_unittests PRIVATE ${gmock_SOURCE_DIR}/include)
|
tint_unittests PRIVATE ${gmock_SOURCE_DIR}/include)
|
||||||
target_link_libraries(tint_unittests libtint gmock_main)
|
target_link_libraries(tint_unittests libtint gmock)
|
||||||
tint_default_compile_options(tint_unittests)
|
tint_default_compile_options(tint_unittests)
|
||||||
|
|
||||||
if(${TINT_BUILD_SPV_READER} OR ${TINT_BUILD_SPV_WRITER})
|
if(${TINT_BUILD_SPV_READER} OR ${TINT_BUILD_SPV_WRITER})
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "src/ast/intrinsic_texture_helper_test.h"
|
#include "src/ast/intrinsic_texture_helper_test.h"
|
||||||
|
|
||||||
#include "src/ast/type_constructor_expression.h"
|
#include "src/ast/type_constructor_expression.h"
|
||||||
|
#include "src/debug.h"
|
||||||
#include "src/program_builder.h"
|
#include "src/program_builder.h"
|
||||||
#include "src/type/access_control_type.h"
|
#include "src/type/access_control_type.h"
|
||||||
#include "src/type/depth_texture_type.h"
|
#include "src/type/depth_texture_type.h"
|
||||||
@ -146,7 +147,7 @@ type::Type* TextureOverloadCase::resultVectorComponentType(
|
|||||||
return b->ty.i32();
|
return b->ty.i32();
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(false /* unreachable */);
|
TINT_UNREACHABLE(b->Diagnostics());
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,7 +187,7 @@ ast::Variable* TextureOverloadCase::buildTextureVariable(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(false /* unreachable */);
|
TINT_UNREACHABLE(b->Diagnostics());
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include "src/debug.h"
|
||||||
#include "src/program_builder.h"
|
#include "src/program_builder.h"
|
||||||
#include "src/type/alias_type.h"
|
#include "src/type/alias_type.h"
|
||||||
#include "src/type/struct_type.h"
|
#include "src/type/struct_type.h"
|
||||||
@ -43,7 +44,8 @@ Module::Module(const Source& source, std::vector<CastableBase*> global_decls)
|
|||||||
} else if (auto* var = decl->As<Variable>()) {
|
} else if (auto* var = decl->As<Variable>()) {
|
||||||
global_variables_.push_back(var);
|
global_variables_.push_back(var);
|
||||||
} else {
|
} else {
|
||||||
assert(false /* unreachable */);
|
diag::List diagnostics;
|
||||||
|
TINT_ICE(diagnostics, "Unknown global declaration type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -106,7 +108,7 @@ void Module::Copy(CloneContext* ctx, const Module* src) {
|
|||||||
} else if (auto* var = decl->As<Variable>()) {
|
} else if (auto* var = decl->As<Variable>()) {
|
||||||
AddGlobalVariable(ctx->Clone(var));
|
AddGlobalVariable(ctx->Clone(var));
|
||||||
} else {
|
} else {
|
||||||
assert(false /* unreachable */);
|
TINT_ICE(ctx->dst->Diagnostics(), "Unknown global declaration type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,4 +44,8 @@ ast::FunctionList CloneContext::Clone(const ast::FunctionList& v) {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
diag::List& CloneContext::Diagnostics() const {
|
||||||
|
return dst->Diagnostics();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace tint
|
} // namespace tint
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "src/castable.h"
|
#include "src/castable.h"
|
||||||
|
#include "src/debug.h"
|
||||||
#include "src/source.h"
|
#include "src/source.h"
|
||||||
#include "src/symbol.h"
|
#include "src/symbol.h"
|
||||||
#include "src/traits.h"
|
#include "src/traits.h"
|
||||||
@ -301,14 +302,19 @@ class CloneContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Cast `obj` from type `FROM` to type `TO`, returning the cast object.
|
/// Cast `obj` from type `FROM` to type `TO`, returning the cast object.
|
||||||
/// Asserts if the cast failed.
|
/// Reports an internal compiler error if the cast failed.
|
||||||
template <typename TO, typename FROM>
|
template <typename TO, typename FROM>
|
||||||
TO* CheckedCast(FROM* obj) {
|
TO* CheckedCast(FROM* obj) {
|
||||||
TO* cast = obj->template As<TO>();
|
TO* cast = obj->template As<TO>();
|
||||||
assert(cast /* cloned object was not of the expected type */);
|
if (!cast) {
|
||||||
|
TINT_ICE(Diagnostics(), "Cloned object was not of the expected type");
|
||||||
|
}
|
||||||
return cast;
|
return cast;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @returns the diagnostic list of #dst
|
||||||
|
diag::List& Diagnostics() const;
|
||||||
|
|
||||||
/// A vector of Cloneable*
|
/// A vector of Cloneable*
|
||||||
using CloneableList = std::vector<Cloneable*>;
|
using CloneableList = std::vector<Cloneable*>;
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest-spi.h"
|
||||||
|
|
||||||
#include "src/program_builder.h"
|
#include "src/program_builder.h"
|
||||||
|
|
||||||
@ -257,6 +257,8 @@ TEST(CloneContext, CloneWithInsertBefore) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(CloneContext, CloneWithReplace_WithNotANode) {
|
TEST(CloneContext, CloneWithReplace_WithNotANode) {
|
||||||
|
EXPECT_FATAL_FAILURE(
|
||||||
|
{
|
||||||
ProgramBuilder builder;
|
ProgramBuilder builder;
|
||||||
auto* original_root = builder.create<Node>("root");
|
auto* original_root = builder.create<Node>("root");
|
||||||
original_root->a = builder.create<Node>("a");
|
original_root->a = builder.create<Node>("a");
|
||||||
@ -275,15 +277,9 @@ TEST(CloneContext, CloneWithReplace_WithNotANode) {
|
|||||||
CloneContext ctx(&cloned, &original);
|
CloneContext ctx(&cloned, &original);
|
||||||
ctx.Replace(original_root->b, replacement);
|
ctx.Replace(original_root->b, replacement);
|
||||||
|
|
||||||
#ifndef NDEBUG
|
ctx.Clone(original_root);
|
||||||
#pragma clang diagnostic push
|
},
|
||||||
#pragma clang diagnostic ignored "-Wused-but-marked-unused"
|
"internal compiler error");
|
||||||
#pragma clang diagnostic ignored "-Wcovered-switch-default"
|
|
||||||
|
|
||||||
EXPECT_DEATH_IF_SUPPORTED(ctx.Clone(original_root), "");
|
|
||||||
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
#endif // NDEBUG
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
83
src/debug.cc
Normal file
83
src/debug.cc
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
// Copyright 2021 The Tint Authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "src/debug.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "src/diagnostic/diagnostic.h"
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
InternalCompilerErrorReporter* ice_reporter = nullptr;
|
||||||
|
|
||||||
|
class SourceFileToDelete {
|
||||||
|
public:
|
||||||
|
static SourceFileToDelete& Get() {
|
||||||
|
static SourceFileToDelete* instance = new SourceFileToDelete();
|
||||||
|
return *instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds file to the list that will be deleted on call to Free()
|
||||||
|
/// Note - this function is _not_ thread safe. If we have multiple internal
|
||||||
|
/// compiler errors occurring at the same time on different threads, then
|
||||||
|
/// we're in serious trouble.
|
||||||
|
void Add(Source::File* file) { files.emplace_back(file); }
|
||||||
|
|
||||||
|
/// Free deletes all the source files added by calls to Add() and then this
|
||||||
|
/// SourceFileToDelete object. The SourceFileToDelete must not be used after
|
||||||
|
/// calling.
|
||||||
|
void Free() {
|
||||||
|
for (auto* file : files) {
|
||||||
|
delete file;
|
||||||
|
}
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Source::File*> files;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void FreeInternalCompilerErrors() {
|
||||||
|
SourceFileToDelete::Get().Free();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetInternalCompilerErrorReporter(InternalCompilerErrorReporter* reporter) {
|
||||||
|
ice_reporter = reporter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InternalCompilerError(const char* filepath,
|
||||||
|
size_t line,
|
||||||
|
const std::string& msg,
|
||||||
|
diag::List& diagnostics) {
|
||||||
|
auto* file = new Source::File(filepath, "");
|
||||||
|
|
||||||
|
SourceFileToDelete::Get().Add(file);
|
||||||
|
|
||||||
|
Source source{Source::Range{Source::Location{line}}, file};
|
||||||
|
diagnostics.add_ice(msg, source);
|
||||||
|
|
||||||
|
if (ice_reporter) {
|
||||||
|
ice_reporter(diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace tint
|
69
src/debug.h
Normal file
69
src/debug.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// Copyright 2021 The Tint Authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#ifndef SRC_DEBUG_H_
|
||||||
|
#define SRC_DEBUG_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "src/diagnostic/diagnostic.h"
|
||||||
|
#include "src/diagnostic/formatter.h"
|
||||||
|
#include "src/diagnostic/printer.h"
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
|
||||||
|
/// Function type used for registering an internal compiler error reporter
|
||||||
|
using InternalCompilerErrorReporter = void(const diag::List&);
|
||||||
|
|
||||||
|
/// Frees any memory allocated for reporting internal compiler errors.
|
||||||
|
/// Must only be called once on application termination.
|
||||||
|
/// If an internal compiler error is raised and this function is not called,
|
||||||
|
/// then memory will leak.
|
||||||
|
void FreeInternalCompilerErrors();
|
||||||
|
|
||||||
|
/// Sets the global error reporter to be called in case of internal compiler
|
||||||
|
/// errors.
|
||||||
|
/// @param reporter the error reporter
|
||||||
|
void SetInternalCompilerErrorReporter(InternalCompilerErrorReporter* reporter);
|
||||||
|
|
||||||
|
/// InternalCompilerError adds the internal compiler error message to the
|
||||||
|
/// diagnostics list, and then calls the InternalCompilerErrorReporter if one is
|
||||||
|
/// set.
|
||||||
|
/// @param file the file containing the ICE
|
||||||
|
/// @param line the line containing the ICE
|
||||||
|
/// @param msg the ICE message
|
||||||
|
/// @param diagnostics the list of diagnostics to append the ICE message to
|
||||||
|
void InternalCompilerError(const char* file,
|
||||||
|
size_t line,
|
||||||
|
const std::string& msg,
|
||||||
|
diag::List& diagnostics);
|
||||||
|
|
||||||
|
} // namespace tint
|
||||||
|
|
||||||
|
/// TINT_ICE() is a macro for appending an internal compiler error message
|
||||||
|
/// to the diagnostics list `diagnostics`, and calling the
|
||||||
|
/// InternalCompilerErrorReporter with the full diagnostic list if a reporter is
|
||||||
|
/// set.
|
||||||
|
/// The ICE message contains the callsite's file and line.
|
||||||
|
#define TINT_ICE(diagnostics, msg) \
|
||||||
|
tint::InternalCompilerError(__FILE__, __LINE__, msg, diagnostics)
|
||||||
|
|
||||||
|
/// TINT_UNREACHABLE() is a macro for appending a "TINT_UNREACHABLE"
|
||||||
|
/// internal compiler error message to the diagnostics list `diagnostics`, and
|
||||||
|
/// calling the InternalCompilerErrorReporter with the full diagnostic list if a
|
||||||
|
/// reporter is set.
|
||||||
|
/// The ICE message contains the callsite's file and line.
|
||||||
|
#define TINT_UNREACHABLE(diagnostics) TINT_ICE(diagnostics, "TINT_UNREACHABLE")
|
||||||
|
|
||||||
|
#endif // SRC_DEBUG_H_
|
32
src/debug_test.cc
Normal file
32
src/debug_test.cc
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright 2021 The Tint Authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "src/debug.h"
|
||||||
|
|
||||||
|
#include "gtest/gtest-spi.h"
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
TEST(DebugTest, Unreachable) {
|
||||||
|
EXPECT_FATAL_FAILURE(
|
||||||
|
{
|
||||||
|
diag::List diagnostics;
|
||||||
|
TINT_UNREACHABLE(diagnostics);
|
||||||
|
},
|
||||||
|
"internal compiler error");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace tint
|
@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
#include "src/diagnostic/diagnostic.h"
|
#include "src/diagnostic/diagnostic.h"
|
||||||
|
|
||||||
|
#include "src/diagnostic/formatter.h"
|
||||||
|
|
||||||
namespace tint {
|
namespace tint {
|
||||||
namespace diag {
|
namespace diag {
|
||||||
|
|
||||||
@ -27,5 +29,11 @@ List::~List() = default;
|
|||||||
List& List::operator=(const List&) = default;
|
List& List::operator=(const List&) = default;
|
||||||
List& List::operator=(List&&) = default;
|
List& List::operator=(List&&) = default;
|
||||||
|
|
||||||
|
std::string List::str() const {
|
||||||
|
diag::Formatter::Style style;
|
||||||
|
style.print_newline_at_end = false;
|
||||||
|
return Formatter{style}.format(*this);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace diag
|
} // namespace diag
|
||||||
} // namespace tint
|
} // namespace tint
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#ifndef SRC_DIAGNOSTIC_DIAGNOSTIC_H_
|
#ifndef SRC_DIAGNOSTIC_DIAGNOSTIC_H_
|
||||||
#define SRC_DIAGNOSTIC_DIAGNOSTIC_H_
|
#define SRC_DIAGNOSTIC_DIAGNOSTIC_H_
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@ -26,7 +27,7 @@ namespace tint {
|
|||||||
namespace diag {
|
namespace diag {
|
||||||
|
|
||||||
/// Severity is an enumerator of diagnostic severities.
|
/// Severity is an enumerator of diagnostic severities.
|
||||||
enum class Severity { Info, Warning, Error, Fatal };
|
enum class Severity { Info, Warning, Error, InternalCompilerError, Fatal };
|
||||||
|
|
||||||
/// @return true iff `a` is more than, or of equal severity to `b`
|
/// @return true iff `a` is more than, or of equal severity to `b`
|
||||||
inline bool operator>=(Severity a, Severity b) {
|
inline bool operator>=(Severity a, Severity b) {
|
||||||
@ -118,6 +119,17 @@ class List {
|
|||||||
add(std::move(error));
|
add(std::move(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// adds an internal compiler error message to the end of this list.
|
||||||
|
/// @param err_msg the error message
|
||||||
|
/// @param source the source of the internal compiler error
|
||||||
|
void add_ice(const std::string& err_msg, const Source& source) {
|
||||||
|
diag::Diagnostic ice{};
|
||||||
|
ice.severity = diag::Severity::InternalCompilerError;
|
||||||
|
ice.source = source;
|
||||||
|
ice.message = err_msg;
|
||||||
|
add(std::move(ice));
|
||||||
|
}
|
||||||
|
|
||||||
/// @returns true iff the diagnostic list contains errors diagnostics (or of
|
/// @returns true iff the diagnostic list contains errors diagnostics (or of
|
||||||
/// higher severity).
|
/// higher severity).
|
||||||
bool contains_errors() const { return error_count_ > 0; }
|
bool contains_errors() const { return error_count_ > 0; }
|
||||||
@ -130,6 +142,9 @@ class List {
|
|||||||
/// @returns the last diagnostic in the list.
|
/// @returns the last diagnostic in the list.
|
||||||
iterator end() const { return entries_.end(); }
|
iterator end() const { return entries_.end(); }
|
||||||
|
|
||||||
|
/// @returns a formatted string of all the diagnostics in this list.
|
||||||
|
std::string str() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<Diagnostic> entries_;
|
std::vector<Diagnostic> entries_;
|
||||||
size_t error_count_ = 0;
|
size_t error_count_ = 0;
|
||||||
|
@ -33,6 +33,8 @@ const char* to_str(Severity severity) {
|
|||||||
return "warning";
|
return "warning";
|
||||||
case Severity::Error:
|
case Severity::Error:
|
||||||
return "error";
|
return "error";
|
||||||
|
case Severity::InternalCompilerError:
|
||||||
|
return "internal compiler error";
|
||||||
case Severity::Fatal:
|
case Severity::Fatal:
|
||||||
return "fatal";
|
return "fatal";
|
||||||
}
|
}
|
||||||
@ -112,6 +114,7 @@ Formatter::Formatter(const Style& style) : style_(style) {}
|
|||||||
void Formatter::format(const List& list, Printer* printer) const {
|
void Formatter::format(const List& list, Printer* printer) const {
|
||||||
State state{printer};
|
State state{printer};
|
||||||
|
|
||||||
|
bool please_report_bug = false;
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (auto diag : list) {
|
for (auto diag : list) {
|
||||||
state.set_style({});
|
state.set_style({});
|
||||||
@ -120,7 +123,23 @@ void Formatter::format(const List& list, Printer* printer) const {
|
|||||||
}
|
}
|
||||||
format(diag, state);
|
format(diag, state);
|
||||||
first = false;
|
first = false;
|
||||||
|
|
||||||
|
if (static_cast<int>(diag.severity) > static_cast<int>(Severity::Error)) {
|
||||||
|
please_report_bug = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (please_report_bug) {
|
||||||
|
state.set_style({Color::kRed, true});
|
||||||
|
state << R"(
|
||||||
|
********************************************************************
|
||||||
|
* The tint shader compiler has encountered an unexpected error. *
|
||||||
|
* *
|
||||||
|
* Please help us fix this issue by submitting a bug report at *
|
||||||
|
* crbug.com/tint with the source program that triggered the bug. *
|
||||||
|
********************************************************************
|
||||||
|
)";
|
||||||
|
}
|
||||||
|
|
||||||
if (style_.print_newline_at_end) {
|
if (style_.print_newline_at_end) {
|
||||||
state.newline();
|
state.newline();
|
||||||
}
|
}
|
||||||
@ -154,14 +173,17 @@ void Formatter::format(const Diagnostic& diag, State& state) const {
|
|||||||
|
|
||||||
Color severity_color = Color::kDefault;
|
Color severity_color = Color::kDefault;
|
||||||
switch (diag.severity) {
|
switch (diag.severity) {
|
||||||
|
case Severity::Info:
|
||||||
|
break;
|
||||||
case Severity::Warning:
|
case Severity::Warning:
|
||||||
severity_color = Color::kYellow;
|
severity_color = Color::kYellow;
|
||||||
break;
|
break;
|
||||||
case Severity::Error:
|
case Severity::Error:
|
||||||
case Severity::Fatal:
|
|
||||||
severity_color = Color::kRed;
|
severity_color = Color::kRed;
|
||||||
break;
|
break;
|
||||||
default:
|
case Severity::Fatal:
|
||||||
|
case Severity::InternalCompilerError:
|
||||||
|
severity_color = Color::kMagenta;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (style_.print_severity) {
|
if (style_.print_severity) {
|
||||||
|
@ -39,6 +39,9 @@ class DiagFormatterTest : public testing::Test {
|
|||||||
Diagnostic diag_err{Severity::Error,
|
Diagnostic diag_err{Severity::Error,
|
||||||
Source{Source::Range{{3, 16}, {3, 21}}, &file}, "hiss",
|
Source{Source::Range{{3, 16}, {3, 21}}, &file}, "hiss",
|
||||||
"abc123"};
|
"abc123"};
|
||||||
|
Diagnostic diag_ice{Severity::InternalCompilerError,
|
||||||
|
Source{Source::Range{{4, 16}, {4, 19}}, &file},
|
||||||
|
"unreachable"};
|
||||||
Diagnostic diag_fatal{Severity::Fatal,
|
Diagnostic diag_fatal{Severity::Fatal,
|
||||||
Source{Source::Range{{4, 16}, {4, 19}}, &file},
|
Source{Source::Range{{4, 16}, {4, 19}}, &file},
|
||||||
"nothing"};
|
"nothing"};
|
||||||
@ -46,21 +49,19 @@ class DiagFormatterTest : public testing::Test {
|
|||||||
|
|
||||||
TEST_F(DiagFormatterTest, Simple) {
|
TEST_F(DiagFormatterTest, Simple) {
|
||||||
Formatter fmt{{false, false, false, false}};
|
Formatter fmt{{false, false, false, false}};
|
||||||
auto got = fmt.format(List{diag_info, diag_warn, diag_err, diag_fatal});
|
auto got = fmt.format(List{diag_info, diag_warn, diag_err});
|
||||||
auto* expect = R"(1:14: purr
|
auto* expect = R"(1:14: purr
|
||||||
2:14: grrr
|
2:14: grrr
|
||||||
3:16 abc123: hiss
|
3:16 abc123: hiss)";
|
||||||
4:16: nothing)";
|
|
||||||
ASSERT_EQ(expect, got);
|
ASSERT_EQ(expect, got);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DiagFormatterTest, SimpleNewlineAtEnd) {
|
TEST_F(DiagFormatterTest, SimpleNewlineAtEnd) {
|
||||||
Formatter fmt{{false, false, false, true}};
|
Formatter fmt{{false, false, false, true}};
|
||||||
auto got = fmt.format(List{diag_info, diag_warn, diag_err, diag_fatal});
|
auto got = fmt.format(List{diag_info, diag_warn, diag_err});
|
||||||
auto* expect = R"(1:14: purr
|
auto* expect = R"(1:14: purr
|
||||||
2:14: grrr
|
2:14: grrr
|
||||||
3:16 abc123: hiss
|
3:16 abc123: hiss
|
||||||
4:16: nothing
|
|
||||||
)";
|
)";
|
||||||
ASSERT_EQ(expect, got);
|
ASSERT_EQ(expect, got);
|
||||||
}
|
}
|
||||||
@ -75,27 +76,25 @@ TEST_F(DiagFormatterTest, SimpleNoSource) {
|
|||||||
|
|
||||||
TEST_F(DiagFormatterTest, WithFile) {
|
TEST_F(DiagFormatterTest, WithFile) {
|
||||||
Formatter fmt{{true, false, false, false}};
|
Formatter fmt{{true, false, false, false}};
|
||||||
auto got = fmt.format(List{diag_info, diag_warn, diag_err, diag_fatal});
|
auto got = fmt.format(List{diag_info, diag_warn, diag_err});
|
||||||
auto* expect = R"(file.name:1:14: purr
|
auto* expect = R"(file.name:1:14: purr
|
||||||
file.name:2:14: grrr
|
file.name:2:14: grrr
|
||||||
file.name:3:16 abc123: hiss
|
file.name:3:16 abc123: hiss)";
|
||||||
file.name:4:16: nothing)";
|
|
||||||
ASSERT_EQ(expect, got);
|
ASSERT_EQ(expect, got);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DiagFormatterTest, WithSeverity) {
|
TEST_F(DiagFormatterTest, WithSeverity) {
|
||||||
Formatter fmt{{false, true, false, false}};
|
Formatter fmt{{false, true, false, false}};
|
||||||
auto got = fmt.format(List{diag_info, diag_warn, diag_err, diag_fatal});
|
auto got = fmt.format(List{diag_info, diag_warn, diag_err});
|
||||||
auto* expect = R"(1:14 info: purr
|
auto* expect = R"(1:14 info: purr
|
||||||
2:14 warning: grrr
|
2:14 warning: grrr
|
||||||
3:16 error abc123: hiss
|
3:16 error abc123: hiss)";
|
||||||
4:16 fatal: nothing)";
|
|
||||||
ASSERT_EQ(expect, got);
|
ASSERT_EQ(expect, got);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DiagFormatterTest, WithLine) {
|
TEST_F(DiagFormatterTest, WithLine) {
|
||||||
Formatter fmt{{false, false, true, false}};
|
Formatter fmt{{false, false, true, false}};
|
||||||
auto got = fmt.format(List{diag_info, diag_warn, diag_err, diag_fatal});
|
auto got = fmt.format(List{diag_info, diag_warn, diag_err});
|
||||||
auto* expect = R"(1:14: purr
|
auto* expect = R"(1:14: purr
|
||||||
the cat says meow
|
the cat says meow
|
||||||
^
|
^
|
||||||
@ -107,17 +106,13 @@ the dog says woof
|
|||||||
3:16 abc123: hiss
|
3:16 abc123: hiss
|
||||||
the snake says quack
|
the snake says quack
|
||||||
^^^^^
|
^^^^^
|
||||||
|
|
||||||
4:16: nothing
|
|
||||||
the snail says ???
|
|
||||||
^^^
|
|
||||||
)";
|
)";
|
||||||
ASSERT_EQ(expect, got);
|
ASSERT_EQ(expect, got);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DiagFormatterTest, BasicWithFileSeverityLine) {
|
TEST_F(DiagFormatterTest, BasicWithFileSeverityLine) {
|
||||||
Formatter fmt{{true, true, true, false}};
|
Formatter fmt{{true, true, true, false}};
|
||||||
auto got = fmt.format(List{diag_info, diag_warn, diag_err, diag_fatal});
|
auto got = fmt.format(List{diag_info, diag_warn, diag_err});
|
||||||
auto* expect = R"(file.name:1:14 info: purr
|
auto* expect = R"(file.name:1:14 info: purr
|
||||||
the cat says meow
|
the cat says meow
|
||||||
^
|
^
|
||||||
@ -129,10 +124,6 @@ the dog says woof
|
|||||||
file.name:3:16 error abc123: hiss
|
file.name:3:16 error abc123: hiss
|
||||||
the snake says quack
|
the snake says quack
|
||||||
^^^^^
|
^^^^^
|
||||||
|
|
||||||
file.name:4:16 fatal: nothing
|
|
||||||
the snail says ???
|
|
||||||
^^^
|
|
||||||
)";
|
)";
|
||||||
ASSERT_EQ(expect, got);
|
ASSERT_EQ(expect, got);
|
||||||
}
|
}
|
||||||
@ -154,6 +145,42 @@ the snail says ???
|
|||||||
ASSERT_EQ(expect, got);
|
ASSERT_EQ(expect, got);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DiagFormatterTest, ICE) {
|
||||||
|
Formatter fmt{{}};
|
||||||
|
auto got = fmt.format(List{diag_ice});
|
||||||
|
auto* expect = R"(file.name:4:16 internal compiler error: unreachable
|
||||||
|
the snail says ???
|
||||||
|
^^^
|
||||||
|
|
||||||
|
********************************************************************
|
||||||
|
* The tint shader compiler has encountered an unexpected error. *
|
||||||
|
* *
|
||||||
|
* Please help us fix this issue by submitting a bug report at *
|
||||||
|
* crbug.com/tint with the source program that triggered the bug. *
|
||||||
|
********************************************************************
|
||||||
|
|
||||||
|
)";
|
||||||
|
ASSERT_EQ(expect, got);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DiagFormatterTest, Fatal) {
|
||||||
|
Formatter fmt{{}};
|
||||||
|
auto got = fmt.format(List{diag_fatal});
|
||||||
|
auto* expect = R"(file.name:4:16 fatal: nothing
|
||||||
|
the snail says ???
|
||||||
|
^^^
|
||||||
|
|
||||||
|
********************************************************************
|
||||||
|
* The tint shader compiler has encountered an unexpected error. *
|
||||||
|
* *
|
||||||
|
* Please help us fix this issue by submitting a bug report at *
|
||||||
|
* crbug.com/tint with the source program that triggered the bug. *
|
||||||
|
********************************************************************
|
||||||
|
|
||||||
|
)";
|
||||||
|
ASSERT_EQ(expect, got);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace diag
|
} // namespace diag
|
||||||
} // namespace tint
|
} // namespace tint
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "src/block_allocator.h"
|
#include "src/block_allocator.h"
|
||||||
|
#include "src/debug.h"
|
||||||
#include "src/program_builder.h"
|
#include "src/program_builder.h"
|
||||||
#include "src/semantic/intrinsic.h"
|
#include "src/semantic/intrinsic.h"
|
||||||
#include "src/type/access_control_type.h"
|
#include "src/type/access_control_type.h"
|
||||||
@ -641,10 +642,10 @@ class Impl : public IntrinsicTable {
|
|||||||
public:
|
public:
|
||||||
Impl();
|
Impl();
|
||||||
|
|
||||||
IntrinsicTable::Result Lookup(
|
IntrinsicTable::Result Lookup(ProgramBuilder& builder,
|
||||||
ProgramBuilder& builder,
|
|
||||||
semantic::IntrinsicType type,
|
semantic::IntrinsicType type,
|
||||||
const std::vector<type::Type*>& args) const override;
|
const std::vector<type::Type*>& args,
|
||||||
|
const Source& source) const override;
|
||||||
|
|
||||||
/// Holds the information about a single overload parameter used for matching
|
/// Holds the information about a single overload parameter used for matching
|
||||||
struct Parameter {
|
struct Parameter {
|
||||||
@ -660,9 +661,6 @@ class Impl : public IntrinsicTable {
|
|||||||
|
|
||||||
/// A single overload definition.
|
/// A single overload definition.
|
||||||
struct Overload {
|
struct Overload {
|
||||||
/// @returns a human readable string representation of the overload
|
|
||||||
std::string str() const;
|
|
||||||
|
|
||||||
/// Attempts to match this overload given the IntrinsicType and argument
|
/// Attempts to match this overload given the IntrinsicType and argument
|
||||||
/// types. If a match is made, the build intrinsic is returned, otherwise
|
/// types. If a match is made, the build intrinsic is returned, otherwise
|
||||||
/// `match_score` is assigned a score of how closely the overload matched
|
/// `match_score` is assigned a score of how closely the overload matched
|
||||||
@ -670,6 +668,7 @@ class Impl : public IntrinsicTable {
|
|||||||
semantic::Intrinsic* Match(ProgramBuilder& builder,
|
semantic::Intrinsic* Match(ProgramBuilder& builder,
|
||||||
semantic::IntrinsicType type,
|
semantic::IntrinsicType type,
|
||||||
const std::vector<type::Type*>& arg_types,
|
const std::vector<type::Type*>& arg_types,
|
||||||
|
diag::List& diagnostics,
|
||||||
int& match_score) const;
|
int& match_score) const;
|
||||||
|
|
||||||
semantic::IntrinsicType type;
|
semantic::IntrinsicType type;
|
||||||
@ -1288,12 +1287,13 @@ Impl::Impl() {
|
|||||||
// clang-format on
|
// clang-format on
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Impl::Overload::str() const {
|
/// @returns a human readable string representation of the overload
|
||||||
|
std::string str(const Impl::Overload& overload) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << type << "(";
|
ss << overload.type << "(";
|
||||||
{
|
{
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (auto param : parameters) {
|
for (auto param : overload.parameters) {
|
||||||
if (!first) {
|
if (!first) {
|
||||||
ss << ", ";
|
ss << ", ";
|
||||||
}
|
}
|
||||||
@ -1305,15 +1305,15 @@ std::string Impl::Overload::str() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ss << ") -> ";
|
ss << ") -> ";
|
||||||
ss << return_type->str();
|
ss << overload.return_type->str();
|
||||||
|
|
||||||
if (!open_type_matchers.empty()) {
|
if (!overload.open_type_matchers.empty()) {
|
||||||
ss << " where: ";
|
ss << " where: ";
|
||||||
|
|
||||||
for (uint32_t i = 0; i < static_cast<uint32_t>(OpenType::Count); i++) {
|
for (uint32_t i = 0; i < static_cast<uint32_t>(OpenType::Count); i++) {
|
||||||
auto open_type = static_cast<OpenType>(i);
|
auto open_type = static_cast<OpenType>(i);
|
||||||
auto it = open_type_matchers.find(open_type);
|
auto it = overload.open_type_matchers.find(open_type);
|
||||||
if (it != open_type_matchers.end()) {
|
if (it != overload.open_type_matchers.end()) {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
ss << ", ";
|
ss << ", ";
|
||||||
}
|
}
|
||||||
@ -1324,10 +1324,33 @@ std::string Impl::Overload::str() const {
|
|||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
IntrinsicTable::Result Impl::Lookup(
|
/// @return a string representing a call to an intrinsic with the given argument
|
||||||
ProgramBuilder& builder,
|
/// types.
|
||||||
|
std::string CallSignature(ProgramBuilder& builder,
|
||||||
semantic::IntrinsicType type,
|
semantic::IntrinsicType type,
|
||||||
const std::vector<type::Type*>& args) const {
|
const std::vector<type::Type*>& args) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << semantic::str(type) << "(";
|
||||||
|
{
|
||||||
|
bool first = true;
|
||||||
|
for (auto* arg : args) {
|
||||||
|
if (!first) {
|
||||||
|
ss << ", ";
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
ss << arg->FriendlyName(builder.Symbols());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ss << ")";
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
IntrinsicTable::Result Impl::Lookup(ProgramBuilder& builder,
|
||||||
|
semantic::IntrinsicType type,
|
||||||
|
const std::vector<type::Type*>& args,
|
||||||
|
const Source& source) const {
|
||||||
|
diag::List diagnostics;
|
||||||
// Candidate holds information about a mismatched overload that could be what
|
// Candidate holds information about a mismatched overload that could be what
|
||||||
// the user intended to call.
|
// the user intended to call.
|
||||||
struct Candidate {
|
struct Candidate {
|
||||||
@ -1342,8 +1365,9 @@ IntrinsicTable::Result Impl::Lookup(
|
|||||||
// type. This is horribly inefficient.
|
// type. This is horribly inefficient.
|
||||||
for (auto& overload : overloads_) {
|
for (auto& overload : overloads_) {
|
||||||
int match_score = 0;
|
int match_score = 0;
|
||||||
if (auto* intrinsic = overload.Match(builder, type, args, match_score)) {
|
if (auto* intrinsic =
|
||||||
return Result{intrinsic, ""}; // Match found
|
overload.Match(builder, type, args, diagnostics, match_score)) {
|
||||||
|
return Result{intrinsic, {}}; // Match found
|
||||||
}
|
}
|
||||||
if (match_score > 0) {
|
if (match_score > 0) {
|
||||||
candidates.emplace_back(Candidate{&overload, match_score});
|
candidates.emplace_back(Candidate{&overload, match_score});
|
||||||
@ -1357,34 +1381,25 @@ IntrinsicTable::Result Impl::Lookup(
|
|||||||
|
|
||||||
// Generate an error message
|
// Generate an error message
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "no matching call to " << semantic::str(type) << "(";
|
ss << "no matching call to " << CallSignature(builder, type, args)
|
||||||
{
|
<< std::endl;
|
||||||
bool first = true;
|
|
||||||
for (auto* arg : args) {
|
|
||||||
if (!first) {
|
|
||||||
ss << ", ";
|
|
||||||
}
|
|
||||||
first = false;
|
|
||||||
ss << arg->FriendlyName(builder.Symbols());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ss << ")" << std::endl;
|
|
||||||
|
|
||||||
if (!candidates.empty()) {
|
if (!candidates.empty()) {
|
||||||
ss << std::endl;
|
ss << std::endl;
|
||||||
ss << candidates.size() << " candidate function"
|
ss << candidates.size() << " candidate function"
|
||||||
<< (candidates.size() > 1 ? "s:" : ":") << std::endl;
|
<< (candidates.size() > 1 ? "s:" : ":") << std::endl;
|
||||||
for (auto& candidate : candidates) {
|
for (auto& candidate : candidates) {
|
||||||
ss << " " << candidate.overload->str() << std::endl;
|
ss << " " << str(*candidate.overload) << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
diagnostics.add_error(ss.str(), source);
|
||||||
|
|
||||||
return Result{nullptr, ss.str()};
|
return Result{nullptr, std::move(diagnostics)};
|
||||||
}
|
}
|
||||||
|
|
||||||
semantic::Intrinsic* Impl::Overload::Match(ProgramBuilder& builder,
|
semantic::Intrinsic* Impl::Overload::Match(ProgramBuilder& builder,
|
||||||
semantic::IntrinsicType intrinsic,
|
semantic::IntrinsicType intrinsic,
|
||||||
const std::vector<type::Type*>& args,
|
const std::vector<type::Type*>& args,
|
||||||
|
diag::List& diagnostics,
|
||||||
int& match_score) const {
|
int& match_score) const {
|
||||||
if (type != intrinsic) {
|
if (type != intrinsic) {
|
||||||
match_score = std::numeric_limits<int>::min();
|
match_score = std::numeric_limits<int>::min();
|
||||||
@ -1433,14 +1448,20 @@ semantic::Intrinsic* Impl::Overload::Match(ProgramBuilder& builder,
|
|||||||
if (type_it == matcher_state.open_types.end()) {
|
if (type_it == matcher_state.open_types.end()) {
|
||||||
// We have an overload that claims to have matched, but didn't actually
|
// We have an overload that claims to have matched, but didn't actually
|
||||||
// resolve the open type. This is a bug that needs fixing.
|
// resolve the open type. This is a bug that needs fixing.
|
||||||
assert(false);
|
TINT_ICE(diagnostics, "IntrinsicTable overload matched for " +
|
||||||
|
CallSignature(builder, intrinsic, args) +
|
||||||
|
", but didn't resolve the open type " +
|
||||||
|
str(open_type));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto* resolved_type = type_it->second;
|
auto* resolved_type = type_it->second;
|
||||||
if (resolved_type == nullptr) {
|
if (resolved_type == nullptr) {
|
||||||
// We have an overload that claims to have matched, but has a nullptr
|
// We have an overload that claims to have matched, but has a nullptr
|
||||||
// resolved open type. This is a bug that needs fixing.
|
// resolved open type. This is a bug that needs fixing.
|
||||||
assert(false);
|
TINT_ICE(diagnostics, "IntrinsicTable overload matched for " +
|
||||||
|
CallSignature(builder, intrinsic, args) +
|
||||||
|
", but open type " + str(open_type) +
|
||||||
|
" is nullptr");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (!matcher->Match(matcher_state, resolved_type)) {
|
if (!matcher->Match(matcher_state, resolved_type)) {
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "src/diagnostic/diagnostic.h"
|
||||||
#include "src/semantic/intrinsic.h"
|
#include "src/semantic/intrinsic.h"
|
||||||
|
|
||||||
namespace tint {
|
namespace tint {
|
||||||
@ -39,18 +40,20 @@ class IntrinsicTable {
|
|||||||
struct Result {
|
struct Result {
|
||||||
/// The intrinsic, if the lookup succeeded, otherwise nullptr
|
/// The intrinsic, if the lookup succeeded, otherwise nullptr
|
||||||
semantic::Intrinsic* intrinsic;
|
semantic::Intrinsic* intrinsic;
|
||||||
/// The error message, if the lookup failed, otherwise empty
|
/// Diagnostic messages
|
||||||
std::string error;
|
diag::List diagnostics;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Lookup looks for the intrinsic overload with the given signature.
|
/// Lookup looks for the intrinsic overload with the given signature.
|
||||||
/// @param builder the program builder
|
/// @param builder the program builder
|
||||||
/// @param type the intrinsic type
|
/// @param type the intrinsic type
|
||||||
/// @param args the argument types passed to the intrinsic function
|
/// @param args the argument types passed to the intrinsic function
|
||||||
|
/// @param source the source of the intrinsic call
|
||||||
/// @return the semantic intrinsic if found, otherwise nullptr
|
/// @return the semantic intrinsic if found, otherwise nullptr
|
||||||
virtual Result Lookup(ProgramBuilder& builder,
|
virtual Result Lookup(ProgramBuilder& builder,
|
||||||
semantic::IntrinsicType type,
|
semantic::IntrinsicType type,
|
||||||
const std::vector<type::Type*>& args) const = 0;
|
const std::vector<type::Type*>& args,
|
||||||
|
const Source& source) const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace tint
|
} // namespace tint
|
||||||
|
@ -38,44 +38,44 @@ class IntrinsicTableTest : public testing::Test, public ProgramBuilder {
|
|||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchF32) {
|
TEST_F(IntrinsicTableTest, MatchF32) {
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kCos, {ty.f32()});
|
auto result = table->Lookup(*this, IntrinsicType::kCos, {ty.f32()}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result.intrinsic, nullptr);
|
||||||
ASSERT_EQ(result.error, "");
|
ASSERT_EQ(result.diagnostics.str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCos);
|
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCos);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
|
EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{ty.f32()}));
|
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{ty.f32()}));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MismatchF32) {
|
TEST_F(IntrinsicTableTest, MismatchF32) {
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kCos, {ty.i32()});
|
auto result = table->Lookup(*this, IntrinsicType::kCos, {ty.i32()}, Source{});
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_EQ(result.intrinsic, nullptr);
|
||||||
ASSERT_THAT(result.error, HasSubstr("no matching call"));
|
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchU32) {
|
TEST_F(IntrinsicTableTest, MatchU32) {
|
||||||
auto result =
|
auto result = table->Lookup(*this, IntrinsicType::kUnpack2x16Float,
|
||||||
table->Lookup(*this, IntrinsicType::kUnpack2x16Float, {ty.u32()});
|
{ty.u32()}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result.intrinsic, nullptr);
|
||||||
ASSERT_EQ(result.error, "");
|
ASSERT_EQ(result.diagnostics.str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kUnpack2x16Float);
|
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kUnpack2x16Float);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.vec2<f32>());
|
EXPECT_THAT(result.intrinsic->ReturnType(), ty.vec2<f32>());
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{ty.u32()}));
|
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{ty.u32()}));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MismatchU32) {
|
TEST_F(IntrinsicTableTest, MismatchU32) {
|
||||||
auto result =
|
auto result = table->Lookup(*this, IntrinsicType::kUnpack2x16Float,
|
||||||
table->Lookup(*this, IntrinsicType::kUnpack2x16Float, {ty.f32()});
|
{ty.f32()}, Source{});
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_EQ(result.intrinsic, nullptr);
|
||||||
ASSERT_THAT(result.error, HasSubstr("no matching call"));
|
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchI32) {
|
TEST_F(IntrinsicTableTest, MatchI32) {
|
||||||
auto* tex =
|
auto* tex =
|
||||||
create<type::SampledTexture>(type::TextureDimension::k1d, ty.f32());
|
create<type::SampledTexture>(type::TextureDimension::k1d, ty.f32());
|
||||||
auto result =
|
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
|
||||||
table->Lookup(*this, IntrinsicType::kTextureLoad, {tex, ty.i32()});
|
{tex, ty.i32()}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result.intrinsic, nullptr);
|
||||||
ASSERT_EQ(result.error, "");
|
ASSERT_EQ(result.diagnostics.str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
|
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.vec4<f32>());
|
EXPECT_THAT(result.intrinsic->ReturnType(), ty.vec4<f32>());
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
EXPECT_THAT(result.intrinsic->Parameters(),
|
||||||
@ -86,41 +86,44 @@ TEST_F(IntrinsicTableTest, MatchI32) {
|
|||||||
TEST_F(IntrinsicTableTest, MismatchI32) {
|
TEST_F(IntrinsicTableTest, MismatchI32) {
|
||||||
auto* tex =
|
auto* tex =
|
||||||
create<type::SampledTexture>(type::TextureDimension::k1d, ty.f32());
|
create<type::SampledTexture>(type::TextureDimension::k1d, ty.f32());
|
||||||
auto result =
|
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
|
||||||
table->Lookup(*this, IntrinsicType::kTextureLoad, {tex, ty.f32()});
|
{tex, ty.f32()}, Source{});
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_EQ(result.intrinsic, nullptr);
|
||||||
ASSERT_THAT(result.error, HasSubstr("no matching call"));
|
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchIU32AsI32) {
|
TEST_F(IntrinsicTableTest, MatchIU32AsI32) {
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kCountOneBits, {ty.i32()});
|
auto result =
|
||||||
|
table->Lookup(*this, IntrinsicType::kCountOneBits, {ty.i32()}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result.intrinsic, nullptr);
|
||||||
ASSERT_EQ(result.error, "");
|
ASSERT_EQ(result.diagnostics.str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCountOneBits);
|
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCountOneBits);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.i32());
|
EXPECT_THAT(result.intrinsic->ReturnType(), ty.i32());
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{ty.i32()}));
|
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{ty.i32()}));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchIU32AsU32) {
|
TEST_F(IntrinsicTableTest, MatchIU32AsU32) {
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kCountOneBits, {ty.u32()});
|
auto result =
|
||||||
|
table->Lookup(*this, IntrinsicType::kCountOneBits, {ty.u32()}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result.intrinsic, nullptr);
|
||||||
ASSERT_EQ(result.error, "");
|
ASSERT_EQ(result.diagnostics.str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCountOneBits);
|
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCountOneBits);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.u32());
|
EXPECT_THAT(result.intrinsic->ReturnType(), ty.u32());
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{ty.u32()}));
|
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{ty.u32()}));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MismatchIU32) {
|
TEST_F(IntrinsicTableTest, MismatchIU32) {
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kCountOneBits, {ty.f32()});
|
auto result =
|
||||||
|
table->Lookup(*this, IntrinsicType::kCountOneBits, {ty.f32()}, Source{});
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_EQ(result.intrinsic, nullptr);
|
||||||
ASSERT_THAT(result.error, HasSubstr("no matching call"));
|
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchFIU32AsI32) {
|
TEST_F(IntrinsicTableTest, MatchFIU32AsI32) {
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kClamp,
|
auto result = table->Lookup(*this, IntrinsicType::kClamp,
|
||||||
{ty.i32(), ty.i32(), ty.i32()});
|
{ty.i32(), ty.i32(), ty.i32()}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result.intrinsic, nullptr);
|
||||||
ASSERT_EQ(result.error, "");
|
ASSERT_EQ(result.diagnostics.str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
|
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.i32());
|
EXPECT_THAT(result.intrinsic->ReturnType(), ty.i32());
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
EXPECT_THAT(result.intrinsic->Parameters(),
|
||||||
@ -130,9 +133,9 @@ TEST_F(IntrinsicTableTest, MatchFIU32AsI32) {
|
|||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchFIU32AsU32) {
|
TEST_F(IntrinsicTableTest, MatchFIU32AsU32) {
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kClamp,
|
auto result = table->Lookup(*this, IntrinsicType::kClamp,
|
||||||
{ty.u32(), ty.u32(), ty.u32()});
|
{ty.u32(), ty.u32(), ty.u32()}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result.intrinsic, nullptr);
|
||||||
ASSERT_EQ(result.error, "");
|
ASSERT_EQ(result.diagnostics.str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
|
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.u32());
|
EXPECT_THAT(result.intrinsic->ReturnType(), ty.u32());
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
EXPECT_THAT(result.intrinsic->Parameters(),
|
||||||
@ -142,9 +145,9 @@ TEST_F(IntrinsicTableTest, MatchFIU32AsU32) {
|
|||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchFIU32AsF32) {
|
TEST_F(IntrinsicTableTest, MatchFIU32AsF32) {
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kClamp,
|
auto result = table->Lookup(*this, IntrinsicType::kClamp,
|
||||||
{ty.f32(), ty.f32(), ty.f32()});
|
{ty.f32(), ty.f32(), ty.f32()}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result.intrinsic, nullptr);
|
||||||
ASSERT_EQ(result.error, "");
|
ASSERT_EQ(result.diagnostics.str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
|
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
|
EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
EXPECT_THAT(result.intrinsic->Parameters(),
|
||||||
@ -154,16 +157,16 @@ TEST_F(IntrinsicTableTest, MatchFIU32AsF32) {
|
|||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MismatchFIU32) {
|
TEST_F(IntrinsicTableTest, MismatchFIU32) {
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kClamp,
|
auto result = table->Lookup(*this, IntrinsicType::kClamp,
|
||||||
{ty.bool_(), ty.bool_(), ty.bool_()});
|
{ty.bool_(), ty.bool_(), ty.bool_()}, Source{});
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_EQ(result.intrinsic, nullptr);
|
||||||
ASSERT_THAT(result.error, HasSubstr("no matching call"));
|
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchBool) {
|
TEST_F(IntrinsicTableTest, MatchBool) {
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kSelect,
|
auto result = table->Lookup(*this, IntrinsicType::kSelect,
|
||||||
{ty.f32(), ty.f32(), ty.bool_()});
|
{ty.f32(), ty.f32(), ty.bool_()}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result.intrinsic, nullptr);
|
||||||
ASSERT_EQ(result.error, "");
|
ASSERT_EQ(result.diagnostics.str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kSelect);
|
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kSelect);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
|
EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
EXPECT_THAT(result.intrinsic->Parameters(),
|
||||||
@ -173,17 +176,17 @@ TEST_F(IntrinsicTableTest, MatchBool) {
|
|||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MismatchBool) {
|
TEST_F(IntrinsicTableTest, MismatchBool) {
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kSelect,
|
auto result = table->Lookup(*this, IntrinsicType::kSelect,
|
||||||
{ty.f32(), ty.f32(), ty.f32()});
|
{ty.f32(), ty.f32(), ty.f32()}, Source{});
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_EQ(result.intrinsic, nullptr);
|
||||||
ASSERT_THAT(result.error, HasSubstr("no matching call"));
|
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchPointer) {
|
TEST_F(IntrinsicTableTest, MatchPointer) {
|
||||||
auto result =
|
auto result = table->Lookup(
|
||||||
table->Lookup(*this, IntrinsicType::kModf,
|
*this, IntrinsicType::kModf,
|
||||||
{ty.f32(), ty.pointer<f32>(ast::StorageClass::kNone)});
|
{ty.f32(), ty.pointer<f32>(ast::StorageClass::kNone)}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result.intrinsic, nullptr);
|
||||||
ASSERT_EQ(result.error, "");
|
ASSERT_EQ(result.diagnostics.str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kModf);
|
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kModf);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
|
EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
|
||||||
EXPECT_THAT(
|
EXPECT_THAT(
|
||||||
@ -193,17 +196,17 @@ TEST_F(IntrinsicTableTest, MatchPointer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MismatchPointer) {
|
TEST_F(IntrinsicTableTest, MismatchPointer) {
|
||||||
auto result =
|
auto result = table->Lookup(*this, IntrinsicType::kModf, {ty.f32(), ty.f32()},
|
||||||
table->Lookup(*this, IntrinsicType::kModf, {ty.f32(), ty.f32()});
|
Source{});
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_EQ(result.intrinsic, nullptr);
|
||||||
ASSERT_THAT(result.error, HasSubstr("no matching call"));
|
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchArray) {
|
TEST_F(IntrinsicTableTest, MatchArray) {
|
||||||
auto result =
|
auto result = table->Lookup(*this, IntrinsicType::kArrayLength,
|
||||||
table->Lookup(*this, IntrinsicType::kArrayLength, {ty.array<f32>()});
|
{ty.array<f32>()}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result.intrinsic, nullptr);
|
||||||
ASSERT_EQ(result.error, "");
|
ASSERT_EQ(result.diagnostics.str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kArrayLength);
|
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kArrayLength);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.u32());
|
EXPECT_THAT(result.intrinsic->ReturnType(), ty.u32());
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
EXPECT_THAT(result.intrinsic->Parameters(),
|
||||||
@ -211,9 +214,10 @@ TEST_F(IntrinsicTableTest, MatchArray) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MismatchArray) {
|
TEST_F(IntrinsicTableTest, MismatchArray) {
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kArrayLength, {ty.f32()});
|
auto result =
|
||||||
|
table->Lookup(*this, IntrinsicType::kArrayLength, {ty.f32()}, Source{});
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_EQ(result.intrinsic, nullptr);
|
||||||
ASSERT_THAT(result.error, HasSubstr("no matching call"));
|
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchSampler) {
|
TEST_F(IntrinsicTableTest, MatchSampler) {
|
||||||
@ -221,9 +225,9 @@ TEST_F(IntrinsicTableTest, MatchSampler) {
|
|||||||
create<type::SampledTexture>(type::TextureDimension::k2d, ty.f32());
|
create<type::SampledTexture>(type::TextureDimension::k2d, ty.f32());
|
||||||
auto* sampler = create<type::Sampler>(type::SamplerKind::kSampler);
|
auto* sampler = create<type::Sampler>(type::SamplerKind::kSampler);
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kTextureSample,
|
auto result = table->Lookup(*this, IntrinsicType::kTextureSample,
|
||||||
{tex, sampler, ty.vec2<f32>()});
|
{tex, sampler, ty.vec2<f32>()}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result.intrinsic, nullptr);
|
||||||
ASSERT_EQ(result.error, "");
|
ASSERT_EQ(result.diagnostics.str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureSample);
|
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureSample);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.vec4<f32>());
|
EXPECT_THAT(result.intrinsic->ReturnType(), ty.vec4<f32>());
|
||||||
EXPECT_THAT(
|
EXPECT_THAT(
|
||||||
@ -237,18 +241,18 @@ TEST_F(IntrinsicTableTest, MismatchSampler) {
|
|||||||
auto* tex =
|
auto* tex =
|
||||||
create<type::SampledTexture>(type::TextureDimension::k2d, ty.f32());
|
create<type::SampledTexture>(type::TextureDimension::k2d, ty.f32());
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kTextureSample,
|
auto result = table->Lookup(*this, IntrinsicType::kTextureSample,
|
||||||
{tex, ty.f32(), ty.vec2<f32>()});
|
{tex, ty.f32(), ty.vec2<f32>()}, Source{});
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_EQ(result.intrinsic, nullptr);
|
||||||
ASSERT_THAT(result.error, HasSubstr("no matching call"));
|
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchSampledTexture) {
|
TEST_F(IntrinsicTableTest, MatchSampledTexture) {
|
||||||
auto* tex =
|
auto* tex =
|
||||||
create<type::SampledTexture>(type::TextureDimension::k2d, ty.f32());
|
create<type::SampledTexture>(type::TextureDimension::k2d, ty.f32());
|
||||||
auto result =
|
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
|
||||||
table->Lookup(*this, IntrinsicType::kTextureLoad, {tex, ty.vec2<i32>()});
|
{tex, ty.vec2<i32>()}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result.intrinsic, nullptr);
|
||||||
ASSERT_EQ(result.error, "");
|
ASSERT_EQ(result.diagnostics.str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
|
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.vec4<f32>());
|
EXPECT_THAT(result.intrinsic->ReturnType(), ty.vec4<f32>());
|
||||||
EXPECT_THAT(
|
EXPECT_THAT(
|
||||||
@ -261,9 +265,9 @@ TEST_F(IntrinsicTableTest, MatchMultisampledTexture) {
|
|||||||
auto* tex =
|
auto* tex =
|
||||||
create<type::MultisampledTexture>(type::TextureDimension::k2d, ty.f32());
|
create<type::MultisampledTexture>(type::TextureDimension::k2d, ty.f32());
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
|
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
|
||||||
{tex, ty.vec2<i32>(), ty.i32()});
|
{tex, ty.vec2<i32>(), ty.i32()}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result.intrinsic, nullptr);
|
||||||
ASSERT_EQ(result.error, "");
|
ASSERT_EQ(result.diagnostics.str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
|
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.vec4<f32>());
|
EXPECT_THAT(result.intrinsic->ReturnType(), ty.vec4<f32>());
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
EXPECT_THAT(result.intrinsic->Parameters(),
|
||||||
@ -274,10 +278,10 @@ TEST_F(IntrinsicTableTest, MatchMultisampledTexture) {
|
|||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchDepthTexture) {
|
TEST_F(IntrinsicTableTest, MatchDepthTexture) {
|
||||||
auto* tex = create<type::DepthTexture>(type::TextureDimension::k2d);
|
auto* tex = create<type::DepthTexture>(type::TextureDimension::k2d);
|
||||||
auto result =
|
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
|
||||||
table->Lookup(*this, IntrinsicType::kTextureLoad, {tex, ty.vec2<i32>()});
|
{tex, ty.vec2<i32>()}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result.intrinsic, nullptr);
|
||||||
ASSERT_EQ(result.error, "");
|
ASSERT_EQ(result.diagnostics.str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
|
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
|
EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
|
||||||
EXPECT_THAT(
|
EXPECT_THAT(
|
||||||
@ -293,9 +297,9 @@ TEST_F(IntrinsicTableTest, MatchROStorageTexture) {
|
|||||||
auto* tex_ac =
|
auto* tex_ac =
|
||||||
create<type::AccessControl>(ast::AccessControl::kReadOnly, tex);
|
create<type::AccessControl>(ast::AccessControl::kReadOnly, tex);
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
|
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
|
||||||
{tex_ac, ty.vec2<i32>()});
|
{tex_ac, ty.vec2<i32>()}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result.intrinsic, nullptr);
|
||||||
ASSERT_EQ(result.error, "");
|
ASSERT_EQ(result.diagnostics.str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
|
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.vec4<f32>());
|
EXPECT_THAT(result.intrinsic->ReturnType(), ty.vec4<f32>());
|
||||||
EXPECT_THAT(
|
EXPECT_THAT(
|
||||||
@ -310,10 +314,11 @@ TEST_F(IntrinsicTableTest, MatchWOStorageTexture) {
|
|||||||
type::StorageTexture::SubtypeFor(type::ImageFormat::kR16Float, Types()));
|
type::StorageTexture::SubtypeFor(type::ImageFormat::kR16Float, Types()));
|
||||||
auto* tex_ac =
|
auto* tex_ac =
|
||||||
create<type::AccessControl>(ast::AccessControl::kWriteOnly, tex);
|
create<type::AccessControl>(ast::AccessControl::kWriteOnly, tex);
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kTextureStore,
|
auto result =
|
||||||
{tex_ac, ty.vec2<i32>(), ty.vec4<f32>()});
|
table->Lookup(*this, IntrinsicType::kTextureStore,
|
||||||
|
{tex_ac, ty.vec2<i32>(), ty.vec4<f32>()}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result.intrinsic, nullptr);
|
||||||
ASSERT_EQ(result.error, "");
|
ASSERT_EQ(result.diagnostics.str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureStore);
|
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureStore);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.void_());
|
EXPECT_THAT(result.intrinsic->ReturnType(), ty.void_());
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
EXPECT_THAT(result.intrinsic->Parameters(),
|
||||||
@ -324,16 +329,17 @@ TEST_F(IntrinsicTableTest, MatchWOStorageTexture) {
|
|||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MismatchTexture) {
|
TEST_F(IntrinsicTableTest, MismatchTexture) {
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
|
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
|
||||||
{ty.f32(), ty.vec2<i32>()});
|
{ty.f32(), ty.vec2<i32>()}, Source{});
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_EQ(result.intrinsic, nullptr);
|
||||||
ASSERT_THAT(result.error, HasSubstr("no matching call"));
|
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchAutoPointerDereference) {
|
TEST_F(IntrinsicTableTest, MatchAutoPointerDereference) {
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kCos,
|
auto result =
|
||||||
{ty.pointer<f32>(ast::StorageClass::kNone)});
|
table->Lookup(*this, IntrinsicType::kCos,
|
||||||
|
{ty.pointer<f32>(ast::StorageClass::kNone)}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result.intrinsic, nullptr);
|
||||||
ASSERT_EQ(result.error, "");
|
ASSERT_EQ(result.diagnostics.str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCos);
|
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCos);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
|
EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{ty.f32()}));
|
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{ty.f32()}));
|
||||||
@ -343,9 +349,9 @@ TEST_F(IntrinsicTableTest, MatchWithAliasUnwrapping) {
|
|||||||
auto* alias_a = ty.alias("alias_a", ty.f32());
|
auto* alias_a = ty.alias("alias_a", ty.f32());
|
||||||
auto* alias_b = ty.alias("alias_b", alias_a);
|
auto* alias_b = ty.alias("alias_b", alias_a);
|
||||||
auto* alias_c = ty.alias("alias_c", alias_b);
|
auto* alias_c = ty.alias("alias_c", alias_b);
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kCos, {alias_c});
|
auto result = table->Lookup(*this, IntrinsicType::kCos, {alias_c}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result.intrinsic, nullptr);
|
||||||
ASSERT_EQ(result.error, "");
|
ASSERT_EQ(result.diagnostics.str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCos);
|
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCos);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
|
EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{ty.f32()}));
|
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{ty.f32()}));
|
||||||
@ -353,9 +359,9 @@ TEST_F(IntrinsicTableTest, MatchWithAliasUnwrapping) {
|
|||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchOpenType) {
|
TEST_F(IntrinsicTableTest, MatchOpenType) {
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kClamp,
|
auto result = table->Lookup(*this, IntrinsicType::kClamp,
|
||||||
{ty.f32(), ty.f32(), ty.f32()});
|
{ty.f32(), ty.f32(), ty.f32()}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result.intrinsic, nullptr);
|
||||||
ASSERT_EQ(result.error, "");
|
ASSERT_EQ(result.diagnostics.str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
|
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
|
EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
EXPECT_THAT(result.intrinsic->Parameters(),
|
||||||
@ -365,16 +371,17 @@ TEST_F(IntrinsicTableTest, MatchOpenType) {
|
|||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MismatchOpenType) {
|
TEST_F(IntrinsicTableTest, MismatchOpenType) {
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kClamp,
|
auto result = table->Lookup(*this, IntrinsicType::kClamp,
|
||||||
{ty.f32(), ty.u32(), ty.f32()});
|
{ty.f32(), ty.u32(), ty.f32()}, Source{});
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_EQ(result.intrinsic, nullptr);
|
||||||
ASSERT_THAT(result.error, HasSubstr("no matching call"));
|
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchOpenSizeVector) {
|
TEST_F(IntrinsicTableTest, MatchOpenSizeVector) {
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kClamp,
|
auto result =
|
||||||
{ty.vec2<f32>(), ty.vec2<f32>(), ty.vec2<f32>()});
|
table->Lookup(*this, IntrinsicType::kClamp,
|
||||||
|
{ty.vec2<f32>(), ty.vec2<f32>(), ty.vec2<f32>()}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result.intrinsic, nullptr);
|
||||||
ASSERT_EQ(result.error, "");
|
ASSERT_EQ(result.diagnostics.str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
|
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.vec2<f32>());
|
EXPECT_THAT(result.intrinsic->ReturnType(), ty.vec2<f32>());
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
EXPECT_THAT(result.intrinsic->Parameters(),
|
||||||
@ -383,17 +390,18 @@ TEST_F(IntrinsicTableTest, MatchOpenSizeVector) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MismatchOpenSizeVector) {
|
TEST_F(IntrinsicTableTest, MismatchOpenSizeVector) {
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kClamp,
|
auto result =
|
||||||
{ty.vec2<f32>(), ty.vec2<u32>(), ty.vec2<f32>()});
|
table->Lookup(*this, IntrinsicType::kClamp,
|
||||||
|
{ty.vec2<f32>(), ty.vec2<u32>(), ty.vec2<f32>()}, Source{});
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_EQ(result.intrinsic, nullptr);
|
||||||
ASSERT_THAT(result.error, HasSubstr("no matching call"));
|
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MatchOpenSizeMatrix) {
|
TEST_F(IntrinsicTableTest, MatchOpenSizeMatrix) {
|
||||||
auto result =
|
auto result = table->Lookup(*this, IntrinsicType::kDeterminant,
|
||||||
table->Lookup(*this, IntrinsicType::kDeterminant, {ty.mat3x3<f32>()});
|
{ty.mat3x3<f32>()}, Source{});
|
||||||
ASSERT_NE(result.intrinsic, nullptr);
|
ASSERT_NE(result.intrinsic, nullptr);
|
||||||
ASSERT_EQ(result.error, "");
|
ASSERT_EQ(result.diagnostics.str(), "");
|
||||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kDeterminant);
|
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kDeterminant);
|
||||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
|
EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
|
||||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
EXPECT_THAT(result.intrinsic->Parameters(),
|
||||||
@ -401,19 +409,19 @@ TEST_F(IntrinsicTableTest, MatchOpenSizeMatrix) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, MismatchOpenSizeMatrix) {
|
TEST_F(IntrinsicTableTest, MismatchOpenSizeMatrix) {
|
||||||
auto result =
|
auto result = table->Lookup(*this, IntrinsicType::kDeterminant,
|
||||||
table->Lookup(*this, IntrinsicType::kDeterminant, {ty.mat3x2<f32>()});
|
{ty.mat3x2<f32>()}, Source{});
|
||||||
ASSERT_EQ(result.intrinsic, nullptr);
|
ASSERT_EQ(result.intrinsic, nullptr);
|
||||||
ASSERT_THAT(result.error, HasSubstr("no matching call"));
|
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntrinsicTableTest, OverloadOrderByNumberOfParameters) {
|
TEST_F(IntrinsicTableTest, OverloadOrderByNumberOfParameters) {
|
||||||
// None of the arguments match, so expect the overloads with 2 parameters to
|
// None of the arguments match, so expect the overloads with 2 parameters to
|
||||||
// come first
|
// come first
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kTextureDimensions,
|
auto result = table->Lookup(*this, IntrinsicType::kTextureDimensions,
|
||||||
{ty.bool_(), ty.bool_()});
|
{ty.bool_(), ty.bool_()}, Source{});
|
||||||
ASSERT_EQ(result.error,
|
ASSERT_EQ(result.diagnostics.str(),
|
||||||
R"(no matching call to textureDimensions(bool, bool)
|
R"(error: no matching call to textureDimensions(bool, bool)
|
||||||
|
|
||||||
27 candidate functions:
|
27 candidate functions:
|
||||||
textureDimensions(texture : texture_2d<T>, level : i32) -> vec2<i32>
|
textureDimensions(texture : texture_2d<T>, level : i32) -> vec2<i32>
|
||||||
@ -449,9 +457,10 @@ TEST_F(IntrinsicTableTest, OverloadOrderByNumberOfParameters) {
|
|||||||
TEST_F(IntrinsicTableTest, OverloadOrderByMatchingParameter) {
|
TEST_F(IntrinsicTableTest, OverloadOrderByMatchingParameter) {
|
||||||
auto* tex = create<type::DepthTexture>(type::TextureDimension::k2d);
|
auto* tex = create<type::DepthTexture>(type::TextureDimension::k2d);
|
||||||
auto result = table->Lookup(*this, IntrinsicType::kTextureDimensions,
|
auto result = table->Lookup(*this, IntrinsicType::kTextureDimensions,
|
||||||
{tex, ty.bool_()});
|
{tex, ty.bool_()}, Source{});
|
||||||
ASSERT_EQ(result.error,
|
ASSERT_EQ(
|
||||||
R"(no matching call to textureDimensions(texture_depth_2d, bool)
|
result.diagnostics.str(),
|
||||||
|
R"(error: no matching call to textureDimensions(texture_depth_2d, bool)
|
||||||
|
|
||||||
27 candidate functions:
|
27 candidate functions:
|
||||||
textureDimensions(texture : texture_depth_2d, level : i32) -> vec2<i32>
|
textureDimensions(texture : texture_depth_2d, level : i32) -> vec2<i32>
|
||||||
|
38
src/test_main.cc
Normal file
38
src/test_main.cc
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2021 The Tint Authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "gmock/gmock.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "src/debug.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void TintInternalCompilerErrorReporter(const tint::diag::List& diagnostics) {
|
||||||
|
FAIL() << diagnostics.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// Entry point for tint unit tests
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
testing::InitGoogleMock(&argc, argv);
|
||||||
|
|
||||||
|
tint::SetInternalCompilerErrorReporter(&TintInternalCompilerErrorReporter);
|
||||||
|
|
||||||
|
auto res = RUN_ALL_TESTS();
|
||||||
|
|
||||||
|
tint::FreeInternalCompilerErrors();
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
@ -55,7 +55,13 @@ void Hlsl::PromoteArrayInitializerToConstVar(CloneContext& ctx) const {
|
|||||||
|
|
||||||
for (auto* src_node : ctx.src->ASTNodes().Objects()) {
|
for (auto* src_node : ctx.src->ASTNodes().Objects()) {
|
||||||
if (auto* src_init = src_node->As<ast::TypeConstructorExpression>()) {
|
if (auto* src_init = src_node->As<ast::TypeConstructorExpression>()) {
|
||||||
if (auto* src_sem_expr = ctx.src->Sem().Get(src_init)) {
|
auto* src_sem_expr = ctx.src->Sem().Get(src_init);
|
||||||
|
if (!src_sem_expr) {
|
||||||
|
TINT_ICE(
|
||||||
|
ctx.dst->Diagnostics(),
|
||||||
|
"ast::TypeConstructorExpression has no semantic expression node");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
auto* src_sem_stmt = src_sem_expr->Stmt();
|
auto* src_sem_stmt = src_sem_expr->Stmt();
|
||||||
if (!src_sem_stmt) {
|
if (!src_sem_stmt) {
|
||||||
// Expression is outside of a statement. This usually means the
|
// Expression is outside of a statement. This usually means the
|
||||||
@ -97,7 +103,6 @@ void Hlsl::PromoteArrayInitializerToConstVar(CloneContext& ctx) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace transform
|
} // namespace transform
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
#include "src/ast/type_constructor_expression.h"
|
#include "src/ast/type_constructor_expression.h"
|
||||||
#include "src/ast/unary_op_expression.h"
|
#include "src/ast/unary_op_expression.h"
|
||||||
#include "src/ast/variable_decl_statement.h"
|
#include "src/ast/variable_decl_statement.h"
|
||||||
|
#include "src/diagnostic/formatter.h"
|
||||||
#include "src/program_builder.h"
|
#include "src/program_builder.h"
|
||||||
#include "src/semantic/call.h"
|
#include "src/semantic/call.h"
|
||||||
#include "src/semantic/expression.h"
|
#include "src/semantic/expression.h"
|
||||||
@ -106,15 +107,6 @@ diag::List TypeDeterminer::Run(Program* program) {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void TypeDeterminer::set_error(const Source& src, const std::string& msg) {
|
|
||||||
error_ = "";
|
|
||||||
if (src.range.begin.line > 0) {
|
|
||||||
error_ += std::to_string(src.range.begin.line) + ":" +
|
|
||||||
std::to_string(src.range.begin.column) + ": ";
|
|
||||||
}
|
|
||||||
error_ += msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TypeDeterminer::set_referenced_from_function_if_needed(VariableInfo* var,
|
void TypeDeterminer::set_referenced_from_function_if_needed(VariableInfo* var,
|
||||||
bool local) {
|
bool local) {
|
||||||
if (current_function_ == nullptr) {
|
if (current_function_ == nullptr) {
|
||||||
@ -242,8 +234,8 @@ bool TypeDeterminer::DetermineVariableStorageClass(ast::Statement* stmt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (info->storage_class != ast::StorageClass::kNone) {
|
if (info->storage_class != ast::StorageClass::kNone) {
|
||||||
set_error(stmt->source(),
|
diagnostics_.add_error("function variable has a non-function storage class",
|
||||||
"function variable has a non-function storage class");
|
stmt->source());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,8 +313,9 @@ bool TypeDeterminer::DetermineResultType(ast::Statement* stmt) {
|
|||||||
return DetermineResultType(v->variable()->constructor());
|
return DetermineResultType(v->variable()->constructor());
|
||||||
}
|
}
|
||||||
|
|
||||||
set_error(stmt->source(), "unknown statement type for type determination: " +
|
diagnostics_.add_error(
|
||||||
builder_->str(stmt));
|
"unknown statement type for type determination: " + builder_->str(stmt),
|
||||||
|
stmt->source());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,7 +363,8 @@ bool TypeDeterminer::DetermineResultType(ast::Expression* expr) {
|
|||||||
return DetermineUnaryOp(u);
|
return DetermineUnaryOp(u);
|
||||||
}
|
}
|
||||||
|
|
||||||
set_error(expr->source(), "unknown expression for type determination");
|
diagnostics_.add_error("unknown expression for type determination",
|
||||||
|
expr->source());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -393,9 +387,9 @@ bool TypeDeterminer::DetermineArrayAccessor(
|
|||||||
} else if (auto* mat = parent_type->As<type::Matrix>()) {
|
} else if (auto* mat = parent_type->As<type::Matrix>()) {
|
||||||
ret = builder_->create<type::Vector>(mat->type(), mat->rows());
|
ret = builder_->create<type::Vector>(mat->type(), mat->rows());
|
||||||
} else {
|
} else {
|
||||||
set_error(expr->source(), "invalid parent type (" +
|
diagnostics_.add_error("invalid parent type (" + parent_type->type_name() +
|
||||||
parent_type->type_name() +
|
") in array accessor",
|
||||||
") in array accessor");
|
expr->source());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -436,7 +430,7 @@ bool TypeDeterminer::DetermineCall(ast::CallExpression* call) {
|
|||||||
// the safe side.
|
// the safe side.
|
||||||
auto* ident = call->func()->As<ast::IdentifierExpression>();
|
auto* ident = call->func()->As<ast::IdentifierExpression>();
|
||||||
if (!ident) {
|
if (!ident) {
|
||||||
set_error(call->source(), "call target is not an identifier");
|
diagnostics_.add_error("call target is not an identifier", call->source());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -454,7 +448,8 @@ bool TypeDeterminer::DetermineCall(ast::CallExpression* call) {
|
|||||||
|
|
||||||
auto callee_func_it = symbol_to_function_.find(ident->symbol());
|
auto callee_func_it = symbol_to_function_.find(ident->symbol());
|
||||||
if (callee_func_it == symbol_to_function_.end()) {
|
if (callee_func_it == symbol_to_function_.end()) {
|
||||||
set_error(call->source(), "unable to find called function: " + name);
|
diagnostics_.add_error("unable to find called function: " + name,
|
||||||
|
call->source());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto* callee_func = callee_func_it->second;
|
auto* callee_func = callee_func_it->second;
|
||||||
@ -467,8 +462,9 @@ bool TypeDeterminer::DetermineCall(ast::CallExpression* call) {
|
|||||||
|
|
||||||
auto iter = symbol_to_function_.find(ident->symbol());
|
auto iter = symbol_to_function_.find(ident->symbol());
|
||||||
if (iter == symbol_to_function_.end()) {
|
if (iter == symbol_to_function_.end()) {
|
||||||
set_error(call->source(),
|
diagnostics_.add_error(
|
||||||
"v-0005: function must be declared before use: '" + name + "'");
|
"v-0005: function must be declared before use: '" + name + "'",
|
||||||
|
call->source());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -490,10 +486,11 @@ bool TypeDeterminer::DetermineIntrinsicCall(
|
|||||||
arg_tys.emplace_back(TypeOf(expr));
|
arg_tys.emplace_back(TypeOf(expr));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto result = intrinsic_table_->Lookup(*builder_, intrinsic_type, arg_tys);
|
auto result = intrinsic_table_->Lookup(*builder_, intrinsic_type, arg_tys,
|
||||||
|
call->source());
|
||||||
if (!result.intrinsic) {
|
if (!result.intrinsic) {
|
||||||
// Intrinsic lookup failed.
|
// Intrinsic lookup failed.
|
||||||
set_error(call->source(), result.error);
|
diagnostics_.add(result.diagnostics);
|
||||||
|
|
||||||
// TODO(bclayton): https://crbug.com/tint/487
|
// TODO(bclayton): https://crbug.com/tint/487
|
||||||
// The Validator expects intrinsic signature mismatches to still produce
|
// The Validator expects intrinsic signature mismatches to still produce
|
||||||
@ -583,8 +580,9 @@ bool TypeDeterminer::DetermineIdentifier(ast::IdentifierExpression* expr) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_error(expr->source(),
|
diagnostics_.add_error(
|
||||||
"v-0006: identifier must be declared before use: " + name);
|
"v-0006: identifier must be declared before use: " + name,
|
||||||
|
expr->source());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -781,9 +779,9 @@ bool TypeDeterminer::DetermineMemberAccessor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ret == nullptr) {
|
if (ret == nullptr) {
|
||||||
set_error(expr->source(), "struct member " +
|
diagnostics_.add_error(
|
||||||
builder_->Symbols().NameFor(symbol) +
|
"struct member " + builder_->Symbols().NameFor(symbol) + " not found",
|
||||||
" not found");
|
expr->source());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -810,10 +808,10 @@ bool TypeDeterminer::DetermineMemberAccessor(
|
|||||||
static_cast<uint32_t>(size));
|
static_cast<uint32_t>(size));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
set_error(
|
diagnostics_.add_error(
|
||||||
expr->source(),
|
|
||||||
"v-0007: invalid use of member accessor on a non-vector/non-struct " +
|
"v-0007: invalid use of member accessor on a non-vector/non-struct " +
|
||||||
data_type->type_name());
|
data_type->type_name(),
|
||||||
|
expr->source());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -893,7 +891,7 @@ bool TypeDeterminer::DetermineBinary(ast::BinaryExpression* expr) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_error(expr->source(), "Unknown binary expression");
|
diagnostics_.add_error("Unknown binary expression", expr->source());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ class TypeDeterminer {
|
|||||||
static diag::List Run(Program* program);
|
static diag::List Run(Program* program);
|
||||||
|
|
||||||
/// @returns error messages from the type determiner
|
/// @returns error messages from the type determiner
|
||||||
const std::string& error() { return error_; }
|
std::string error() const { return diagnostics_.str(); }
|
||||||
|
|
||||||
/// @returns true if the type determiner was successful
|
/// @returns true if the type determiner was successful
|
||||||
bool Determine();
|
bool Determine();
|
||||||
@ -169,7 +169,6 @@ class TypeDeterminer {
|
|||||||
const ast::ExpressionList& params,
|
const ast::ExpressionList& params,
|
||||||
uint32_t* id);
|
uint32_t* id);
|
||||||
|
|
||||||
void set_error(const Source& src, const std::string& msg);
|
|
||||||
void set_referenced_from_function_if_needed(VariableInfo* var, bool local);
|
void set_referenced_from_function_if_needed(VariableInfo* var, bool local);
|
||||||
void set_entry_points(const Symbol& fn_sym, Symbol ep_sym);
|
void set_entry_points(const Symbol& fn_sym, Symbol ep_sym);
|
||||||
|
|
||||||
@ -198,7 +197,7 @@ class TypeDeterminer {
|
|||||||
|
|
||||||
ProgramBuilder* const builder_;
|
ProgramBuilder* const builder_;
|
||||||
std::unique_ptr<IntrinsicTable> const intrinsic_table_;
|
std::unique_ptr<IntrinsicTable> const intrinsic_table_;
|
||||||
std::string error_;
|
diag::List diagnostics_;
|
||||||
ScopeStack<VariableInfo*> variable_stack_;
|
ScopeStack<VariableInfo*> variable_stack_;
|
||||||
std::unordered_map<Symbol, FunctionInfo*> symbol_to_function_;
|
std::unordered_map<Symbol, FunctionInfo*> symbol_to_function_;
|
||||||
std::unordered_map<ast::Function*, FunctionInfo*> function_to_info_;
|
std::unordered_map<ast::Function*, FunctionInfo*> function_to_info_;
|
||||||
|
@ -142,7 +142,7 @@ TEST_F(TypeDeterminerTest, Error_WithEmptySource) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
"unknown statement type for type determination: Fake");
|
"error: unknown statement type for type determination: Fake");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TypeDeterminerTest, Stmt_Error_Unknown) {
|
TEST_F(TypeDeterminerTest, Stmt_Error_Unknown) {
|
||||||
@ -152,7 +152,7 @@ TEST_F(TypeDeterminerTest, Stmt_Error_Unknown) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
"2:30: unknown statement type for type determination: Fake");
|
"2:30 error: unknown statement type for type determination: Fake");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TypeDeterminerTest, Stmt_Assign) {
|
TEST_F(TypeDeterminerTest, Stmt_Assign) {
|
||||||
@ -396,8 +396,9 @@ TEST_F(TypeDeterminerTest, Stmt_Call_undeclared) {
|
|||||||
|
|
||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(
|
||||||
"12:34: v-0006: identifier must be declared before use: func");
|
td()->error(),
|
||||||
|
"12:34 error: v-0006: identifier must be declared before use: func");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TypeDeterminerTest, Stmt_VariableDecl) {
|
TEST_F(TypeDeterminerTest, Stmt_VariableDecl) {
|
||||||
@ -530,7 +531,8 @@ TEST_F(TypeDeterminerTest, Expr_Error_Unknown) {
|
|||||||
|
|
||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(), "2:30: unknown expression for type determination");
|
EXPECT_EQ(td()->error(),
|
||||||
|
"2:30 error: unknown expression for type determination");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TypeDeterminerTest, Expr_ArrayAccessor_Array) {
|
TEST_F(TypeDeterminerTest, Expr_ArrayAccessor_Array) {
|
||||||
@ -1358,7 +1360,7 @@ TEST_P(IntrinsicDerivativeTest, MissingParam) {
|
|||||||
|
|
||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(), "no matching call to " + name +
|
EXPECT_EQ(td()->error(), "error: no matching call to " + name +
|
||||||
"()\n\n"
|
"()\n\n"
|
||||||
"2 candidate functions:\n " +
|
"2 candidate functions:\n " +
|
||||||
name + "(f32) -> f32\n " + name +
|
name + "(f32) -> f32\n " + name +
|
||||||
@ -1436,7 +1438,7 @@ TEST_P(Intrinsic_FloatMethod, MissingParam) {
|
|||||||
|
|
||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(), "no matching call to " + name +
|
EXPECT_EQ(td()->error(), "error: no matching call to " + name +
|
||||||
"()\n\n"
|
"()\n\n"
|
||||||
"2 candidate functions:\n " +
|
"2 candidate functions:\n " +
|
||||||
name + "(f32) -> bool\n " + name +
|
name + "(f32) -> bool\n " + name +
|
||||||
@ -1453,7 +1455,7 @@ TEST_P(Intrinsic_FloatMethod, TooManyParams) {
|
|||||||
|
|
||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(), "no matching call to " + name +
|
EXPECT_EQ(td()->error(), "error: no matching call to " + name +
|
||||||
"(ptr<f32>, f32)\n\n"
|
"(ptr<f32>, f32)\n\n"
|
||||||
"2 candidate functions:\n " +
|
"2 candidate functions:\n " +
|
||||||
name + "(f32) -> bool\n " + name +
|
name + "(f32) -> bool\n " + name +
|
||||||
@ -1687,7 +1689,7 @@ TEST_F(TypeDeterminerTest, Intrinsic_Dot_Error_Scalar) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
R"(no matching call to dot(f32, f32)
|
R"(error: no matching call to dot(f32, f32)
|
||||||
|
|
||||||
1 candidate function:
|
1 candidate function:
|
||||||
dot(vecN<f32>, vecN<f32>) -> f32
|
dot(vecN<f32>, vecN<f32>) -> f32
|
||||||
@ -1703,7 +1705,7 @@ TEST_F(TypeDeterminerTest, Intrinsic_Dot_Error_VectorInt) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
R"(no matching call to dot(ptr<vec4<i32>>, ptr<vec4<i32>>)
|
R"(error: no matching call to dot(ptr<vec4<i32>>, ptr<vec4<i32>>)
|
||||||
|
|
||||||
1 candidate function:
|
1 candidate function:
|
||||||
dot(vecN<f32>, vecN<f32>) -> f32
|
dot(vecN<f32>, vecN<f32>) -> f32
|
||||||
@ -1733,7 +1735,7 @@ TEST_F(TypeDeterminerTest, Intrinsic_Select_Error_NoParams) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
R"(no matching call to select()
|
R"(error: no matching call to select()
|
||||||
|
|
||||||
2 candidate functions:
|
2 candidate functions:
|
||||||
select(T, T, bool) -> T where: T is scalar
|
select(T, T, bool) -> T where: T is scalar
|
||||||
@ -1748,7 +1750,7 @@ TEST_F(TypeDeterminerTest, Intrinsic_Select_Error_SelectorInt) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
R"(no matching call to select(i32, i32, i32)
|
R"(error: no matching call to select(i32, i32, i32)
|
||||||
|
|
||||||
2 candidate functions:
|
2 candidate functions:
|
||||||
select(T, T, bool) -> T where: T is scalar
|
select(T, T, bool) -> T where: T is scalar
|
||||||
@ -1764,7 +1766,7 @@ TEST_F(TypeDeterminerTest, Intrinsic_Select_Error_Matrix) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
R"(no matching call to select(mat2x2<f32>, mat2x2<f32>, bool)
|
R"(error: no matching call to select(mat2x2<f32>, mat2x2<f32>, bool)
|
||||||
|
|
||||||
2 candidate functions:
|
2 candidate functions:
|
||||||
select(T, T, bool) -> T where: T is scalar
|
select(T, T, bool) -> T where: T is scalar
|
||||||
@ -1779,7 +1781,7 @@ TEST_F(TypeDeterminerTest, Intrinsic_Select_Error_MismatchTypes) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
R"(no matching call to select(f32, vec2<f32>, bool)
|
R"(error: no matching call to select(f32, vec2<f32>, bool)
|
||||||
|
|
||||||
2 candidate functions:
|
2 candidate functions:
|
||||||
select(T, T, bool) -> T where: T is scalar
|
select(T, T, bool) -> T where: T is scalar
|
||||||
@ -1795,7 +1797,7 @@ TEST_F(TypeDeterminerTest, Intrinsic_Select_Error_MismatchVectorSize) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
R"(no matching call to select(vec2<f32>, vec3<f32>, bool)
|
R"(error: no matching call to select(vec2<f32>, vec3<f32>, bool)
|
||||||
|
|
||||||
2 candidate functions:
|
2 candidate functions:
|
||||||
select(T, T, bool) -> T where: T is scalar
|
select(T, T, bool) -> T where: T is scalar
|
||||||
@ -1856,7 +1858,7 @@ TEST_F(TypeDeterminerTest, StorageClass_NonFunctionClassError) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
"function variable has a non-function storage class");
|
"error: function variable has a non-function storage class");
|
||||||
}
|
}
|
||||||
|
|
||||||
struct IntrinsicData {
|
struct IntrinsicData {
|
||||||
@ -1983,8 +1985,8 @@ TEST_P(Intrinsic_DataPackingTest, Error_IncorrectParamType) {
|
|||||||
|
|
||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_THAT(td()->error(),
|
EXPECT_THAT(td()->error(), HasSubstr("error: no matching call to " +
|
||||||
HasSubstr("no matching call to " + std::string(param.name)));
|
std::string(param.name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(Intrinsic_DataPackingTest, Error_NoParams) {
|
TEST_P(Intrinsic_DataPackingTest, Error_NoParams) {
|
||||||
@ -1995,8 +1997,8 @@ TEST_P(Intrinsic_DataPackingTest, Error_NoParams) {
|
|||||||
|
|
||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_THAT(td()->error(),
|
EXPECT_THAT(td()->error(), HasSubstr("error: no matching call to " +
|
||||||
HasSubstr("no matching call to " + std::string(param.name)));
|
std::string(param.name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(Intrinsic_DataPackingTest, Error_TooManyParams) {
|
TEST_P(Intrinsic_DataPackingTest, Error_TooManyParams) {
|
||||||
@ -2011,8 +2013,8 @@ TEST_P(Intrinsic_DataPackingTest, Error_TooManyParams) {
|
|||||||
|
|
||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_THAT(td()->error(),
|
EXPECT_THAT(td()->error(), HasSubstr("error: no matching call to " +
|
||||||
HasSubstr("no matching call to " + std::string(param.name)));
|
std::string(param.name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(
|
INSTANTIATE_TEST_SUITE_P(
|
||||||
@ -2089,12 +2091,12 @@ TEST_P(Intrinsic_SingleParamTest, Error_NoParams) {
|
|||||||
|
|
||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(), "no matching call to " + std::string(param.name) +
|
EXPECT_EQ(td()->error(),
|
||||||
|
"error: no matching call to " + std::string(param.name) +
|
||||||
"()\n\n"
|
"()\n\n"
|
||||||
"2 candidate functions:\n " +
|
"2 candidate functions:\n " +
|
||||||
std::string(param.name) + "(f32) -> f32\n " +
|
std::string(param.name) + "(f32) -> f32\n " +
|
||||||
std::string(param.name) +
|
std::string(param.name) + "(vecN<f32>) -> vecN<f32>\n");
|
||||||
"(vecN<f32>) -> vecN<f32>\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(Intrinsic_SingleParamTest, Error_TooManyParams) {
|
TEST_P(Intrinsic_SingleParamTest, Error_TooManyParams) {
|
||||||
@ -2105,12 +2107,12 @@ TEST_P(Intrinsic_SingleParamTest, Error_TooManyParams) {
|
|||||||
|
|
||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(), "no matching call to " + std::string(param.name) +
|
EXPECT_EQ(td()->error(),
|
||||||
|
"error: no matching call to " + std::string(param.name) +
|
||||||
"(i32, i32, i32)\n\n"
|
"(i32, i32, i32)\n\n"
|
||||||
"2 candidate functions:\n " +
|
"2 candidate functions:\n " +
|
||||||
std::string(param.name) + "(f32) -> f32\n " +
|
std::string(param.name) + "(f32) -> f32\n " +
|
||||||
std::string(param.name) +
|
std::string(param.name) + "(vecN<f32>) -> vecN<f32>\n");
|
||||||
"(vecN<f32>) -> vecN<f32>\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(
|
INSTANTIATE_TEST_SUITE_P(
|
||||||
@ -2157,7 +2159,7 @@ TEST_F(IntrinsicDataTest, ArrayLength_Error_ArraySized) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
"no matching call to arrayLength(ptr<array<i32, 4>>)\n\n"
|
"error: no matching call to arrayLength(ptr<array<i32, 4>>)\n\n"
|
||||||
"1 candidate function:\n"
|
"1 candidate function:\n"
|
||||||
" arrayLength(array<T>) -> u32\n");
|
" arrayLength(array<T>) -> u32\n");
|
||||||
}
|
}
|
||||||
@ -2180,7 +2182,7 @@ TEST_F(IntrinsicDataTest, Normalize_Error_NoParams) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
"no matching call to normalize()\n\n"
|
"error: no matching call to normalize()\n\n"
|
||||||
"1 candidate function:\n"
|
"1 candidate function:\n"
|
||||||
" normalize(vecN<f32>) -> vecN<f32>\n");
|
" normalize(vecN<f32>) -> vecN<f32>\n");
|
||||||
}
|
}
|
||||||
@ -2216,7 +2218,7 @@ TEST_F(IntrinsicDataTest, Frexp_Error_FirstParamInt) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
"no matching call to frexp(i32, ptr<workgroup, i32>)\n\n"
|
"error: no matching call to frexp(i32, ptr<workgroup, i32>)\n\n"
|
||||||
"2 candidate functions:\n"
|
"2 candidate functions:\n"
|
||||||
" frexp(f32, ptr<T>) -> f32 where: T is i32 or u32\n"
|
" frexp(f32, ptr<T>) -> f32 where: T is i32 or u32\n"
|
||||||
" frexp(vecN<f32>, ptr<vecN<T>>) -> vecN<f32> "
|
" frexp(vecN<f32>, ptr<vecN<T>>) -> vecN<f32> "
|
||||||
@ -2231,7 +2233,7 @@ TEST_F(IntrinsicDataTest, Frexp_Error_SecondParamFloatPtr) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
"no matching call to frexp(f32, ptr<workgroup, f32>)\n\n"
|
"error: no matching call to frexp(f32, ptr<workgroup, f32>)\n\n"
|
||||||
"2 candidate functions:\n"
|
"2 candidate functions:\n"
|
||||||
" frexp(f32, ptr<T>) -> f32 where: T is i32 or u32\n"
|
" frexp(f32, ptr<T>) -> f32 where: T is i32 or u32\n"
|
||||||
" frexp(vecN<f32>, ptr<vecN<T>>) -> vecN<f32> "
|
" frexp(vecN<f32>, ptr<vecN<T>>) -> vecN<f32> "
|
||||||
@ -2245,7 +2247,7 @@ TEST_F(IntrinsicDataTest, Frexp_Error_SecondParamNotAPointer) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
"no matching call to frexp(f32, i32)\n\n"
|
"error: no matching call to frexp(f32, i32)\n\n"
|
||||||
"2 candidate functions:\n"
|
"2 candidate functions:\n"
|
||||||
" frexp(f32, ptr<T>) -> f32 where: T is i32 or u32\n"
|
" frexp(f32, ptr<T>) -> f32 where: T is i32 or u32\n"
|
||||||
" frexp(vecN<f32>, ptr<vecN<T>>) -> vecN<f32> "
|
" frexp(vecN<f32>, ptr<vecN<T>>) -> vecN<f32> "
|
||||||
@ -2259,9 +2261,9 @@ TEST_F(IntrinsicDataTest, Frexp_Error_VectorSizesDontMatch) {
|
|||||||
|
|
||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(td()->error(),
|
||||||
td()->error(),
|
"error: no matching call to frexp(vec2<f32>, ptr<workgroup, "
|
||||||
"no matching call to frexp(vec2<f32>, ptr<workgroup, vec4<i32>>)\n\n"
|
"vec4<i32>>)\n\n"
|
||||||
"2 candidate functions:\n"
|
"2 candidate functions:\n"
|
||||||
" frexp(f32, ptr<T>) -> f32 where: T is i32 or u32\n"
|
" frexp(f32, ptr<T>) -> f32 where: T is i32 or u32\n"
|
||||||
" frexp(vecN<f32>, ptr<vecN<T>>) -> vecN<f32> "
|
" frexp(vecN<f32>, ptr<vecN<T>>) -> vecN<f32> "
|
||||||
@ -2299,7 +2301,7 @@ TEST_F(IntrinsicDataTest, Modf_Error_FirstParamInt) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
"no matching call to modf(i32, ptr<workgroup, f32>)\n\n"
|
"error: no matching call to modf(i32, ptr<workgroup, f32>)\n\n"
|
||||||
"2 candidate functions:\n"
|
"2 candidate functions:\n"
|
||||||
" modf(f32, ptr<f32>) -> f32\n"
|
" modf(f32, ptr<f32>) -> f32\n"
|
||||||
" modf(vecN<f32>, ptr<vecN<f32>>) -> vecN<f32>\n");
|
" modf(vecN<f32>, ptr<vecN<f32>>) -> vecN<f32>\n");
|
||||||
@ -2313,7 +2315,7 @@ TEST_F(IntrinsicDataTest, Modf_Error_SecondParamIntPtr) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
"no matching call to modf(f32, ptr<workgroup, i32>)\n\n"
|
"error: no matching call to modf(f32, ptr<workgroup, i32>)\n\n"
|
||||||
"2 candidate functions:\n"
|
"2 candidate functions:\n"
|
||||||
" modf(f32, ptr<f32>) -> f32\n"
|
" modf(f32, ptr<f32>) -> f32\n"
|
||||||
" modf(vecN<f32>, ptr<vecN<f32>>) -> vecN<f32>\n");
|
" modf(vecN<f32>, ptr<vecN<f32>>) -> vecN<f32>\n");
|
||||||
@ -2326,7 +2328,7 @@ TEST_F(IntrinsicDataTest, Modf_Error_SecondParamNotAPointer) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
"no matching call to modf(f32, f32)\n\n"
|
"error: no matching call to modf(f32, f32)\n\n"
|
||||||
"2 candidate functions:\n"
|
"2 candidate functions:\n"
|
||||||
" modf(f32, ptr<f32>) -> f32\n"
|
" modf(f32, ptr<f32>) -> f32\n"
|
||||||
" modf(vecN<f32>, ptr<vecN<f32>>) -> vecN<f32>\n");
|
" modf(vecN<f32>, ptr<vecN<f32>>) -> vecN<f32>\n");
|
||||||
@ -2340,7 +2342,8 @@ TEST_F(IntrinsicDataTest, Modf_Error_VectorSizesDontMatch) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
"no matching call to modf(vec2<f32>, ptr<workgroup, vec4<f32>>)\n\n"
|
"error: no matching call to modf(vec2<f32>, ptr<workgroup, "
|
||||||
|
"vec4<f32>>)\n\n"
|
||||||
"2 candidate functions:\n"
|
"2 candidate functions:\n"
|
||||||
" modf(vecN<f32>, ptr<vecN<f32>>) -> vecN<f32>\n"
|
" modf(vecN<f32>, ptr<vecN<f32>>) -> vecN<f32>\n"
|
||||||
" modf(f32, ptr<f32>) -> f32\n");
|
" modf(f32, ptr<f32>) -> f32\n");
|
||||||
@ -2443,7 +2446,7 @@ TEST_P(Intrinsic_SingleParam_FloatOrInt_Test, Error_NoParams) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
"no matching call to " + std::string(param.name) +
|
"error: no matching call to " + std::string(param.name) +
|
||||||
"()\n\n"
|
"()\n\n"
|
||||||
"2 candidate functions:\n " +
|
"2 candidate functions:\n " +
|
||||||
std::string(param.name) +
|
std::string(param.name) +
|
||||||
@ -2515,11 +2518,11 @@ TEST_P(Intrinsic_TwoParamTest, Error_NoTooManyParams) {
|
|||||||
|
|
||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(), "no matching call to " + std::string(param.name) +
|
EXPECT_EQ(td()->error(),
|
||||||
|
"error: no matching call to " + std::string(param.name) +
|
||||||
"(i32, i32, i32)\n\n"
|
"(i32, i32, i32)\n\n"
|
||||||
"2 candidate functions:\n " +
|
"2 candidate functions:\n " +
|
||||||
std::string(param.name) +
|
std::string(param.name) + "(f32, f32) -> f32\n " +
|
||||||
"(f32, f32) -> f32\n " +
|
|
||||||
std::string(param.name) +
|
std::string(param.name) +
|
||||||
"(vecN<f32>, vecN<f32>) -> vecN<f32>\n");
|
"(vecN<f32>, vecN<f32>) -> vecN<f32>\n");
|
||||||
}
|
}
|
||||||
@ -2532,11 +2535,11 @@ TEST_P(Intrinsic_TwoParamTest, Error_NoParams) {
|
|||||||
|
|
||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(), "no matching call to " + std::string(param.name) +
|
EXPECT_EQ(td()->error(),
|
||||||
|
"error: no matching call to " + std::string(param.name) +
|
||||||
"()\n\n"
|
"()\n\n"
|
||||||
"2 candidate functions:\n " +
|
"2 candidate functions:\n " +
|
||||||
std::string(param.name) +
|
std::string(param.name) + "(f32, f32) -> f32\n " +
|
||||||
"(f32, f32) -> f32\n " +
|
|
||||||
std::string(param.name) +
|
std::string(param.name) +
|
||||||
"(vecN<f32>, vecN<f32>) -> vecN<f32>\n");
|
"(vecN<f32>, vecN<f32>) -> vecN<f32>\n");
|
||||||
}
|
}
|
||||||
@ -2588,7 +2591,7 @@ TEST_F(TypeDeterminerTest, Intrinsic_Cross_Error_NoArgs) {
|
|||||||
|
|
||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(), R"(no matching call to cross()
|
EXPECT_EQ(td()->error(), R"(error: no matching call to cross()
|
||||||
|
|
||||||
1 candidate function:
|
1 candidate function:
|
||||||
cross(vec3<f32>, vec3<f32>) -> vec3<f32>
|
cross(vec3<f32>, vec3<f32>) -> vec3<f32>
|
||||||
@ -2601,7 +2604,7 @@ TEST_F(TypeDeterminerTest, Intrinsic_Cross_Error_Scalar) {
|
|||||||
|
|
||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(), R"(no matching call to cross(f32, f32)
|
EXPECT_EQ(td()->error(), R"(error: no matching call to cross(f32, f32)
|
||||||
|
|
||||||
1 candidate function:
|
1 candidate function:
|
||||||
cross(vec3<f32>, vec3<f32>) -> vec3<f32>
|
cross(vec3<f32>, vec3<f32>) -> vec3<f32>
|
||||||
@ -2614,7 +2617,8 @@ TEST_F(TypeDeterminerTest, Intrinsic_Cross_Error_Vec3Int) {
|
|||||||
|
|
||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(), R"(no matching call to cross(vec3<i32>, vec3<i32>)
|
EXPECT_EQ(td()->error(),
|
||||||
|
R"(error: no matching call to cross(vec3<i32>, vec3<i32>)
|
||||||
|
|
||||||
1 candidate function:
|
1 candidate function:
|
||||||
cross(vec3<f32>, vec3<f32>) -> vec3<f32>
|
cross(vec3<f32>, vec3<f32>) -> vec3<f32>
|
||||||
@ -2629,7 +2633,8 @@ TEST_F(TypeDeterminerTest, Intrinsic_Cross_Error_Vec4) {
|
|||||||
|
|
||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(), R"(no matching call to cross(vec4<f32>, vec4<f32>)
|
EXPECT_EQ(td()->error(),
|
||||||
|
R"(error: no matching call to cross(vec4<f32>, vec4<f32>)
|
||||||
|
|
||||||
1 candidate function:
|
1 candidate function:
|
||||||
cross(vec3<f32>, vec3<f32>) -> vec3<f32>
|
cross(vec3<f32>, vec3<f32>) -> vec3<f32>
|
||||||
@ -2645,7 +2650,7 @@ TEST_F(TypeDeterminerTest, Intrinsic_Cross_Error_TooManyParams) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
R"(no matching call to cross(vec3<f32>, vec3<f32>, vec3<f32>)
|
R"(error: no matching call to cross(vec3<f32>, vec3<f32>, vec3<f32>)
|
||||||
|
|
||||||
1 candidate function:
|
1 candidate function:
|
||||||
cross(vec3<f32>, vec3<f32>) -> vec3<f32>
|
cross(vec3<f32>, vec3<f32>) -> vec3<f32>
|
||||||
@ -2668,7 +2673,7 @@ TEST_F(TypeDeterminerTest, Intrinsic_Normalize_NoArgs) {
|
|||||||
|
|
||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(), R"(no matching call to normalize()
|
EXPECT_EQ(td()->error(), R"(error: no matching call to normalize()
|
||||||
|
|
||||||
1 candidate function:
|
1 candidate function:
|
||||||
normalize(vecN<f32>) -> vecN<f32>
|
normalize(vecN<f32>) -> vecN<f32>
|
||||||
@ -2710,7 +2715,7 @@ TEST_P(Intrinsic_ThreeParamTest, Error_NoParams) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
"no matching call to " + std::string(param.name) +
|
"error: no matching call to " + std::string(param.name) +
|
||||||
"()\n\n"
|
"()\n\n"
|
||||||
"2 candidate functions:\n " +
|
"2 candidate functions:\n " +
|
||||||
std::string(param.name) + "(f32, f32, f32) -> f32\n " +
|
std::string(param.name) + "(f32, f32, f32) -> f32\n " +
|
||||||
@ -2815,7 +2820,7 @@ TEST_P(Intrinsic_ThreeParam_FloatOrInt_Test, Error_NoParams) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
"no matching call to " + std::string(param.name) +
|
"error: no matching call to " + std::string(param.name) +
|
||||||
"()\n\n"
|
"()\n\n"
|
||||||
"2 candidate functions:\n " +
|
"2 candidate functions:\n " +
|
||||||
std::string(param.name) +
|
std::string(param.name) +
|
||||||
@ -2866,7 +2871,7 @@ TEST_P(Intrinsic_Int_SingleParamTest, Error_NoParams) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
"no matching call to " + std::string(param.name) +
|
"error: no matching call to " + std::string(param.name) +
|
||||||
"()\n\n"
|
"()\n\n"
|
||||||
"2 candidate functions:\n " +
|
"2 candidate functions:\n " +
|
||||||
std::string(param.name) +
|
std::string(param.name) +
|
||||||
@ -2968,7 +2973,7 @@ TEST_P(Intrinsic_FloatOrInt_TwoParamTest, Error_NoParams) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
"no matching call to " + std::string(param.name) +
|
"error: no matching call to " + std::string(param.name) +
|
||||||
"()\n\n"
|
"()\n\n"
|
||||||
"2 candidate functions:\n " +
|
"2 candidate functions:\n " +
|
||||||
std::string(param.name) +
|
std::string(param.name) +
|
||||||
@ -3027,8 +3032,9 @@ TEST_F(TypeDeterminerTest, Intrinsic_Determinant_NotSquare) {
|
|||||||
|
|
||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(
|
||||||
"no matching call to determinant(ptr<function, mat2x3<f32>>)\n\n"
|
td()->error(),
|
||||||
|
"error: no matching call to determinant(ptr<function, mat2x3<f32>>)\n\n"
|
||||||
"1 candidate function:\n"
|
"1 candidate function:\n"
|
||||||
" determinant(matNxN<f32>) -> f32\n");
|
" determinant(matNxN<f32>) -> f32\n");
|
||||||
}
|
}
|
||||||
@ -3042,7 +3048,7 @@ TEST_F(TypeDeterminerTest, Intrinsic_Determinant_NotMatrix) {
|
|||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
|
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
"no matching call to determinant(ptr<function, f32>)\n\n"
|
"error: no matching call to determinant(ptr<function, f32>)\n\n"
|
||||||
"1 candidate function:\n"
|
"1 candidate function:\n"
|
||||||
" determinant(matNxN<f32>) -> f32\n");
|
" determinant(matNxN<f32>) -> f32\n");
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "src/ast/switch_statement.h"
|
#include "src/ast/switch_statement.h"
|
||||||
#include "src/ast/uint_literal.h"
|
#include "src/ast/uint_literal.h"
|
||||||
#include "src/ast/variable_decl_statement.h"
|
#include "src/ast/variable_decl_statement.h"
|
||||||
|
#include "src/debug.h"
|
||||||
#include "src/semantic/call.h"
|
#include "src/semantic/call.h"
|
||||||
#include "src/semantic/expression.h"
|
#include "src/semantic/expression.h"
|
||||||
#include "src/semantic/function.h"
|
#include "src/semantic/function.h"
|
||||||
@ -92,7 +93,8 @@ bool ValidatorImpl::Validate() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assert(false /* unreachable */);
|
TINT_UNREACHABLE(diags_);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!ValidateEntryPoint(program_->AST().Functions())) {
|
if (!ValidateEntryPoint(program_->AST().Functions())) {
|
||||||
|
@ -96,7 +96,7 @@ TEST_F(ValidatorTest, UsingUndefinedVariable_Fail) {
|
|||||||
|
|
||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
"12:34: v-0006: identifier must be declared before use: b");
|
"12:34 error: v-0006: identifier must be declared before use: b");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidatorTest, UsingUndefinedVariableInBlockStatement_Fail) {
|
TEST_F(ValidatorTest, UsingUndefinedVariableInBlockStatement_Fail) {
|
||||||
@ -115,7 +115,7 @@ TEST_F(ValidatorTest, UsingUndefinedVariableInBlockStatement_Fail) {
|
|||||||
|
|
||||||
EXPECT_FALSE(td()->Determine());
|
EXPECT_FALSE(td()->Determine());
|
||||||
EXPECT_EQ(td()->error(),
|
EXPECT_EQ(td()->error(),
|
||||||
"12:34: v-0006: identifier must be declared before use: b");
|
"12:34 error: v-0006: identifier must be declared before use: b");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidatorTest, AssignCompatibleTypes_Pass) {
|
TEST_F(ValidatorTest, AssignCompatibleTypes_Pass) {
|
||||||
|
@ -44,6 +44,7 @@
|
|||||||
#include "src/ast/unary_op_expression.h"
|
#include "src/ast/unary_op_expression.h"
|
||||||
#include "src/ast/variable.h"
|
#include "src/ast/variable.h"
|
||||||
#include "src/ast/variable_decl_statement.h"
|
#include "src/ast/variable_decl_statement.h"
|
||||||
|
#include "src/debug.h"
|
||||||
#include "src/program_builder.h"
|
#include "src/program_builder.h"
|
||||||
#include "src/semantic/call.h"
|
#include "src/semantic/call.h"
|
||||||
#include "src/semantic/expression.h"
|
#include "src/semantic/expression.h"
|
||||||
@ -137,12 +138,6 @@ GeneratorImpl::GeneratorImpl(const Program* program)
|
|||||||
|
|
||||||
GeneratorImpl::~GeneratorImpl() = default;
|
GeneratorImpl::~GeneratorImpl() = default;
|
||||||
|
|
||||||
void GeneratorImpl::make_indent(std::ostream& out) {
|
|
||||||
for (size_t i = 0; i < indent_; i++) {
|
|
||||||
out << " ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GeneratorImpl::Generate(std::ostream& out) {
|
bool GeneratorImpl::Generate(std::ostream& out) {
|
||||||
for (auto* global : builder_.AST().GlobalVariables()) {
|
for (auto* global : builder_.AST().GlobalVariables()) {
|
||||||
register_global(global);
|
register_global(global);
|
||||||
@ -258,7 +253,7 @@ bool GeneratorImpl::EmitConstructedType(std::ostream& out,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error_ = "unknown constructed type: " + ty->type_name();
|
diagnostics_.add_error("unknown constructed type: " + ty->type_name());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,7 +285,8 @@ bool GeneratorImpl::EmitBitcast(std::ostream& pre,
|
|||||||
std::ostream& out,
|
std::ostream& out,
|
||||||
ast::BitcastExpression* expr) {
|
ast::BitcastExpression* expr) {
|
||||||
if (!expr->type()->is_integer_scalar() && !expr->type()->is_float_scalar()) {
|
if (!expr->type()->is_integer_scalar() && !expr->type()->is_float_scalar()) {
|
||||||
error_ = "Unable to do bitcast to type " + expr->type()->type_name();
|
diagnostics_.add_error("Unable to do bitcast to type " +
|
||||||
|
expr->type()->type_name());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -429,7 +425,7 @@ bool GeneratorImpl::EmitBinary(std::ostream& pre,
|
|||||||
case ast::BinaryOp::kLogicalAnd:
|
case ast::BinaryOp::kLogicalAnd:
|
||||||
case ast::BinaryOp::kLogicalOr: {
|
case ast::BinaryOp::kLogicalOr: {
|
||||||
// These are both handled above.
|
// These are both handled above.
|
||||||
assert(false);
|
TINT_UNREACHABLE(diagnostics_);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
case ast::BinaryOp::kEqual:
|
case ast::BinaryOp::kEqual:
|
||||||
@ -477,7 +473,7 @@ bool GeneratorImpl::EmitBinary(std::ostream& pre,
|
|||||||
out << "%";
|
out << "%";
|
||||||
break;
|
break;
|
||||||
case ast::BinaryOp::kNone:
|
case ast::BinaryOp::kNone:
|
||||||
error_ = "missing binary operation type";
|
diagnostics_.add_error("missing binary operation type");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out << " ";
|
out << " ";
|
||||||
@ -538,7 +534,7 @@ bool GeneratorImpl::EmitCall(std::ostream& pre,
|
|||||||
ast::CallExpression* expr) {
|
ast::CallExpression* expr) {
|
||||||
auto* ident = expr->func()->As<ast::IdentifierExpression>();
|
auto* ident = expr->func()->As<ast::IdentifierExpression>();
|
||||||
if (ident == nullptr) {
|
if (ident == nullptr) {
|
||||||
error_ = "invalid function name";
|
diagnostics_.add_error("invalid function name");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -549,10 +545,10 @@ bool GeneratorImpl::EmitCall(std::ostream& pre,
|
|||||||
}
|
}
|
||||||
const auto& params = expr->params();
|
const auto& params = expr->params();
|
||||||
if (intrinsic->Type() == semantic::IntrinsicType::kSelect) {
|
if (intrinsic->Type() == semantic::IntrinsicType::kSelect) {
|
||||||
error_ = "select not supported in HLSL backend yet";
|
diagnostics_.add_error("select not supported in HLSL backend yet");
|
||||||
return false;
|
return false;
|
||||||
} else if (intrinsic->Type() == semantic::IntrinsicType::kIsNormal) {
|
} else if (intrinsic->Type() == semantic::IntrinsicType::kIsNormal) {
|
||||||
error_ = "is_normal not supported in HLSL backend yet";
|
diagnostics_.add_error("is_normal not supported in HLSL backend yet");
|
||||||
return false;
|
return false;
|
||||||
} else if (intrinsic->IsDataPacking()) {
|
} else if (intrinsic->IsDataPacking()) {
|
||||||
return EmitDataPackingCall(pre, out, expr, intrinsic);
|
return EmitDataPackingCall(pre, out, expr, intrinsic);
|
||||||
@ -593,8 +589,8 @@ bool GeneratorImpl::EmitCall(std::ostream& pre,
|
|||||||
|
|
||||||
auto* func = builder_.AST().Functions().Find(ident->symbol());
|
auto* func = builder_.AST().Functions().Find(ident->symbol());
|
||||||
if (func == nullptr) {
|
if (func == nullptr) {
|
||||||
error_ = "Unable to find function: " +
|
diagnostics_.add_error("Unable to find function: " +
|
||||||
builder_.Symbols().NameFor(ident->symbol());
|
builder_.Symbols().NameFor(ident->symbol()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -689,7 +685,8 @@ bool GeneratorImpl::EmitDataPackingCall(std::ostream& pre,
|
|||||||
out << "(" << tmp_name << ".x | " << tmp_name << ".y << 16)";
|
out << "(" << tmp_name << ".x | " << tmp_name << ".y << 16)";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error_ = "Internal error: unhandled data packing intrinsic";
|
diagnostics_.add_error(
|
||||||
|
"Internal error: unhandled data packing intrinsic");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -760,7 +757,8 @@ bool GeneratorImpl::EmitDataUnpackingCall(
|
|||||||
<< " >> 16))";
|
<< " >> 16))";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error_ = "Internal error: unhandled data packing intrinsic";
|
diagnostics_.add_error(
|
||||||
|
"Internal error: unhandled data packing intrinsic");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -801,7 +799,7 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& pre,
|
|||||||
case semantic::IntrinsicType::kTextureDimensions:
|
case semantic::IntrinsicType::kTextureDimensions:
|
||||||
switch (texture_type->dim()) {
|
switch (texture_type->dim()) {
|
||||||
case type::TextureDimension::kNone:
|
case type::TextureDimension::kNone:
|
||||||
error_ = "texture dimension is kNone";
|
diagnostics_.add_error("texture dimension is kNone");
|
||||||
return false;
|
return false;
|
||||||
case type::TextureDimension::k1d:
|
case type::TextureDimension::k1d:
|
||||||
num_dimensions = 1;
|
num_dimensions = 1;
|
||||||
@ -837,7 +835,7 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& pre,
|
|||||||
case semantic::IntrinsicType::kTextureNumLayers:
|
case semantic::IntrinsicType::kTextureNumLayers:
|
||||||
switch (texture_type->dim()) {
|
switch (texture_type->dim()) {
|
||||||
default:
|
default:
|
||||||
error_ = "texture dimension is not arrayed";
|
diagnostics_.add_error("texture dimension is not arrayed");
|
||||||
return false;
|
return false;
|
||||||
case type::TextureDimension::k1dArray:
|
case type::TextureDimension::k1dArray:
|
||||||
num_dimensions = 2;
|
num_dimensions = 2;
|
||||||
@ -854,7 +852,7 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& pre,
|
|||||||
add_mip_level_in = true;
|
add_mip_level_in = true;
|
||||||
switch (texture_type->dim()) {
|
switch (texture_type->dim()) {
|
||||||
default:
|
default:
|
||||||
error_ = "texture dimension does not support mips";
|
diagnostics_.add_error("texture dimension does not support mips");
|
||||||
return false;
|
return false;
|
||||||
case type::TextureDimension::k2d:
|
case type::TextureDimension::k2d:
|
||||||
case type::TextureDimension::kCube:
|
case type::TextureDimension::kCube:
|
||||||
@ -872,7 +870,8 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& pre,
|
|||||||
case semantic::IntrinsicType::kTextureNumSamples:
|
case semantic::IntrinsicType::kTextureNumSamples:
|
||||||
switch (texture_type->dim()) {
|
switch (texture_type->dim()) {
|
||||||
default:
|
default:
|
||||||
error_ = "texture dimension does not support multisampling";
|
diagnostics_.add_error(
|
||||||
|
"texture dimension does not support multisampling");
|
||||||
return false;
|
return false;
|
||||||
case type::TextureDimension::k2d:
|
case type::TextureDimension::k2d:
|
||||||
num_dimensions = 3;
|
num_dimensions = 3;
|
||||||
@ -885,7 +884,7 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& pre,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error_ = "unexpected intrinsic";
|
diagnostics_.add_error("unexpected intrinsic");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -967,8 +966,9 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& pre,
|
|||||||
out << "[";
|
out << "[";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error_ = "Internal compiler error: Unhandled texture intrinsic '" +
|
diagnostics_.add_error(
|
||||||
std::string(intrinsic->str()) + "'";
|
"Internal compiler error: Unhandled texture intrinsic '" +
|
||||||
|
std::string(intrinsic->str()) + "'");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1127,7 +1127,8 @@ std::string GeneratorImpl::generate_builtin_name(
|
|||||||
out = "smoothstep";
|
out = "smoothstep";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error_ = "Unknown builtin method: " + std::string(intrinsic->str());
|
diagnostics_.add_error("Unknown builtin method: " +
|
||||||
|
std::string(intrinsic->str()));
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1277,7 +1278,7 @@ bool GeneratorImpl::EmitExpression(std::ostream& pre,
|
|||||||
return EmitUnaryOp(pre, out, u);
|
return EmitUnaryOp(pre, out, u);
|
||||||
}
|
}
|
||||||
|
|
||||||
error_ = "unknown expression type: " + builder_.str(expr);
|
diagnostics_.add_error("unknown expression type: " + builder_.str(expr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1302,7 +1303,7 @@ bool GeneratorImpl::EmitIdentifier(std::ostream&,
|
|||||||
: VarType::kOut;
|
: VarType::kOut;
|
||||||
auto name = current_ep_var_name(var_type);
|
auto name = current_ep_var_name(var_type);
|
||||||
if (name.empty()) {
|
if (name.empty()) {
|
||||||
error_ = "unable to find entry point data for variable";
|
diagnostics_.add_error("unable to find entry point data for variable");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out << name << ".";
|
out << name << ".";
|
||||||
@ -1581,8 +1582,9 @@ bool GeneratorImpl::EmitEntryPointData(
|
|||||||
// set. https://bugs.chromium.org/p/tint/issues/detail?id=104
|
// set. https://bugs.chromium.org/p/tint/issues/detail?id=104
|
||||||
auto* binding = data.second.binding;
|
auto* binding = data.second.binding;
|
||||||
if (binding == nullptr) {
|
if (binding == nullptr) {
|
||||||
error_ = "unable to find binding information for uniform: " +
|
diagnostics_.add_error(
|
||||||
builder_.Symbols().NameFor(decl->symbol());
|
"unable to find binding information for uniform: " +
|
||||||
|
builder_.Symbols().NameFor(decl->symbol()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// auto* set = data.second.set;
|
// auto* set = data.second.set;
|
||||||
@ -1642,7 +1644,7 @@ bool GeneratorImpl::EmitEntryPointData(
|
|||||||
|
|
||||||
auto* ac = decl->type()->As<type::AccessControl>();
|
auto* ac = decl->type()->As<type::AccessControl>();
|
||||||
if (ac == nullptr) {
|
if (ac == nullptr) {
|
||||||
error_ = "access control type required for storage buffer";
|
diagnostics_.add_error("access control type required for storage buffer");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1682,19 +1684,21 @@ bool GeneratorImpl::EmitEntryPointData(
|
|||||||
out << " " << builder_.Symbols().NameFor(var->symbol()) << " : ";
|
out << " " << builder_.Symbols().NameFor(var->symbol()) << " : ";
|
||||||
if (auto* location = deco->As<ast::LocationDecoration>()) {
|
if (auto* location = deco->As<ast::LocationDecoration>()) {
|
||||||
if (func->pipeline_stage() == ast::PipelineStage::kCompute) {
|
if (func->pipeline_stage() == ast::PipelineStage::kCompute) {
|
||||||
error_ = "invalid location variable for pipeline stage";
|
diagnostics_.add_error(
|
||||||
|
"invalid location variable for pipeline stage");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out << "TEXCOORD" << location->value();
|
out << "TEXCOORD" << location->value();
|
||||||
} else if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
|
} else if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
|
||||||
auto attr = builtin_to_attribute(builtin->value());
|
auto attr = builtin_to_attribute(builtin->value());
|
||||||
if (attr.empty()) {
|
if (attr.empty()) {
|
||||||
error_ = "unsupported builtin";
|
diagnostics_.add_error("unsupported builtin");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out << attr;
|
out << attr;
|
||||||
} else {
|
} else {
|
||||||
error_ = "unsupported variable decoration for entry point output";
|
diagnostics_.add_error(
|
||||||
|
"unsupported variable decoration for entry point output");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out << ";" << std::endl;
|
out << ";" << std::endl;
|
||||||
@ -1734,18 +1738,20 @@ bool GeneratorImpl::EmitEntryPointData(
|
|||||||
} else if (func->pipeline_stage() == ast::PipelineStage::kFragment) {
|
} else if (func->pipeline_stage() == ast::PipelineStage::kFragment) {
|
||||||
out << "SV_Target" << loc << "";
|
out << "SV_Target" << loc << "";
|
||||||
} else {
|
} else {
|
||||||
error_ = "invalid location variable for pipeline stage";
|
diagnostics_.add_error(
|
||||||
|
"invalid location variable for pipeline stage");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
|
} else if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
|
||||||
auto attr = builtin_to_attribute(builtin->value());
|
auto attr = builtin_to_attribute(builtin->value());
|
||||||
if (attr.empty()) {
|
if (attr.empty()) {
|
||||||
error_ = "unsupported builtin";
|
diagnostics_.add_error("unsupported builtin");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out << attr;
|
out << attr;
|
||||||
} else {
|
} else {
|
||||||
error_ = "unsupported variable decoration for entry point output";
|
diagnostics_.add_error(
|
||||||
|
"unsupported variable decoration for entry point output");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out << ";" << std::endl;
|
out << ";" << std::endl;
|
||||||
@ -1866,7 +1872,7 @@ bool GeneratorImpl::EmitLiteral(std::ostream& out, ast::Literal* lit) {
|
|||||||
} else if (auto* ul = lit->As<ast::UintLiteral>()) {
|
} else if (auto* ul = lit->As<ast::UintLiteral>()) {
|
||||||
out << ul->value() << "u";
|
out << ul->value() << "u";
|
||||||
} else {
|
} else {
|
||||||
error_ = "unknown literal type";
|
diagnostics_.add_error("unknown literal type");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -1893,7 +1899,8 @@ bool GeneratorImpl::EmitZeroValue(std::ostream& out, type::Type* type) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error_ = "Invalid type for zero emission: " + type->type_name();
|
diagnostics_.add_error("Invalid type for zero emission: " +
|
||||||
|
type->type_name());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -2016,7 +2023,7 @@ std::string GeneratorImpl::generate_storage_buffer_index_expression(
|
|||||||
auto* str_member = str_type->get_member(mem->member()->symbol());
|
auto* str_member = str_type->get_member(mem->member()->symbol());
|
||||||
|
|
||||||
if (!str_member->has_offset_decoration()) {
|
if (!str_member->has_offset_decoration()) {
|
||||||
error_ = "missing offset decoration for struct member";
|
diagnostics_.add_error("missing offset decoration for struct member");
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
out << str_member->offset();
|
out << str_member->offset();
|
||||||
@ -2027,9 +2034,9 @@ std::string GeneratorImpl::generate_storage_buffer_index_expression(
|
|||||||
// This must be a single element swizzle if we've got a vector at this
|
// This must be a single element swizzle if we've got a vector at this
|
||||||
// point.
|
// point.
|
||||||
if (builder_.Symbols().NameFor(mem->member()->symbol()).size() != 1) {
|
if (builder_.Symbols().NameFor(mem->member()->symbol()).size() != 1) {
|
||||||
error_ =
|
diagnostics_.add_error(
|
||||||
"Encountered multi-element swizzle when should have only one "
|
"Encountered multi-element swizzle when should have only one "
|
||||||
"level";
|
"level");
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2041,8 +2048,8 @@ std::string GeneratorImpl::generate_storage_buffer_index_expression(
|
|||||||
builder_.Symbols().NameFor(mem->member()->symbol()))
|
builder_.Symbols().NameFor(mem->member()->symbol()))
|
||||||
<< ")";
|
<< ")";
|
||||||
} else {
|
} else {
|
||||||
error_ =
|
diagnostics_.add_error("Invalid result type for member accessor: " +
|
||||||
"Invalid result type for member accessor: " + res_type->type_name();
|
res_type->type_name());
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2065,7 +2072,7 @@ std::string GeneratorImpl::generate_storage_buffer_index_expression(
|
|||||||
out << "16";
|
out << "16";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error_ = "Invalid array type in storage buffer access";
|
diagnostics_.add_error("Invalid array type in storage buffer access");
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
out << " * ";
|
out << " * ";
|
||||||
@ -2076,7 +2083,7 @@ std::string GeneratorImpl::generate_storage_buffer_index_expression(
|
|||||||
|
|
||||||
expr = ary->array();
|
expr = ary->array();
|
||||||
} else {
|
} else {
|
||||||
error_ = "error emitting storage buffer access";
|
diagnostics_.add_error("error emitting storage buffer access");
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2119,7 +2126,7 @@ bool GeneratorImpl::EmitStorageBufferAccessor(std::ostream& pre,
|
|||||||
|
|
||||||
auto buffer_name = get_buffer_name(expr);
|
auto buffer_name = get_buffer_name(expr);
|
||||||
if (buffer_name.empty()) {
|
if (buffer_name.empty()) {
|
||||||
error_ = "error emitting storage buffer access";
|
diagnostics_.add_error("error emitting storage buffer access");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2336,7 +2343,7 @@ bool GeneratorImpl::EmitStatement(std::ostream& out, ast::Statement* stmt) {
|
|||||||
return EmitVariable(out, v->variable(), false);
|
return EmitVariable(out, v->variable(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
error_ = "unknown statement type: " + builder_.str(stmt);
|
diagnostics_.add_error("unknown statement type: " + builder_.str(stmt));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2385,7 +2392,7 @@ bool GeneratorImpl::EmitType(std::ostream& out,
|
|||||||
if (arr->IsRuntimeArray()) {
|
if (arr->IsRuntimeArray()) {
|
||||||
// TODO(dsinclair): Support runtime arrays
|
// TODO(dsinclair): Support runtime arrays
|
||||||
// https://bugs.chromium.org/p/tint/issues/detail?id=185
|
// https://bugs.chromium.org/p/tint/issues/detail?id=185
|
||||||
error_ = "runtime array not supported yet.";
|
diagnostics_.add_error("runtime array not supported yet.");
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
sizes.push_back(arr->size());
|
sizes.push_back(arr->size());
|
||||||
@ -2415,7 +2422,7 @@ bool GeneratorImpl::EmitType(std::ostream& out,
|
|||||||
} else if (type->Is<type::Pointer>()) {
|
} else if (type->Is<type::Pointer>()) {
|
||||||
// TODO(dsinclair): What do we do with pointers in HLSL?
|
// TODO(dsinclair): What do we do with pointers in HLSL?
|
||||||
// https://bugs.chromium.org/p/tint/issues/detail?id=183
|
// https://bugs.chromium.org/p/tint/issues/detail?id=183
|
||||||
error_ = "pointers not supported in HLSL";
|
diagnostics_.add_error("pointers not supported in HLSL");
|
||||||
return false;
|
return false;
|
||||||
} else if (auto* sampler = type->As<type::Sampler>()) {
|
} else if (auto* sampler = type->As<type::Sampler>()) {
|
||||||
out << "Sampler";
|
out << "Sampler";
|
||||||
@ -2454,15 +2461,16 @@ bool GeneratorImpl::EmitType(std::ostream& out,
|
|||||||
out << "CubeArray";
|
out << "CubeArray";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error_ = "Invalid texture dimensions";
|
diagnostics_.add_error("Invalid texture dimensions");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto* st = tex->As<type::StorageTexture>()) {
|
if (auto* st = tex->As<type::StorageTexture>()) {
|
||||||
auto* component = image_format_to_rwtexture_type(st->image_format());
|
auto* component = image_format_to_rwtexture_type(st->image_format());
|
||||||
if (component == nullptr) {
|
if (component == nullptr) {
|
||||||
error_ = "Unsupported StorageTexture ImageFormat: " +
|
diagnostics_.add_error(
|
||||||
std::to_string(static_cast<int>(st->image_format()));
|
"Unsupported StorageTexture ImageFormat: " +
|
||||||
|
std::to_string(static_cast<int>(st->image_format())));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out << "<" << component << ">";
|
out << "<" << component << ">";
|
||||||
@ -2487,7 +2495,7 @@ bool GeneratorImpl::EmitType(std::ostream& out,
|
|||||||
} else if (type->Is<type::Void>()) {
|
} else if (type->Is<type::Void>()) {
|
||||||
out << "void";
|
out << "void";
|
||||||
} else {
|
} else {
|
||||||
error_ = "unknown type in EmitType";
|
diagnostics_.add_error("unknown type in EmitType");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2555,7 +2563,7 @@ bool GeneratorImpl::EmitVariable(std::ostream& out,
|
|||||||
|
|
||||||
// TODO(dsinclair): Handle variable decorations
|
// TODO(dsinclair): Handle variable decorations
|
||||||
if (!var->decorations().empty()) {
|
if (!var->decorations().empty()) {
|
||||||
error_ = "Variable decorations are not handled yet";
|
diagnostics_.add_error("Variable decorations are not handled yet");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2590,12 +2598,12 @@ bool GeneratorImpl::EmitProgramConstVariable(std::ostream& out,
|
|||||||
|
|
||||||
for (auto* d : var->decorations()) {
|
for (auto* d : var->decorations()) {
|
||||||
if (!d->Is<ast::ConstantIdDecoration>()) {
|
if (!d->Is<ast::ConstantIdDecoration>()) {
|
||||||
error_ = "Decorated const values not valid";
|
diagnostics_.add_error("Decorated const values not valid");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!var->is_const()) {
|
if (!var->is_const()) {
|
||||||
error_ = "Expected a const value";
|
diagnostics_.add_error("Expected a const value");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
#include "src/semantic/intrinsic.h"
|
#include "src/semantic/intrinsic.h"
|
||||||
#include "src/type/struct_type.h"
|
#include "src/type/struct_type.h"
|
||||||
#include "src/writer/hlsl/namer.h"
|
#include "src/writer/hlsl/namer.h"
|
||||||
|
#include "src/writer/text_generator.h"
|
||||||
|
|
||||||
namespace tint {
|
namespace tint {
|
||||||
|
|
||||||
@ -56,31 +57,13 @@ namespace writer {
|
|||||||
namespace hlsl {
|
namespace hlsl {
|
||||||
|
|
||||||
/// Implementation class for HLSL generator
|
/// Implementation class for HLSL generator
|
||||||
class GeneratorImpl {
|
class GeneratorImpl : public TextGenerator {
|
||||||
public:
|
public:
|
||||||
/// Constructor
|
/// Constructor
|
||||||
/// @param program the program to generate
|
/// @param program the program to generate
|
||||||
explicit GeneratorImpl(const Program* program);
|
explicit GeneratorImpl(const Program* program);
|
||||||
~GeneratorImpl();
|
~GeneratorImpl();
|
||||||
|
|
||||||
/// Increment the emitter indent level
|
|
||||||
void increment_indent() { indent_ += 2; }
|
|
||||||
/// Decrement the emiter indent level
|
|
||||||
void decrement_indent() {
|
|
||||||
if (indent_ < 2) {
|
|
||||||
indent_ = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
indent_ -= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writes the current indent to the output stream
|
|
||||||
/// @param out the output stream
|
|
||||||
void make_indent(std::ostream& out);
|
|
||||||
|
|
||||||
/// @returns the error
|
|
||||||
std::string error() const { return error_; }
|
|
||||||
|
|
||||||
/// @param out the output stream
|
/// @param out the output stream
|
||||||
/// @returns true on successful generation; false otherwise
|
/// @returns true on successful generation; false otherwise
|
||||||
bool Generate(std::ostream& out);
|
bool Generate(std::ostream& out);
|
||||||
@ -421,9 +404,6 @@ class GeneratorImpl {
|
|||||||
return builder_.TypeOf(expr);
|
return builder_.TypeOf(expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string error_;
|
|
||||||
size_t indent_ = 0;
|
|
||||||
|
|
||||||
Namer namer_;
|
Namer namer_;
|
||||||
ProgramBuilder builder_;
|
ProgramBuilder builder_;
|
||||||
Symbol current_ep_sym_;
|
Symbol current_ep_sym_;
|
||||||
|
@ -238,7 +238,8 @@ TEST_F(HlslGeneratorImplTest_EntryPoint,
|
|||||||
|
|
||||||
auto* func = program->AST().Functions()[0];
|
auto* func = program->AST().Functions()[0];
|
||||||
ASSERT_FALSE(gen.EmitEntryPointData(out, func, globals)) << gen.error();
|
ASSERT_FALSE(gen.EmitEntryPointData(out, func, globals)) << gen.error();
|
||||||
EXPECT_EQ(gen.error(), R"(invalid location variable for pipeline stage)");
|
EXPECT_EQ(gen.error(),
|
||||||
|
R"(error: invalid location variable for pipeline stage)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(HlslGeneratorImplTest_EntryPoint,
|
TEST_F(HlslGeneratorImplTest_EntryPoint,
|
||||||
@ -273,7 +274,8 @@ TEST_F(HlslGeneratorImplTest_EntryPoint,
|
|||||||
|
|
||||||
auto* func = program->AST().Functions()[0];
|
auto* func = program->AST().Functions()[0];
|
||||||
ASSERT_FALSE(gen.EmitEntryPointData(out, func, globals)) << gen.error();
|
ASSERT_FALSE(gen.EmitEntryPointData(out, func, globals)) << gen.error();
|
||||||
EXPECT_EQ(gen.error(), R"(invalid location variable for pipeline stage)");
|
EXPECT_EQ(gen.error(),
|
||||||
|
R"(error: invalid location variable for pipeline stage)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(HlslGeneratorImplTest_EntryPoint,
|
TEST_F(HlslGeneratorImplTest_EntryPoint,
|
||||||
|
@ -49,6 +49,7 @@
|
|||||||
#include "src/ast/unary_op_expression.h"
|
#include "src/ast/unary_op_expression.h"
|
||||||
#include "src/ast/variable.h"
|
#include "src/ast/variable.h"
|
||||||
#include "src/ast/variable_decl_statement.h"
|
#include "src/ast/variable_decl_statement.h"
|
||||||
|
#include "src/debug.h"
|
||||||
#include "src/program.h"
|
#include "src/program.h"
|
||||||
#include "src/semantic/call.h"
|
#include "src/semantic/call.h"
|
||||||
#include "src/semantic/expression.h"
|
#include "src/semantic/expression.h"
|
||||||
@ -273,7 +274,7 @@ bool GeneratorImpl::EmitConstructedType(const type::Type* ty) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error_ = "unknown alias type: " + ty->type_name();
|
diagnostics_.add_error("unknown alias type: " + ty->type_name());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,7 +397,7 @@ bool GeneratorImpl::EmitBinary(ast::BinaryExpression* expr) {
|
|||||||
out_ << "%";
|
out_ << "%";
|
||||||
break;
|
break;
|
||||||
case ast::BinaryOp::kNone:
|
case ast::BinaryOp::kNone:
|
||||||
error_ = "missing binary operation type";
|
diagnostics_.add_error("missing binary operation type");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out_ << " ";
|
out_ << " ";
|
||||||
@ -440,7 +441,7 @@ bool GeneratorImpl::EmitCall(ast::CallExpression* expr) {
|
|||||||
auto* ident = expr->func()->As<ast::IdentifierExpression>();
|
auto* ident = expr->func()->As<ast::IdentifierExpression>();
|
||||||
|
|
||||||
if (ident == nullptr) {
|
if (ident == nullptr) {
|
||||||
error_ = "invalid function name";
|
diagnostics_.add_error("invalid function name");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -498,8 +499,8 @@ bool GeneratorImpl::EmitCall(ast::CallExpression* expr) {
|
|||||||
|
|
||||||
auto* func = program_->AST().Functions().Find(ident->symbol());
|
auto* func = program_->AST().Functions().Find(ident->symbol());
|
||||||
if (func == nullptr) {
|
if (func == nullptr) {
|
||||||
error_ = "Unable to find function: " +
|
diagnostics_.add_error("Unable to find function: " +
|
||||||
program_->Symbols().NameFor(ident->symbol());
|
program_->Symbols().NameFor(ident->symbol()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -595,7 +596,7 @@ bool GeneratorImpl::EmitTextureCall(ast::CallExpression* expr,
|
|||||||
std::vector<const char*> dims;
|
std::vector<const char*> dims;
|
||||||
switch (texture_type->dim()) {
|
switch (texture_type->dim()) {
|
||||||
case type::TextureDimension::kNone:
|
case type::TextureDimension::kNone:
|
||||||
error_ = "texture dimension is kNone";
|
diagnostics_.add_error("texture dimension is kNone");
|
||||||
return false;
|
return false;
|
||||||
case type::TextureDimension::k1d:
|
case type::TextureDimension::k1d:
|
||||||
case type::TextureDimension::k1dArray:
|
case type::TextureDimension::k1dArray:
|
||||||
@ -698,8 +699,8 @@ bool GeneratorImpl::EmitTextureCall(ast::CallExpression* expr,
|
|||||||
out_ << ".write(";
|
out_ << ".write(";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error_ = "Internal compiler error: Unhandled texture intrinsic '" +
|
TINT_ICE(diagnostics_, "Unhandled texture intrinsic '" +
|
||||||
std::string(intrinsic->str()) + "'";
|
std::string(intrinsic->str()) + "'");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -761,7 +762,7 @@ bool GeneratorImpl::EmitTextureCall(ast::CallExpression* expr,
|
|||||||
default: {
|
default: {
|
||||||
std::stringstream err;
|
std::stringstream err;
|
||||||
err << "MSL does not support gradients for " << dim << " textures";
|
err << "MSL does not support gradients for " << dim << " textures";
|
||||||
error_ = err.str();
|
diagnostics_.add_error(err.str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -918,7 +919,8 @@ std::string GeneratorImpl::generate_builtin_name(
|
|||||||
out += "unpack_unorm2x16_to_float";
|
out += "unpack_unorm2x16_to_float";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error_ = "Unknown import method: " + std::string(intrinsic->str());
|
diagnostics_.add_error("Unknown import method: " +
|
||||||
|
std::string(intrinsic->str()));
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
@ -1041,7 +1043,8 @@ bool GeneratorImpl::EmitZeroValue(type::Type* type) {
|
|||||||
} else if (type->As<type::Struct>()) {
|
} else if (type->As<type::Struct>()) {
|
||||||
out_ << "{}";
|
out_ << "{}";
|
||||||
} else {
|
} else {
|
||||||
error_ = "Invalid type for zero emission: " + type->type_name();
|
diagnostics_.add_error("Invalid type for zero emission: " +
|
||||||
|
type->type_name());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -1062,7 +1065,7 @@ bool GeneratorImpl::EmitLiteral(ast::Literal* lit) {
|
|||||||
} else if (auto* ul = lit->As<ast::UintLiteral>()) {
|
} else if (auto* ul = lit->As<ast::UintLiteral>()) {
|
||||||
out_ << ul->value() << "u";
|
out_ << ul->value() << "u";
|
||||||
} else {
|
} else {
|
||||||
error_ = "unknown literal type";
|
diagnostics_.add_error("unknown literal type");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -1122,7 +1125,7 @@ bool GeneratorImpl::EmitEntryPointData(ast::Function* func) {
|
|||||||
} else if (func->pipeline_stage() == ast::PipelineStage::kFragment) {
|
} else if (func->pipeline_stage() == ast::PipelineStage::kFragment) {
|
||||||
out_ << "user(locn" << loc << ")";
|
out_ << "user(locn" << loc << ")";
|
||||||
} else {
|
} else {
|
||||||
error_ = "invalid location variable for pipeline stage";
|
diagnostics_.add_error("invalid location variable for pipeline stage");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out_ << "]];" << std::endl;
|
out_ << "]];" << std::endl;
|
||||||
@ -1162,18 +1165,20 @@ bool GeneratorImpl::EmitEntryPointData(ast::Function* func) {
|
|||||||
} else if (func->pipeline_stage() == ast::PipelineStage::kFragment) {
|
} else if (func->pipeline_stage() == ast::PipelineStage::kFragment) {
|
||||||
out_ << "color(" << loc << ")";
|
out_ << "color(" << loc << ")";
|
||||||
} else {
|
} else {
|
||||||
error_ = "invalid location variable for pipeline stage";
|
diagnostics_.add_error(
|
||||||
|
"invalid location variable for pipeline stage");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
|
} else if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
|
||||||
auto attr = builtin_to_attribute(builtin->value());
|
auto attr = builtin_to_attribute(builtin->value());
|
||||||
if (attr.empty()) {
|
if (attr.empty()) {
|
||||||
error_ = "unsupported builtin";
|
diagnostics_.add_error("unsupported builtin");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out_ << attr;
|
out_ << attr;
|
||||||
} else {
|
} else {
|
||||||
error_ = "unsupported variable decoration for entry point output";
|
diagnostics_.add_error(
|
||||||
|
"unsupported variable decoration for entry point output");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out_ << "]];" << std::endl;
|
out_ << "]];" << std::endl;
|
||||||
@ -1212,7 +1217,7 @@ bool GeneratorImpl::EmitExpression(ast::Expression* expr) {
|
|||||||
return EmitUnaryOp(u);
|
return EmitUnaryOp(u);
|
||||||
}
|
}
|
||||||
|
|
||||||
error_ = "unknown expression type: " + program_->str(expr);
|
diagnostics_.add_error("unknown expression type: " + program_->str(expr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1393,7 +1398,8 @@ bool GeneratorImpl::EmitFunctionInternal(ast::Function* func,
|
|||||||
|
|
||||||
auto* ac = var->Declaration()->type()->As<type::AccessControl>();
|
auto* ac = var->Declaration()->type()->As<type::AccessControl>();
|
||||||
if (ac == nullptr) {
|
if (ac == nullptr) {
|
||||||
error_ = "invalid type for storage buffer, expected access control";
|
diagnostics_.add_error(
|
||||||
|
"invalid type for storage buffer, expected access control");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ac->IsReadOnly()) {
|
if (ac->IsReadOnly()) {
|
||||||
@ -1516,7 +1522,7 @@ bool GeneratorImpl::EmitEntryPointFunction(ast::Function* func) {
|
|||||||
|
|
||||||
auto attr = builtin_to_attribute(builtin->value());
|
auto attr = builtin_to_attribute(builtin->value());
|
||||||
if (attr.empty()) {
|
if (attr.empty()) {
|
||||||
error_ = "unknown builtin";
|
diagnostics_.add_error("unknown builtin");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out_ << " " << program_->Symbols().NameFor(var->Declaration()->symbol())
|
out_ << " " << program_->Symbols().NameFor(var->Declaration()->symbol())
|
||||||
@ -1535,8 +1541,9 @@ bool GeneratorImpl::EmitEntryPointFunction(ast::Function* func) {
|
|||||||
// set. https://bugs.chromium.org/p/tint/issues/detail?id=104
|
// set. https://bugs.chromium.org/p/tint/issues/detail?id=104
|
||||||
auto* binding = data.second.binding;
|
auto* binding = data.second.binding;
|
||||||
if (binding == nullptr) {
|
if (binding == nullptr) {
|
||||||
error_ = "unable to find binding information for uniform: " +
|
diagnostics_.add_error(
|
||||||
program_->Symbols().NameFor(var->Declaration()->symbol());
|
"unable to find binding information for uniform: " +
|
||||||
|
program_->Symbols().NameFor(var->Declaration()->symbol()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// auto* set = data.second.set;
|
// auto* set = data.second.set;
|
||||||
@ -1566,7 +1573,8 @@ bool GeneratorImpl::EmitEntryPointFunction(ast::Function* func) {
|
|||||||
|
|
||||||
auto* ac = var->Declaration()->type()->As<type::AccessControl>();
|
auto* ac = var->Declaration()->type()->As<type::AccessControl>();
|
||||||
if (ac == nullptr) {
|
if (ac == nullptr) {
|
||||||
error_ = "invalid type for storage buffer, expected access control";
|
diagnostics_.add_error(
|
||||||
|
"invalid type for storage buffer, expected access control");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ac->IsReadOnly()) {
|
if (ac->IsReadOnly()) {
|
||||||
@ -1636,7 +1644,7 @@ bool GeneratorImpl::EmitIdentifier(ast::IdentifierExpression* expr) {
|
|||||||
: VarType::kOut;
|
: VarType::kOut;
|
||||||
auto name = current_ep_var_name(var_type);
|
auto name = current_ep_var_name(var_type);
|
||||||
if (name.empty()) {
|
if (name.empty()) {
|
||||||
error_ = "unable to find entry point data for variable";
|
diagnostics_.add_error("unable to find entry point data for variable");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out_ << name << ".";
|
out_ << name << ".";
|
||||||
@ -1897,7 +1905,7 @@ bool GeneratorImpl::EmitStatement(ast::Statement* stmt) {
|
|||||||
return EmitVariable(var, false);
|
return EmitVariable(var, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
error_ = "unknown statement type: " + program_->str(stmt);
|
diagnostics_.add_error("unknown statement type: " + program_->str(stmt));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1933,7 +1941,7 @@ bool GeneratorImpl::EmitType(type::Type* type, const std::string& name) {
|
|||||||
} else if (ac->access_control() == ast::AccessControl::kWriteOnly) {
|
} else if (ac->access_control() == ast::AccessControl::kWriteOnly) {
|
||||||
access_str = "write";
|
access_str = "write";
|
||||||
} else {
|
} else {
|
||||||
error_ = "Invalid access control for storage texture";
|
diagnostics_.add_error("Invalid access control for storage texture");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2015,7 +2023,7 @@ bool GeneratorImpl::EmitType(type::Type* type, const std::string& name) {
|
|||||||
out_ << "cube_array";
|
out_ << "cube_array";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error_ = "Invalid texture dimensions";
|
diagnostics_.add_error("Invalid texture dimensions");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (tex->Is<type::MultisampledTexture>()) {
|
if (tex->Is<type::MultisampledTexture>()) {
|
||||||
@ -2040,7 +2048,7 @@ bool GeneratorImpl::EmitType(type::Type* type, const std::string& name) {
|
|||||||
}
|
}
|
||||||
out_ << ", access::sample";
|
out_ << ", access::sample";
|
||||||
} else {
|
} else {
|
||||||
error_ = "invalid texture type";
|
diagnostics_.add_error("invalid texture type");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out_ << ">";
|
out_ << ">";
|
||||||
@ -2055,7 +2063,7 @@ bool GeneratorImpl::EmitType(type::Type* type, const std::string& name) {
|
|||||||
} else if (type->Is<type::Void>()) {
|
} else if (type->Is<type::Void>()) {
|
||||||
out_ << "void";
|
out_ << "void";
|
||||||
} else {
|
} else {
|
||||||
error_ = "unknown type in EmitType: " + type->type_name();
|
diagnostics_.add_error("unknown type in EmitType: " + type->type_name());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2085,7 +2093,8 @@ bool GeneratorImpl::EmitStructType(const type::Struct* str) {
|
|||||||
}
|
}
|
||||||
current_offset = offset;
|
current_offset = offset;
|
||||||
} else {
|
} else {
|
||||||
error_ = "unsupported member decoration: " + program_->str(deco);
|
diagnostics_.add_error("unsupported member decoration: " +
|
||||||
|
program_->str(deco));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2095,7 +2104,8 @@ bool GeneratorImpl::EmitStructType(const type::Struct* str) {
|
|||||||
}
|
}
|
||||||
auto size = calculate_alignment_size(mem->type());
|
auto size = calculate_alignment_size(mem->type());
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
error_ = "unable to calculate byte size for: " + mem->type()->type_name();
|
diagnostics_.add_error("unable to calculate byte size for: " +
|
||||||
|
mem->type()->type_name());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
current_offset += size;
|
current_offset += size;
|
||||||
@ -2141,7 +2151,7 @@ bool GeneratorImpl::EmitVariable(const semantic::Variable* var,
|
|||||||
|
|
||||||
// TODO(dsinclair): Handle variable decorations
|
// TODO(dsinclair): Handle variable decorations
|
||||||
if (!decl->decorations().empty()) {
|
if (!decl->decorations().empty()) {
|
||||||
error_ = "Variable decorations are not handled yet";
|
diagnostics_.add_error("Variable decorations are not handled yet");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (decl->is_const()) {
|
if (decl->is_const()) {
|
||||||
@ -2179,12 +2189,12 @@ bool GeneratorImpl::EmitProgramConstVariable(const ast::Variable* var) {
|
|||||||
|
|
||||||
for (auto* d : var->decorations()) {
|
for (auto* d : var->decorations()) {
|
||||||
if (!d->Is<ast::ConstantIdDecoration>()) {
|
if (!d->Is<ast::ConstantIdDecoration>()) {
|
||||||
error_ = "Decorated const values not valid";
|
diagnostics_.add_error("Decorated const values not valid");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!var->is_const()) {
|
if (!var->is_const()) {
|
||||||
error_ = "Expected a const value";
|
diagnostics_.add_error("Expected a const value");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +212,8 @@ TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Compute_Input) {
|
|||||||
|
|
||||||
auto* func = program->AST().Functions()[0];
|
auto* func = program->AST().Functions()[0];
|
||||||
ASSERT_FALSE(gen.EmitEntryPointData(func)) << gen.error();
|
ASSERT_FALSE(gen.EmitEntryPointData(func)) << gen.error();
|
||||||
EXPECT_EQ(gen.error(), R"(invalid location variable for pipeline stage)");
|
EXPECT_EQ(gen.error(),
|
||||||
|
R"(error: invalid location variable for pipeline stage)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Compute_Output) {
|
TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Compute_Output) {
|
||||||
@ -241,7 +242,8 @@ TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Compute_Output) {
|
|||||||
|
|
||||||
auto* func = program->AST().Functions()[0];
|
auto* func = program->AST().Functions()[0];
|
||||||
ASSERT_FALSE(gen.EmitEntryPointData(func)) << gen.error();
|
ASSERT_FALSE(gen.EmitEntryPointData(func)) << gen.error();
|
||||||
EXPECT_EQ(gen.error(), R"(invalid location variable for pipeline stage)");
|
EXPECT_EQ(gen.error(),
|
||||||
|
R"(error: invalid location variable for pipeline stage)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Builtins) {
|
TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Builtins) {
|
||||||
|
@ -57,6 +57,7 @@
|
|||||||
#include "src/ast/unary_op_expression.h"
|
#include "src/ast/unary_op_expression.h"
|
||||||
#include "src/ast/variable.h"
|
#include "src/ast/variable.h"
|
||||||
#include "src/ast/variable_decl_statement.h"
|
#include "src/ast/variable_decl_statement.h"
|
||||||
|
#include "src/debug.h"
|
||||||
#include "src/program.h"
|
#include "src/program.h"
|
||||||
#include "src/semantic/call.h"
|
#include "src/semantic/call.h"
|
||||||
#include "src/semantic/expression.h"
|
#include "src/semantic/expression.h"
|
||||||
@ -2408,7 +2409,8 @@ bool Builder::GenerateTextureIntrinsic(ast::CallExpression* call,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break; // unreachable
|
TINT_UNREACHABLE(builder_.Diagnostics());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto* offset = arg(Usage::kOffset)) {
|
if (auto* offset = arg(Usage::kOffset)) {
|
||||||
|
@ -26,8 +26,12 @@ TextGenerator::TextGenerator() = default;
|
|||||||
TextGenerator::~TextGenerator() = default;
|
TextGenerator::~TextGenerator() = default;
|
||||||
|
|
||||||
void TextGenerator::make_indent() {
|
void TextGenerator::make_indent() {
|
||||||
|
make_indent(out_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextGenerator::make_indent(std::ostream& out) const {
|
||||||
for (size_t i = 0; i < indent_; i++) {
|
for (size_t i = 0; i < indent_; i++) {
|
||||||
out_ << " ";
|
out << " ";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "src/diagnostic/diagnostic.h"
|
||||||
|
|
||||||
namespace tint {
|
namespace tint {
|
||||||
namespace writer {
|
namespace writer {
|
||||||
|
|
||||||
@ -42,17 +44,24 @@ class TextGenerator {
|
|||||||
/// Writes the current indent to the output stream
|
/// Writes the current indent to the output stream
|
||||||
void make_indent();
|
void make_indent();
|
||||||
|
|
||||||
|
/// Writes the current indent to `out`
|
||||||
|
/// @param out the stream to write the indent to
|
||||||
|
void make_indent(std::ostream& out) const;
|
||||||
|
|
||||||
/// @returns the result data
|
/// @returns the result data
|
||||||
std::string result() const { return out_.str(); }
|
std::string result() const { return out_.str(); }
|
||||||
|
|
||||||
|
/// @returns the list of diagnostics raised by the generator.
|
||||||
|
const diag::List& Diagnostics() const { return diagnostics_; }
|
||||||
|
|
||||||
/// @returns the error
|
/// @returns the error
|
||||||
std::string error() const { return error_; }
|
std::string error() const { return diagnostics_.str(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// The text output stream
|
/// The text output stream
|
||||||
std::ostringstream out_;
|
std::ostringstream out_;
|
||||||
/// Error generated by the generator
|
/// Diagnostics generated by the generator
|
||||||
std::string error_;
|
diag::List diagnostics_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t indent_ = 0;
|
size_t indent_ = 0;
|
||||||
|
@ -58,6 +58,7 @@
|
|||||||
#include "src/ast/variable.h"
|
#include "src/ast/variable.h"
|
||||||
#include "src/ast/variable_decl_statement.h"
|
#include "src/ast/variable_decl_statement.h"
|
||||||
#include "src/ast/workgroup_decoration.h"
|
#include "src/ast/workgroup_decoration.h"
|
||||||
|
#include "src/debug.h"
|
||||||
#include "src/program.h"
|
#include "src/program.h"
|
||||||
#include "src/semantic/function.h"
|
#include "src/semantic/function.h"
|
||||||
#include "src/semantic/variable.h"
|
#include "src/semantic/variable.h"
|
||||||
@ -120,7 +121,8 @@ bool GeneratorImpl::Generate(const ast::Function* entry) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assert(false /* unreachable */);
|
TINT_UNREACHABLE(diagnostics_);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decl != program_->AST().GlobalDeclarations().back()) {
|
if (decl != program_->AST().GlobalDeclarations().back()) {
|
||||||
@ -136,7 +138,7 @@ bool GeneratorImpl::GenerateEntryPoint(ast::PipelineStage stage,
|
|||||||
auto* func =
|
auto* func =
|
||||||
program_->AST().Functions().Find(program_->Symbols().Get(name), stage);
|
program_->AST().Functions().Find(program_->Symbols().Get(name), stage);
|
||||||
if (func == nullptr) {
|
if (func == nullptr) {
|
||||||
error_ = "Unable to find requested entry point: " + name;
|
diagnostics_.add_error("Unable to find requested entry point: " + name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return Generate(func);
|
return Generate(func);
|
||||||
@ -155,7 +157,7 @@ bool GeneratorImpl::EmitConstructedType(const type::Type* ty) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error_ = "unknown constructed type: " + ty->type_name();
|
diagnostics_.add_error("unknown constructed type: " + ty->type_name());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +190,7 @@ bool GeneratorImpl::EmitExpression(ast::Expression* expr) {
|
|||||||
return EmitUnaryOp(u);
|
return EmitUnaryOp(u);
|
||||||
}
|
}
|
||||||
|
|
||||||
error_ = "unknown expression type";
|
diagnostics_.add_error("unknown expression type");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,7 +302,7 @@ bool GeneratorImpl::EmitLiteral(ast::Literal* lit) {
|
|||||||
} else if (auto* ul = lit->As<ast::UintLiteral>()) {
|
} else if (auto* ul = lit->As<ast::UintLiteral>()) {
|
||||||
out_ << ul->value() << "u";
|
out_ << ul->value() << "u";
|
||||||
} else {
|
} else {
|
||||||
error_ = "unknown literal type";
|
diagnostics_.add_error("unknown literal type");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -360,7 +362,7 @@ bool GeneratorImpl::EmitFunction(ast::Function* func) {
|
|||||||
bool GeneratorImpl::EmitImageFormat(const type::ImageFormat fmt) {
|
bool GeneratorImpl::EmitImageFormat(const type::ImageFormat fmt) {
|
||||||
switch (fmt) {
|
switch (fmt) {
|
||||||
case type::ImageFormat::kNone:
|
case type::ImageFormat::kNone:
|
||||||
error_ = "unknown image format";
|
diagnostics_.add_error("unknown image format");
|
||||||
return false;
|
return false;
|
||||||
default:
|
default:
|
||||||
out_ << fmt;
|
out_ << fmt;
|
||||||
@ -379,7 +381,7 @@ bool GeneratorImpl::EmitType(type::Type* type) {
|
|||||||
} else if (ac->IsReadWrite()) {
|
} else if (ac->IsReadWrite()) {
|
||||||
out_ << "read_write";
|
out_ << "read_write";
|
||||||
} else {
|
} else {
|
||||||
error_ = "invalid access control";
|
diagnostics_.add_error("invalid access control");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out_ << ")]]" << std::endl;
|
out_ << ")]]" << std::endl;
|
||||||
@ -444,7 +446,7 @@ bool GeneratorImpl::EmitType(type::Type* type) {
|
|||||||
} else if (texture->Is<type::StorageTexture>()) {
|
} else if (texture->Is<type::StorageTexture>()) {
|
||||||
out_ << "storage_";
|
out_ << "storage_";
|
||||||
} else {
|
} else {
|
||||||
error_ = "unknown texture type";
|
diagnostics_.add_error("unknown texture type");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -471,7 +473,7 @@ bool GeneratorImpl::EmitType(type::Type* type) {
|
|||||||
out_ << "cube_array";
|
out_ << "cube_array";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error_ = "unknown texture dimension";
|
diagnostics_.add_error("unknown texture dimension");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -506,7 +508,7 @@ bool GeneratorImpl::EmitType(type::Type* type) {
|
|||||||
} else if (type->Is<type::Void>()) {
|
} else if (type->Is<type::Void>()) {
|
||||||
out_ << "void";
|
out_ << "void";
|
||||||
} else {
|
} else {
|
||||||
error_ = "unknown type in EmitType: " + type->type_name();
|
diagnostics_.add_error("unknown type in EmitType: " + type->type_name());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -605,7 +607,7 @@ bool GeneratorImpl::EmitVariableDecorations(const semantic::Variable* var) {
|
|||||||
} else if (auto* constant = deco->As<ast::ConstantIdDecoration>()) {
|
} else if (auto* constant = deco->As<ast::ConstantIdDecoration>()) {
|
||||||
out_ << "constant_id(" << constant->value() << ")";
|
out_ << "constant_id(" << constant->value() << ")";
|
||||||
} else {
|
} else {
|
||||||
error_ = "unknown variable decoration";
|
diagnostics_.add_error("unknown variable decoration");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -678,7 +680,7 @@ bool GeneratorImpl::EmitBinary(ast::BinaryExpression* expr) {
|
|||||||
out_ << "%";
|
out_ << "%";
|
||||||
break;
|
break;
|
||||||
case ast::BinaryOp::kNone:
|
case ast::BinaryOp::kNone:
|
||||||
error_ = "missing binary operation type";
|
diagnostics_.add_error("missing binary operation type");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out_ << " ";
|
out_ << " ";
|
||||||
@ -789,7 +791,7 @@ bool GeneratorImpl::EmitStatement(ast::Statement* stmt) {
|
|||||||
return EmitVariable(v->variable());
|
return EmitVariable(v->variable());
|
||||||
}
|
}
|
||||||
|
|
||||||
error_ = "unknown statement type: " + program_->str(stmt);
|
diagnostics_.add_error("unknown statement type: " + program_->str(stmt));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
#include "src/ast/type_constructor_expression.h"
|
#include "src/ast/type_constructor_expression.h"
|
||||||
#include "src/ast/unary_op_expression.h"
|
#include "src/ast/unary_op_expression.h"
|
||||||
#include "src/ast/variable.h"
|
#include "src/ast/variable.h"
|
||||||
|
#include "src/diagnostic/diagnostic.h"
|
||||||
#include "src/program.h"
|
#include "src/program.h"
|
||||||
#include "src/type/storage_texture_type.h"
|
#include "src/type/storage_texture_type.h"
|
||||||
#include "src/type/struct_type.h"
|
#include "src/type/struct_type.h"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user