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:
Ben Clayton 2021-02-17 20:13:34 +00:00 committed by Commit Bot service account
parent c7ca7668cc
commit 6b4924fb0e
35 changed files with 858 additions and 476 deletions

View File

@ -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",

View File

@ -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;

View File

@ -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})

View File

@ -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;
}

View File

@ -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");
}
}
}

View File

@ -44,4 +44,8 @@ ast::FunctionList CloneContext::Clone(const ast::FunctionList& v) {
return out;
}
diag::List& CloneContext::Diagnostics() const {
return dst->Diagnostics();
}
} // namespace tint

View File

@ -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*>;

View File

@ -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

83
src/debug.cc Normal file
View File

@ -0,0 +1,83 @@
// Copyright 2021 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/debug.h"
#include <stdio.h>
#include <sstream>
#include <string>
#include <vector>
#include "src/diagnostic/diagnostic.h"
namespace tint {
namespace {
InternalCompilerErrorReporter* ice_reporter = nullptr;
class SourceFileToDelete {
public:
static SourceFileToDelete& Get() {
static SourceFileToDelete* instance = new SourceFileToDelete();
return *instance;
}
/// Adds file to the list that will be deleted on call to Free()
/// Note - this function is _not_ thread safe. If we have multiple internal
/// compiler errors occurring at the same time on different threads, then
/// we're in serious trouble.
void Add(Source::File* file) { files.emplace_back(file); }
/// Free deletes all the source files added by calls to Add() and then this
/// SourceFileToDelete object. The SourceFileToDelete must not be used after
/// calling.
void Free() {
for (auto* file : files) {
delete file;
}
delete this;
}
private:
std::vector<Source::File*> files;
};
} // namespace
void FreeInternalCompilerErrors() {
SourceFileToDelete::Get().Free();
}
void SetInternalCompilerErrorReporter(InternalCompilerErrorReporter* reporter) {
ice_reporter = reporter;
}
void InternalCompilerError(const char* filepath,
size_t line,
const std::string& msg,
diag::List& diagnostics) {
auto* file = new Source::File(filepath, "");
SourceFileToDelete::Get().Add(file);
Source source{Source::Range{Source::Location{line}}, file};
diagnostics.add_ice(msg, source);
if (ice_reporter) {
ice_reporter(diagnostics);
}
}
} // namespace tint

69
src/debug.h Normal file
View File

@ -0,0 +1,69 @@
// Copyright 2021 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_DEBUG_H_
#define SRC_DEBUG_H_
#include <string>
#include "src/diagnostic/diagnostic.h"
#include "src/diagnostic/formatter.h"
#include "src/diagnostic/printer.h"
namespace tint {
/// Function type used for registering an internal compiler error reporter
using InternalCompilerErrorReporter = void(const diag::List&);
/// Frees any memory allocated for reporting internal compiler errors.
/// Must only be called once on application termination.
/// If an internal compiler error is raised and this function is not called,
/// then memory will leak.
void FreeInternalCompilerErrors();
/// Sets the global error reporter to be called in case of internal compiler
/// errors.
/// @param reporter the error reporter
void SetInternalCompilerErrorReporter(InternalCompilerErrorReporter* reporter);
/// InternalCompilerError adds the internal compiler error message to the
/// diagnostics list, and then calls the InternalCompilerErrorReporter if one is
/// set.
/// @param file the file containing the ICE
/// @param line the line containing the ICE
/// @param msg the ICE message
/// @param diagnostics the list of diagnostics to append the ICE message to
void InternalCompilerError(const char* file,
size_t line,
const std::string& msg,
diag::List& diagnostics);
} // namespace tint
/// TINT_ICE() is a macro for appending an internal compiler error message
/// to the diagnostics list `diagnostics`, and calling the
/// InternalCompilerErrorReporter with the full diagnostic list if a reporter is
/// set.
/// The ICE message contains the callsite's file and line.
#define TINT_ICE(diagnostics, msg) \
tint::InternalCompilerError(__FILE__, __LINE__, msg, diagnostics)
/// TINT_UNREACHABLE() is a macro for appending a "TINT_UNREACHABLE"
/// internal compiler error message to the diagnostics list `diagnostics`, and
/// calling the InternalCompilerErrorReporter with the full diagnostic list if a
/// reporter is set.
/// The ICE message contains the callsite's file and line.
#define TINT_UNREACHABLE(diagnostics) TINT_ICE(diagnostics, "TINT_UNREACHABLE")
#endif // SRC_DEBUG_H_

32
src/debug_test.cc Normal file
View File

@ -0,0 +1,32 @@
// Copyright 2021 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/debug.h"
#include "gtest/gtest-spi.h"
namespace tint {
namespace {
TEST(DebugTest, Unreachable) {
EXPECT_FATAL_FAILURE(
{
diag::List diagnostics;
TINT_UNREACHABLE(diagnostics);
},
"internal compiler error");
}
} // namespace
} // namespace tint

View File

@ -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

View File

@ -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;

View File

@ -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) {

View File

@ -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

View File

@ -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)) {

View File

@ -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

View File

@ -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>

38
src/test_main.cc Normal file
View File

@ -0,0 +1,38 @@
// Copyright 2021 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "src/debug.h"
namespace {
void TintInternalCompilerErrorReporter(const tint::diag::List& diagnostics) {
FAIL() << diagnostics.str();
}
} // namespace
// Entry point for tint unit tests
int main(int argc, char** argv) {
testing::InitGoogleMock(&argc, argv);
tint::SetInternalCompilerErrorReporter(&TintInternalCompilerErrorReporter);
auto res = RUN_ALL_TESTS();
tint::FreeInternalCompilerErrors();
return res;
}

View File

@ -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);
}
}
}

View File

@ -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;
}

View File

@ -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_;

View File

@ -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");
}

View File

@ -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())) {

View File

@ -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) {

View File

@ -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;
}

View File

@ -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_;

View File

@ -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,

View File

@ -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;
}

View File

@ -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) {

View File

@ -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)) {

View File

@ -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 << " ";
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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"