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/clone_context.cc",
|
||||
"src/clone_context.h",
|
||||
"src/debug.cc",
|
||||
"src/debug.h",
|
||||
"src/demangler.cc",
|
||||
"src/demangler.h",
|
||||
"src/diagnostic/diagnostic.cc",
|
||||
|
@ -728,7 +730,6 @@ if (!build_with_chromium) {
|
|||
testonly = true
|
||||
sources = [
|
||||
"${tint_googletest_dir}/googlemock/src/gmock-all.cc",
|
||||
"${tint_googletest_dir}/googlemock/src/gmock_main.cc",
|
||||
]
|
||||
public_configs = [ ":gmock_config" ]
|
||||
}
|
||||
|
@ -833,6 +834,7 @@ source_set("tint_unittests_core_src") {
|
|||
"src/block_allocator_test.cc",
|
||||
"src/castable_test.cc",
|
||||
"src/clone_context_test.cc",
|
||||
"src/debug_test.cc",
|
||||
"src/demangler_test.cc",
|
||||
"src/diagnostic/formatter_test.cc",
|
||||
"src/diagnostic/printer_test.cc",
|
||||
|
@ -844,6 +846,7 @@ source_set("tint_unittests_core_src") {
|
|||
"src/scope_stack_test.cc",
|
||||
"src/symbol_table_test.cc",
|
||||
"src/symbol_test.cc",
|
||||
"src/test_main.cc",
|
||||
"src/traits_test.cc",
|
||||
"src/transform/bound_array_accessors_test.cc",
|
||||
"src/transform/emit_vertex_point_size_test.cc",
|
||||
|
|
|
@ -28,6 +28,13 @@
|
|||
|
||||
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 {
|
||||
kNone = -1,
|
||||
kSpirv,
|
||||
|
@ -419,6 +426,8 @@ int main(int argc, const char** argv) {
|
|||
std::vector<std::string> args(argv, argv + argc);
|
||||
Options options;
|
||||
|
||||
tint::SetInternalCompilerErrorReporter(&TintInternalCompilerErrorReporter);
|
||||
|
||||
if (!ParseArgs(args, &options)) {
|
||||
std::cerr << "Failed to parse arguments." << std::endl;
|
||||
return 1;
|
||||
|
|
|
@ -167,8 +167,10 @@ set(TINT_LIB_SRCS
|
|||
castable.h
|
||||
clone_context.cc
|
||||
clone_context.h
|
||||
debug.cc
|
||||
debug.h
|
||||
demangler.cc
|
||||
demangler.h;
|
||||
demangler.h
|
||||
intrinsic_table.cc
|
||||
intrinsic_table.h
|
||||
diagnostic/diagnostic.cc
|
||||
|
@ -463,6 +465,7 @@ if(${TINT_BUILD_TESTS})
|
|||
block_allocator_test.cc
|
||||
castable_test.cc
|
||||
clone_context_test.cc
|
||||
debug_test.cc
|
||||
demangler_test.cc
|
||||
diagnostic/formatter_test.cc
|
||||
diagnostic/printer_test.cc
|
||||
|
@ -474,6 +477,7 @@ if(${TINT_BUILD_TESTS})
|
|||
symbol_table_test.cc
|
||||
symbol_test.cc
|
||||
traits_test.cc
|
||||
test_main.cc
|
||||
type_determiner_test.cc
|
||||
type/access_control_type_test.cc
|
||||
type/alias_type_test.cc
|
||||
|
@ -782,7 +786,7 @@ if(${TINT_BUILD_TESTS})
|
|||
## Test executable
|
||||
target_include_directories(
|
||||
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)
|
||||
|
||||
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/type_constructor_expression.h"
|
||||
#include "src/debug.h"
|
||||
#include "src/program_builder.h"
|
||||
#include "src/type/access_control_type.h"
|
||||
#include "src/type/depth_texture_type.h"
|
||||
|
@ -146,7 +147,7 @@ type::Type* TextureOverloadCase::resultVectorComponentType(
|
|||
return b->ty.i32();
|
||||
}
|
||||
|
||||
assert(false /* unreachable */);
|
||||
TINT_UNREACHABLE(b->Diagnostics());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -186,7 +187,7 @@ ast::Variable* TextureOverloadCase::buildTextureVariable(
|
|||
}
|
||||
}
|
||||
|
||||
assert(false /* unreachable */);
|
||||
TINT_UNREACHABLE(b->Diagnostics());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "src/debug.h"
|
||||
#include "src/program_builder.h"
|
||||
#include "src/type/alias_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>()) {
|
||||
global_variables_.push_back(var);
|
||||
} 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>()) {
|
||||
AddGlobalVariable(ctx->Clone(var));
|
||||
} 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;
|
||||
}
|
||||
|
||||
diag::List& CloneContext::Diagnostics() const {
|
||||
return dst->Diagnostics();
|
||||
}
|
||||
|
||||
} // namespace tint
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include "src/castable.h"
|
||||
#include "src/debug.h"
|
||||
#include "src/source.h"
|
||||
#include "src/symbol.h"
|
||||
#include "src/traits.h"
|
||||
|
@ -301,14 +302,19 @@ class CloneContext {
|
|||
}
|
||||
|
||||
/// 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>
|
||||
TO* CheckedCast(FROM* obj) {
|
||||
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;
|
||||
}
|
||||
|
||||
/// @returns the diagnostic list of #dst
|
||||
diag::List& Diagnostics() const;
|
||||
|
||||
/// A vector of Cloneable*
|
||||
using CloneableList = std::vector<Cloneable*>;
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "gtest/gtest-spi.h"
|
||||
|
||||
#include "src/program_builder.h"
|
||||
|
||||
|
@ -257,33 +257,29 @@ TEST(CloneContext, CloneWithInsertBefore) {
|
|||
}
|
||||
|
||||
TEST(CloneContext, CloneWithReplace_WithNotANode) {
|
||||
ProgramBuilder builder;
|
||||
auto* original_root = builder.create<Node>("root");
|
||||
original_root->a = builder.create<Node>("a");
|
||||
original_root->b = builder.create<Node>("b");
|
||||
original_root->c = builder.create<Node>("c");
|
||||
Program original(std::move(builder));
|
||||
EXPECT_FATAL_FAILURE(
|
||||
{
|
||||
ProgramBuilder builder;
|
||||
auto* original_root = builder.create<Node>("root");
|
||||
original_root->a = builder.create<Node>("a");
|
||||
original_root->b = builder.create<Node>("b");
|
||||
original_root->c = builder.create<Node>("c");
|
||||
Program original(std::move(builder));
|
||||
|
||||
// root
|
||||
// ╭──────────────────┼──────────────────╮
|
||||
// (a) (b) (c)
|
||||
// Replaced
|
||||
// root
|
||||
// ╭──────────────────┼──────────────────╮
|
||||
// (a) (b) (c)
|
||||
// Replaced
|
||||
|
||||
ProgramBuilder cloned;
|
||||
auto* replacement = cloned.create<NotANode>();
|
||||
ProgramBuilder cloned;
|
||||
auto* replacement = cloned.create<NotANode>();
|
||||
|
||||
CloneContext ctx(&cloned, &original);
|
||||
ctx.Replace(original_root->b, replacement);
|
||||
CloneContext ctx(&cloned, &original);
|
||||
ctx.Replace(original_root->b, replacement);
|
||||
|
||||
#ifndef NDEBUG
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wused-but-marked-unused"
|
||||
#pragma clang diagnostic ignored "-Wcovered-switch-default"
|
||||
|
||||
EXPECT_DEATH_IF_SUPPORTED(ctx.Clone(original_root), "");
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
#endif // NDEBUG
|
||||
ctx.Clone(original_root);
|
||||
},
|
||||
"internal compiler error");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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/formatter.h"
|
||||
|
||||
namespace tint {
|
||||
namespace diag {
|
||||
|
||||
|
@ -27,5 +29,11 @@ List::~List() = default;
|
|||
List& List::operator=(const 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 tint
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#ifndef SRC_DIAGNOSTIC_DIAGNOSTIC_H_
|
||||
#define SRC_DIAGNOSTIC_DIAGNOSTIC_H_
|
||||
|
||||
#include <cassert>
|
||||
#include <initializer_list>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
@ -26,7 +27,7 @@ namespace tint {
|
|||
namespace diag {
|
||||
|
||||
/// 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`
|
||||
inline bool operator>=(Severity a, Severity b) {
|
||||
|
@ -118,6 +119,17 @@ class List {
|
|||
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
|
||||
/// higher severity).
|
||||
bool contains_errors() const { return error_count_ > 0; }
|
||||
|
@ -130,6 +142,9 @@ class List {
|
|||
/// @returns the last diagnostic in the list.
|
||||
iterator end() const { return entries_.end(); }
|
||||
|
||||
/// @returns a formatted string of all the diagnostics in this list.
|
||||
std::string str() const;
|
||||
|
||||
private:
|
||||
std::vector<Diagnostic> entries_;
|
||||
size_t error_count_ = 0;
|
||||
|
|
|
@ -33,6 +33,8 @@ const char* to_str(Severity severity) {
|
|||
return "warning";
|
||||
case Severity::Error:
|
||||
return "error";
|
||||
case Severity::InternalCompilerError:
|
||||
return "internal compiler error";
|
||||
case Severity::Fatal:
|
||||
return "fatal";
|
||||
}
|
||||
|
@ -112,6 +114,7 @@ Formatter::Formatter(const Style& style) : style_(style) {}
|
|||
void Formatter::format(const List& list, Printer* printer) const {
|
||||
State state{printer};
|
||||
|
||||
bool please_report_bug = false;
|
||||
bool first = true;
|
||||
for (auto diag : list) {
|
||||
state.set_style({});
|
||||
|
@ -120,7 +123,23 @@ void Formatter::format(const List& list, Printer* printer) const {
|
|||
}
|
||||
format(diag, state);
|
||||
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) {
|
||||
state.newline();
|
||||
}
|
||||
|
@ -154,14 +173,17 @@ void Formatter::format(const Diagnostic& diag, State& state) const {
|
|||
|
||||
Color severity_color = Color::kDefault;
|
||||
switch (diag.severity) {
|
||||
case Severity::Info:
|
||||
break;
|
||||
case Severity::Warning:
|
||||
severity_color = Color::kYellow;
|
||||
break;
|
||||
case Severity::Error:
|
||||
case Severity::Fatal:
|
||||
severity_color = Color::kRed;
|
||||
break;
|
||||
default:
|
||||
case Severity::Fatal:
|
||||
case Severity::InternalCompilerError:
|
||||
severity_color = Color::kMagenta;
|
||||
break;
|
||||
}
|
||||
if (style_.print_severity) {
|
||||
|
|
|
@ -39,6 +39,9 @@ class DiagFormatterTest : public testing::Test {
|
|||
Diagnostic diag_err{Severity::Error,
|
||||
Source{Source::Range{{3, 16}, {3, 21}}, &file}, "hiss",
|
||||
"abc123"};
|
||||
Diagnostic diag_ice{Severity::InternalCompilerError,
|
||||
Source{Source::Range{{4, 16}, {4, 19}}, &file},
|
||||
"unreachable"};
|
||||
Diagnostic diag_fatal{Severity::Fatal,
|
||||
Source{Source::Range{{4, 16}, {4, 19}}, &file},
|
||||
"nothing"};
|
||||
|
@ -46,21 +49,19 @@ class DiagFormatterTest : public testing::Test {
|
|||
|
||||
TEST_F(DiagFormatterTest, Simple) {
|
||||
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
|
||||
2:14: grrr
|
||||
3:16 abc123: hiss
|
||||
4:16: nothing)";
|
||||
3:16 abc123: hiss)";
|
||||
ASSERT_EQ(expect, got);
|
||||
}
|
||||
|
||||
TEST_F(DiagFormatterTest, SimpleNewlineAtEnd) {
|
||||
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
|
||||
2:14: grrr
|
||||
3:16 abc123: hiss
|
||||
4:16: nothing
|
||||
)";
|
||||
ASSERT_EQ(expect, got);
|
||||
}
|
||||
|
@ -75,27 +76,25 @@ TEST_F(DiagFormatterTest, SimpleNoSource) {
|
|||
|
||||
TEST_F(DiagFormatterTest, WithFile) {
|
||||
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
|
||||
file.name:2:14: grrr
|
||||
file.name:3:16 abc123: hiss
|
||||
file.name:4:16: nothing)";
|
||||
file.name:3:16 abc123: hiss)";
|
||||
ASSERT_EQ(expect, got);
|
||||
}
|
||||
|
||||
TEST_F(DiagFormatterTest, WithSeverity) {
|
||||
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
|
||||
2:14 warning: grrr
|
||||
3:16 error abc123: hiss
|
||||
4:16 fatal: nothing)";
|
||||
3:16 error abc123: hiss)";
|
||||
ASSERT_EQ(expect, got);
|
||||
}
|
||||
|
||||
TEST_F(DiagFormatterTest, WithLine) {
|
||||
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
|
||||
the cat says meow
|
||||
^
|
||||
|
@ -107,17 +106,13 @@ the dog says woof
|
|||
3:16 abc123: hiss
|
||||
the snake says quack
|
||||
^^^^^
|
||||
|
||||
4:16: nothing
|
||||
the snail says ???
|
||||
^^^
|
||||
)";
|
||||
ASSERT_EQ(expect, got);
|
||||
}
|
||||
|
||||
TEST_F(DiagFormatterTest, BasicWithFileSeverityLine) {
|
||||
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
|
||||
the cat says meow
|
||||
^
|
||||
|
@ -129,10 +124,6 @@ the dog says woof
|
|||
file.name:3:16 error abc123: hiss
|
||||
the snake says quack
|
||||
^^^^^
|
||||
|
||||
file.name:4:16 fatal: nothing
|
||||
the snail says ???
|
||||
^^^
|
||||
)";
|
||||
ASSERT_EQ(expect, got);
|
||||
}
|
||||
|
@ -154,6 +145,42 @@ the snail says ???
|
|||
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 diag
|
||||
} // namespace tint
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <utility>
|
||||
|
||||
#include "src/block_allocator.h"
|
||||
#include "src/debug.h"
|
||||
#include "src/program_builder.h"
|
||||
#include "src/semantic/intrinsic.h"
|
||||
#include "src/type/access_control_type.h"
|
||||
|
@ -641,10 +642,10 @@ class Impl : public IntrinsicTable {
|
|||
public:
|
||||
Impl();
|
||||
|
||||
IntrinsicTable::Result Lookup(
|
||||
ProgramBuilder& builder,
|
||||
semantic::IntrinsicType type,
|
||||
const std::vector<type::Type*>& args) const override;
|
||||
IntrinsicTable::Result Lookup(ProgramBuilder& builder,
|
||||
semantic::IntrinsicType type,
|
||||
const std::vector<type::Type*>& args,
|
||||
const Source& source) const override;
|
||||
|
||||
/// Holds the information about a single overload parameter used for matching
|
||||
struct Parameter {
|
||||
|
@ -660,9 +661,6 @@ class Impl : public IntrinsicTable {
|
|||
|
||||
/// A single overload definition.
|
||||
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
|
||||
/// types. If a match is made, the build intrinsic is returned, otherwise
|
||||
/// `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::IntrinsicType type,
|
||||
const std::vector<type::Type*>& arg_types,
|
||||
diag::List& diagnostics,
|
||||
int& match_score) const;
|
||||
|
||||
semantic::IntrinsicType type;
|
||||
|
@ -1288,12 +1287,13 @@ Impl::Impl() {
|
|||
// 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;
|
||||
ss << type << "(";
|
||||
ss << overload.type << "(";
|
||||
{
|
||||
bool first = true;
|
||||
for (auto param : parameters) {
|
||||
for (auto param : overload.parameters) {
|
||||
if (!first) {
|
||||
ss << ", ";
|
||||
}
|
||||
|
@ -1305,15 +1305,15 @@ std::string Impl::Overload::str() const {
|
|||
}
|
||||
}
|
||||
ss << ") -> ";
|
||||
ss << return_type->str();
|
||||
ss << overload.return_type->str();
|
||||
|
||||
if (!open_type_matchers.empty()) {
|
||||
if (!overload.open_type_matchers.empty()) {
|
||||
ss << " where: ";
|
||||
|
||||
for (uint32_t i = 0; i < static_cast<uint32_t>(OpenType::Count); i++) {
|
||||
auto open_type = static_cast<OpenType>(i);
|
||||
auto it = open_type_matchers.find(open_type);
|
||||
if (it != open_type_matchers.end()) {
|
||||
auto it = overload.open_type_matchers.find(open_type);
|
||||
if (it != overload.open_type_matchers.end()) {
|
||||
if (i > 0) {
|
||||
ss << ", ";
|
||||
}
|
||||
|
@ -1324,10 +1324,33 @@ std::string Impl::Overload::str() const {
|
|||
return ss.str();
|
||||
}
|
||||
|
||||
IntrinsicTable::Result Impl::Lookup(
|
||||
ProgramBuilder& builder,
|
||||
semantic::IntrinsicType type,
|
||||
const std::vector<type::Type*>& args) const {
|
||||
/// @return a string representing a call to an intrinsic with the given argument
|
||||
/// types.
|
||||
std::string CallSignature(ProgramBuilder& builder,
|
||||
semantic::IntrinsicType type,
|
||||
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
|
||||
// the user intended to call.
|
||||
struct Candidate {
|
||||
|
@ -1342,8 +1365,9 @@ IntrinsicTable::Result Impl::Lookup(
|
|||
// type. This is horribly inefficient.
|
||||
for (auto& overload : overloads_) {
|
||||
int match_score = 0;
|
||||
if (auto* intrinsic = overload.Match(builder, type, args, match_score)) {
|
||||
return Result{intrinsic, ""}; // Match found
|
||||
if (auto* intrinsic =
|
||||
overload.Match(builder, type, args, diagnostics, match_score)) {
|
||||
return Result{intrinsic, {}}; // Match found
|
||||
}
|
||||
if (match_score > 0) {
|
||||
candidates.emplace_back(Candidate{&overload, match_score});
|
||||
|
@ -1357,34 +1381,25 @@ IntrinsicTable::Result Impl::Lookup(
|
|||
|
||||
// Generate an error message
|
||||
std::stringstream ss;
|
||||
ss << "no matching call to " << semantic::str(type) << "(";
|
||||
{
|
||||
bool first = true;
|
||||
for (auto* arg : args) {
|
||||
if (!first) {
|
||||
ss << ", ";
|
||||
}
|
||||
first = false;
|
||||
ss << arg->FriendlyName(builder.Symbols());
|
||||
}
|
||||
}
|
||||
ss << ")" << std::endl;
|
||||
|
||||
ss << "no matching call to " << CallSignature(builder, type, args)
|
||||
<< std::endl;
|
||||
if (!candidates.empty()) {
|
||||
ss << std::endl;
|
||||
ss << candidates.size() << " candidate function"
|
||||
<< (candidates.size() > 1 ? "s:" : ":") << std::endl;
|
||||
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::IntrinsicType intrinsic,
|
||||
const std::vector<type::Type*>& args,
|
||||
diag::List& diagnostics,
|
||||
int& match_score) const {
|
||||
if (type != intrinsic) {
|
||||
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()) {
|
||||
// We have an overload that claims to have matched, but didn't actually
|
||||
// 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;
|
||||
}
|
||||
auto* resolved_type = type_it->second;
|
||||
if (resolved_type == nullptr) {
|
||||
// We have an overload that claims to have matched, but has a nullptr
|
||||
// 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;
|
||||
}
|
||||
if (!matcher->Match(matcher_state, resolved_type)) {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "src/diagnostic/diagnostic.h"
|
||||
#include "src/semantic/intrinsic.h"
|
||||
|
||||
namespace tint {
|
||||
|
@ -39,18 +40,20 @@ class IntrinsicTable {
|
|||
struct Result {
|
||||
/// The intrinsic, if the lookup succeeded, otherwise nullptr
|
||||
semantic::Intrinsic* intrinsic;
|
||||
/// The error message, if the lookup failed, otherwise empty
|
||||
std::string error;
|
||||
/// Diagnostic messages
|
||||
diag::List diagnostics;
|
||||
};
|
||||
|
||||
/// Lookup looks for the intrinsic overload with the given signature.
|
||||
/// @param builder the program builder
|
||||
/// @param type the intrinsic type
|
||||
/// @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
|
||||
virtual Result Lookup(ProgramBuilder& builder,
|
||||
semantic::IntrinsicType type,
|
||||
const std::vector<type::Type*>& args) const = 0;
|
||||
const std::vector<type::Type*>& args,
|
||||
const Source& source) const = 0;
|
||||
};
|
||||
|
||||
} // namespace tint
|
||||
|
|
|
@ -38,44 +38,44 @@ class IntrinsicTableTest : public testing::Test, public ProgramBuilder {
|
|||
};
|
||||
|
||||
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_EQ(result.error, "");
|
||||
ASSERT_EQ(result.diagnostics.str(), "");
|
||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCos);
|
||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
|
||||
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{ty.f32()}));
|
||||
}
|
||||
|
||||
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_THAT(result.error, HasSubstr("no matching call"));
|
||||
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
||||
}
|
||||
|
||||
TEST_F(IntrinsicTableTest, MatchU32) {
|
||||
auto result =
|
||||
table->Lookup(*this, IntrinsicType::kUnpack2x16Float, {ty.u32()});
|
||||
auto result = table->Lookup(*this, IntrinsicType::kUnpack2x16Float,
|
||||
{ty.u32()}, Source{});
|
||||
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->ReturnType(), ty.vec2<f32>());
|
||||
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{ty.u32()}));
|
||||
}
|
||||
|
||||
TEST_F(IntrinsicTableTest, MismatchU32) {
|
||||
auto result =
|
||||
table->Lookup(*this, IntrinsicType::kUnpack2x16Float, {ty.f32()});
|
||||
auto result = table->Lookup(*this, IntrinsicType::kUnpack2x16Float,
|
||||
{ty.f32()}, Source{});
|
||||
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) {
|
||||
auto* tex =
|
||||
create<type::SampledTexture>(type::TextureDimension::k1d, ty.f32());
|
||||
auto result =
|
||||
table->Lookup(*this, IntrinsicType::kTextureLoad, {tex, ty.i32()});
|
||||
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
|
||||
{tex, ty.i32()}, Source{});
|
||||
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->ReturnType(), ty.vec4<f32>());
|
||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
||||
|
@ -86,41 +86,44 @@ TEST_F(IntrinsicTableTest, MatchI32) {
|
|||
TEST_F(IntrinsicTableTest, MismatchI32) {
|
||||
auto* tex =
|
||||
create<type::SampledTexture>(type::TextureDimension::k1d, ty.f32());
|
||||
auto result =
|
||||
table->Lookup(*this, IntrinsicType::kTextureLoad, {tex, ty.f32()});
|
||||
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
|
||||
{tex, ty.f32()}, Source{});
|
||||
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) {
|
||||
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_EQ(result.error, "");
|
||||
ASSERT_EQ(result.diagnostics.str(), "");
|
||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCountOneBits);
|
||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.i32());
|
||||
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{ty.i32()}));
|
||||
}
|
||||
|
||||
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_EQ(result.error, "");
|
||||
ASSERT_EQ(result.diagnostics.str(), "");
|
||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCountOneBits);
|
||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.u32());
|
||||
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{ty.u32()}));
|
||||
}
|
||||
|
||||
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_THAT(result.error, HasSubstr("no matching call"));
|
||||
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
||||
}
|
||||
|
||||
TEST_F(IntrinsicTableTest, MatchFIU32AsI32) {
|
||||
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_EQ(result.error, "");
|
||||
ASSERT_EQ(result.diagnostics.str(), "");
|
||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
|
||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.i32());
|
||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
||||
|
@ -130,9 +133,9 @@ TEST_F(IntrinsicTableTest, MatchFIU32AsI32) {
|
|||
|
||||
TEST_F(IntrinsicTableTest, MatchFIU32AsU32) {
|
||||
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_EQ(result.error, "");
|
||||
ASSERT_EQ(result.diagnostics.str(), "");
|
||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
|
||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.u32());
|
||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
||||
|
@ -142,9 +145,9 @@ TEST_F(IntrinsicTableTest, MatchFIU32AsU32) {
|
|||
|
||||
TEST_F(IntrinsicTableTest, MatchFIU32AsF32) {
|
||||
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_EQ(result.error, "");
|
||||
ASSERT_EQ(result.diagnostics.str(), "");
|
||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
|
||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
|
||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
||||
|
@ -154,16 +157,16 @@ TEST_F(IntrinsicTableTest, MatchFIU32AsF32) {
|
|||
|
||||
TEST_F(IntrinsicTableTest, MismatchFIU32) {
|
||||
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_THAT(result.error, HasSubstr("no matching call"));
|
||||
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
||||
}
|
||||
|
||||
TEST_F(IntrinsicTableTest, MatchBool) {
|
||||
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_EQ(result.error, "");
|
||||
ASSERT_EQ(result.diagnostics.str(), "");
|
||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kSelect);
|
||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
|
||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
||||
|
@ -173,17 +176,17 @@ TEST_F(IntrinsicTableTest, MatchBool) {
|
|||
|
||||
TEST_F(IntrinsicTableTest, MismatchBool) {
|
||||
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_THAT(result.error, HasSubstr("no matching call"));
|
||||
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
||||
}
|
||||
|
||||
TEST_F(IntrinsicTableTest, MatchPointer) {
|
||||
auto result =
|
||||
table->Lookup(*this, IntrinsicType::kModf,
|
||||
{ty.f32(), ty.pointer<f32>(ast::StorageClass::kNone)});
|
||||
auto result = table->Lookup(
|
||||
*this, IntrinsicType::kModf,
|
||||
{ty.f32(), ty.pointer<f32>(ast::StorageClass::kNone)}, Source{});
|
||||
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->ReturnType(), ty.f32());
|
||||
EXPECT_THAT(
|
||||
|
@ -193,17 +196,17 @@ TEST_F(IntrinsicTableTest, MatchPointer) {
|
|||
}
|
||||
|
||||
TEST_F(IntrinsicTableTest, MismatchPointer) {
|
||||
auto result =
|
||||
table->Lookup(*this, IntrinsicType::kModf, {ty.f32(), ty.f32()});
|
||||
auto result = table->Lookup(*this, IntrinsicType::kModf, {ty.f32(), ty.f32()},
|
||||
Source{});
|
||||
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) {
|
||||
auto result =
|
||||
table->Lookup(*this, IntrinsicType::kArrayLength, {ty.array<f32>()});
|
||||
auto result = table->Lookup(*this, IntrinsicType::kArrayLength,
|
||||
{ty.array<f32>()}, Source{});
|
||||
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->ReturnType(), ty.u32());
|
||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
||||
|
@ -211,9 +214,10 @@ TEST_F(IntrinsicTableTest, MatchArray) {
|
|||
}
|
||||
|
||||
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_THAT(result.error, HasSubstr("no matching call"));
|
||||
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
||||
}
|
||||
|
||||
TEST_F(IntrinsicTableTest, MatchSampler) {
|
||||
|
@ -221,9 +225,9 @@ TEST_F(IntrinsicTableTest, MatchSampler) {
|
|||
create<type::SampledTexture>(type::TextureDimension::k2d, ty.f32());
|
||||
auto* sampler = create<type::Sampler>(type::SamplerKind::kSampler);
|
||||
auto result = table->Lookup(*this, IntrinsicType::kTextureSample,
|
||||
{tex, sampler, ty.vec2<f32>()});
|
||||
{tex, sampler, ty.vec2<f32>()}, Source{});
|
||||
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->ReturnType(), ty.vec4<f32>());
|
||||
EXPECT_THAT(
|
||||
|
@ -237,18 +241,18 @@ TEST_F(IntrinsicTableTest, MismatchSampler) {
|
|||
auto* tex =
|
||||
create<type::SampledTexture>(type::TextureDimension::k2d, ty.f32());
|
||||
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_THAT(result.error, HasSubstr("no matching call"));
|
||||
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
||||
}
|
||||
|
||||
TEST_F(IntrinsicTableTest, MatchSampledTexture) {
|
||||
auto* tex =
|
||||
create<type::SampledTexture>(type::TextureDimension::k2d, ty.f32());
|
||||
auto result =
|
||||
table->Lookup(*this, IntrinsicType::kTextureLoad, {tex, ty.vec2<i32>()});
|
||||
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
|
||||
{tex, ty.vec2<i32>()}, Source{});
|
||||
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->ReturnType(), ty.vec4<f32>());
|
||||
EXPECT_THAT(
|
||||
|
@ -261,9 +265,9 @@ TEST_F(IntrinsicTableTest, MatchMultisampledTexture) {
|
|||
auto* tex =
|
||||
create<type::MultisampledTexture>(type::TextureDimension::k2d, ty.f32());
|
||||
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_EQ(result.error, "");
|
||||
ASSERT_EQ(result.diagnostics.str(), "");
|
||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
|
||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.vec4<f32>());
|
||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
||||
|
@ -274,10 +278,10 @@ TEST_F(IntrinsicTableTest, MatchMultisampledTexture) {
|
|||
|
||||
TEST_F(IntrinsicTableTest, MatchDepthTexture) {
|
||||
auto* tex = create<type::DepthTexture>(type::TextureDimension::k2d);
|
||||
auto result =
|
||||
table->Lookup(*this, IntrinsicType::kTextureLoad, {tex, ty.vec2<i32>()});
|
||||
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
|
||||
{tex, ty.vec2<i32>()}, Source{});
|
||||
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->ReturnType(), ty.f32());
|
||||
EXPECT_THAT(
|
||||
|
@ -293,9 +297,9 @@ TEST_F(IntrinsicTableTest, MatchROStorageTexture) {
|
|||
auto* tex_ac =
|
||||
create<type::AccessControl>(ast::AccessControl::kReadOnly, tex);
|
||||
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
|
||||
{tex_ac, ty.vec2<i32>()});
|
||||
{tex_ac, ty.vec2<i32>()}, Source{});
|
||||
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->ReturnType(), ty.vec4<f32>());
|
||||
EXPECT_THAT(
|
||||
|
@ -310,10 +314,11 @@ TEST_F(IntrinsicTableTest, MatchWOStorageTexture) {
|
|||
type::StorageTexture::SubtypeFor(type::ImageFormat::kR16Float, Types()));
|
||||
auto* tex_ac =
|
||||
create<type::AccessControl>(ast::AccessControl::kWriteOnly, tex);
|
||||
auto result = table->Lookup(*this, IntrinsicType::kTextureStore,
|
||||
{tex_ac, ty.vec2<i32>(), ty.vec4<f32>()});
|
||||
auto result =
|
||||
table->Lookup(*this, IntrinsicType::kTextureStore,
|
||||
{tex_ac, ty.vec2<i32>(), ty.vec4<f32>()}, Source{});
|
||||
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->ReturnType(), ty.void_());
|
||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
||||
|
@ -324,16 +329,17 @@ TEST_F(IntrinsicTableTest, MatchWOStorageTexture) {
|
|||
|
||||
TEST_F(IntrinsicTableTest, MismatchTexture) {
|
||||
auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
|
||||
{ty.f32(), ty.vec2<i32>()});
|
||||
{ty.f32(), ty.vec2<i32>()}, Source{});
|
||||
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) {
|
||||
auto result = table->Lookup(*this, IntrinsicType::kCos,
|
||||
{ty.pointer<f32>(ast::StorageClass::kNone)});
|
||||
auto result =
|
||||
table->Lookup(*this, IntrinsicType::kCos,
|
||||
{ty.pointer<f32>(ast::StorageClass::kNone)}, Source{});
|
||||
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->ReturnType(), 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_b = ty.alias("alias_b", alias_a);
|
||||
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_EQ(result.error, "");
|
||||
ASSERT_EQ(result.diagnostics.str(), "");
|
||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCos);
|
||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
|
||||
EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{ty.f32()}));
|
||||
|
@ -353,9 +359,9 @@ TEST_F(IntrinsicTableTest, MatchWithAliasUnwrapping) {
|
|||
|
||||
TEST_F(IntrinsicTableTest, MatchOpenType) {
|
||||
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_EQ(result.error, "");
|
||||
ASSERT_EQ(result.diagnostics.str(), "");
|
||||
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
|
||||
EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
|
||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
||||
|
@ -365,16 +371,17 @@ TEST_F(IntrinsicTableTest, MatchOpenType) {
|
|||
|
||||
TEST_F(IntrinsicTableTest, MismatchOpenType) {
|
||||
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_THAT(result.error, HasSubstr("no matching call"));
|
||||
ASSERT_THAT(result.diagnostics.str(), HasSubstr("no matching call"));
|
||||
}
|
||||
|
||||
TEST_F(IntrinsicTableTest, MatchOpenSizeVector) {
|
||||
auto result = table->Lookup(*this, IntrinsicType::kClamp,
|
||||
{ty.vec2<f32>(), ty.vec2<f32>(), ty.vec2<f32>()});
|
||||
auto result =
|
||||
table->Lookup(*this, IntrinsicType::kClamp,
|
||||
{ty.vec2<f32>(), ty.vec2<f32>(), ty.vec2<f32>()}, Source{});
|
||||
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->ReturnType(), ty.vec2<f32>());
|
||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
||||
|
@ -383,17 +390,18 @@ TEST_F(IntrinsicTableTest, MatchOpenSizeVector) {
|
|||
}
|
||||
|
||||
TEST_F(IntrinsicTableTest, MismatchOpenSizeVector) {
|
||||
auto result = table->Lookup(*this, IntrinsicType::kClamp,
|
||||
{ty.vec2<f32>(), ty.vec2<u32>(), ty.vec2<f32>()});
|
||||
auto result =
|
||||
table->Lookup(*this, IntrinsicType::kClamp,
|
||||
{ty.vec2<f32>(), ty.vec2<u32>(), ty.vec2<f32>()}, Source{});
|
||||
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) {
|
||||
auto result =
|
||||
table->Lookup(*this, IntrinsicType::kDeterminant, {ty.mat3x3<f32>()});
|
||||
auto result = table->Lookup(*this, IntrinsicType::kDeterminant,
|
||||
{ty.mat3x3<f32>()}, Source{});
|
||||
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->ReturnType(), ty.f32());
|
||||
EXPECT_THAT(result.intrinsic->Parameters(),
|
||||
|
@ -401,19 +409,19 @@ TEST_F(IntrinsicTableTest, MatchOpenSizeMatrix) {
|
|||
}
|
||||
|
||||
TEST_F(IntrinsicTableTest, MismatchOpenSizeMatrix) {
|
||||
auto result =
|
||||
table->Lookup(*this, IntrinsicType::kDeterminant, {ty.mat3x2<f32>()});
|
||||
auto result = table->Lookup(*this, IntrinsicType::kDeterminant,
|
||||
{ty.mat3x2<f32>()}, Source{});
|
||||
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) {
|
||||
// None of the arguments match, so expect the overloads with 2 parameters to
|
||||
// come first
|
||||
auto result = table->Lookup(*this, IntrinsicType::kTextureDimensions,
|
||||
{ty.bool_(), ty.bool_()});
|
||||
ASSERT_EQ(result.error,
|
||||
R"(no matching call to textureDimensions(bool, bool)
|
||||
{ty.bool_(), ty.bool_()}, Source{});
|
||||
ASSERT_EQ(result.diagnostics.str(),
|
||||
R"(error: no matching call to textureDimensions(bool, bool)
|
||||
|
||||
27 candidate functions:
|
||||
textureDimensions(texture : texture_2d<T>, level : i32) -> vec2<i32>
|
||||
|
@ -449,9 +457,10 @@ TEST_F(IntrinsicTableTest, OverloadOrderByNumberOfParameters) {
|
|||
TEST_F(IntrinsicTableTest, OverloadOrderByMatchingParameter) {
|
||||
auto* tex = create<type::DepthTexture>(type::TextureDimension::k2d);
|
||||
auto result = table->Lookup(*this, IntrinsicType::kTextureDimensions,
|
||||
{tex, ty.bool_()});
|
||||
ASSERT_EQ(result.error,
|
||||
R"(no matching call to textureDimensions(texture_depth_2d, bool)
|
||||
{tex, ty.bool_()}, Source{});
|
||||
ASSERT_EQ(
|
||||
result.diagnostics.str(),
|
||||
R"(error: no matching call to textureDimensions(texture_depth_2d, bool)
|
||||
|
||||
27 candidate functions:
|
||||
textureDimensions(texture : texture_depth_2d, level : i32) -> vec2<i32>
|
||||
|
|
|
@ -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,46 +55,51 @@ void Hlsl::PromoteArrayInitializerToConstVar(CloneContext& ctx) const {
|
|||
|
||||
for (auto* src_node : ctx.src->ASTNodes().Objects()) {
|
||||
if (auto* src_init = src_node->As<ast::TypeConstructorExpression>()) {
|
||||
if (auto* src_sem_expr = ctx.src->Sem().Get(src_init)) {
|
||||
auto* src_sem_stmt = src_sem_expr->Stmt();
|
||||
if (!src_sem_stmt) {
|
||||
// Expression is outside of a statement. This usually means the
|
||||
// expression is part of a global (module-scope) constant declaration.
|
||||
// These must be constexpr, and so cannot contain the type of
|
||||
// expressions that must be sanitized.
|
||||
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();
|
||||
if (!src_sem_stmt) {
|
||||
// Expression is outside of a statement. This usually means the
|
||||
// expression is part of a global (module-scope) constant declaration.
|
||||
// These must be constexpr, and so cannot contain the type of
|
||||
// expressions that must be sanitized.
|
||||
continue;
|
||||
}
|
||||
auto* src_stmt = src_sem_stmt->Declaration();
|
||||
|
||||
if (auto* src_var_decl = src_stmt->As<ast::VariableDeclStatement>()) {
|
||||
if (src_var_decl->variable()->constructor() == src_init) {
|
||||
// This statement is just a variable declaration with the array
|
||||
// initializer as the constructor value. This is what we're
|
||||
// attempting to transform to, and so ignore.
|
||||
continue;
|
||||
}
|
||||
auto* src_stmt = src_sem_stmt->Declaration();
|
||||
}
|
||||
|
||||
if (auto* src_var_decl = src_stmt->As<ast::VariableDeclStatement>()) {
|
||||
if (src_var_decl->variable()->constructor() == src_init) {
|
||||
// This statement is just a variable declaration with the array
|
||||
// initializer as the constructor value. This is what we're
|
||||
// attempting to transform to, and so ignore.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (auto* src_array_ty = src_sem_expr->Type()->As<type::Array>()) {
|
||||
// Create a new symbol for the constant
|
||||
auto dst_symbol = ctx.dst->Symbols().New();
|
||||
// Clone the array type
|
||||
auto* dst_array_ty = ctx.Clone(src_array_ty);
|
||||
// Clone the array initializer
|
||||
auto* dst_init = ctx.Clone(src_init);
|
||||
// Construct the constant that holds the array
|
||||
auto* dst_var = ctx.dst->Const(dst_symbol, dst_array_ty, dst_init);
|
||||
// Construct the variable declaration statement
|
||||
auto* dst_var_decl =
|
||||
ctx.dst->create<ast::VariableDeclStatement>(dst_var);
|
||||
// Construct the identifier for referencing the constant
|
||||
auto* dst_ident = ctx.dst->Expr(dst_symbol);
|
||||
|
||||
if (auto* src_array_ty = src_sem_expr->Type()->As<type::Array>()) {
|
||||
// Create a new symbol for the constant
|
||||
auto dst_symbol = ctx.dst->Symbols().New();
|
||||
// Clone the array type
|
||||
auto* dst_array_ty = ctx.Clone(src_array_ty);
|
||||
// Clone the array initializer
|
||||
auto* dst_init = ctx.Clone(src_init);
|
||||
// Construct the constant that holds the array
|
||||
auto* dst_var = ctx.dst->Const(dst_symbol, dst_array_ty, dst_init);
|
||||
// Construct the variable declaration statement
|
||||
auto* dst_var_decl =
|
||||
ctx.dst->create<ast::VariableDeclStatement>(dst_var);
|
||||
// Construct the identifier for referencing the constant
|
||||
auto* dst_ident = ctx.dst->Expr(dst_symbol);
|
||||
|
||||
// Insert the constant before the usage
|
||||
ctx.InsertBefore(src_stmt, dst_var_decl);
|
||||
// Replace the inlined array with a reference to the constant
|
||||
ctx.Replace(src_init, dst_ident);
|
||||
}
|
||||
// Insert the constant before the usage
|
||||
ctx.InsertBefore(src_stmt, dst_var_decl);
|
||||
// Replace the inlined array with a reference to the constant
|
||||
ctx.Replace(src_init, dst_ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "src/ast/type_constructor_expression.h"
|
||||
#include "src/ast/unary_op_expression.h"
|
||||
#include "src/ast/variable_decl_statement.h"
|
||||
#include "src/diagnostic/formatter.h"
|
||||
#include "src/program_builder.h"
|
||||
#include "src/semantic/call.h"
|
||||
#include "src/semantic/expression.h"
|
||||
|
@ -106,15 +107,6 @@ diag::List TypeDeterminer::Run(Program* program) {
|
|||
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,
|
||||
bool local) {
|
||||
if (current_function_ == nullptr) {
|
||||
|
@ -242,8 +234,8 @@ bool TypeDeterminer::DetermineVariableStorageClass(ast::Statement* stmt) {
|
|||
}
|
||||
|
||||
if (info->storage_class != ast::StorageClass::kNone) {
|
||||
set_error(stmt->source(),
|
||||
"function variable has a non-function storage class");
|
||||
diagnostics_.add_error("function variable has a non-function storage class",
|
||||
stmt->source());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -321,8 +313,9 @@ bool TypeDeterminer::DetermineResultType(ast::Statement* stmt) {
|
|||
return DetermineResultType(v->variable()->constructor());
|
||||
}
|
||||
|
||||
set_error(stmt->source(), "unknown statement type for type determination: " +
|
||||
builder_->str(stmt));
|
||||
diagnostics_.add_error(
|
||||
"unknown statement type for type determination: " + builder_->str(stmt),
|
||||
stmt->source());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -370,7 +363,8 @@ bool TypeDeterminer::DetermineResultType(ast::Expression* expr) {
|
|||
return DetermineUnaryOp(u);
|
||||
}
|
||||
|
||||
set_error(expr->source(), "unknown expression for type determination");
|
||||
diagnostics_.add_error("unknown expression for type determination",
|
||||
expr->source());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -393,9 +387,9 @@ bool TypeDeterminer::DetermineArrayAccessor(
|
|||
} else if (auto* mat = parent_type->As<type::Matrix>()) {
|
||||
ret = builder_->create<type::Vector>(mat->type(), mat->rows());
|
||||
} else {
|
||||
set_error(expr->source(), "invalid parent type (" +
|
||||
parent_type->type_name() +
|
||||
") in array accessor");
|
||||
diagnostics_.add_error("invalid parent type (" + parent_type->type_name() +
|
||||
") in array accessor",
|
||||
expr->source());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -436,7 +430,7 @@ bool TypeDeterminer::DetermineCall(ast::CallExpression* call) {
|
|||
// the safe side.
|
||||
auto* ident = call->func()->As<ast::IdentifierExpression>();
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -454,7 +448,8 @@ bool TypeDeterminer::DetermineCall(ast::CallExpression* call) {
|
|||
|
||||
auto callee_func_it = symbol_to_function_.find(ident->symbol());
|
||||
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;
|
||||
}
|
||||
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());
|
||||
if (iter == symbol_to_function_.end()) {
|
||||
set_error(call->source(),
|
||||
"v-0005: function must be declared before use: '" + name + "'");
|
||||
diagnostics_.add_error(
|
||||
"v-0005: function must be declared before use: '" + name + "'",
|
||||
call->source());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -490,10 +486,11 @@ bool TypeDeterminer::DetermineIntrinsicCall(
|
|||
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) {
|
||||
// Intrinsic lookup failed.
|
||||
set_error(call->source(), result.error);
|
||||
diagnostics_.add(result.diagnostics);
|
||||
|
||||
// TODO(bclayton): https://crbug.com/tint/487
|
||||
// The Validator expects intrinsic signature mismatches to still produce
|
||||
|
@ -583,8 +580,9 @@ bool TypeDeterminer::DetermineIdentifier(ast::IdentifierExpression* expr) {
|
|||
return true;
|
||||
}
|
||||
|
||||
set_error(expr->source(),
|
||||
"v-0006: identifier must be declared before use: " + name);
|
||||
diagnostics_.add_error(
|
||||
"v-0006: identifier must be declared before use: " + name,
|
||||
expr->source());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -781,9 +779,9 @@ bool TypeDeterminer::DetermineMemberAccessor(
|
|||
}
|
||||
|
||||
if (ret == nullptr) {
|
||||
set_error(expr->source(), "struct member " +
|
||||
builder_->Symbols().NameFor(symbol) +
|
||||
" not found");
|
||||
diagnostics_.add_error(
|
||||
"struct member " + builder_->Symbols().NameFor(symbol) + " not found",
|
||||
expr->source());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -810,10 +808,10 @@ bool TypeDeterminer::DetermineMemberAccessor(
|
|||
static_cast<uint32_t>(size));
|
||||
}
|
||||
} else {
|
||||
set_error(
|
||||
expr->source(),
|
||||
diagnostics_.add_error(
|
||||
"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;
|
||||
}
|
||||
|
||||
|
@ -893,7 +891,7 @@ bool TypeDeterminer::DetermineBinary(ast::BinaryExpression* expr) {
|
|||
return true;
|
||||
}
|
||||
|
||||
set_error(expr->source(), "Unknown binary expression");
|
||||
diagnostics_.add_error("Unknown binary expression", expr->source());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ class TypeDeterminer {
|
|||
static diag::List Run(Program* program);
|
||||
|
||||
/// @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
|
||||
bool Determine();
|
||||
|
@ -169,7 +169,6 @@ class TypeDeterminer {
|
|||
const ast::ExpressionList& params,
|
||||
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_entry_points(const Symbol& fn_sym, Symbol ep_sym);
|
||||
|
||||
|
@ -198,7 +197,7 @@ class TypeDeterminer {
|
|||
|
||||
ProgramBuilder* const builder_;
|
||||
std::unique_ptr<IntrinsicTable> const intrinsic_table_;
|
||||
std::string error_;
|
||||
diag::List diagnostics_;
|
||||
ScopeStack<VariableInfo*> variable_stack_;
|
||||
std::unordered_map<Symbol, FunctionInfo*> symbol_to_function_;
|
||||
std::unordered_map<ast::Function*, FunctionInfo*> function_to_info_;
|
||||
|
|
|
@ -142,7 +142,7 @@ TEST_F(TypeDeterminerTest, Error_WithEmptySource) {
|
|||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
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) {
|
||||
|
@ -152,7 +152,7 @@ TEST_F(TypeDeterminerTest, Stmt_Error_Unknown) {
|
|||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
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) {
|
||||
|
@ -396,8 +396,9 @@ TEST_F(TypeDeterminerTest, Stmt_Call_undeclared) {
|
|||
|
||||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
EXPECT_EQ(td()->error(),
|
||||
"12:34: v-0006: identifier must be declared before use: func");
|
||||
EXPECT_EQ(
|
||||
td()->error(),
|
||||
"12:34 error: v-0006: identifier must be declared before use: func");
|
||||
}
|
||||
|
||||
TEST_F(TypeDeterminerTest, Stmt_VariableDecl) {
|
||||
|
@ -530,7 +531,8 @@ TEST_F(TypeDeterminerTest, Expr_Error_Unknown) {
|
|||
|
||||
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) {
|
||||
|
@ -1358,7 +1360,7 @@ TEST_P(IntrinsicDerivativeTest, MissingParam) {
|
|||
|
||||
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"
|
||||
"2 candidate functions:\n " +
|
||||
name + "(f32) -> f32\n " + name +
|
||||
|
@ -1436,7 +1438,7 @@ TEST_P(Intrinsic_FloatMethod, MissingParam) {
|
|||
|
||||
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"
|
||||
"2 candidate functions:\n " +
|
||||
name + "(f32) -> bool\n " + name +
|
||||
|
@ -1453,7 +1455,7 @@ TEST_P(Intrinsic_FloatMethod, TooManyParams) {
|
|||
|
||||
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"
|
||||
"2 candidate functions:\n " +
|
||||
name + "(f32) -> bool\n " + name +
|
||||
|
@ -1687,7 +1689,7 @@ TEST_F(TypeDeterminerTest, Intrinsic_Dot_Error_Scalar) {
|
|||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
EXPECT_EQ(td()->error(),
|
||||
R"(no matching call to dot(f32, f32)
|
||||
R"(error: no matching call to dot(f32, f32)
|
||||
|
||||
1 candidate function:
|
||||
dot(vecN<f32>, vecN<f32>) -> f32
|
||||
|
@ -1703,7 +1705,7 @@ TEST_F(TypeDeterminerTest, Intrinsic_Dot_Error_VectorInt) {
|
|||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
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:
|
||||
dot(vecN<f32>, vecN<f32>) -> f32
|
||||
|
@ -1733,7 +1735,7 @@ TEST_F(TypeDeterminerTest, Intrinsic_Select_Error_NoParams) {
|
|||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
EXPECT_EQ(td()->error(),
|
||||
R"(no matching call to select()
|
||||
R"(error: no matching call to select()
|
||||
|
||||
2 candidate functions:
|
||||
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_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:
|
||||
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_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:
|
||||
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_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:
|
||||
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_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:
|
||||
select(T, T, bool) -> T where: T is scalar
|
||||
|
@ -1856,7 +1858,7 @@ TEST_F(TypeDeterminerTest, StorageClass_NonFunctionClassError) {
|
|||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
EXPECT_EQ(td()->error(),
|
||||
"function variable has a non-function storage class");
|
||||
"error: function variable has a non-function storage class");
|
||||
}
|
||||
|
||||
struct IntrinsicData {
|
||||
|
@ -1983,8 +1985,8 @@ TEST_P(Intrinsic_DataPackingTest, Error_IncorrectParamType) {
|
|||
|
||||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
EXPECT_THAT(td()->error(),
|
||||
HasSubstr("no matching call to " + std::string(param.name)));
|
||||
EXPECT_THAT(td()->error(), HasSubstr("error: no matching call to " +
|
||||
std::string(param.name)));
|
||||
}
|
||||
|
||||
TEST_P(Intrinsic_DataPackingTest, Error_NoParams) {
|
||||
|
@ -1995,8 +1997,8 @@ TEST_P(Intrinsic_DataPackingTest, Error_NoParams) {
|
|||
|
||||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
EXPECT_THAT(td()->error(),
|
||||
HasSubstr("no matching call to " + std::string(param.name)));
|
||||
EXPECT_THAT(td()->error(), HasSubstr("error: no matching call to " +
|
||||
std::string(param.name)));
|
||||
}
|
||||
|
||||
TEST_P(Intrinsic_DataPackingTest, Error_TooManyParams) {
|
||||
|
@ -2011,8 +2013,8 @@ TEST_P(Intrinsic_DataPackingTest, Error_TooManyParams) {
|
|||
|
||||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
EXPECT_THAT(td()->error(),
|
||||
HasSubstr("no matching call to " + std::string(param.name)));
|
||||
EXPECT_THAT(td()->error(), HasSubstr("error: no matching call to " +
|
||||
std::string(param.name)));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
|
@ -2089,12 +2091,12 @@ TEST_P(Intrinsic_SingleParamTest, Error_NoParams) {
|
|||
|
||||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
EXPECT_EQ(td()->error(), "no matching call to " + std::string(param.name) +
|
||||
"()\n\n"
|
||||
"2 candidate functions:\n " +
|
||||
std::string(param.name) + "(f32) -> f32\n " +
|
||||
std::string(param.name) +
|
||||
"(vecN<f32>) -> vecN<f32>\n");
|
||||
EXPECT_EQ(td()->error(),
|
||||
"error: no matching call to " + std::string(param.name) +
|
||||
"()\n\n"
|
||||
"2 candidate functions:\n " +
|
||||
std::string(param.name) + "(f32) -> f32\n " +
|
||||
std::string(param.name) + "(vecN<f32>) -> vecN<f32>\n");
|
||||
}
|
||||
|
||||
TEST_P(Intrinsic_SingleParamTest, Error_TooManyParams) {
|
||||
|
@ -2105,12 +2107,12 @@ TEST_P(Intrinsic_SingleParamTest, Error_TooManyParams) {
|
|||
|
||||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
EXPECT_EQ(td()->error(), "no matching call to " + std::string(param.name) +
|
||||
"(i32, i32, i32)\n\n"
|
||||
"2 candidate functions:\n " +
|
||||
std::string(param.name) + "(f32) -> f32\n " +
|
||||
std::string(param.name) +
|
||||
"(vecN<f32>) -> vecN<f32>\n");
|
||||
EXPECT_EQ(td()->error(),
|
||||
"error: no matching call to " + std::string(param.name) +
|
||||
"(i32, i32, i32)\n\n"
|
||||
"2 candidate functions:\n " +
|
||||
std::string(param.name) + "(f32) -> f32\n " +
|
||||
std::string(param.name) + "(vecN<f32>) -> vecN<f32>\n");
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
|
@ -2157,7 +2159,7 @@ TEST_F(IntrinsicDataTest, ArrayLength_Error_ArraySized) {
|
|||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
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"
|
||||
" arrayLength(array<T>) -> u32\n");
|
||||
}
|
||||
|
@ -2180,7 +2182,7 @@ TEST_F(IntrinsicDataTest, Normalize_Error_NoParams) {
|
|||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
EXPECT_EQ(td()->error(),
|
||||
"no matching call to normalize()\n\n"
|
||||
"error: no matching call to normalize()\n\n"
|
||||
"1 candidate function:\n"
|
||||
" normalize(vecN<f32>) -> vecN<f32>\n");
|
||||
}
|
||||
|
@ -2216,7 +2218,7 @@ TEST_F(IntrinsicDataTest, Frexp_Error_FirstParamInt) {
|
|||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
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"
|
||||
" frexp(f32, ptr<T>) -> f32 where: T is i32 or u32\n"
|
||||
" frexp(vecN<f32>, ptr<vecN<T>>) -> vecN<f32> "
|
||||
|
@ -2231,7 +2233,7 @@ TEST_F(IntrinsicDataTest, Frexp_Error_SecondParamFloatPtr) {
|
|||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
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"
|
||||
" frexp(f32, ptr<T>) -> f32 where: T is i32 or u32\n"
|
||||
" frexp(vecN<f32>, ptr<vecN<T>>) -> vecN<f32> "
|
||||
|
@ -2245,7 +2247,7 @@ TEST_F(IntrinsicDataTest, Frexp_Error_SecondParamNotAPointer) {
|
|||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
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"
|
||||
" frexp(f32, ptr<T>) -> f32 where: T is i32 or u32\n"
|
||||
" frexp(vecN<f32>, ptr<vecN<T>>) -> vecN<f32> "
|
||||
|
@ -2259,13 +2261,13 @@ TEST_F(IntrinsicDataTest, Frexp_Error_VectorSizesDontMatch) {
|
|||
|
||||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
EXPECT_EQ(
|
||||
td()->error(),
|
||||
"no matching call to frexp(vec2<f32>, ptr<workgroup, vec4<i32>>)\n\n"
|
||||
"2 candidate functions:\n"
|
||||
" frexp(f32, ptr<T>) -> f32 where: T is i32 or u32\n"
|
||||
" frexp(vecN<f32>, ptr<vecN<T>>) -> vecN<f32> "
|
||||
"where: T is i32 or u32\n");
|
||||
EXPECT_EQ(td()->error(),
|
||||
"error: no matching call to frexp(vec2<f32>, ptr<workgroup, "
|
||||
"vec4<i32>>)\n\n"
|
||||
"2 candidate functions:\n"
|
||||
" frexp(f32, ptr<T>) -> f32 where: T is i32 or u32\n"
|
||||
" frexp(vecN<f32>, ptr<vecN<T>>) -> vecN<f32> "
|
||||
"where: T is i32 or u32\n");
|
||||
}
|
||||
|
||||
TEST_F(IntrinsicDataTest, ModfScalar) {
|
||||
|
@ -2299,7 +2301,7 @@ TEST_F(IntrinsicDataTest, Modf_Error_FirstParamInt) {
|
|||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
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"
|
||||
" modf(f32, ptr<f32>) -> 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_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"
|
||||
" modf(f32, ptr<f32>) -> 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_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"
|
||||
" modf(f32, ptr<f32>) -> 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_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"
|
||||
" modf(vecN<f32>, ptr<vecN<f32>>) -> vecN<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_EQ(td()->error(),
|
||||
"no matching call to " + std::string(param.name) +
|
||||
"error: no matching call to " + std::string(param.name) +
|
||||
"()\n\n"
|
||||
"2 candidate functions:\n " +
|
||||
std::string(param.name) +
|
||||
|
@ -2515,13 +2518,13 @@ TEST_P(Intrinsic_TwoParamTest, Error_NoTooManyParams) {
|
|||
|
||||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
EXPECT_EQ(td()->error(), "no matching call to " + std::string(param.name) +
|
||||
"(i32, i32, i32)\n\n"
|
||||
"2 candidate functions:\n " +
|
||||
std::string(param.name) +
|
||||
"(f32, f32) -> f32\n " +
|
||||
std::string(param.name) +
|
||||
"(vecN<f32>, vecN<f32>) -> vecN<f32>\n");
|
||||
EXPECT_EQ(td()->error(),
|
||||
"error: no matching call to " + std::string(param.name) +
|
||||
"(i32, i32, i32)\n\n"
|
||||
"2 candidate functions:\n " +
|
||||
std::string(param.name) + "(f32, f32) -> f32\n " +
|
||||
std::string(param.name) +
|
||||
"(vecN<f32>, vecN<f32>) -> vecN<f32>\n");
|
||||
}
|
||||
|
||||
TEST_P(Intrinsic_TwoParamTest, Error_NoParams) {
|
||||
|
@ -2532,13 +2535,13 @@ TEST_P(Intrinsic_TwoParamTest, Error_NoParams) {
|
|||
|
||||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
EXPECT_EQ(td()->error(), "no matching call to " + std::string(param.name) +
|
||||
"()\n\n"
|
||||
"2 candidate functions:\n " +
|
||||
std::string(param.name) +
|
||||
"(f32, f32) -> f32\n " +
|
||||
std::string(param.name) +
|
||||
"(vecN<f32>, vecN<f32>) -> vecN<f32>\n");
|
||||
EXPECT_EQ(td()->error(),
|
||||
"error: no matching call to " + std::string(param.name) +
|
||||
"()\n\n"
|
||||
"2 candidate functions:\n " +
|
||||
std::string(param.name) + "(f32, f32) -> f32\n " +
|
||||
std::string(param.name) +
|
||||
"(vecN<f32>, vecN<f32>) -> vecN<f32>\n");
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
|
@ -2588,7 +2591,7 @@ TEST_F(TypeDeterminerTest, Intrinsic_Cross_Error_NoArgs) {
|
|||
|
||||
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:
|
||||
cross(vec3<f32>, vec3<f32>) -> vec3<f32>
|
||||
|
@ -2601,7 +2604,7 @@ TEST_F(TypeDeterminerTest, Intrinsic_Cross_Error_Scalar) {
|
|||
|
||||
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:
|
||||
cross(vec3<f32>, vec3<f32>) -> vec3<f32>
|
||||
|
@ -2614,7 +2617,8 @@ TEST_F(TypeDeterminerTest, Intrinsic_Cross_Error_Vec3Int) {
|
|||
|
||||
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:
|
||||
cross(vec3<f32>, vec3<f32>) -> vec3<f32>
|
||||
|
@ -2629,7 +2633,8 @@ TEST_F(TypeDeterminerTest, Intrinsic_Cross_Error_Vec4) {
|
|||
|
||||
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:
|
||||
cross(vec3<f32>, vec3<f32>) -> vec3<f32>
|
||||
|
@ -2645,7 +2650,7 @@ TEST_F(TypeDeterminerTest, Intrinsic_Cross_Error_TooManyParams) {
|
|||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
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:
|
||||
cross(vec3<f32>, vec3<f32>) -> vec3<f32>
|
||||
|
@ -2668,7 +2673,7 @@ TEST_F(TypeDeterminerTest, Intrinsic_Normalize_NoArgs) {
|
|||
|
||||
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:
|
||||
normalize(vecN<f32>) -> vecN<f32>
|
||||
|
@ -2710,7 +2715,7 @@ TEST_P(Intrinsic_ThreeParamTest, Error_NoParams) {
|
|||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
EXPECT_EQ(td()->error(),
|
||||
"no matching call to " + std::string(param.name) +
|
||||
"error: no matching call to " + std::string(param.name) +
|
||||
"()\n\n"
|
||||
"2 candidate functions:\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_EQ(td()->error(),
|
||||
"no matching call to " + std::string(param.name) +
|
||||
"error: no matching call to " + std::string(param.name) +
|
||||
"()\n\n"
|
||||
"2 candidate functions:\n " +
|
||||
std::string(param.name) +
|
||||
|
@ -2866,7 +2871,7 @@ TEST_P(Intrinsic_Int_SingleParamTest, Error_NoParams) {
|
|||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
EXPECT_EQ(td()->error(),
|
||||
"no matching call to " + std::string(param.name) +
|
||||
"error: no matching call to " + std::string(param.name) +
|
||||
"()\n\n"
|
||||
"2 candidate functions:\n " +
|
||||
std::string(param.name) +
|
||||
|
@ -2968,7 +2973,7 @@ TEST_P(Intrinsic_FloatOrInt_TwoParamTest, Error_NoParams) {
|
|||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
EXPECT_EQ(td()->error(),
|
||||
"no matching call to " + std::string(param.name) +
|
||||
"error: no matching call to " + std::string(param.name) +
|
||||
"()\n\n"
|
||||
"2 candidate functions:\n " +
|
||||
std::string(param.name) +
|
||||
|
@ -3027,10 +3032,11 @@ TEST_F(TypeDeterminerTest, Intrinsic_Determinant_NotSquare) {
|
|||
|
||||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
EXPECT_EQ(td()->error(),
|
||||
"no matching call to determinant(ptr<function, mat2x3<f32>>)\n\n"
|
||||
"1 candidate function:\n"
|
||||
" determinant(matNxN<f32>) -> f32\n");
|
||||
EXPECT_EQ(
|
||||
td()->error(),
|
||||
"error: no matching call to determinant(ptr<function, mat2x3<f32>>)\n\n"
|
||||
"1 candidate function:\n"
|
||||
" determinant(matNxN<f32>) -> f32\n");
|
||||
}
|
||||
|
||||
TEST_F(TypeDeterminerTest, Intrinsic_Determinant_NotMatrix) {
|
||||
|
@ -3042,7 +3048,7 @@ TEST_F(TypeDeterminerTest, Intrinsic_Determinant_NotMatrix) {
|
|||
EXPECT_FALSE(td()->Determine());
|
||||
|
||||
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"
|
||||
" determinant(matNxN<f32>) -> f32\n");
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "src/ast/switch_statement.h"
|
||||
#include "src/ast/uint_literal.h"
|
||||
#include "src/ast/variable_decl_statement.h"
|
||||
#include "src/debug.h"
|
||||
#include "src/semantic/call.h"
|
||||
#include "src/semantic/expression.h"
|
||||
#include "src/semantic/function.h"
|
||||
|
@ -92,7 +93,8 @@ bool ValidatorImpl::Validate() {
|
|||
return false;
|
||||
}
|
||||
} else {
|
||||
assert(false /* unreachable */);
|
||||
TINT_UNREACHABLE(diags_);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!ValidateEntryPoint(program_->AST().Functions())) {
|
||||
|
|
|
@ -96,7 +96,7 @@ TEST_F(ValidatorTest, UsingUndefinedVariable_Fail) {
|
|||
|
||||
EXPECT_FALSE(td()->Determine());
|
||||
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) {
|
||||
|
@ -115,7 +115,7 @@ TEST_F(ValidatorTest, UsingUndefinedVariableInBlockStatement_Fail) {
|
|||
|
||||
EXPECT_FALSE(td()->Determine());
|
||||
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) {
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include "src/ast/unary_op_expression.h"
|
||||
#include "src/ast/variable.h"
|
||||
#include "src/ast/variable_decl_statement.h"
|
||||
#include "src/debug.h"
|
||||
#include "src/program_builder.h"
|
||||
#include "src/semantic/call.h"
|
||||
#include "src/semantic/expression.h"
|
||||
|
@ -137,12 +138,6 @@ GeneratorImpl::GeneratorImpl(const Program* program)
|
|||
|
||||
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) {
|
||||
for (auto* global : builder_.AST().GlobalVariables()) {
|
||||
register_global(global);
|
||||
|
@ -258,7 +253,7 @@ bool GeneratorImpl::EmitConstructedType(std::ostream& out,
|
|||
return false;
|
||||
}
|
||||
} else {
|
||||
error_ = "unknown constructed type: " + ty->type_name();
|
||||
diagnostics_.add_error("unknown constructed type: " + ty->type_name());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -290,7 +285,8 @@ bool GeneratorImpl::EmitBitcast(std::ostream& pre,
|
|||
std::ostream& out,
|
||||
ast::BitcastExpression* expr) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -429,7 +425,7 @@ bool GeneratorImpl::EmitBinary(std::ostream& pre,
|
|||
case ast::BinaryOp::kLogicalAnd:
|
||||
case ast::BinaryOp::kLogicalOr: {
|
||||
// These are both handled above.
|
||||
assert(false);
|
||||
TINT_UNREACHABLE(diagnostics_);
|
||||
return false;
|
||||
}
|
||||
case ast::BinaryOp::kEqual:
|
||||
|
@ -477,7 +473,7 @@ bool GeneratorImpl::EmitBinary(std::ostream& pre,
|
|||
out << "%";
|
||||
break;
|
||||
case ast::BinaryOp::kNone:
|
||||
error_ = "missing binary operation type";
|
||||
diagnostics_.add_error("missing binary operation type");
|
||||
return false;
|
||||
}
|
||||
out << " ";
|
||||
|
@ -538,7 +534,7 @@ bool GeneratorImpl::EmitCall(std::ostream& pre,
|
|||
ast::CallExpression* expr) {
|
||||
auto* ident = expr->func()->As<ast::IdentifierExpression>();
|
||||
if (ident == nullptr) {
|
||||
error_ = "invalid function name";
|
||||
diagnostics_.add_error("invalid function name");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -549,10 +545,10 @@ bool GeneratorImpl::EmitCall(std::ostream& pre,
|
|||
}
|
||||
const auto& params = expr->params();
|
||||
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;
|
||||
} 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;
|
||||
} else if (intrinsic->IsDataPacking()) {
|
||||
return EmitDataPackingCall(pre, out, expr, intrinsic);
|
||||
|
@ -593,8 +589,8 @@ bool GeneratorImpl::EmitCall(std::ostream& pre,
|
|||
|
||||
auto* func = builder_.AST().Functions().Find(ident->symbol());
|
||||
if (func == nullptr) {
|
||||
error_ = "Unable to find function: " +
|
||||
builder_.Symbols().NameFor(ident->symbol());
|
||||
diagnostics_.add_error("Unable to find function: " +
|
||||
builder_.Symbols().NameFor(ident->symbol()));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -689,7 +685,8 @@ bool GeneratorImpl::EmitDataPackingCall(std::ostream& pre,
|
|||
out << "(" << tmp_name << ".x | " << tmp_name << ".y << 16)";
|
||||
break;
|
||||
default:
|
||||
error_ = "Internal error: unhandled data packing intrinsic";
|
||||
diagnostics_.add_error(
|
||||
"Internal error: unhandled data packing intrinsic");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -760,7 +757,8 @@ bool GeneratorImpl::EmitDataUnpackingCall(
|
|||
<< " >> 16))";
|
||||
break;
|
||||
default:
|
||||
error_ = "Internal error: unhandled data packing intrinsic";
|
||||
diagnostics_.add_error(
|
||||
"Internal error: unhandled data packing intrinsic");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -801,7 +799,7 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& pre,
|
|||
case semantic::IntrinsicType::kTextureDimensions:
|
||||
switch (texture_type->dim()) {
|
||||
case type::TextureDimension::kNone:
|
||||
error_ = "texture dimension is kNone";
|
||||
diagnostics_.add_error("texture dimension is kNone");
|
||||
return false;
|
||||
case type::TextureDimension::k1d:
|
||||
num_dimensions = 1;
|
||||
|
@ -837,7 +835,7 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& pre,
|
|||
case semantic::IntrinsicType::kTextureNumLayers:
|
||||
switch (texture_type->dim()) {
|
||||
default:
|
||||
error_ = "texture dimension is not arrayed";
|
||||
diagnostics_.add_error("texture dimension is not arrayed");
|
||||
return false;
|
||||
case type::TextureDimension::k1dArray:
|
||||
num_dimensions = 2;
|
||||
|
@ -854,7 +852,7 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& pre,
|
|||
add_mip_level_in = true;
|
||||
switch (texture_type->dim()) {
|
||||
default:
|
||||
error_ = "texture dimension does not support mips";
|
||||
diagnostics_.add_error("texture dimension does not support mips");
|
||||
return false;
|
||||
case type::TextureDimension::k2d:
|
||||
case type::TextureDimension::kCube:
|
||||
|
@ -872,7 +870,8 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& pre,
|
|||
case semantic::IntrinsicType::kTextureNumSamples:
|
||||
switch (texture_type->dim()) {
|
||||
default:
|
||||
error_ = "texture dimension does not support multisampling";
|
||||
diagnostics_.add_error(
|
||||
"texture dimension does not support multisampling");
|
||||
return false;
|
||||
case type::TextureDimension::k2d:
|
||||
num_dimensions = 3;
|
||||
|
@ -885,7 +884,7 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& pre,
|
|||
}
|
||||
break;
|
||||
default:
|
||||
error_ = "unexpected intrinsic";
|
||||
diagnostics_.add_error("unexpected intrinsic");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -967,8 +966,9 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& pre,
|
|||
out << "[";
|
||||
break;
|
||||
default:
|
||||
error_ = "Internal compiler error: Unhandled texture intrinsic '" +
|
||||
std::string(intrinsic->str()) + "'";
|
||||
diagnostics_.add_error(
|
||||
"Internal compiler error: Unhandled texture intrinsic '" +
|
||||
std::string(intrinsic->str()) + "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1127,7 +1127,8 @@ std::string GeneratorImpl::generate_builtin_name(
|
|||
out = "smoothstep";
|
||||
break;
|
||||
default:
|
||||
error_ = "Unknown builtin method: " + std::string(intrinsic->str());
|
||||
diagnostics_.add_error("Unknown builtin method: " +
|
||||
std::string(intrinsic->str()));
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -1277,7 +1278,7 @@ bool GeneratorImpl::EmitExpression(std::ostream& pre,
|
|||
return EmitUnaryOp(pre, out, u);
|
||||
}
|
||||
|
||||
error_ = "unknown expression type: " + builder_.str(expr);
|
||||
diagnostics_.add_error("unknown expression type: " + builder_.str(expr));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1302,7 +1303,7 @@ bool GeneratorImpl::EmitIdentifier(std::ostream&,
|
|||
: VarType::kOut;
|
||||
auto name = current_ep_var_name(var_type);
|
||||
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;
|
||||
}
|
||||
out << name << ".";
|
||||
|
@ -1581,8 +1582,9 @@ bool GeneratorImpl::EmitEntryPointData(
|
|||
// set. https://bugs.chromium.org/p/tint/issues/detail?id=104
|
||||
auto* binding = data.second.binding;
|
||||
if (binding == nullptr) {
|
||||
error_ = "unable to find binding information for uniform: " +
|
||||
builder_.Symbols().NameFor(decl->symbol());
|
||||
diagnostics_.add_error(
|
||||
"unable to find binding information for uniform: " +
|
||||
builder_.Symbols().NameFor(decl->symbol()));
|
||||
return false;
|
||||
}
|
||||
// auto* set = data.second.set;
|
||||
|
@ -1642,7 +1644,7 @@ bool GeneratorImpl::EmitEntryPointData(
|
|||
|
||||
auto* ac = decl->type()->As<type::AccessControl>();
|
||||
if (ac == nullptr) {
|
||||
error_ = "access control type required for storage buffer";
|
||||
diagnostics_.add_error("access control type required for storage buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1682,19 +1684,21 @@ bool GeneratorImpl::EmitEntryPointData(
|
|||
out << " " << builder_.Symbols().NameFor(var->symbol()) << " : ";
|
||||
if (auto* location = deco->As<ast::LocationDecoration>()) {
|
||||
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;
|
||||
}
|
||||
out << "TEXCOORD" << location->value();
|
||||
} else if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
|
||||
auto attr = builtin_to_attribute(builtin->value());
|
||||
if (attr.empty()) {
|
||||
error_ = "unsupported builtin";
|
||||
diagnostics_.add_error("unsupported builtin");
|
||||
return false;
|
||||
}
|
||||
out << attr;
|
||||
} else {
|
||||
error_ = "unsupported variable decoration for entry point output";
|
||||
diagnostics_.add_error(
|
||||
"unsupported variable decoration for entry point output");
|
||||
return false;
|
||||
}
|
||||
out << ";" << std::endl;
|
||||
|
@ -1734,18 +1738,20 @@ bool GeneratorImpl::EmitEntryPointData(
|
|||
} else if (func->pipeline_stage() == ast::PipelineStage::kFragment) {
|
||||
out << "SV_Target" << loc << "";
|
||||
} else {
|
||||
error_ = "invalid location variable for pipeline stage";
|
||||
diagnostics_.add_error(
|
||||
"invalid location variable for pipeline stage");
|
||||
return false;
|
||||
}
|
||||
} else if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
|
||||
auto attr = builtin_to_attribute(builtin->value());
|
||||
if (attr.empty()) {
|
||||
error_ = "unsupported builtin";
|
||||
diagnostics_.add_error("unsupported builtin");
|
||||
return false;
|
||||
}
|
||||
out << attr;
|
||||
} else {
|
||||
error_ = "unsupported variable decoration for entry point output";
|
||||
diagnostics_.add_error(
|
||||
"unsupported variable decoration for entry point output");
|
||||
return false;
|
||||
}
|
||||
out << ";" << std::endl;
|
||||
|
@ -1866,7 +1872,7 @@ bool GeneratorImpl::EmitLiteral(std::ostream& out, ast::Literal* lit) {
|
|||
} else if (auto* ul = lit->As<ast::UintLiteral>()) {
|
||||
out << ul->value() << "u";
|
||||
} else {
|
||||
error_ = "unknown literal type";
|
||||
diagnostics_.add_error("unknown literal type");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -1893,7 +1899,8 @@ bool GeneratorImpl::EmitZeroValue(std::ostream& out, type::Type* type) {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
error_ = "Invalid type for zero emission: " + type->type_name();
|
||||
diagnostics_.add_error("Invalid type for zero emission: " +
|
||||
type->type_name());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -2016,7 +2023,7 @@ std::string GeneratorImpl::generate_storage_buffer_index_expression(
|
|||
auto* str_member = str_type->get_member(mem->member()->symbol());
|
||||
|
||||
if (!str_member->has_offset_decoration()) {
|
||||
error_ = "missing offset decoration for struct member";
|
||||
diagnostics_.add_error("missing offset decoration for struct member");
|
||||
return "";
|
||||
}
|
||||
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
|
||||
// point.
|
||||
if (builder_.Symbols().NameFor(mem->member()->symbol()).size() != 1) {
|
||||
error_ =
|
||||
diagnostics_.add_error(
|
||||
"Encountered multi-element swizzle when should have only one "
|
||||
"level";
|
||||
"level");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -2041,8 +2048,8 @@ std::string GeneratorImpl::generate_storage_buffer_index_expression(
|
|||
builder_.Symbols().NameFor(mem->member()->symbol()))
|
||||
<< ")";
|
||||
} else {
|
||||
error_ =
|
||||
"Invalid result type for member accessor: " + res_type->type_name();
|
||||
diagnostics_.add_error("Invalid result type for member accessor: " +
|
||||
res_type->type_name());
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -2065,7 +2072,7 @@ std::string GeneratorImpl::generate_storage_buffer_index_expression(
|
|||
out << "16";
|
||||
}
|
||||
} else {
|
||||
error_ = "Invalid array type in storage buffer access";
|
||||
diagnostics_.add_error("Invalid array type in storage buffer access");
|
||||
return "";
|
||||
}
|
||||
out << " * ";
|
||||
|
@ -2076,7 +2083,7 @@ std::string GeneratorImpl::generate_storage_buffer_index_expression(
|
|||
|
||||
expr = ary->array();
|
||||
} else {
|
||||
error_ = "error emitting storage buffer access";
|
||||
diagnostics_.add_error("error emitting storage buffer access");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
@ -2119,7 +2126,7 @@ bool GeneratorImpl::EmitStorageBufferAccessor(std::ostream& pre,
|
|||
|
||||
auto buffer_name = get_buffer_name(expr);
|
||||
if (buffer_name.empty()) {
|
||||
error_ = "error emitting storage buffer access";
|
||||
diagnostics_.add_error("error emitting storage buffer access");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2336,7 +2343,7 @@ bool GeneratorImpl::EmitStatement(std::ostream& out, ast::Statement* stmt) {
|
|||
return EmitVariable(out, v->variable(), false);
|
||||
}
|
||||
|
||||
error_ = "unknown statement type: " + builder_.str(stmt);
|
||||
diagnostics_.add_error("unknown statement type: " + builder_.str(stmt));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2385,7 +2392,7 @@ bool GeneratorImpl::EmitType(std::ostream& out,
|
|||
if (arr->IsRuntimeArray()) {
|
||||
// TODO(dsinclair): Support runtime arrays
|
||||
// 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;
|
||||
} else {
|
||||
sizes.push_back(arr->size());
|
||||
|
@ -2415,7 +2422,7 @@ bool GeneratorImpl::EmitType(std::ostream& out,
|
|||
} else if (type->Is<type::Pointer>()) {
|
||||
// TODO(dsinclair): What do we do with pointers in HLSL?
|
||||
// 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;
|
||||
} else if (auto* sampler = type->As<type::Sampler>()) {
|
||||
out << "Sampler";
|
||||
|
@ -2454,15 +2461,16 @@ bool GeneratorImpl::EmitType(std::ostream& out,
|
|||
out << "CubeArray";
|
||||
break;
|
||||
default:
|
||||
error_ = "Invalid texture dimensions";
|
||||
diagnostics_.add_error("Invalid texture dimensions");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto* st = tex->As<type::StorageTexture>()) {
|
||||
auto* component = image_format_to_rwtexture_type(st->image_format());
|
||||
if (component == nullptr) {
|
||||
error_ = "Unsupported StorageTexture ImageFormat: " +
|
||||
std::to_string(static_cast<int>(st->image_format()));
|
||||
diagnostics_.add_error(
|
||||
"Unsupported StorageTexture ImageFormat: " +
|
||||
std::to_string(static_cast<int>(st->image_format())));
|
||||
return false;
|
||||
}
|
||||
out << "<" << component << ">";
|
||||
|
@ -2487,7 +2495,7 @@ bool GeneratorImpl::EmitType(std::ostream& out,
|
|||
} else if (type->Is<type::Void>()) {
|
||||
out << "void";
|
||||
} else {
|
||||
error_ = "unknown type in EmitType";
|
||||
diagnostics_.add_error("unknown type in EmitType");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2555,7 +2563,7 @@ bool GeneratorImpl::EmitVariable(std::ostream& out,
|
|||
|
||||
// TODO(dsinclair): Handle variable decorations
|
||||
if (!var->decorations().empty()) {
|
||||
error_ = "Variable decorations are not handled yet";
|
||||
diagnostics_.add_error("Variable decorations are not handled yet");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2590,12 +2598,12 @@ bool GeneratorImpl::EmitProgramConstVariable(std::ostream& out,
|
|||
|
||||
for (auto* d : var->decorations()) {
|
||||
if (!d->Is<ast::ConstantIdDecoration>()) {
|
||||
error_ = "Decorated const values not valid";
|
||||
diagnostics_.add_error("Decorated const values not valid");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!var->is_const()) {
|
||||
error_ = "Expected a const value";
|
||||
diagnostics_.add_error("Expected a const value");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "src/semantic/intrinsic.h"
|
||||
#include "src/type/struct_type.h"
|
||||
#include "src/writer/hlsl/namer.h"
|
||||
#include "src/writer/text_generator.h"
|
||||
|
||||
namespace tint {
|
||||
|
||||
|
@ -56,31 +57,13 @@ namespace writer {
|
|||
namespace hlsl {
|
||||
|
||||
/// Implementation class for HLSL generator
|
||||
class GeneratorImpl {
|
||||
class GeneratorImpl : public TextGenerator {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param program the program to generate
|
||||
explicit GeneratorImpl(const Program* program);
|
||||
~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
|
||||
/// @returns true on successful generation; false otherwise
|
||||
bool Generate(std::ostream& out);
|
||||
|
@ -421,9 +404,6 @@ class GeneratorImpl {
|
|||
return builder_.TypeOf(expr);
|
||||
}
|
||||
|
||||
std::string error_;
|
||||
size_t indent_ = 0;
|
||||
|
||||
Namer namer_;
|
||||
ProgramBuilder builder_;
|
||||
Symbol current_ep_sym_;
|
||||
|
|
|
@ -238,7 +238,8 @@ TEST_F(HlslGeneratorImplTest_EntryPoint,
|
|||
|
||||
auto* func = program->AST().Functions()[0];
|
||||
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,
|
||||
|
@ -273,7 +274,8 @@ TEST_F(HlslGeneratorImplTest_EntryPoint,
|
|||
|
||||
auto* func = program->AST().Functions()[0];
|
||||
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,
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "src/ast/unary_op_expression.h"
|
||||
#include "src/ast/variable.h"
|
||||
#include "src/ast/variable_decl_statement.h"
|
||||
#include "src/debug.h"
|
||||
#include "src/program.h"
|
||||
#include "src/semantic/call.h"
|
||||
#include "src/semantic/expression.h"
|
||||
|
@ -273,7 +274,7 @@ bool GeneratorImpl::EmitConstructedType(const type::Type* ty) {
|
|||
return false;
|
||||
}
|
||||
} else {
|
||||
error_ = "unknown alias type: " + ty->type_name();
|
||||
diagnostics_.add_error("unknown alias type: " + ty->type_name());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -396,7 +397,7 @@ bool GeneratorImpl::EmitBinary(ast::BinaryExpression* expr) {
|
|||
out_ << "%";
|
||||
break;
|
||||
case ast::BinaryOp::kNone:
|
||||
error_ = "missing binary operation type";
|
||||
diagnostics_.add_error("missing binary operation type");
|
||||
return false;
|
||||
}
|
||||
out_ << " ";
|
||||
|
@ -440,7 +441,7 @@ bool GeneratorImpl::EmitCall(ast::CallExpression* expr) {
|
|||
auto* ident = expr->func()->As<ast::IdentifierExpression>();
|
||||
|
||||
if (ident == nullptr) {
|
||||
error_ = "invalid function name";
|
||||
diagnostics_.add_error("invalid function name");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -498,8 +499,8 @@ bool GeneratorImpl::EmitCall(ast::CallExpression* expr) {
|
|||
|
||||
auto* func = program_->AST().Functions().Find(ident->symbol());
|
||||
if (func == nullptr) {
|
||||
error_ = "Unable to find function: " +
|
||||
program_->Symbols().NameFor(ident->symbol());
|
||||
diagnostics_.add_error("Unable to find function: " +
|
||||
program_->Symbols().NameFor(ident->symbol()));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -595,7 +596,7 @@ bool GeneratorImpl::EmitTextureCall(ast::CallExpression* expr,
|
|||
std::vector<const char*> dims;
|
||||
switch (texture_type->dim()) {
|
||||
case type::TextureDimension::kNone:
|
||||
error_ = "texture dimension is kNone";
|
||||
diagnostics_.add_error("texture dimension is kNone");
|
||||
return false;
|
||||
case type::TextureDimension::k1d:
|
||||
case type::TextureDimension::k1dArray:
|
||||
|
@ -698,8 +699,8 @@ bool GeneratorImpl::EmitTextureCall(ast::CallExpression* expr,
|
|||
out_ << ".write(";
|
||||
break;
|
||||
default:
|
||||
error_ = "Internal compiler error: Unhandled texture intrinsic '" +
|
||||
std::string(intrinsic->str()) + "'";
|
||||
TINT_ICE(diagnostics_, "Unhandled texture intrinsic '" +
|
||||
std::string(intrinsic->str()) + "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -761,7 +762,7 @@ bool GeneratorImpl::EmitTextureCall(ast::CallExpression* expr,
|
|||
default: {
|
||||
std::stringstream err;
|
||||
err << "MSL does not support gradients for " << dim << " textures";
|
||||
error_ = err.str();
|
||||
diagnostics_.add_error(err.str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -918,7 +919,8 @@ std::string GeneratorImpl::generate_builtin_name(
|
|||
out += "unpack_unorm2x16_to_float";
|
||||
break;
|
||||
default:
|
||||
error_ = "Unknown import method: " + std::string(intrinsic->str());
|
||||
diagnostics_.add_error("Unknown import method: " +
|
||||
std::string(intrinsic->str()));
|
||||
return "";
|
||||
}
|
||||
return out;
|
||||
|
@ -1041,7 +1043,8 @@ bool GeneratorImpl::EmitZeroValue(type::Type* type) {
|
|||
} else if (type->As<type::Struct>()) {
|
||||
out_ << "{}";
|
||||
} else {
|
||||
error_ = "Invalid type for zero emission: " + type->type_name();
|
||||
diagnostics_.add_error("Invalid type for zero emission: " +
|
||||
type->type_name());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -1062,7 +1065,7 @@ bool GeneratorImpl::EmitLiteral(ast::Literal* lit) {
|
|||
} else if (auto* ul = lit->As<ast::UintLiteral>()) {
|
||||
out_ << ul->value() << "u";
|
||||
} else {
|
||||
error_ = "unknown literal type";
|
||||
diagnostics_.add_error("unknown literal type");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -1122,7 +1125,7 @@ bool GeneratorImpl::EmitEntryPointData(ast::Function* func) {
|
|||
} else if (func->pipeline_stage() == ast::PipelineStage::kFragment) {
|
||||
out_ << "user(locn" << loc << ")";
|
||||
} else {
|
||||
error_ = "invalid location variable for pipeline stage";
|
||||
diagnostics_.add_error("invalid location variable for pipeline stage");
|
||||
return false;
|
||||
}
|
||||
out_ << "]];" << std::endl;
|
||||
|
@ -1162,18 +1165,20 @@ bool GeneratorImpl::EmitEntryPointData(ast::Function* func) {
|
|||
} else if (func->pipeline_stage() == ast::PipelineStage::kFragment) {
|
||||
out_ << "color(" << loc << ")";
|
||||
} else {
|
||||
error_ = "invalid location variable for pipeline stage";
|
||||
diagnostics_.add_error(
|
||||
"invalid location variable for pipeline stage");
|
||||
return false;
|
||||
}
|
||||
} else if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
|
||||
auto attr = builtin_to_attribute(builtin->value());
|
||||
if (attr.empty()) {
|
||||
error_ = "unsupported builtin";
|
||||
diagnostics_.add_error("unsupported builtin");
|
||||
return false;
|
||||
}
|
||||
out_ << attr;
|
||||
} else {
|
||||
error_ = "unsupported variable decoration for entry point output";
|
||||
diagnostics_.add_error(
|
||||
"unsupported variable decoration for entry point output");
|
||||
return false;
|
||||
}
|
||||
out_ << "]];" << std::endl;
|
||||
|
@ -1212,7 +1217,7 @@ bool GeneratorImpl::EmitExpression(ast::Expression* expr) {
|
|||
return EmitUnaryOp(u);
|
||||
}
|
||||
|
||||
error_ = "unknown expression type: " + program_->str(expr);
|
||||
diagnostics_.add_error("unknown expression type: " + program_->str(expr));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1393,7 +1398,8 @@ bool GeneratorImpl::EmitFunctionInternal(ast::Function* func,
|
|||
|
||||
auto* ac = var->Declaration()->type()->As<type::AccessControl>();
|
||||
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;
|
||||
}
|
||||
if (ac->IsReadOnly()) {
|
||||
|
@ -1516,7 +1522,7 @@ bool GeneratorImpl::EmitEntryPointFunction(ast::Function* func) {
|
|||
|
||||
auto attr = builtin_to_attribute(builtin->value());
|
||||
if (attr.empty()) {
|
||||
error_ = "unknown builtin";
|
||||
diagnostics_.add_error("unknown builtin");
|
||||
return false;
|
||||
}
|
||||
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
|
||||
auto* binding = data.second.binding;
|
||||
if (binding == nullptr) {
|
||||
error_ = "unable to find binding information for uniform: " +
|
||||
program_->Symbols().NameFor(var->Declaration()->symbol());
|
||||
diagnostics_.add_error(
|
||||
"unable to find binding information for uniform: " +
|
||||
program_->Symbols().NameFor(var->Declaration()->symbol()));
|
||||
return false;
|
||||
}
|
||||
// auto* set = data.second.set;
|
||||
|
@ -1566,7 +1573,8 @@ bool GeneratorImpl::EmitEntryPointFunction(ast::Function* func) {
|
|||
|
||||
auto* ac = var->Declaration()->type()->As<type::AccessControl>();
|
||||
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;
|
||||
}
|
||||
if (ac->IsReadOnly()) {
|
||||
|
@ -1636,7 +1644,7 @@ bool GeneratorImpl::EmitIdentifier(ast::IdentifierExpression* expr) {
|
|||
: VarType::kOut;
|
||||
auto name = current_ep_var_name(var_type);
|
||||
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;
|
||||
}
|
||||
out_ << name << ".";
|
||||
|
@ -1897,7 +1905,7 @@ bool GeneratorImpl::EmitStatement(ast::Statement* stmt) {
|
|||
return EmitVariable(var, false);
|
||||
}
|
||||
|
||||
error_ = "unknown statement type: " + program_->str(stmt);
|
||||
diagnostics_.add_error("unknown statement type: " + program_->str(stmt));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1933,7 +1941,7 @@ bool GeneratorImpl::EmitType(type::Type* type, const std::string& name) {
|
|||
} else if (ac->access_control() == ast::AccessControl::kWriteOnly) {
|
||||
access_str = "write";
|
||||
} else {
|
||||
error_ = "Invalid access control for storage texture";
|
||||
diagnostics_.add_error("Invalid access control for storage texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2015,7 +2023,7 @@ bool GeneratorImpl::EmitType(type::Type* type, const std::string& name) {
|
|||
out_ << "cube_array";
|
||||
break;
|
||||
default:
|
||||
error_ = "Invalid texture dimensions";
|
||||
diagnostics_.add_error("Invalid texture dimensions");
|
||||
return false;
|
||||
}
|
||||
if (tex->Is<type::MultisampledTexture>()) {
|
||||
|
@ -2040,7 +2048,7 @@ bool GeneratorImpl::EmitType(type::Type* type, const std::string& name) {
|
|||
}
|
||||
out_ << ", access::sample";
|
||||
} else {
|
||||
error_ = "invalid texture type";
|
||||
diagnostics_.add_error("invalid texture type");
|
||||
return false;
|
||||
}
|
||||
out_ << ">";
|
||||
|
@ -2055,7 +2063,7 @@ bool GeneratorImpl::EmitType(type::Type* type, const std::string& name) {
|
|||
} else if (type->Is<type::Void>()) {
|
||||
out_ << "void";
|
||||
} else {
|
||||
error_ = "unknown type in EmitType: " + type->type_name();
|
||||
diagnostics_.add_error("unknown type in EmitType: " + type->type_name());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2085,7 +2093,8 @@ bool GeneratorImpl::EmitStructType(const type::Struct* str) {
|
|||
}
|
||||
current_offset = offset;
|
||||
} else {
|
||||
error_ = "unsupported member decoration: " + program_->str(deco);
|
||||
diagnostics_.add_error("unsupported member decoration: " +
|
||||
program_->str(deco));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -2095,7 +2104,8 @@ bool GeneratorImpl::EmitStructType(const type::Struct* str) {
|
|||
}
|
||||
auto size = calculate_alignment_size(mem->type());
|
||||
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;
|
||||
}
|
||||
current_offset += size;
|
||||
|
@ -2141,7 +2151,7 @@ bool GeneratorImpl::EmitVariable(const semantic::Variable* var,
|
|||
|
||||
// TODO(dsinclair): Handle variable decorations
|
||||
if (!decl->decorations().empty()) {
|
||||
error_ = "Variable decorations are not handled yet";
|
||||
diagnostics_.add_error("Variable decorations are not handled yet");
|
||||
return false;
|
||||
}
|
||||
if (decl->is_const()) {
|
||||
|
@ -2179,12 +2189,12 @@ bool GeneratorImpl::EmitProgramConstVariable(const ast::Variable* var) {
|
|||
|
||||
for (auto* d : var->decorations()) {
|
||||
if (!d->Is<ast::ConstantIdDecoration>()) {
|
||||
error_ = "Decorated const values not valid";
|
||||
diagnostics_.add_error("Decorated const values not valid");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!var->is_const()) {
|
||||
error_ = "Expected a const value";
|
||||
diagnostics_.add_error("Expected a const value");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -212,7 +212,8 @@ TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Compute_Input) {
|
|||
|
||||
auto* func = program->AST().Functions()[0];
|
||||
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) {
|
||||
|
@ -241,7 +242,8 @@ TEST_F(MslGeneratorImplTest, Emit_Function_EntryPointData_Compute_Output) {
|
|||
|
||||
auto* func = program->AST().Functions()[0];
|
||||
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) {
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
#include "src/ast/unary_op_expression.h"
|
||||
#include "src/ast/variable.h"
|
||||
#include "src/ast/variable_decl_statement.h"
|
||||
#include "src/debug.h"
|
||||
#include "src/program.h"
|
||||
#include "src/semantic/call.h"
|
||||
#include "src/semantic/expression.h"
|
||||
|
@ -2408,7 +2409,8 @@ bool Builder::GenerateTextureIntrinsic(ast::CallExpression* call,
|
|||
break;
|
||||
}
|
||||
default:
|
||||
break; // unreachable
|
||||
TINT_UNREACHABLE(builder_.Diagnostics());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto* offset = arg(Usage::kOffset)) {
|
||||
|
|
|
@ -26,8 +26,12 @@ TextGenerator::TextGenerator() = default;
|
|||
TextGenerator::~TextGenerator() = default;
|
||||
|
||||
void TextGenerator::make_indent() {
|
||||
make_indent(out_);
|
||||
}
|
||||
|
||||
void TextGenerator::make_indent(std::ostream& out) const {
|
||||
for (size_t i = 0; i < indent_; i++) {
|
||||
out_ << " ";
|
||||
out << " ";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "src/diagnostic/diagnostic.h"
|
||||
|
||||
namespace tint {
|
||||
namespace writer {
|
||||
|
||||
|
@ -42,17 +44,24 @@ class TextGenerator {
|
|||
/// Writes the current indent to the output stream
|
||||
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
|
||||
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
|
||||
std::string error() const { return error_; }
|
||||
std::string error() const { return diagnostics_.str(); }
|
||||
|
||||
protected:
|
||||
/// The text output stream
|
||||
std::ostringstream out_;
|
||||
/// Error generated by the generator
|
||||
std::string error_;
|
||||
/// Diagnostics generated by the generator
|
||||
diag::List diagnostics_;
|
||||
|
||||
private:
|
||||
size_t indent_ = 0;
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
#include "src/ast/variable.h"
|
||||
#include "src/ast/variable_decl_statement.h"
|
||||
#include "src/ast/workgroup_decoration.h"
|
||||
#include "src/debug.h"
|
||||
#include "src/program.h"
|
||||
#include "src/semantic/function.h"
|
||||
#include "src/semantic/variable.h"
|
||||
|
@ -120,7 +121,8 @@ bool GeneratorImpl::Generate(const ast::Function* entry) {
|
|||
return false;
|
||||
}
|
||||
} else {
|
||||
assert(false /* unreachable */);
|
||||
TINT_UNREACHABLE(diagnostics_);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (decl != program_->AST().GlobalDeclarations().back()) {
|
||||
|
@ -136,7 +138,7 @@ bool GeneratorImpl::GenerateEntryPoint(ast::PipelineStage stage,
|
|||
auto* func =
|
||||
program_->AST().Functions().Find(program_->Symbols().Get(name), stage);
|
||||
if (func == nullptr) {
|
||||
error_ = "Unable to find requested entry point: " + name;
|
||||
diagnostics_.add_error("Unable to find requested entry point: " + name);
|
||||
return false;
|
||||
}
|
||||
return Generate(func);
|
||||
|
@ -155,7 +157,7 @@ bool GeneratorImpl::EmitConstructedType(const type::Type* ty) {
|
|||
return false;
|
||||
}
|
||||
} else {
|
||||
error_ = "unknown constructed type: " + ty->type_name();
|
||||
diagnostics_.add_error("unknown constructed type: " + ty->type_name());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -188,7 +190,7 @@ bool GeneratorImpl::EmitExpression(ast::Expression* expr) {
|
|||
return EmitUnaryOp(u);
|
||||
}
|
||||
|
||||
error_ = "unknown expression type";
|
||||
diagnostics_.add_error("unknown expression type");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -300,7 +302,7 @@ bool GeneratorImpl::EmitLiteral(ast::Literal* lit) {
|
|||
} else if (auto* ul = lit->As<ast::UintLiteral>()) {
|
||||
out_ << ul->value() << "u";
|
||||
} else {
|
||||
error_ = "unknown literal type";
|
||||
diagnostics_.add_error("unknown literal type");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -360,7 +362,7 @@ bool GeneratorImpl::EmitFunction(ast::Function* func) {
|
|||
bool GeneratorImpl::EmitImageFormat(const type::ImageFormat fmt) {
|
||||
switch (fmt) {
|
||||
case type::ImageFormat::kNone:
|
||||
error_ = "unknown image format";
|
||||
diagnostics_.add_error("unknown image format");
|
||||
return false;
|
||||
default:
|
||||
out_ << fmt;
|
||||
|
@ -379,7 +381,7 @@ bool GeneratorImpl::EmitType(type::Type* type) {
|
|||
} else if (ac->IsReadWrite()) {
|
||||
out_ << "read_write";
|
||||
} else {
|
||||
error_ = "invalid access control";
|
||||
diagnostics_.add_error("invalid access control");
|
||||
return false;
|
||||
}
|
||||
out_ << ")]]" << std::endl;
|
||||
|
@ -444,7 +446,7 @@ bool GeneratorImpl::EmitType(type::Type* type) {
|
|||
} else if (texture->Is<type::StorageTexture>()) {
|
||||
out_ << "storage_";
|
||||
} else {
|
||||
error_ = "unknown texture type";
|
||||
diagnostics_.add_error("unknown texture type");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -471,7 +473,7 @@ bool GeneratorImpl::EmitType(type::Type* type) {
|
|||
out_ << "cube_array";
|
||||
break;
|
||||
default:
|
||||
error_ = "unknown texture dimension";
|
||||
diagnostics_.add_error("unknown texture dimension");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -506,7 +508,7 @@ bool GeneratorImpl::EmitType(type::Type* type) {
|
|||
} else if (type->Is<type::Void>()) {
|
||||
out_ << "void";
|
||||
} else {
|
||||
error_ = "unknown type in EmitType: " + type->type_name();
|
||||
diagnostics_.add_error("unknown type in EmitType: " + type->type_name());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -605,7 +607,7 @@ bool GeneratorImpl::EmitVariableDecorations(const semantic::Variable* var) {
|
|||
} else if (auto* constant = deco->As<ast::ConstantIdDecoration>()) {
|
||||
out_ << "constant_id(" << constant->value() << ")";
|
||||
} else {
|
||||
error_ = "unknown variable decoration";
|
||||
diagnostics_.add_error("unknown variable decoration");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -678,7 +680,7 @@ bool GeneratorImpl::EmitBinary(ast::BinaryExpression* expr) {
|
|||
out_ << "%";
|
||||
break;
|
||||
case ast::BinaryOp::kNone:
|
||||
error_ = "missing binary operation type";
|
||||
diagnostics_.add_error("missing binary operation type");
|
||||
return false;
|
||||
}
|
||||
out_ << " ";
|
||||
|
@ -789,7 +791,7 @@ bool GeneratorImpl::EmitStatement(ast::Statement* stmt) {
|
|||
return EmitVariable(v->variable());
|
||||
}
|
||||
|
||||
error_ = "unknown statement type: " + program_->str(stmt);
|
||||
diagnostics_.add_error("unknown statement type: " + program_->str(stmt));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "src/ast/type_constructor_expression.h"
|
||||
#include "src/ast/unary_op_expression.h"
|
||||
#include "src/ast/variable.h"
|
||||
#include "src/diagnostic/diagnostic.h"
|
||||
#include "src/program.h"
|
||||
#include "src/type/storage_texture_type.h"
|
||||
#include "src/type/struct_type.h"
|
||||
|
|
Loading…
Reference in New Issue