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>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 2a9376c..cc6732a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -167,8 +167,10 @@
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 @@
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 @@
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 @@
## 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})
diff --git a/src/ast/intrinsic_texture_helper_test.cc b/src/ast/intrinsic_texture_helper_test.cc
index 9c92015..69c9c67 100644
--- a/src/ast/intrinsic_texture_helper_test.cc
+++ b/src/ast/intrinsic_texture_helper_test.cc
@@ -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 @@
return b->ty.i32();
}
- assert(false /* unreachable */);
+ TINT_UNREACHABLE(b->Diagnostics());
return nullptr;
}
@@ -186,7 +187,7 @@
}
}
- assert(false /* unreachable */);
+ TINT_UNREACHABLE(b->Diagnostics());
return nullptr;
}
diff --git a/src/ast/module.cc b/src/ast/module.cc
index a5c19c9..996ecc9 100644
--- a/src/ast/module.cc
+++ b/src/ast/module.cc
@@ -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 @@
} 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 @@
} else if (auto* var = decl->As<Variable>()) {
AddGlobalVariable(ctx->Clone(var));
} else {
- assert(false /* unreachable */);
+ TINT_ICE(ctx->dst->Diagnostics(), "Unknown global declaration type");
}
}
}
diff --git a/src/clone_context.cc b/src/clone_context.cc
index 1cb1ca2..cba462d 100644
--- a/src/clone_context.cc
+++ b/src/clone_context.cc
@@ -44,4 +44,8 @@
return out;
}
+diag::List& CloneContext::Diagnostics() const {
+ return dst->Diagnostics();
+}
+
} // namespace tint
diff --git a/src/clone_context.h b/src/clone_context.h
index 5f9605d..a533bff 100644
--- a/src/clone_context.h
+++ b/src/clone_context.h
@@ -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 @@
}
/// 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*>;
diff --git a/src/clone_context_test.cc b/src/clone_context_test.cc
index bb32334..1a97fb8 100644
--- a/src/clone_context_test.cc
+++ b/src/clone_context_test.cc
@@ -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, 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
diff --git a/src/debug.cc b/src/debug.cc
new file mode 100644
index 0000000..b6d9b01
--- /dev/null
+++ b/src/debug.cc
@@ -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
diff --git a/src/debug.h b/src/debug.h
new file mode 100644
index 0000000..3e7e7de
--- /dev/null
+++ b/src/debug.h
@@ -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_
diff --git a/src/debug_test.cc b/src/debug_test.cc
new file mode 100644
index 0000000..45e8a1f
--- /dev/null
+++ b/src/debug_test.cc
@@ -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
diff --git a/src/diagnostic/diagnostic.cc b/src/diagnostic/diagnostic.cc
index ee6eff6..516c5bc 100644
--- a/src/diagnostic/diagnostic.cc
+++ b/src/diagnostic/diagnostic.cc
@@ -14,6 +14,8 @@
#include "src/diagnostic/diagnostic.h"
+#include "src/diagnostic/formatter.h"
+
namespace tint {
namespace diag {
@@ -27,5 +29,11 @@
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
diff --git a/src/diagnostic/diagnostic.h b/src/diagnostic/diagnostic.h
index e85e8ca..b3b53db 100644
--- a/src/diagnostic/diagnostic.h
+++ b/src/diagnostic/diagnostic.h
@@ -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 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 @@
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 @@
/// @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;
diff --git a/src/diagnostic/formatter.cc b/src/diagnostic/formatter.cc
index 6529eec..4e96d2b 100644
--- a/src/diagnostic/formatter.cc
+++ b/src/diagnostic/formatter.cc
@@ -33,6 +33,8 @@
return "warning";
case Severity::Error:
return "error";
+ case Severity::InternalCompilerError:
+ return "internal compiler error";
case Severity::Fatal:
return "fatal";
}
@@ -112,6 +114,7 @@
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 @@
}
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 @@
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) {
diff --git a/src/diagnostic/formatter_test.cc b/src/diagnostic/formatter_test.cc
index 6c4518f..8bd763d 100644
--- a/src/diagnostic/formatter_test.cc
+++ b/src/diagnostic/formatter_test.cc
@@ -39,6 +39,9 @@
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 @@
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, 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 @@
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 @@
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 @@
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
diff --git a/src/intrinsic_table.cc b/src/intrinsic_table.cc
index f43d6e9..0475721 100644
--- a/src/intrinsic_table.cc
+++ b/src/intrinsic_table.cc
@@ -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 @@
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 @@
/// 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 @@
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 @@
// 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 @@
}
}
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 @@
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 @@
// 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 @@
// 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 @@
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)) {
diff --git a/src/intrinsic_table.h b/src/intrinsic_table.h
index 2f58872..e0771ef 100644
--- a/src/intrinsic_table.h
+++ b/src/intrinsic_table.h
@@ -19,6 +19,7 @@
#include <string>
#include <vector>
+#include "src/diagnostic/diagnostic.h"
#include "src/semantic/intrinsic.h"
namespace tint {
@@ -39,18 +40,20 @@
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
diff --git a/src/intrinsic_table_test.cc b/src/intrinsic_table_test.cc
index fc75e0c..0b7c1d5 100644
--- a/src/intrinsic_table_test.cc
+++ b/src/intrinsic_table_test.cc
@@ -38,44 +38,44 @@
};
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, 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, 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, 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, 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, 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, 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, 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 @@
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 @@
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 @@
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, 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 @@
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 @@
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, 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 @@
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, 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, 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, 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, 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, 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>
diff --git a/src/test_main.cc b/src/test_main.cc
new file mode 100644
index 0000000..5fca747
--- /dev/null
+++ b/src/test_main.cc
@@ -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;
+}
diff --git a/src/transform/hlsl.cc b/src/transform/hlsl.cc
index 67651e6..b0ce373 100644
--- a/src/transform/hlsl.cc
+++ b/src/transform/hlsl.cc
@@ -55,46 +55,51 @@
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);
}
}
}
diff --git a/src/type_determiner.cc b/src/type_determiner.cc
index 3e0a797..fc49224 100644
--- a/src/type_determiner.cc
+++ b/src/type_determiner.cc
@@ -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 @@
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 @@
}
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 @@
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 @@
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 @@
} 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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
return true;
}
- set_error(expr->source(), "Unknown binary expression");
+ diagnostics_.add_error("Unknown binary expression", expr->source());
return false;
}
diff --git a/src/type_determiner.h b/src/type_determiner.h
index f661c27..0a4fb3e 100644
--- a/src/type_determiner.h
+++ b/src/type_determiner.h
@@ -68,7 +68,7 @@
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 @@
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 @@
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_;
diff --git a/src/type_determiner_test.cc b/src/type_determiner_test.cc
index 243d172..1d29466 100644
--- a/src/type_determiner_test.cc
+++ b/src/type_determiner_test.cc
@@ -142,7 +142,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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");
}
diff --git a/src/validator/validator_impl.cc b/src/validator/validator_impl.cc
index c25e3d2..a6d9e35 100644
--- a/src/validator/validator_impl.cc
+++ b/src/validator/validator_impl.cc
@@ -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 @@
return false;
}
} else {
- assert(false /* unreachable */);
+ TINT_UNREACHABLE(diags_);
+ return false;
}
}
if (!ValidateEntryPoint(program_->AST().Functions())) {
diff --git a/src/validator/validator_test.cc b/src/validator/validator_test.cc
index d74b1f4..e486543 100644
--- a/src/validator/validator_test.cc
+++ b/src/validator/validator_test.cc
@@ -96,7 +96,7 @@
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 @@
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) {
diff --git a/src/writer/hlsl/generator_impl.cc b/src/writer/hlsl/generator_impl.cc
index 97bee9b..d8ee19d 100644
--- a/src/writer/hlsl/generator_impl.cc
+++ b/src/writer/hlsl/generator_impl.cc
@@ -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() = 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
<< " >> 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 @@
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 @@
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 @@
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 @@
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 @@
}
break;
default:
- error_ = "unexpected intrinsic";
+ diagnostics_.add_error("unexpected intrinsic");
return false;
}
@@ -967,8 +966,9 @@
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 @@
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 @@
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 @@
: 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 @@
// 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 @@
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 @@
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 @@
} 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 @@
} 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 @@
}
}
} 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 @@
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 @@
// 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 @@
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 @@
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 @@
expr = ary->array();
} else {
- error_ = "error emitting storage buffer access";
+ diagnostics_.add_error("error emitting storage buffer access");
return "";
}
}
@@ -2119,7 +2126,7 @@
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 @@
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 @@
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 @@
} 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 @@
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 @@
} 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 @@
// 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 @@
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;
}
diff --git a/src/writer/hlsl/generator_impl.h b/src/writer/hlsl/generator_impl.h
index 23ac598..48cfafd 100644
--- a/src/writer/hlsl/generator_impl.h
+++ b/src/writer/hlsl/generator_impl.h
@@ -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 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 @@
return builder_.TypeOf(expr);
}
- std::string error_;
- size_t indent_ = 0;
-
Namer namer_;
ProgramBuilder builder_;
Symbol current_ep_sym_;
diff --git a/src/writer/hlsl/generator_impl_function_entry_point_data_test.cc b/src/writer/hlsl/generator_impl_function_entry_point_data_test.cc
index b2b6c94..00b0c7a 100644
--- a/src/writer/hlsl/generator_impl_function_entry_point_data_test.cc
+++ b/src/writer/hlsl/generator_impl_function_entry_point_data_test.cc
@@ -238,7 +238,8 @@
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 @@
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,
diff --git a/src/writer/msl/generator_impl.cc b/src/writer/msl/generator_impl.cc
index 6732b0f..ac835bd 100644
--- a/src/writer/msl/generator_impl.cc
+++ b/src/writer/msl/generator_impl.cc
@@ -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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
} 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 @@
} 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 @@
} 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 @@
} 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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
: 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 @@
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 @@
} 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 @@
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 @@
}
out_ << ", access::sample";
} else {
- error_ = "invalid texture type";
+ diagnostics_.add_error("invalid texture type");
return false;
}
out_ << ">";
@@ -2055,7 +2063,7 @@
} 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 @@
}
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 @@
}
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 @@
// 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 @@
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;
}
diff --git a/src/writer/msl/generator_impl_function_entry_point_data_test.cc b/src/writer/msl/generator_impl_function_entry_point_data_test.cc
index ada6284..e2051aa 100644
--- a/src/writer/msl/generator_impl_function_entry_point_data_test.cc
+++ b/src/writer/msl/generator_impl_function_entry_point_data_test.cc
@@ -212,7 +212,8 @@
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 @@
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) {
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index bd75938..0526ff9 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -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 @@
break;
}
default:
- break; // unreachable
+ TINT_UNREACHABLE(builder_.Diagnostics());
+ return false;
}
if (auto* offset = arg(Usage::kOffset)) {
diff --git a/src/writer/text_generator.cc b/src/writer/text_generator.cc
index e057abe..bfd13fc 100644
--- a/src/writer/text_generator.cc
+++ b/src/writer/text_generator.cc
@@ -26,8 +26,12 @@
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 << " ";
}
}
diff --git a/src/writer/text_generator.h b/src/writer/text_generator.h
index 46da1bc..ab6d47a 100644
--- a/src/writer/text_generator.h
+++ b/src/writer/text_generator.h
@@ -18,6 +18,8 @@
#include <sstream>
#include <string>
+#include "src/diagnostic/diagnostic.h"
+
namespace tint {
namespace writer {
@@ -42,17 +44,24 @@
/// 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;
diff --git a/src/writer/wgsl/generator_impl.cc b/src/writer/wgsl/generator_impl.cc
index 674709d..754327a 100644
--- a/src/writer/wgsl/generator_impl.cc
+++ b/src/writer/wgsl/generator_impl.cc
@@ -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 @@
return false;
}
} else {
- assert(false /* unreachable */);
+ TINT_UNREACHABLE(diagnostics_);
+ return false;
}
if (decl != program_->AST().GlobalDeclarations().back()) {
@@ -136,7 +138,7 @@
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 @@
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 @@
return EmitUnaryOp(u);
}
- error_ = "unknown expression type";
+ diagnostics_.add_error("unknown expression type");
return false;
}
@@ -300,7 +302,7 @@
} 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::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 @@
} 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 @@
} 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 @@
out_ << "cube_array";
break;
default:
- error_ = "unknown texture dimension";
+ diagnostics_.add_error("unknown texture dimension");
return false;
}
@@ -506,7 +508,7 @@
} 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 @@
} 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 @@
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 @@
return EmitVariable(v->variable());
}
- error_ = "unknown statement type: " + program_->str(stmt);
+ diagnostics_.add_error("unknown statement type: " + program_->str(stmt));
return false;
}
diff --git a/src/writer/wgsl/generator_impl.h b/src/writer/wgsl/generator_impl.h
index 9befd51..25a84fb 100644
--- a/src/writer/wgsl/generator_impl.h
+++ b/src/writer/wgsl/generator_impl.h
@@ -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"