Import Tint changes from Dawn
Changes:
- 958a4642f14e66b9dec8e7b38f8ebe9b62a65297 tint/resolver: Use utils::Vector in a few places by Ben Clayton <bclayton@google.com>
- d9a61cedaf0923473425249c0df7a73867b4f392 Remove source copies in the parser. by dan sinclair <dsinclair@chromium.org>
- 4d1d143977dd4272dc61eb7c3f6fc96f539f77f8 tint/utils: More Vector polish by Ben Clayton <bclayton@google.com>
- 0cbf5a922fe7b0b759db48ab07a329645b53e6bd Pre-parse token list. by dan sinclair <dsinclair@chromium.org>
- 833ccab3842a72c302f84d9b5e4aefb64c8be66f Optimize the lexer match method. by dan sinclair <dsinclair@chromium.org>
- 08482ec7ed7c45e27b02f4e8000eace9bb933cfa Expand ASCII short circuit. by dan sinclair <dsinclair@chromium.org>
- b0499e446f858077061eda00181a80bcd50120de Add optional attribute trailing commas. by dan sinclair <dsinclair@chromium.org>
- 659d58ceca0e07f4d1a2a292268397b58326c80c tint/resolver: Fix BuiltinCall validator nullptr deref by Zhaoming Jiang <zhaoming.jiang@intel.com>
GitOrigin-RevId: 958a4642f14e66b9dec8e7b38f8ebe9b62a65297
Change-Id: I28358e6be6d405ea3811edf79d0b8c1800285dd0
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/97183
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/ast/traverse_expressions.h b/src/tint/ast/traverse_expressions.h
index 59b00e9..0974fd8 100644
--- a/src/tint/ast/traverse_expressions.h
+++ b/src/tint/ast/traverse_expressions.h
@@ -26,6 +26,7 @@
#include "src/tint/ast/phony_expression.h"
#include "src/tint/ast/unary_op_expression.h"
#include "src/tint/utils/reverse.h"
+#include "src/tint/utils/vector.h"
namespace tint::ast {
@@ -67,35 +68,34 @@
size_t depth;
};
- std::vector<Pending> to_visit{{root, 0}};
+ utils::Vector<Pending, 64> to_visit{{root, 0}};
auto push_single = [&](const ast::Expression* expr, size_t depth) {
- to_visit.push_back({expr, depth});
+ to_visit.Push({expr, depth});
};
auto push_pair = [&](const ast::Expression* left, const ast::Expression* right, size_t depth) {
if (ORDER == TraverseOrder::LeftToRight) {
- to_visit.push_back({right, depth});
- to_visit.push_back({left, depth});
+ to_visit.Push({right, depth});
+ to_visit.Push({left, depth});
} else {
- to_visit.push_back({left, depth});
- to_visit.push_back({right, depth});
+ to_visit.Push({left, depth});
+ to_visit.Push({right, depth});
}
};
auto push_list = [&](const std::vector<const ast::Expression*>& exprs, size_t depth) {
if (ORDER == TraverseOrder::LeftToRight) {
for (auto* expr : utils::Reverse(exprs)) {
- to_visit.push_back({expr, depth});
+ to_visit.Push({expr, depth});
}
} else {
for (auto* expr : exprs) {
- to_visit.push_back({expr, depth});
+ to_visit.Push({expr, depth});
}
}
};
- while (!to_visit.empty()) {
- auto p = to_visit.back();
- to_visit.pop_back();
+ while (!to_visit.IsEmpty()) {
+ auto p = to_visit.Pop();
const ast::Expression* expr = p.expr;
if (auto* filtered = expr->template As<EXPR_TYPE>()) {
diff --git a/src/tint/castable.h b/src/tint/castable.h
index 806d0f0..d654fca 100644
--- a/src/tint/castable.h
+++ b/src/tint/castable.h
@@ -468,7 +468,7 @@
/// Alias to typename CastableCommonBaseImpl<TYPES>::type
template <typename... TYPES>
-using CastableCommonBase = typename detail::CastableCommonBaseImpl<TYPES...>::type;
+using CastableCommonBase = typename CastableCommonBaseImpl<TYPES...>::type;
/// CastableCommonBaseImpl template specialization for a single type
template <typename T>
diff --git a/src/tint/reader/wgsl/lexer.cc b/src/tint/reader/wgsl/lexer.cc
index 93392b6..c9056e0 100644
--- a/src/tint/reader/wgsl/lexer.cc
+++ b/src/tint/reader/wgsl/lexer.cc
@@ -37,6 +37,8 @@
"tint::reader::wgsl requires the size of a std::string element "
"to be a single byte");
+static constexpr size_t kDefaultListSize = 512;
+
bool read_blankspace(std::string_view str, size_t i, bool* is_blankspace, size_t* blankspace_size) {
// See https://www.w3.org/TR/WGSL/#blankspace
@@ -88,6 +90,27 @@
Lexer::~Lexer() = default;
+std::vector<Token> Lexer::Lex() {
+ std::vector<Token> tokens;
+ tokens.reserve(kDefaultListSize);
+ while (true) {
+ tokens.emplace_back(next());
+
+ // If the token can be split, we insert a placeholder element into
+ // the stream to hold the split character.
+ if (tokens.back().IsSplittable()) {
+ auto src = tokens.back().source();
+ src.range.begin.column++;
+ tokens.emplace_back(Token(Token::Type::kPlaceholder, src));
+ }
+
+ if (tokens.back().IsEof() || tokens.back().IsError()) {
+ break;
+ }
+ }
+ return tokens;
+}
+
const std::string_view Lexer::line() const {
if (file_->content.lines.size() == 0) {
static const char* empty_string = "";
@@ -204,6 +227,13 @@
return substr(pos, sub_string.size()) == sub_string;
}
+bool Lexer::matches(size_t pos, char ch) {
+ if (pos >= length()) {
+ return false;
+ }
+ return line()[pos] == ch;
+}
+
Token Lexer::skip_blankspace_and_comments() {
for (;;) {
auto loc = location_;
@@ -298,7 +328,7 @@
auto source = begin_source();
bool has_mantissa_digits = false;
- if (matches(end, "-")) {
+ if (matches(end, '-')) {
end++;
}
while (end < length() && is_digit(at(end))) {
@@ -307,7 +337,7 @@
}
bool has_point = false;
- if (end < length() && matches(end, ".")) {
+ if (end < length() && matches(end, '.')) {
has_point = true;
end++;
}
@@ -323,9 +353,9 @@
// Parse the exponent if one exists
bool has_exponent = false;
- if (end < length() && (matches(end, "e") || matches(end, "E"))) {
+ if (end < length() && (matches(end, 'e') || matches(end, 'E'))) {
end++;
- if (end < length() && (matches(end, "+") || matches(end, "-"))) {
+ if (end < length() && (matches(end, '+') || matches(end, '-'))) {
end++;
}
@@ -344,10 +374,10 @@
bool has_f_suffix = false;
bool has_h_suffix = false;
- if (end < length() && matches(end, "f")) {
+ if (end < length() && matches(end, 'f')) {
end++;
has_f_suffix = true;
- } else if (end < length() && matches(end, "h")) {
+ } else if (end < length() && matches(end, 'h')) {
end++;
has_h_suffix = true;
}
@@ -410,12 +440,12 @@
// -?
uint64_t sign_bit = 0;
- if (matches(end, "-")) {
+ if (matches(end, '-')) {
sign_bit = 1;
end++;
}
// 0[xX]
- if (matches(end, "0x") || matches(end, "0X")) {
+ if (matches(end, '0') && (matches(end + 1, 'x') || matches(end + 1, 'X'))) {
end += 2;
} else {
return {};
@@ -461,7 +491,7 @@
// .?
bool hex_point = false;
- if (matches(end, ".")) {
+ if (matches(end, '.')) {
hex_point = true;
end++;
}
@@ -479,7 +509,7 @@
}
// Is the binary exponent present? It's optional.
- const bool has_exponent = (matches(end, "p") || matches(end, "P"));
+ const bool has_exponent = (matches(end, 'p') || matches(end, 'P'));
if (has_exponent) {
end++;
}
@@ -560,9 +590,9 @@
if (has_exponent) {
// Parse the rest of the exponent.
// (+|-)?
- if (matches(end, "+")) {
+ if (matches(end, '+')) {
end++;
- } else if (matches(end, "-")) {
+ } else if (matches(end, '-')) {
exponent_sign = -1;
end++;
}
@@ -587,10 +617,10 @@
// Parse optional 'f' or 'h' suffix. For a hex float, it can only exist
// when the exponent is present. Otherwise it will look like
// one of the mantissa digits.
- if (end < length() && matches(end, "f")) {
+ if (end < length() && matches(end, 'f')) {
has_f_suffix = true;
end++;
- } else if (end < length() && matches(end, "h")) {
+ } else if (end < length() && matches(end, 'h')) {
has_h_suffix = true;
end++;
}
@@ -794,7 +824,7 @@
advance(static_cast<size_t>(end_ptr - start_ptr));
}
- if (matches(pos(), "u")) {
+ if (matches(pos(), 'u')) {
if (!overflow && CheckedConvert<u32>(AInt(res))) {
advance(1);
end_source(source);
@@ -803,7 +833,7 @@
return {Token::Type::kError, source, "value cannot be represented as 'u32'"};
}
- if (matches(pos(), "i")) {
+ if (matches(pos(), 'i')) {
if (!overflow && CheckedConvert<i32>(AInt(res))) {
advance(1);
end_source(source);
@@ -825,11 +855,11 @@
auto source = begin_source();
- if (matches(curr, "-")) {
+ if (matches(curr, '-')) {
curr++;
}
- if (matches(curr, "0x") || matches(curr, "0X")) {
+ if (matches(curr, '0') && (matches(curr + 1, 'x') || matches(curr + 1, 'X'))) {
curr += 2;
} else {
return {};
@@ -849,7 +879,7 @@
auto source = begin_source();
- if (matches(curr, "-")) {
+ if (matches(curr, '-')) {
curr++;
}
@@ -927,138 +957,162 @@
auto source = begin_source();
auto type = Token::Type::kUninitialized;
- if (matches(pos(), "@")) {
+ if (matches(pos(), '@')) {
type = Token::Type::kAttr;
advance(1);
- } else if (matches(pos(), "(")) {
+ } else if (matches(pos(), '(')) {
type = Token::Type::kParenLeft;
advance(1);
- } else if (matches(pos(), ")")) {
+ } else if (matches(pos(), ')')) {
type = Token::Type::kParenRight;
advance(1);
- } else if (matches(pos(), "[")) {
+ } else if (matches(pos(), '[')) {
type = Token::Type::kBracketLeft;
advance(1);
- } else if (matches(pos(), "]")) {
+ } else if (matches(pos(), ']')) {
type = Token::Type::kBracketRight;
advance(1);
- } else if (matches(pos(), "{")) {
+ } else if (matches(pos(), '{')) {
type = Token::Type::kBraceLeft;
advance(1);
- } else if (matches(pos(), "}")) {
+ } else if (matches(pos(), '}')) {
type = Token::Type::kBraceRight;
advance(1);
- } else if (matches(pos(), "&&")) {
- type = Token::Type::kAndAnd;
- advance(2);
- } else if (matches(pos(), "&=")) {
- type = Token::Type::kAndEqual;
- advance(2);
- } else if (matches(pos(), "&")) {
- type = Token::Type::kAnd;
- advance(1);
- } else if (matches(pos(), "/=")) {
- type = Token::Type::kDivisionEqual;
- advance(2);
- } else if (matches(pos(), "/")) {
- type = Token::Type::kForwardSlash;
- advance(1);
- } else if (matches(pos(), "!=")) {
- type = Token::Type::kNotEqual;
- advance(2);
- } else if (matches(pos(), "!")) {
- type = Token::Type::kBang;
- advance(1);
- } else if (matches(pos(), ":")) {
+ } else if (matches(pos(), '&')) {
+ if (matches(pos() + 1, '&')) {
+ type = Token::Type::kAndAnd;
+ advance(2);
+ } else if (matches(pos() + 1, '=')) {
+ type = Token::Type::kAndEqual;
+ advance(2);
+ } else {
+ type = Token::Type::kAnd;
+ advance(1);
+ }
+ } else if (matches(pos(), '/')) {
+ if (matches(pos() + 1, '=')) {
+ type = Token::Type::kDivisionEqual;
+ advance(2);
+ } else {
+ type = Token::Type::kForwardSlash;
+ advance(1);
+ }
+ } else if (matches(pos(), '!')) {
+ if (matches(pos() + 1, '=')) {
+ type = Token::Type::kNotEqual;
+ advance(2);
+ } else {
+ type = Token::Type::kBang;
+ advance(1);
+ }
+ } else if (matches(pos(), ':')) {
type = Token::Type::kColon;
advance(1);
- } else if (matches(pos(), ",")) {
+ } else if (matches(pos(), ',')) {
type = Token::Type::kComma;
advance(1);
- } else if (matches(pos(), "==")) {
- type = Token::Type::kEqualEqual;
- advance(2);
- } else if (matches(pos(), "=")) {
- type = Token::Type::kEqual;
- advance(1);
- } else if (matches(pos(), ">=")) {
- type = Token::Type::kGreaterThanEqual;
- advance(2);
- } else if (matches(pos(), ">>")) {
- type = Token::Type::kShiftRight;
- advance(2);
- } else if (matches(pos(), ">")) {
- type = Token::Type::kGreaterThan;
- advance(1);
- } else if (matches(pos(), "<=")) {
- type = Token::Type::kLessThanEqual;
- advance(2);
- } else if (matches(pos(), "<<")) {
- type = Token::Type::kShiftLeft;
- advance(2);
- } else if (matches(pos(), "<")) {
- type = Token::Type::kLessThan;
- advance(1);
- } else if (matches(pos(), "%=")) {
- type = Token::Type::kModuloEqual;
- advance(2);
- } else if (matches(pos(), "%")) {
- type = Token::Type::kMod;
- advance(1);
- } else if (matches(pos(), "->")) {
- type = Token::Type::kArrow;
- advance(2);
- } else if (matches(pos(), "--")) {
- type = Token::Type::kMinusMinus;
- advance(2);
- } else if (matches(pos(), "-=")) {
- type = Token::Type::kMinusEqual;
- advance(2);
- } else if (matches(pos(), "-")) {
- type = Token::Type::kMinus;
- advance(1);
- } else if (matches(pos(), ".")) {
+ } else if (matches(pos(), '=')) {
+ if (matches(pos() + 1, '=')) {
+ type = Token::Type::kEqualEqual;
+ advance(2);
+ } else {
+ type = Token::Type::kEqual;
+ advance(1);
+ }
+ } else if (matches(pos(), '>')) {
+ if (matches(pos() + 1, '=')) {
+ type = Token::Type::kGreaterThanEqual;
+ advance(2);
+ } else if (matches(pos() + 1, '>')) {
+ type = Token::Type::kShiftRight;
+ advance(2);
+ } else {
+ type = Token::Type::kGreaterThan;
+ advance(1);
+ }
+ } else if (matches(pos(), '<')) {
+ if (matches(pos() + 1, '=')) {
+ type = Token::Type::kLessThanEqual;
+ advance(2);
+ } else if (matches(pos() + 1, '<')) {
+ type = Token::Type::kShiftLeft;
+ advance(2);
+ } else {
+ type = Token::Type::kLessThan;
+ advance(1);
+ }
+ } else if (matches(pos(), '%')) {
+ if (matches(pos() + 1, '=')) {
+ type = Token::Type::kModuloEqual;
+ advance(2);
+ } else {
+ type = Token::Type::kMod;
+ advance(1);
+ }
+ } else if (matches(pos(), '-')) {
+ if (matches(pos() + 1, '>')) {
+ type = Token::Type::kArrow;
+ advance(2);
+ } else if (matches(pos() + 1, '-')) {
+ type = Token::Type::kMinusMinus;
+ advance(2);
+ } else if (matches(pos() + 1, '=')) {
+ type = Token::Type::kMinusEqual;
+ advance(2);
+ } else {
+ type = Token::Type::kMinus;
+ advance(1);
+ }
+ } else if (matches(pos(), '.')) {
type = Token::Type::kPeriod;
advance(1);
- } else if (matches(pos(), "++")) {
- type = Token::Type::kPlusPlus;
- advance(2);
- } else if (matches(pos(), "+=")) {
- type = Token::Type::kPlusEqual;
- advance(2);
- } else if (matches(pos(), "+")) {
- type = Token::Type::kPlus;
- advance(1);
- } else if (matches(pos(), "||")) {
- type = Token::Type::kOrOr;
- advance(2);
- } else if (matches(pos(), "|=")) {
- type = Token::Type::kOrEqual;
- advance(2);
- } else if (matches(pos(), "|")) {
- type = Token::Type::kOr;
- advance(1);
- } else if (matches(pos(), ";")) {
+ } else if (matches(pos(), '+')) {
+ if (matches(pos() + 1, '+')) {
+ type = Token::Type::kPlusPlus;
+ advance(2);
+ } else if (matches(pos() + 1, '=')) {
+ type = Token::Type::kPlusEqual;
+ advance(2);
+ } else {
+ type = Token::Type::kPlus;
+ advance(1);
+ }
+ } else if (matches(pos(), '|')) {
+ if (matches(pos() + 1, '|')) {
+ type = Token::Type::kOrOr;
+ advance(2);
+ } else if (matches(pos() + 1, '=')) {
+ type = Token::Type::kOrEqual;
+ advance(2);
+ } else {
+ type = Token::Type::kOr;
+ advance(1);
+ }
+ } else if (matches(pos(), ';')) {
type = Token::Type::kSemicolon;
advance(1);
- } else if (matches(pos(), "*=")) {
- type = Token::Type::kTimesEqual;
- advance(2);
- } else if (matches(pos(), "*")) {
- type = Token::Type::kStar;
- advance(1);
- } else if (matches(pos(), "~")) {
+ } else if (matches(pos(), '*')) {
+ if (matches(pos() + 1, '=')) {
+ type = Token::Type::kTimesEqual;
+ advance(2);
+ } else {
+ type = Token::Type::kStar;
+ advance(1);
+ }
+ } else if (matches(pos(), '~')) {
type = Token::Type::kTilde;
advance(1);
- } else if (matches(pos(), "_")) {
+ } else if (matches(pos(), '_')) {
type = Token::Type::kUnderscore;
advance(1);
- } else if (matches(pos(), "^=")) {
- type = Token::Type::kXorEqual;
- advance(2);
- } else if (matches(pos(), "^")) {
- type = Token::Type::kXor;
- advance(1);
+ } else if (matches(pos(), '^')) {
+ if (matches(pos() + 1, '=')) {
+ type = Token::Type::kXorEqual;
+ advance(2);
+ } else {
+ type = Token::Type::kXor;
+ advance(1);
+ }
}
end_source(source);
diff --git a/src/tint/reader/wgsl/lexer.h b/src/tint/reader/wgsl/lexer.h
index d93848f..8e0306b 100644
--- a/src/tint/reader/wgsl/lexer.h
+++ b/src/tint/reader/wgsl/lexer.h
@@ -16,6 +16,7 @@
#define SRC_TINT_READER_WGSL_LEXER_H_
#include <string>
+#include <vector>
#include "src/tint/reader/wgsl/token.h"
@@ -29,11 +30,14 @@
explicit Lexer(const Source::File* file);
~Lexer();
+ /// @return the token list.
+ std::vector<Token> Lex();
+
+ private:
/// Returns the next token in the input stream.
/// @return Token
Token next();
- private:
/// Advances past blankspace and comments, if present at the current position.
/// @returns error token, EOF, or uninitialized
Token skip_blankspace_and_comments();
@@ -96,7 +100,8 @@
bool is_hex(char ch) const;
/// @returns true if string at `pos` matches `substr`
bool matches(size_t pos, std::string_view substr);
-
+ /// @returns true if char at `pos` matches `ch`
+ bool matches(size_t pos, char ch);
/// The source file content
Source::File const* const file_;
/// The current location within the input
diff --git a/src/tint/reader/wgsl/lexer_test.cc b/src/tint/reader/wgsl/lexer_test.cc
index 799f9c2..f82045a 100644
--- a/src/tint/reader/wgsl/lexer_test.cc
+++ b/src/tint/reader/wgsl/lexer_test.cc
@@ -46,24 +46,33 @@
TEST_F(LexerTest, Empty) {
Source::File file("", "");
Lexer l(&file);
- auto t = l.next();
- EXPECT_TRUE(t.IsEof());
+
+ auto list = l.Lex();
+ ASSERT_EQ(1u, list.size());
+ EXPECT_TRUE(list[0].IsEof());
}
TEST_F(LexerTest, Skips_Blankspace_Basic) {
Source::File file("", "\t\r\n\t ident\t\n\t \r ");
Lexer l(&file);
- auto t = l.next();
- EXPECT_TRUE(t.IsIdentifier());
- EXPECT_EQ(t.source().range.begin.line, 2u);
- EXPECT_EQ(t.source().range.begin.column, 6u);
- EXPECT_EQ(t.source().range.end.line, 2u);
- EXPECT_EQ(t.source().range.end.column, 11u);
- EXPECT_EQ(t.to_str(), "ident");
+ auto list = l.Lex();
+ ASSERT_EQ(2u, list.size());
- t = l.next();
- EXPECT_TRUE(t.IsEof());
+ {
+ auto& t = list[0];
+ EXPECT_TRUE(t.IsIdentifier());
+ EXPECT_EQ(t.source().range.begin.line, 2u);
+ EXPECT_EQ(t.source().range.begin.column, 6u);
+ EXPECT_EQ(t.source().range.end.line, 2u);
+ EXPECT_EQ(t.source().range.end.column, 11u);
+ EXPECT_EQ(t.to_str(), "ident");
+ }
+
+ {
+ auto& t = list[1];
+ EXPECT_TRUE(t.IsEof());
+ }
}
TEST_F(LexerTest, Skips_Blankspace_Exotic) {
@@ -73,16 +82,23 @@
kVTab kFF kNL kLS kPS kL2R kR2L);
Lexer l(&file);
- auto t = l.next();
- EXPECT_TRUE(t.IsIdentifier());
- EXPECT_EQ(t.source().range.begin.line, 6u);
- EXPECT_EQ(t.source().range.begin.column, 7u);
- EXPECT_EQ(t.source().range.end.line, 6u);
- EXPECT_EQ(t.source().range.end.column, 12u);
- EXPECT_EQ(t.to_str(), "ident");
+ auto list = l.Lex();
+ ASSERT_EQ(2u, list.size());
- t = l.next();
- EXPECT_TRUE(t.IsEof());
+ {
+ auto& t = list[0];
+ EXPECT_TRUE(t.IsIdentifier());
+ EXPECT_EQ(t.source().range.begin.line, 6u);
+ EXPECT_EQ(t.source().range.begin.column, 7u);
+ EXPECT_EQ(t.source().range.end.line, 6u);
+ EXPECT_EQ(t.source().range.end.column, 12u);
+ EXPECT_EQ(t.to_str(), "ident");
+ }
+
+ {
+ auto& t = list[1];
+ EXPECT_TRUE(t.IsEof());
+ }
}
TEST_F(LexerTest, Skips_Comments_Line) {
@@ -92,24 +108,33 @@
ident2)");
Lexer l(&file);
- auto t = l.next();
- EXPECT_TRUE(t.IsIdentifier());
- EXPECT_EQ(t.source().range.begin.line, 2u);
- EXPECT_EQ(t.source().range.begin.column, 1u);
- EXPECT_EQ(t.source().range.end.line, 2u);
- EXPECT_EQ(t.source().range.end.column, 7u);
- EXPECT_EQ(t.to_str(), "ident1");
+ auto list = l.Lex();
+ ASSERT_EQ(3u, list.size());
- t = l.next();
- EXPECT_TRUE(t.IsIdentifier());
- EXPECT_EQ(t.source().range.begin.line, 4u);
- EXPECT_EQ(t.source().range.begin.column, 2u);
- EXPECT_EQ(t.source().range.end.line, 4u);
- EXPECT_EQ(t.source().range.end.column, 8u);
- EXPECT_EQ(t.to_str(), "ident2");
+ {
+ auto& t = list[0];
+ EXPECT_TRUE(t.IsIdentifier());
+ EXPECT_EQ(t.source().range.begin.line, 2u);
+ EXPECT_EQ(t.source().range.begin.column, 1u);
+ EXPECT_EQ(t.source().range.end.line, 2u);
+ EXPECT_EQ(t.source().range.end.column, 7u);
+ EXPECT_EQ(t.to_str(), "ident1");
+ }
- t = l.next();
- EXPECT_TRUE(t.IsEof());
+ {
+ auto& t = list[1];
+ EXPECT_TRUE(t.IsIdentifier());
+ EXPECT_EQ(t.source().range.begin.line, 4u);
+ EXPECT_EQ(t.source().range.begin.column, 2u);
+ EXPECT_EQ(t.source().range.end.line, 4u);
+ EXPECT_EQ(t.source().range.end.column, 8u);
+ EXPECT_EQ(t.to_str(), "ident2");
+ }
+
+ {
+ auto& t = list[2];
+ EXPECT_TRUE(t.IsEof());
+ }
}
TEST_F(LexerTest, Skips_Comments_Unicode) {
@@ -119,24 +144,33 @@
ident2)");
Lexer l(&file);
- auto t = l.next();
- EXPECT_TRUE(t.IsIdentifier());
- EXPECT_EQ(t.source().range.begin.line, 2u);
- EXPECT_EQ(t.source().range.begin.column, 1u);
- EXPECT_EQ(t.source().range.end.line, 2u);
- EXPECT_EQ(t.source().range.end.column, 7u);
- EXPECT_EQ(t.to_str(), "ident1");
+ auto list = l.Lex();
+ ASSERT_EQ(3u, list.size());
- t = l.next();
- EXPECT_TRUE(t.IsIdentifier());
- EXPECT_EQ(t.source().range.begin.line, 4u);
- EXPECT_EQ(t.source().range.begin.column, 2u);
- EXPECT_EQ(t.source().range.end.line, 4u);
- EXPECT_EQ(t.source().range.end.column, 8u);
- EXPECT_EQ(t.to_str(), "ident2");
+ {
+ auto& t = list[0];
+ EXPECT_TRUE(t.IsIdentifier());
+ EXPECT_EQ(t.source().range.begin.line, 2u);
+ EXPECT_EQ(t.source().range.begin.column, 1u);
+ EXPECT_EQ(t.source().range.end.line, 2u);
+ EXPECT_EQ(t.source().range.end.column, 7u);
+ EXPECT_EQ(t.to_str(), "ident1");
+ }
- t = l.next();
- EXPECT_TRUE(t.IsEof());
+ {
+ auto& t = list[1];
+ EXPECT_TRUE(t.IsIdentifier());
+ EXPECT_EQ(t.source().range.begin.line, 4u);
+ EXPECT_EQ(t.source().range.begin.column, 2u);
+ EXPECT_EQ(t.source().range.end.line, 4u);
+ EXPECT_EQ(t.source().range.end.column, 8u);
+ EXPECT_EQ(t.to_str(), "ident2");
+ }
+
+ {
+ auto& t = list[2];
+ EXPECT_TRUE(t.IsEof());
+ }
}
using LineCommentTerminatorTest = testing::TestWithParam<const char*>;
@@ -150,21 +184,29 @@
Source::File file("", src);
Lexer l(&file);
- auto t = l.next();
- EXPECT_TRUE(t.Is(Token::Type::kConst));
- EXPECT_EQ(t.source().range.begin.line, 1u);
- EXPECT_EQ(t.source().range.begin.column, 1u);
- EXPECT_EQ(t.source().range.end.line, 1u);
- EXPECT_EQ(t.source().range.end.column, 6u);
-
auto is_same_line = [](std::string_view v) {
return v == kSpace || v == kHTab || v == kL2R || v == kR2L;
};
+ auto list = l.Lex();
+ ASSERT_EQ(is_same_line(c) ? 2u : 3u, list.size());
+
+ size_t idx = 0;
+
+ {
+ auto& t = list[idx++];
+ EXPECT_TRUE(t.Is(Token::Type::kConst));
+ EXPECT_EQ(t.source().range.begin.line, 1u);
+ EXPECT_EQ(t.source().range.begin.column, 1u);
+ EXPECT_EQ(t.source().range.end.line, 1u);
+ EXPECT_EQ(t.source().range.end.column, 6u);
+ }
+
if (!is_same_line(c)) {
size_t line = is_same_line(c) ? 1u : 2u;
size_t col = is_same_line(c) ? 25u : 1u;
- t = l.next();
+
+ auto& t = list[idx++];
EXPECT_TRUE(t.IsIdentifier());
EXPECT_EQ(t.source().range.begin.line, line);
EXPECT_EQ(t.source().range.begin.column, col);
@@ -173,8 +215,10 @@
EXPECT_EQ(t.to_str(), "ident");
}
- t = l.next();
- EXPECT_TRUE(t.IsEof());
+ {
+ auto& t = list[idx];
+ EXPECT_TRUE(t.IsEof());
+ }
}
INSTANTIATE_TEST_SUITE_P(LexerTest,
LineCommentTerminatorTest,
@@ -198,16 +242,23 @@
text */ident)");
Lexer l(&file);
- auto t = l.next();
- EXPECT_TRUE(t.IsIdentifier());
- EXPECT_EQ(t.source().range.begin.line, 2u);
- EXPECT_EQ(t.source().range.begin.column, 8u);
- EXPECT_EQ(t.source().range.end.line, 2u);
- EXPECT_EQ(t.source().range.end.column, 13u);
- EXPECT_EQ(t.to_str(), "ident");
+ auto list = l.Lex();
+ ASSERT_EQ(2u, list.size());
- t = l.next();
- EXPECT_TRUE(t.IsEof());
+ {
+ auto& t = list[0];
+ EXPECT_TRUE(t.IsIdentifier());
+ EXPECT_EQ(t.source().range.begin.line, 2u);
+ EXPECT_EQ(t.source().range.begin.column, 8u);
+ EXPECT_EQ(t.source().range.end.line, 2u);
+ EXPECT_EQ(t.source().range.end.column, 13u);
+ EXPECT_EQ(t.to_str(), "ident");
+ }
+
+ {
+ auto& t = list[1];
+ EXPECT_TRUE(t.IsEof());
+ }
}
TEST_F(LexerTest, Skips_Comments_Block_Nested) {
@@ -216,16 +267,23 @@
/////**/ */*/ident)");
Lexer l(&file);
- auto t = l.next();
- EXPECT_TRUE(t.IsIdentifier());
- EXPECT_EQ(t.source().range.begin.line, 3u);
- EXPECT_EQ(t.source().range.begin.column, 14u);
- EXPECT_EQ(t.source().range.end.line, 3u);
- EXPECT_EQ(t.source().range.end.column, 19u);
- EXPECT_EQ(t.to_str(), "ident");
+ auto list = l.Lex();
+ ASSERT_EQ(2u, list.size());
- t = l.next();
- EXPECT_TRUE(t.IsEof());
+ {
+ auto& t = list[0];
+ EXPECT_TRUE(t.IsIdentifier());
+ EXPECT_EQ(t.source().range.begin.line, 3u);
+ EXPECT_EQ(t.source().range.begin.column, 14u);
+ EXPECT_EQ(t.source().range.end.line, 3u);
+ EXPECT_EQ(t.source().range.end.column, 19u);
+ EXPECT_EQ(t.to_str(), "ident");
+ }
+
+ {
+ auto& t = list[1];
+ EXPECT_TRUE(t.IsEof());
+ }
}
TEST_F(LexerTest, Skips_Comments_Block_Unterminated) {
@@ -237,7 +295,10 @@
abcd)");
Lexer l(&file);
- auto t = l.next();
+ auto list = l.Lex();
+ ASSERT_EQ(1u, list.size());
+
+ auto& t = list[0];
ASSERT_TRUE(t.Is(Token::Type::kError));
EXPECT_EQ(t.to_str(), "unterminated block comment");
EXPECT_EQ(t.source().range.begin.line, 2u);
@@ -250,7 +311,10 @@
Source::File file("", std::string{' ', 0, ' '});
Lexer l(&file);
- auto t = l.next();
+ auto list = l.Lex();
+ ASSERT_EQ(1u, list.size());
+
+ auto& t = list[0];
EXPECT_TRUE(t.IsError());
EXPECT_EQ(t.source().range.begin.line, 1u);
EXPECT_EQ(t.source().range.begin.column, 2u);
@@ -263,7 +327,10 @@
Source::File file("", std::string{'/', '/', ' ', 0, ' '});
Lexer l(&file);
- auto t = l.next();
+ auto list = l.Lex();
+ ASSERT_EQ(1u, list.size());
+
+ auto& t = list[0];
EXPECT_TRUE(t.IsError());
EXPECT_EQ(t.source().range.begin.line, 1u);
EXPECT_EQ(t.source().range.begin.column, 4u);
@@ -276,7 +343,10 @@
Source::File file("", std::string{'/', '*', ' ', 0, '*', '/'});
Lexer l(&file);
- auto t = l.next();
+ auto list = l.Lex();
+ ASSERT_EQ(1u, list.size());
+
+ auto& t = list[0];
EXPECT_TRUE(t.IsError());
EXPECT_EQ(t.source().range.begin.line, 1u);
EXPECT_EQ(t.source().range.begin.column, 4u);
@@ -292,16 +362,24 @@
Source::File file("", std::string{'a', 0, 'c'});
Lexer l(&file);
- auto t = l.next();
- EXPECT_TRUE(t.IsIdentifier());
- EXPECT_EQ(t.to_str(), "a");
- t = l.next();
- EXPECT_TRUE(t.IsError());
- EXPECT_EQ(t.source().range.begin.line, 1u);
- EXPECT_EQ(t.source().range.begin.column, 2u);
- EXPECT_EQ(t.source().range.end.line, 1u);
- EXPECT_EQ(t.source().range.end.column, 2u);
- EXPECT_EQ(t.to_str(), "null character found");
+ auto list = l.Lex();
+ ASSERT_EQ(2u, list.size());
+
+ {
+ auto& t = list[0];
+ EXPECT_TRUE(t.IsIdentifier());
+ EXPECT_EQ(t.to_str(), "a");
+ }
+
+ {
+ auto& t = list[1];
+ EXPECT_TRUE(t.IsError());
+ EXPECT_EQ(t.source().range.begin.line, 1u);
+ EXPECT_EQ(t.source().range.begin.column, 2u);
+ EXPECT_EQ(t.source().range.end.line, 1u);
+ EXPECT_EQ(t.source().range.end.column, 2u);
+ EXPECT_EQ(t.to_str(), "null character found");
+ }
}
struct FloatData {
@@ -318,22 +396,29 @@
Source::File file("", params.input);
Lexer l(&file);
- auto t = l.next();
- if (std::string(params.input).back() == 'f') {
- EXPECT_TRUE(t.Is(Token::Type::kFloatLiteral_F));
- } else if (std::string(params.input).back() == 'h') {
- EXPECT_TRUE(t.Is(Token::Type::kFloatLiteral_H));
- } else {
- EXPECT_TRUE(t.Is(Token::Type::kFloatLiteral));
- }
- EXPECT_EQ(t.to_f64(), params.result);
- EXPECT_EQ(t.source().range.begin.line, 1u);
- EXPECT_EQ(t.source().range.begin.column, 1u);
- EXPECT_EQ(t.source().range.end.line, 1u);
- EXPECT_EQ(t.source().range.end.column, 1u + strlen(params.input));
+ auto list = l.Lex();
+ ASSERT_EQ(2u, list.size());
- t = l.next();
- EXPECT_TRUE(t.IsEof());
+ {
+ auto& t = list[0];
+ if (std::string(params.input).back() == 'f') {
+ EXPECT_TRUE(t.Is(Token::Type::kFloatLiteral_F));
+ } else if (std::string(params.input).back() == 'h') {
+ EXPECT_TRUE(t.Is(Token::Type::kFloatLiteral_H));
+ } else {
+ EXPECT_TRUE(t.Is(Token::Type::kFloatLiteral));
+ }
+ EXPECT_EQ(t.to_f64(), params.result);
+ EXPECT_EQ(t.source().range.begin.line, 1u);
+ EXPECT_EQ(t.source().range.begin.column, 1u);
+ EXPECT_EQ(t.source().range.end.line, 1u);
+ EXPECT_EQ(t.source().range.end.column, 1u + strlen(params.input));
+ }
+
+ {
+ auto& t = list[1];
+ EXPECT_TRUE(t.IsEof());
+ }
}
INSTANTIATE_TEST_SUITE_P(LexerTest,
FloatTest,
@@ -437,7 +522,10 @@
Source::File file("", GetParam());
Lexer l(&file);
- auto t = l.next();
+ auto list = l.Lex();
+ ASSERT_FALSE(list.empty());
+
+ auto& t = list[0];
EXPECT_FALSE(t.Is(Token::Type::kFloatLiteral) || t.Is(Token::Type::kFloatLiteral_F) ||
t.Is(Token::Type::kFloatLiteral_H));
}
@@ -478,13 +566,23 @@
Source::File file("", GetParam());
Lexer l(&file);
- auto t = l.next();
- EXPECT_TRUE(t.IsIdentifier());
- EXPECT_EQ(t.source().range.begin.line, 1u);
- EXPECT_EQ(t.source().range.begin.column, 1u);
- EXPECT_EQ(t.source().range.end.line, 1u);
- EXPECT_EQ(t.source().range.end.column, 1u + strlen(GetParam()));
- EXPECT_EQ(t.to_str(), GetParam());
+ auto list = l.Lex();
+ ASSERT_EQ(2u, list.size());
+
+ {
+ auto& t = list[0];
+ EXPECT_TRUE(t.IsIdentifier());
+ EXPECT_EQ(t.source().range.begin.line, 1u);
+ EXPECT_EQ(t.source().range.begin.column, 1u);
+ EXPECT_EQ(t.source().range.end.line, 1u);
+ EXPECT_EQ(t.source().range.end.column, 1u + strlen(GetParam()));
+ EXPECT_EQ(t.to_str(), GetParam());
+ }
+
+ {
+ auto& t = list[1];
+ EXPECT_TRUE(t.IsEof());
+ }
}
INSTANTIATE_TEST_SUITE_P(LexerTest,
AsciiIdentifierTest,
@@ -510,13 +608,23 @@
Source::File file("", GetParam().utf8);
Lexer l(&file);
- auto t = l.next();
- EXPECT_TRUE(t.IsIdentifier());
- EXPECT_EQ(t.source().range.begin.line, 1u);
- EXPECT_EQ(t.source().range.begin.column, 1u);
- EXPECT_EQ(t.source().range.end.line, 1u);
- EXPECT_EQ(t.source().range.end.column, 1u + GetParam().count);
- EXPECT_EQ(t.to_str(), GetParam().utf8);
+ auto list = l.Lex();
+ ASSERT_EQ(2u, list.size());
+
+ {
+ auto& t = list[0];
+ EXPECT_TRUE(t.IsIdentifier());
+ EXPECT_EQ(t.source().range.begin.line, 1u);
+ EXPECT_EQ(t.source().range.begin.column, 1u);
+ EXPECT_EQ(t.source().range.end.line, 1u);
+ EXPECT_EQ(t.source().range.end.column, 1u + GetParam().count);
+ EXPECT_EQ(t.to_str(), GetParam().utf8);
+ }
+
+ {
+ auto& t = list[1];
+ EXPECT_TRUE(t.IsEof());
+ }
}
INSTANTIATE_TEST_SUITE_P(
LexerTest,
@@ -554,7 +662,10 @@
Source::File file("", GetParam());
Lexer l(&file);
- auto t = l.next();
+ auto list = l.Lex();
+ ASSERT_FALSE(list.empty());
+
+ auto& t = list[0];
EXPECT_TRUE(t.IsError());
EXPECT_EQ(t.source().range.begin.line, 1u);
EXPECT_EQ(t.source().range.begin.column, 1u);
@@ -602,7 +713,10 @@
Source::File file("", "_");
Lexer l(&file);
- auto t = l.next();
+ auto list = l.Lex();
+ ASSERT_FALSE(list.empty());
+
+ auto& t = list[0];
EXPECT_FALSE(t.IsIdentifier());
}
@@ -610,7 +724,10 @@
Source::File file("", "__test");
Lexer l(&file);
- auto t = l.next();
+ auto list = l.Lex();
+ ASSERT_FALSE(list.empty());
+
+ auto& t = list[0];
EXPECT_FALSE(t.IsIdentifier());
}
@@ -618,7 +735,10 @@
Source::File file("", "01test");
Lexer l(&file);
- auto t = l.next();
+ auto list = l.Lex();
+ EXPECT_FALSE(list.empty());
+
+ auto& t = list[0];
EXPECT_FALSE(t.IsIdentifier());
}
@@ -641,7 +761,12 @@
auto params = std::get<1>(GetParam());
Source::File file("", params.input);
- auto t = Lexer(&file).next();
+ Lexer l(&file);
+
+ auto list = l.Lex();
+ ASSERT_FALSE(list.empty());
+
+ auto& t = list[0];
switch (suffix) {
case 'i':
EXPECT_TRUE(t.Is(Token::Type::kIntLiteral_I));
@@ -770,7 +895,12 @@
auto type = std::get<0>(GetParam());
auto source = std::get<1>(GetParam());
Source::File file("", source);
- auto t = Lexer(&file).next();
+
+ Lexer l(&file);
+ auto list = l.Lex();
+ ASSERT_FALSE(list.empty());
+
+ auto& t = list[0];
EXPECT_TRUE(t.Is(Token::Type::kError));
auto expect = "value cannot be represented as '" + std::string(type) + "'";
EXPECT_EQ(t.to_str(), expect);
@@ -800,7 +930,12 @@
using ParseIntegerTest_LeadingZeros = testing::TestWithParam<const char*>;
TEST_P(ParseIntegerTest_LeadingZeros, Parse) {
Source::File file("", GetParam());
- auto t = Lexer(&file).next();
+
+ Lexer l(&file);
+ auto list = l.Lex();
+ ASSERT_FALSE(list.empty());
+
+ auto& t = list[0];
EXPECT_TRUE(t.Is(Token::Type::kError));
EXPECT_EQ(t.to_str(), "integer literal cannot have leading 0s");
}
@@ -815,7 +950,12 @@
using ParseIntegerTest_NoSignificantDigits = testing::TestWithParam<const char*>;
TEST_P(ParseIntegerTest_NoSignificantDigits, Parse) {
Source::File file("", GetParam());
- auto t = Lexer(&file).next();
+
+ Lexer l(&file);
+ auto list = l.Lex();
+ ASSERT_FALSE(list.empty());
+
+ auto& t = list[0];
EXPECT_TRUE(t.Is(Token::Type::kError));
EXPECT_EQ(t.to_str(), "integer or float hex literal has no significant digits");
}
@@ -849,15 +989,22 @@
Source::File file("", params.input);
Lexer l(&file);
- auto t = l.next();
- EXPECT_TRUE(t.Is(params.type));
- EXPECT_EQ(t.source().range.begin.line, 1u);
- EXPECT_EQ(t.source().range.begin.column, 1u);
- EXPECT_EQ(t.source().range.end.line, 1u);
- EXPECT_EQ(t.source().range.end.column, 1u + strlen(params.input));
+ auto list = l.Lex();
+ ASSERT_GE(list.size(), 2u);
- t = l.next();
- EXPECT_EQ(t.source().range.begin.column, 1 + std::string(params.input).size());
+ {
+ auto& t = list[0];
+ EXPECT_TRUE(t.Is(params.type));
+ EXPECT_EQ(t.source().range.begin.line, 1u);
+ EXPECT_EQ(t.source().range.begin.column, 1u);
+ EXPECT_EQ(t.source().range.end.line, 1u);
+ EXPECT_EQ(t.source().range.end.column, 1u + strlen(params.input));
+ }
+
+ {
+ auto& t = list[1];
+ EXPECT_EQ(t.source().range.begin.column, 1 + std::string(params.input).size());
+ }
}
INSTANTIATE_TEST_SUITE_P(LexerTest,
PunctuationTest,
@@ -876,8 +1023,6 @@
TokenData{"=", Token::Type::kEqual},
TokenData{"==", Token::Type::kEqualEqual},
TokenData{">", Token::Type::kGreaterThan},
- TokenData{">=", Token::Type::kGreaterThanEqual},
- TokenData{">>", Token::Type::kShiftRight},
TokenData{"<", Token::Type::kLessThan},
TokenData{"<=", Token::Type::kLessThanEqual},
TokenData{"<<", Token::Type::kShiftLeft},
@@ -906,21 +1051,60 @@
TokenData{"|=", Token::Type::kOrEqual},
TokenData{"^=", Token::Type::kXorEqual}));
+using SplittablePunctuationTest = testing::TestWithParam<TokenData>;
+TEST_P(SplittablePunctuationTest, Parses) {
+ auto params = GetParam();
+ Source::File file("", params.input);
+ Lexer l(&file);
+
+ auto list = l.Lex();
+ ASSERT_GE(list.size(), 3u);
+
+ {
+ auto& t = list[0];
+ EXPECT_TRUE(t.Is(params.type));
+ EXPECT_EQ(t.source().range.begin.line, 1u);
+ EXPECT_EQ(t.source().range.begin.column, 1u);
+ EXPECT_EQ(t.source().range.end.line, 1u);
+ EXPECT_EQ(t.source().range.end.column, 1u + strlen(params.input));
+ }
+
+ {
+ auto& t = list[1];
+ EXPECT_TRUE(t.Is(Token::Type::kPlaceholder));
+ EXPECT_EQ(t.source().range.begin.line, 1u);
+ EXPECT_EQ(t.source().range.begin.column, 2u);
+ EXPECT_EQ(t.source().range.end.line, 1u);
+ EXPECT_EQ(t.source().range.end.column, 1u + strlen(params.input));
+ }
+
+ {
+ auto& t = list[2];
+ EXPECT_EQ(t.source().range.begin.column, 1 + std::string(params.input).size());
+ }
+}
+INSTANTIATE_TEST_SUITE_P(LexerTest,
+ SplittablePunctuationTest,
+ testing::Values(TokenData{">=", Token::Type::kGreaterThanEqual},
+ TokenData{">>", Token::Type::kShiftRight}));
+
using KeywordTest = testing::TestWithParam<TokenData>;
TEST_P(KeywordTest, Parses) {
auto params = GetParam();
Source::File file("", params.input);
Lexer l(&file);
- auto t = l.next();
+ auto list = l.Lex();
+ ASSERT_GE(list.size(), 2u);
+
+ auto& t = list[0];
EXPECT_TRUE(t.Is(params.type)) << params.input;
EXPECT_EQ(t.source().range.begin.line, 1u);
EXPECT_EQ(t.source().range.begin.column, 1u);
EXPECT_EQ(t.source().range.end.line, 1u);
EXPECT_EQ(t.source().range.end.column, 1u + strlen(params.input));
- t = l.next();
- EXPECT_EQ(t.source().range.begin.column, 1 + std::string(params.input).size());
+ EXPECT_EQ(list[1].source().range.begin.column, 1 + std::string(params.input).size());
}
INSTANTIATE_TEST_SUITE_P(
LexerTest,
diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc
index 6ca78d6..7f8edd2 100644
--- a/src/tint/reader/wgsl/parser_impl.cc
+++ b/src/tint/reader/wgsl/parser_impl.cc
@@ -123,7 +123,7 @@
const char kWorkgroupSizeAttribute[] = "workgroup_size";
// https://gpuweb.github.io/gpuweb/wgsl.html#reserved-keywords
-bool is_reserved(Token t) {
+bool is_reserved(const Token& t) {
return t == "asm" || t == "bf16" || t == "do" || t == "enum" || t == "f64" || t == "handle" ||
t == "i8" || t == "i16" || t == "i64" || t == "mat" || t == "premerge" ||
t == "regardless" || t == "typedef" || t == "u8" || t == "u16" || t == "u64" ||
@@ -181,7 +181,7 @@
/// Implicit conversion to Source that returns the combined source from start
/// to the current last token's source.
operator Source() const {
- Source end = parser_->last_token().source().End();
+ Source end = parser_->last_source().End();
if (end < start_) {
end = start_;
}
@@ -241,7 +241,7 @@
ParserImpl::VarDeclInfo::~VarDeclInfo() = default;
-ParserImpl::ParserImpl(Source::File const* file) : lexer_(std::make_unique<Lexer>(file)) {}
+ParserImpl::ParserImpl(Source::File const* file) : file_(file) {}
ParserImpl::~ParserImpl() = default;
@@ -274,33 +274,67 @@
"use of deprecated language feature: " + msg, source);
}
-Token ParserImpl::next() {
- if (!token_queue_.empty()) {
- auto t = token_queue_.front();
- token_queue_.pop_front();
- last_token_ = t;
- return last_token_;
+const Token& ParserImpl::next() {
+ if (!tokens_[next_token_idx_].IsEof() && !tokens_[next_token_idx_].IsError()) {
+ // Skip over any placeholder elements
+ while (true) {
+ if (!tokens_[next_token_idx_].IsPlaceholder()) {
+ break;
+ }
+ next_token_idx_++;
+ }
}
- last_token_ = lexer_->next();
- return last_token_;
+ last_source_idx_ = next_token_idx_;
+ return tokens_[next_token_idx_++];
}
-Token ParserImpl::peek(size_t idx) {
- while (token_queue_.size() < (idx + 1)) {
- token_queue_.push_back(lexer_->next());
+const Token& ParserImpl::peek(size_t idx) {
+ if (next_token_idx_ + idx >= tokens_.size()) {
+ return tokens_[tokens_.size() - 1];
}
- return token_queue_[idx];
+
+ // Skip over any placeholder elements
+ while (true) {
+ if (!tokens_[next_token_idx_ + idx].IsPlaceholder()) {
+ break;
+ }
+ idx++;
+ }
+
+ return tokens_[next_token_idx_ + idx];
}
bool ParserImpl::peek_is(Token::Type tok, size_t idx) {
return peek(idx).Is(tok);
}
-Token ParserImpl::last_token() const {
- return last_token_;
+void ParserImpl::split_token(Token::Type lhs, Token::Type rhs) {
+ if (next_token_idx_ == 0) {
+ TINT_ICE(Reader, builder_.Diagnostics())
+ << "attempt to update placeholder at beginning of tokens";
+ }
+ if (next_token_idx_ >= tokens_.size()) {
+ TINT_ICE(Reader, builder_.Diagnostics())
+ << "attempt to update placeholder past end of tokens";
+ }
+ if (!tokens_[next_token_idx_].IsPlaceholder()) {
+ TINT_ICE(Reader, builder_.Diagnostics()) << "attempt to update non-placeholder token";
+ }
+ tokens_[next_token_idx_ - 1].SetType(lhs);
+ tokens_[next_token_idx_].SetType(rhs);
+}
+
+Source ParserImpl::last_source() const {
+ return tokens_[last_source_idx_].source();
+}
+
+void ParserImpl::InitializeLex() {
+ Lexer l{file_};
+ tokens_ = l.Lex();
}
bool ParserImpl::Parse() {
+ InitializeLex();
translation_unit();
return !has_error();
}
@@ -310,7 +344,7 @@
void ParserImpl::translation_unit() {
bool after_global_decl = false;
while (continue_parsing()) {
- auto p = peek();
+ auto& p = peek();
if (p.IsEof()) {
break;
}
@@ -353,7 +387,7 @@
// Match the extension name.
Expect<std::string> name = {""};
- auto t = peek();
+ auto& t = peek();
if (t.IsIdentifier()) {
synchronized_ = true;
next();
@@ -508,7 +542,7 @@
}
// We have a statement outside of a function?
- auto t = peek();
+ auto& t = peek();
auto stat = without_error([&] { return statement(); });
if (stat.matched) {
// Attempt to jump to the next '}' - the function might have just been
@@ -882,7 +916,7 @@
// | 'rgba32sint'
// | 'rgba32float'
Expect<ast::TexelFormat> ParserImpl::expect_texel_format(std::string_view use) {
- auto t = next();
+ auto& t = next();
if (t == "rgba8unorm") {
return ast::TexelFormat::kRgba8Unorm;
}
@@ -951,7 +985,7 @@
return Failure::kErrored;
}
- auto t = peek();
+ auto& t = peek();
auto type = type_decl();
if (type.errored) {
return Failure::kErrored;
@@ -1021,7 +1055,7 @@
return Failure::kNoMatch;
}
- auto t = next();
+ auto& t = next();
const char* use = "type alias";
auto name = expect_ident(use);
@@ -1069,7 +1103,7 @@
// | MAT4x4 LESS_THAN type_decl GREATER_THAN
// | texture_samplers
Maybe<const ast::Type*> ParserImpl::type_decl() {
- auto t = peek();
+ auto& t = peek();
Source source;
if (match(Token::Type::kIdentifier, &source)) {
return builder_.create<ast::TypeName>(source, builder_.Symbols().Register(t.to_str()));
@@ -1139,7 +1173,7 @@
return type.value;
}
-Expect<const ast::Type*> ParserImpl::expect_type_decl_pointer(Token t) {
+Expect<const ast::Type*> ParserImpl::expect_type_decl_pointer(const Token& t) {
const char* use = "ptr declaration";
auto storage_class = ast::StorageClass::kNone;
@@ -1180,7 +1214,7 @@
access);
}
-Expect<const ast::Type*> ParserImpl::expect_type_decl_atomic(Token t) {
+Expect<const ast::Type*> ParserImpl::expect_type_decl_atomic(const Token& t) {
const char* use = "atomic declaration";
auto subtype = expect_lt_gt_block(use, [&] { return expect_type(use); });
@@ -1191,7 +1225,7 @@
return builder_.ty.atomic(make_source_range_from(t.source()), subtype.value);
}
-Expect<const ast::Type*> ParserImpl::expect_type_decl_vector(Token t) {
+Expect<const ast::Type*> ParserImpl::expect_type_decl_vector(const Token& t) {
uint32_t count = 2;
if (t.Is(Token::Type::kVec3)) {
count = 3;
@@ -1212,7 +1246,7 @@
return builder_.ty.vec(make_source_range_from(t.source()), subtype, count);
}
-Expect<const ast::Type*> ParserImpl::expect_type_decl_array(Token t) {
+Expect<const ast::Type*> ParserImpl::expect_type_decl_array(const Token& t) {
const char* use = "array declaration";
const ast::Expression* size = nullptr;
@@ -1244,7 +1278,7 @@
return builder_.ty.array(make_source_range_from(t.source()), subtype.value, size);
}
-Expect<const ast::Type*> ParserImpl::expect_type_decl_matrix(Token t) {
+Expect<const ast::Type*> ParserImpl::expect_type_decl_matrix(const Token& t) {
uint32_t rows = 2;
uint32_t columns = 2;
if (t.IsMat3xN()) {
@@ -1272,7 +1306,7 @@
}
Expect<ast::StorageClass> ParserImpl::expect_storage_class(std::string_view use) {
- auto source = peek().source();
+ auto& t = peek();
auto ident = expect_ident("storage class");
if (ident.errored) {
return Failure::kErrored;
@@ -1280,33 +1314,32 @@
auto name = ident.value;
if (name == "uniform") {
- return {ast::StorageClass::kUniform, source};
+ return {ast::StorageClass::kUniform, t.source()};
}
if (name == "workgroup") {
- return {ast::StorageClass::kWorkgroup, source};
+ return {ast::StorageClass::kWorkgroup, t.source()};
}
if (name == "storage" || name == "storage_buffer") {
- return {ast::StorageClass::kStorage, source};
+ return {ast::StorageClass::kStorage, t.source()};
}
if (name == "private") {
- return {ast::StorageClass::kPrivate, source};
+ return {ast::StorageClass::kPrivate, t.source()};
}
if (name == "function") {
- return {ast::StorageClass::kFunction, source};
+ return {ast::StorageClass::kFunction, t.source()};
}
- return add_error(source, "invalid storage class", use);
+ return add_error(t.source(), "invalid storage class", use);
}
// struct_decl
// : STRUCT IDENT struct_body_decl
Maybe<const ast::Struct*> ParserImpl::struct_decl() {
- auto t = peek();
- auto source = t.source();
+ auto& t = peek();
if (!match(Token::Type::kStruct)) {
return Failure::kNoMatch;
@@ -1323,7 +1356,7 @@
}
auto sym = builder_.Symbols().Register(name.value);
- return create<ast::Struct>(source, sym, std::move(body.value), ast::AttributeList{});
+ return create<ast::Struct>(t.source(), sym, std::move(body.value), ast::AttributeList{});
}
// struct_body_decl
@@ -1334,7 +1367,7 @@
bool errored = false;
while (continue_parsing()) {
// Check for the end of the list.
- auto t = peek();
+ auto& t = peek();
if (!t.IsIdentifier() && !t.Is(Token::Type::kAttr)) {
break;
}
@@ -1481,7 +1514,7 @@
ast::ParameterList ret;
while (continue_parsing()) {
// Check for the end of the list.
- auto t = peek();
+ auto& t = peek();
if (!t.IsIdentifier() && !t.Is(Token::Type::kAttr)) {
break;
}
@@ -1522,7 +1555,7 @@
// | FRAGMENT
// | COMPUTE
Expect<ast::PipelineStage> ParserImpl::expect_pipeline_stage() {
- auto t = peek();
+ auto& t = peek();
if (t == kVertexStage) {
next(); // Consume the peek
return {ast::PipelineStage::kVertex, t.source()};
@@ -2010,8 +2043,7 @@
return Failure::kNoMatch;
}
- auto t = next();
- auto source = t.source();
+ auto& t = next();
ast::CaseSelectorList selector_list;
if (t.Is(Token::Type::kCase)) {
@@ -2036,7 +2068,7 @@
return add_error(body.source, "expected case body");
}
- return create<ast::CaseStatement>(source, selector_list, body.value);
+ return create<ast::CaseStatement>(t.source(), selector_list, body.value);
}
// case_selectors
@@ -2263,26 +2295,24 @@
// func_call_stmt
// : IDENT argument_expression_list
Maybe<const ast::CallStatement*> ParserImpl::func_call_stmt() {
- auto t = peek();
- auto t2 = peek(1);
+ auto& t = peek();
+ auto& t2 = peek(1);
if (!t.IsIdentifier() || !t2.Is(Token::Type::kParenLeft)) {
return Failure::kNoMatch;
}
next(); // Consume the first peek
- auto source = t.source();
- auto name = t.to_str();
-
auto params = expect_argument_expression_list("function call");
if (params.errored) {
return Failure::kErrored;
}
return create<ast::CallStatement>(
- source,
+ t.source(),
create<ast::CallExpression>(
- source, create<ast::IdentifierExpression>(source, builder_.Symbols().Register(name)),
+ t.source(),
+ create<ast::IdentifierExpression>(t.source(), builder_.Symbols().Register(t.to_str())),
std::move(params.value)));
}
@@ -2325,8 +2355,7 @@
// | paren_expression
// | BITCAST LESS_THAN type_decl GREATER_THAN paren_expression
Maybe<const ast::Expression*> ParserImpl::primary_expression() {
- auto t = peek();
- auto source = t.source();
+ auto& t = peek();
auto lit = const_literal();
if (lit.errored) {
@@ -2358,7 +2387,7 @@
return Failure::kErrored;
}
- return create<ast::BitcastExpression>(source, type.value, params.value);
+ return create<ast::BitcastExpression>(t.source(), type.value, params.value);
}
if (t.IsIdentifier()) {
@@ -2373,7 +2402,7 @@
return Failure::kErrored;
}
- return create<ast::CallExpression>(source, ident, std::move(params.value));
+ return create<ast::CallExpression>(t.source(), ident, std::move(params.value));
}
return ident;
@@ -2389,7 +2418,7 @@
return Failure::kErrored;
}
- return builder_.Construct(source, type.value, std::move(params.value));
+ return builder_.Construct(t.source(), type.value, std::move(params.value));
}
return Failure::kNoMatch;
@@ -2491,7 +2520,7 @@
// | STAR unary_expression
// | AND unary_expression
Maybe<const ast::Expression*> ParserImpl::unary_expression() {
- auto t = peek();
+ auto& t = peek();
if (match(Token::Type::kPlusPlus) || match(Token::Type::kMinusMinus)) {
add_error(t.source(),
@@ -2556,20 +2585,18 @@
return lhs;
}
- auto t = next();
- auto source = t.source();
- auto name = t.to_name();
+ auto& t = next();
auto rhs = unary_expression();
if (rhs.errored) {
return Failure::kErrored;
}
if (!rhs.matched) {
- return add_error(peek(),
- "unable to parse right side of " + std::string(name) + " expression");
+ return add_error(peek(), "unable to parse right side of " + std::string(t.to_name()) +
+ " expression");
}
- lhs = create<ast::BinaryExpression>(source, op, lhs, rhs.value);
+ lhs = create<ast::BinaryExpression>(t.source(), op, lhs, rhs.value);
}
return Failure::kErrored;
}
@@ -2603,8 +2630,7 @@
return lhs;
}
- auto t = next();
- auto source = t.source();
+ auto& t = next();
auto rhs = multiplicative_expression();
if (rhs.errored) {
@@ -2614,7 +2640,7 @@
return add_error(peek(), "unable to parse right side of + expression");
}
- lhs = create<ast::BinaryExpression>(source, op, lhs, rhs.value);
+ lhs = create<ast::BinaryExpression>(t.source(), op, lhs, rhs.value);
}
return Failure::kErrored;
}
@@ -2651,8 +2677,7 @@
return lhs;
}
- auto t = next();
- auto source = t.source();
+ auto& t = next();
auto rhs = additive_expression();
if (rhs.errored) {
return Failure::kErrored;
@@ -2662,7 +2687,7 @@
std::string("unable to parse right side of ") + name + " expression");
}
- return lhs = create<ast::BinaryExpression>(source, op, lhs, rhs.value);
+ return lhs = create<ast::BinaryExpression>(t.source(), op, lhs, rhs.value);
}
return Failure::kErrored;
}
@@ -2702,20 +2727,18 @@
return lhs;
}
- auto t = next();
- auto source = t.source();
- auto name = t.to_name();
+ auto& t = next();
auto rhs = shift_expression();
if (rhs.errored) {
return Failure::kErrored;
}
if (!rhs.matched) {
- return add_error(peek(),
- "unable to parse right side of " + std::string(name) + " expression");
+ return add_error(peek(), "unable to parse right side of " + std::string(t.to_name()) +
+ " expression");
}
- lhs = create<ast::BinaryExpression>(source, op, lhs, rhs.value);
+ lhs = create<ast::BinaryExpression>(t.source(), op, lhs, rhs.value);
}
return Failure::kErrored;
}
@@ -2749,20 +2772,18 @@
return lhs;
}
- auto t = next();
- auto source = t.source();
- auto name = t.to_name();
+ auto& t = next();
auto rhs = relational_expression();
if (rhs.errored) {
return Failure::kErrored;
}
if (!rhs.matched) {
- return add_error(peek(),
- "unable to parse right side of " + std::string(name) + " expression");
+ return add_error(peek(), "unable to parse right side of " + std::string(t.to_name()) +
+ " expression");
}
- lhs = create<ast::BinaryExpression>(source, op, lhs, rhs.value);
+ lhs = create<ast::BinaryExpression>(t.source(), op, lhs, rhs.value);
}
return Failure::kErrored;
}
@@ -2790,8 +2811,7 @@
return lhs;
}
- auto t = next();
- auto source = t.source();
+ auto& t = next();
auto rhs = equality_expression();
if (rhs.errored) {
@@ -2801,7 +2821,7 @@
return add_error(peek(), "unable to parse right side of & expression");
}
- lhs = create<ast::BinaryExpression>(source, ast::BinaryOp::kAnd, lhs, rhs.value);
+ lhs = create<ast::BinaryExpression>(t.source(), ast::BinaryOp::kAnd, lhs, rhs.value);
}
return Failure::kErrored;
}
@@ -2903,8 +2923,7 @@
return lhs;
}
- auto t = next();
- auto source = t.source();
+ auto& t = next();
auto rhs = inclusive_or_expression();
if (rhs.errored) {
@@ -2914,7 +2933,7 @@
return add_error(peek(), "unable to parse right side of && expression");
}
- lhs = create<ast::BinaryExpression>(source, ast::BinaryOp::kLogicalAnd, lhs, rhs.value);
+ lhs = create<ast::BinaryExpression>(t.source(), ast::BinaryOp::kLogicalAnd, lhs, rhs.value);
}
return Failure::kErrored;
}
@@ -3008,13 +3027,14 @@
// assignment_stmt
// | lhs_expression ( equal | compound_assignment_operator ) expression
// | underscore equal expression
+//
// increment_stmt
// | lhs_expression PLUS_PLUS
+//
// decrement_stmt
// | lhs_expression MINUS_MINUS
Maybe<const ast::Statement*> ParserImpl::assignment_stmt() {
- auto t = peek();
- auto source = t.source();
+ auto& t = peek();
// tint:295 - Test for `ident COLON` - this is invalid grammar, and without
// special casing will error as "missing = for assignment", which is less
@@ -3028,6 +3048,7 @@
return Failure::kErrored;
}
if (!lhs.matched) {
+ Source source = t.source();
if (!match(Token::Type::kUnderscore, &source)) {
return Failure::kNoMatch;
}
@@ -3039,9 +3060,9 @@
// the assignment statement, and we cannot tell which we are parsing until we
// hit the ++/--/= token.
if (match(Token::Type::kPlusPlus)) {
- return create<ast::IncrementDecrementStatement>(source, lhs.value, true);
+ return create<ast::IncrementDecrementStatement>(t.source(), lhs.value, true);
} else if (match(Token::Type::kMinusMinus)) {
- return create<ast::IncrementDecrementStatement>(source, lhs.value, false);
+ return create<ast::IncrementDecrementStatement>(t.source(), lhs.value, false);
}
auto compound_op = compound_assignment_operator();
@@ -3063,10 +3084,10 @@
}
if (compound_op.value != ast::BinaryOp::kNone) {
- return create<ast::CompoundAssignmentStatement>(source, lhs.value, rhs.value,
+ return create<ast::CompoundAssignmentStatement>(t.source(), lhs.value, rhs.value,
compound_op.value);
} else {
- return create<ast::AssignmentStatement>(source, lhs.value, rhs.value);
+ return create<ast::AssignmentStatement>(t.source(), lhs.value, rhs.value);
}
}
@@ -3076,7 +3097,7 @@
// | TRUE
// | FALSE
Maybe<const ast::LiteralExpression*> ParserImpl::const_literal() {
- auto t = peek();
+ auto& t = peek();
if (match(Token::Type::kIntLiteral)) {
return create<ast::IntLiteralExpression>(t.source(), t.to_i64(),
ast::IntLiteralExpression::Suffix::kNone);
@@ -3141,7 +3162,7 @@
}
Expect<const ast::Attribute*> ParserImpl::expect_attribute() {
- auto t = peek();
+ auto& t = peek();
auto attr = attribute();
if (attr.errored) {
return Failure::kErrored;
@@ -3154,7 +3175,7 @@
Maybe<const ast::Attribute*> ParserImpl::attribute() {
using Result = Maybe<const ast::Attribute*>;
- auto t = next();
+ auto& t = next();
if (!t.IsIdentifier()) {
return Failure::kNoMatch;
@@ -3167,6 +3188,7 @@
if (val.errored) {
return Failure::kErrored;
}
+ match(Token::Type::kComma);
return create<ast::LocationAttribute>(t.source(), val.value);
});
@@ -3179,6 +3201,7 @@
if (val.errored) {
return Failure::kErrored;
}
+ match(Token::Type::kComma);
return create<ast::BindingAttribute>(t.source(), val.value);
});
@@ -3191,6 +3214,7 @@
if (val.errored) {
return Failure::kErrored;
}
+ match(Token::Type::kComma);
return create<ast::GroupAttribute>(t.source(), val.value);
});
@@ -3201,7 +3225,7 @@
ast::InterpolationType type;
ast::InterpolationSampling sampling = ast::InterpolationSampling::kNone;
- auto type_tok = next();
+ auto& type_tok = next();
if (type_tok == "perspective") {
type = ast::InterpolationType::kPerspective;
} else if (type_tok == "linear") {
@@ -3213,15 +3237,18 @@
}
if (match(Token::Type::kComma)) {
- auto sampling_tok = next();
- if (sampling_tok == "center") {
- sampling = ast::InterpolationSampling::kCenter;
- } else if (sampling_tok == "centroid") {
- sampling = ast::InterpolationSampling::kCentroid;
- } else if (sampling_tok == "sample") {
- sampling = ast::InterpolationSampling::kSample;
- } else {
- return add_error(sampling_tok, "invalid interpolation sampling");
+ if (!peek_is(Token::Type::kParenRight)) {
+ auto& sampling_tok = next();
+ if (sampling_tok == "center") {
+ sampling = ast::InterpolationSampling::kCenter;
+ } else if (sampling_tok == "centroid") {
+ sampling = ast::InterpolationSampling::kCentroid;
+ } else if (sampling_tok == "sample") {
+ sampling = ast::InterpolationSampling::kSample;
+ } else {
+ return add_error(sampling_tok, "invalid interpolation sampling");
+ }
+ match(Token::Type::kComma);
}
}
@@ -3240,6 +3267,7 @@
return Failure::kErrored;
}
+ match(Token::Type::kComma);
return create<ast::BuiltinAttribute>(t.source(), builtin.value);
});
}
@@ -3259,22 +3287,28 @@
x = std::move(expr.value);
if (match(Token::Type::kComma)) {
- expr = primary_expression();
- if (expr.errored) {
- return Failure::kErrored;
- } else if (!expr.matched) {
- return add_error(peek(), "expected workgroup_size y parameter");
- }
- y = std::move(expr.value);
-
- if (match(Token::Type::kComma)) {
+ if (!peek_is(Token::Type::kParenRight)) {
expr = primary_expression();
if (expr.errored) {
return Failure::kErrored;
} else if (!expr.matched) {
- return add_error(peek(), "expected workgroup_size z parameter");
+ return add_error(peek(), "expected workgroup_size y parameter");
}
- z = std::move(expr.value);
+ y = std::move(expr.value);
+
+ if (match(Token::Type::kComma)) {
+ if (!peek_is(Token::Type::kParenRight)) {
+ expr = primary_expression();
+ if (expr.errored) {
+ return Failure::kErrored;
+ } else if (!expr.matched) {
+ return add_error(peek(), "expected workgroup_size z parameter");
+ }
+ z = std::move(expr.value);
+
+ match(Token::Type::kComma);
+ }
+ }
}
}
@@ -3326,6 +3360,7 @@
if (val.errored) {
return Failure::kErrored;
}
+ match(Token::Type::kComma);
return create<ast::StructMemberSizeAttribute>(t.source(), val.value);
});
@@ -3338,6 +3373,7 @@
if (val.errored) {
return Failure::kErrored;
}
+ match(Token::Type::kComma);
return create<ast::StructMemberAlignAttribute>(t.source(), val.value);
});
@@ -3350,6 +3386,7 @@
if (val.errored) {
return Failure::kErrored;
}
+ match(Token::Type::kComma);
return create<ast::IdAttribute>(t.source(), val.value);
});
@@ -3367,7 +3404,7 @@
}
bool ParserImpl::match(Token::Type tok, Source* source /*= nullptr*/) {
- auto t = peek();
+ auto& t = peek();
if (source != nullptr) {
*source = t.source();
@@ -3381,7 +3418,7 @@
}
bool ParserImpl::expect(std::string_view use, Token::Type tok) {
- auto t = peek();
+ auto& t = peek();
if (t.Is(tok)) {
next();
synchronized_ = true;
@@ -3394,12 +3431,10 @@
next();
// Push the second character to the token queue.
- auto source = t.source();
- source.range.begin.column++;
if (t.Is(Token::Type::kShiftRight)) {
- token_queue_.push_front(Token(Token::Type::kGreaterThan, source));
+ split_token(Token::Type::kGreaterThan, Token::Type::kGreaterThan);
} else if (t.Is(Token::Type::kGreaterThanEqual)) {
- token_queue_.push_front(Token(Token::Type::kEqual, source));
+ split_token(Token::Type::kGreaterThan, Token::Type::kEqual);
}
synchronized_ = true;
@@ -3422,7 +3457,7 @@
}
Expect<int32_t> ParserImpl::expect_sint(std::string_view use) {
- auto t = peek();
+ auto& t = peek();
if (!t.Is(Token::Type::kIntLiteral) && !t.Is(Token::Type::kIntLiteral_I)) {
return add_error(t.source(), "expected signed integer literal", use);
}
@@ -3465,7 +3500,7 @@
}
Expect<std::string> ParserImpl::expect_ident(std::string_view use) {
- auto t = peek();
+ auto& t = peek();
if (t.IsIdentifier()) {
synchronized_ = true;
next();
@@ -3561,7 +3596,7 @@
BlockCounters counters;
for (size_t i = 0; i < kMaxResynchronizeLookahead; i++) {
- auto t = peek(i);
+ auto& t = peek(i);
if (counters.consume(t) > 0) {
continue; // Nested block
}
diff --git a/src/tint/reader/wgsl/parser_impl.h b/src/tint/reader/wgsl/parser_impl.h
index a9c0423..f86d865 100644
--- a/src/tint/reader/wgsl/parser_impl.h
+++ b/src/tint/reader/wgsl/parser_impl.h
@@ -15,7 +15,6 @@
#ifndef SRC_TINT_READER_WGSL_PARSER_IMPL_H_
#define SRC_TINT_READER_WGSL_PARSER_IMPL_H_
-#include <deque>
#include <memory>
#include <string>
#include <string_view>
@@ -300,6 +299,10 @@
explicit ParserImpl(Source::File const* file);
~ParserImpl();
+ /// Reads tokens from the source file. This will be called automatically
+ /// by |parse|.
+ void InitializeLex();
+
/// Run the parser
/// @returns true if the parse was successful, false otherwise.
bool Parse();
@@ -330,19 +333,19 @@
ProgramBuilder& builder() { return builder_; }
/// @returns the next token
- Token next();
+ const Token& next();
/// Peeks ahead and returns the token at `idx` ahead of the current position
/// @param idx the index of the token to return
/// @returns the token `idx` positions ahead without advancing
- Token peek(size_t idx = 0);
+ const Token& peek(size_t idx = 0);
/// Peeks ahead and returns true if the token at `idx` ahead of the current
/// position is |tok|
/// @param idx the index of the token to return
/// @param tok the token to look for
/// @returns true if the token `idx` positions ahead is |tok|
bool peek_is(Token::Type tok, size_t idx = 0);
- /// @returns the last token that was returned by `next()`
- Token last_token() const;
+ /// @returns the last source location that was returned by `next()`
+ Source last_source() const;
/// Appends an error at `t` with the message `msg`
/// @param t the token to associate the error with
/// @param msg the error message
@@ -812,11 +815,11 @@
/// Used to ensure that all attributes are consumed.
bool expect_attributes_consumed(ast::AttributeList& list);
- Expect<const ast::Type*> expect_type_decl_pointer(Token t);
- Expect<const ast::Type*> expect_type_decl_atomic(Token t);
- Expect<const ast::Type*> expect_type_decl_vector(Token t);
- Expect<const ast::Type*> expect_type_decl_array(Token t);
- Expect<const ast::Type*> expect_type_decl_matrix(Token t);
+ Expect<const ast::Type*> expect_type_decl_pointer(const Token& t);
+ Expect<const ast::Type*> expect_type_decl_atomic(const Token& t);
+ Expect<const ast::Type*> expect_type_decl_vector(const Token& t);
+ Expect<const ast::Type*> expect_type_decl_array(const Token& t);
+ Expect<const ast::Type*> expect_type_decl_matrix(const Token& t);
Expect<const ast::Type*> expect_type(std::string_view use);
@@ -824,6 +827,8 @@
Maybe<const ast::Statement*> for_header_initializer();
Maybe<const ast::Statement*> for_header_continuing();
+ void split_token(Token::Type lhs, Token::Type rhs);
+
class MultiTokenSource;
MultiTokenSource make_source_range();
MultiTokenSource make_source_range_from(const Source& start);
@@ -837,9 +842,10 @@
return builder_.create<T>(std::forward<ARGS>(args)...);
}
- std::unique_ptr<Lexer> lexer_;
- std::deque<Token> token_queue_;
- Token last_token_;
+ Source::File const* const file_;
+ std::vector<Token> tokens_;
+ size_t next_token_idx_ = 0;
+ size_t last_source_idx_ = 0;
bool synchronized_ = true;
uint32_t parse_depth_ = 0;
std::vector<Token::Type> sync_tokens_;
diff --git a/src/tint/reader/wgsl/parser_impl_error_msg_test.cc b/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
index 453e549..6a825de 100644
--- a/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
@@ -357,18 +357,18 @@
}
TEST_F(ParserImplErrorTest, FunctionDeclWorkgroupSizeYInvalid) {
- EXPECT("@workgroup_size(1, ) fn f() {}",
+ EXPECT("@workgroup_size(1, fn) fn f() {}",
R"(test.wgsl:1:20 error: expected workgroup_size y parameter
-@workgroup_size(1, ) fn f() {}
- ^
+@workgroup_size(1, fn) fn f() {}
+ ^^
)");
}
TEST_F(ParserImplErrorTest, FunctionDeclWorkgroupSizeZInvalid) {
- EXPECT("@workgroup_size(1, 2, ) fn f() {}",
+ EXPECT("@workgroup_size(1, 2, fn) fn f() {}",
R"(test.wgsl:1:23 error: expected workgroup_size z parameter
-@workgroup_size(1, 2, ) fn f() {}
- ^
+@workgroup_size(1, 2, fn) fn f() {}
+ ^^
)");
}
diff --git a/src/tint/reader/wgsl/parser_impl_function_attribute_test.cc b/src/tint/reader/wgsl/parser_impl_function_attribute_test.cc
index a32e140..8e56ee7 100644
--- a/src/tint/reader/wgsl/parser_impl_function_attribute_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_function_attribute_test.cc
@@ -41,6 +41,38 @@
EXPECT_EQ(values[2], nullptr);
}
+TEST_F(ParserImplTest, Attribute_Workgroup_1Param_TrailingComma) {
+ auto p = parser("workgroup_size(4,)");
+ auto attr = p->attribute();
+ EXPECT_TRUE(attr.matched);
+ EXPECT_FALSE(attr.errored);
+ ASSERT_NE(attr.value, nullptr) << p->error();
+ ASSERT_FALSE(p->has_error());
+ auto* func_attr = attr.value->As<ast::Attribute>();
+ ASSERT_NE(func_attr, nullptr);
+ ASSERT_TRUE(func_attr->Is<ast::WorkgroupAttribute>());
+
+ auto values = func_attr->As<ast::WorkgroupAttribute>()->Values();
+
+ ASSERT_TRUE(values[0]->Is<ast::IntLiteralExpression>());
+ EXPECT_EQ(values[0]->As<ast::IntLiteralExpression>()->value, 4);
+ EXPECT_EQ(values[0]->As<ast::IntLiteralExpression>()->suffix,
+ ast::IntLiteralExpression::Suffix::kNone);
+
+ EXPECT_EQ(values[1], nullptr);
+ EXPECT_EQ(values[2], nullptr);
+}
+
+TEST_F(ParserImplTest, Attribute_Workgroup_1Param_TrailingComma_Double) {
+ auto p = parser("workgroup_size(4,,)");
+ auto attr = p->attribute();
+ EXPECT_FALSE(attr.matched);
+ EXPECT_TRUE(attr.errored);
+ EXPECT_EQ(attr.value, nullptr);
+ EXPECT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), "1:18: expected workgroup_size y parameter");
+}
+
TEST_F(ParserImplTest, Attribute_Workgroup_2Param) {
auto p = parser("workgroup_size(4, 5)");
auto attr = p->attribute();
@@ -67,6 +99,42 @@
EXPECT_EQ(values[2], nullptr);
}
+TEST_F(ParserImplTest, Attribute_Workgroup_2Param_TrailingComma) {
+ auto p = parser("workgroup_size(4, 5,)");
+ auto attr = p->attribute();
+ EXPECT_TRUE(attr.matched);
+ EXPECT_FALSE(attr.errored);
+ ASSERT_NE(attr.value, nullptr) << p->error();
+ ASSERT_FALSE(p->has_error());
+ auto* func_attr = attr.value->As<ast::Attribute>();
+ ASSERT_NE(func_attr, nullptr) << p->error();
+ ASSERT_TRUE(func_attr->Is<ast::WorkgroupAttribute>());
+
+ auto values = func_attr->As<ast::WorkgroupAttribute>()->Values();
+
+ ASSERT_TRUE(values[0]->Is<ast::IntLiteralExpression>());
+ EXPECT_EQ(values[0]->As<ast::IntLiteralExpression>()->value, 4);
+ EXPECT_EQ(values[0]->As<ast::IntLiteralExpression>()->suffix,
+ ast::IntLiteralExpression::Suffix::kNone);
+
+ ASSERT_TRUE(values[1]->Is<ast::IntLiteralExpression>());
+ EXPECT_EQ(values[1]->As<ast::IntLiteralExpression>()->value, 5);
+ EXPECT_EQ(values[1]->As<ast::IntLiteralExpression>()->suffix,
+ ast::IntLiteralExpression::Suffix::kNone);
+
+ EXPECT_EQ(values[2], nullptr);
+}
+
+TEST_F(ParserImplTest, Attribute_Workgroup21Param_TrailingComma_Double) {
+ auto p = parser("workgroup_size(4,5,,)");
+ auto attr = p->attribute();
+ EXPECT_FALSE(attr.matched);
+ EXPECT_TRUE(attr.errored);
+ EXPECT_EQ(attr.value, nullptr);
+ EXPECT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), "1:20: expected workgroup_size z parameter");
+}
+
TEST_F(ParserImplTest, Attribute_Workgroup_3Param) {
auto p = parser("workgroup_size(4, 5, 6)");
auto attr = p->attribute();
@@ -96,6 +164,35 @@
ast::IntLiteralExpression::Suffix::kNone);
}
+TEST_F(ParserImplTest, Attribute_Workgroup_3Param_TrailingComma) {
+ auto p = parser("workgroup_size(4, 5, 6,)");
+ auto attr = p->attribute();
+ EXPECT_TRUE(attr.matched);
+ EXPECT_FALSE(attr.errored);
+ ASSERT_NE(attr.value, nullptr) << p->error();
+ ASSERT_FALSE(p->has_error());
+ auto* func_attr = attr.value->As<ast::Attribute>();
+ ASSERT_NE(func_attr, nullptr);
+ ASSERT_TRUE(func_attr->Is<ast::WorkgroupAttribute>());
+
+ auto values = func_attr->As<ast::WorkgroupAttribute>()->Values();
+
+ ASSERT_TRUE(values[0]->Is<ast::IntLiteralExpression>());
+ EXPECT_EQ(values[0]->As<ast::IntLiteralExpression>()->value, 4);
+ EXPECT_EQ(values[0]->As<ast::IntLiteralExpression>()->suffix,
+ ast::IntLiteralExpression::Suffix::kNone);
+
+ ASSERT_TRUE(values[1]->Is<ast::IntLiteralExpression>());
+ EXPECT_EQ(values[1]->As<ast::IntLiteralExpression>()->value, 5);
+ EXPECT_EQ(values[1]->As<ast::IntLiteralExpression>()->suffix,
+ ast::IntLiteralExpression::Suffix::kNone);
+
+ ASSERT_TRUE(values[2]->Is<ast::IntLiteralExpression>());
+ EXPECT_EQ(values[2]->As<ast::IntLiteralExpression>()->value, 6);
+ EXPECT_EQ(values[2]->As<ast::IntLiteralExpression>()->suffix,
+ ast::IntLiteralExpression::Suffix::kNone);
+}
+
TEST_F(ParserImplTest, Attribute_Workgroup_WithIdent) {
auto p = parser("workgroup_size(4, height)");
auto attr = p->attribute();
@@ -129,7 +226,7 @@
EXPECT_TRUE(attr.errored);
EXPECT_EQ(attr.value, nullptr);
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:23: expected ')' for workgroup_size attribute");
+ EXPECT_EQ(p->error(), "1:25: expected ')' for workgroup_size attribute");
}
TEST_F(ParserImplTest, Attribute_Workgroup_MissingLeftParam) {
@@ -202,16 +299,6 @@
EXPECT_EQ(p->error(), "1:21: expected ')' for workgroup_size attribute");
}
-TEST_F(ParserImplTest, Attribute_Workgroup_Missing_Z_Value) {
- auto p = parser("workgroup_size(1, 2, )");
- auto attr = p->attribute();
- EXPECT_FALSE(attr.matched);
- EXPECT_TRUE(attr.errored);
- EXPECT_EQ(attr.value, nullptr);
- EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:22: expected workgroup_size z parameter");
-}
-
// TODO(crbug.com/tint/1503): Remove when @stage is removed
TEST_F(ParserImplTest, Attribute_Stage) {
auto p = parser("stage(compute)");
diff --git a/src/tint/reader/wgsl/parser_impl_global_constant_decl_test.cc b/src/tint/reader/wgsl/parser_impl_global_constant_decl_test.cc
index bfa678e..46bfa34 100644
--- a/src/tint/reader/wgsl/parser_impl_global_constant_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_global_constant_decl_test.cc
@@ -204,6 +204,36 @@
EXPECT_EQ(override_attr->value, 7u);
}
+TEST_F(ParserImplTest, GlobalOverrideDecl_WithId_TrailingComma) {
+ auto p = parser("@id(7,) override a : f32 = 1.");
+ auto attrs = p->attribute_list();
+ EXPECT_FALSE(attrs.errored);
+ EXPECT_TRUE(attrs.matched);
+
+ auto e = p->global_constant_decl(attrs.value);
+ EXPECT_FALSE(p->has_error()) << p->error();
+ EXPECT_TRUE(e.matched);
+ EXPECT_FALSE(e.errored);
+ auto* override = e.value->As<ast::Override>();
+ ASSERT_NE(override, nullptr);
+
+ EXPECT_EQ(override->symbol, p->builder().Symbols().Get("a"));
+ ASSERT_NE(override->type, nullptr);
+ EXPECT_TRUE(override->type->Is<ast::F32>());
+
+ EXPECT_EQ(override->source.range.begin.line, 1u);
+ EXPECT_EQ(override->source.range.begin.column, 18u);
+ EXPECT_EQ(override->source.range.end.line, 1u);
+ EXPECT_EQ(override->source.range.end.column, 19u);
+
+ ASSERT_NE(override->constructor, nullptr);
+ EXPECT_TRUE(override->constructor->Is<ast::LiteralExpression>());
+
+ auto* override_attr = ast::GetAttribute<ast::IdAttribute>(override->attributes);
+ ASSERT_NE(override_attr, nullptr);
+ EXPECT_EQ(override_attr->value, 7u);
+}
+
TEST_F(ParserImplTest, GlobalOverrideDecl_WithoutId) {
auto p = parser("override a : f32 = 1.");
auto attrs = p->attribute_list();
diff --git a/src/tint/reader/wgsl/parser_impl_pipeline_stage_test.cc b/src/tint/reader/wgsl/parser_impl_pipeline_stage_test.cc
index 75a1ec2..3c730e2 100644
--- a/src/tint/reader/wgsl/parser_impl_pipeline_stage_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_pipeline_stage_test.cc
@@ -40,7 +40,7 @@
EXPECT_EQ(stage.source.range.end.line, 1u);
EXPECT_EQ(stage.source.range.end.column, 1u + params.input.size());
- auto t = p->next();
+ auto& t = p->next();
EXPECT_TRUE(t.IsEof());
}
INSTANTIATE_TEST_SUITE_P(
diff --git a/src/tint/reader/wgsl/parser_impl_storage_class_test.cc b/src/tint/reader/wgsl/parser_impl_storage_class_test.cc
index deb87fc..7144f2c 100644
--- a/src/tint/reader/wgsl/parser_impl_storage_class_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_storage_class_test.cc
@@ -37,7 +37,7 @@
EXPECT_FALSE(p->has_error());
EXPECT_EQ(sc.value, params.result);
- auto t = p->next();
+ auto& t = p->next();
EXPECT_TRUE(t.IsEof());
}
INSTANTIATE_TEST_SUITE_P(
diff --git a/src/tint/reader/wgsl/parser_impl_struct_member_attribute_test.cc b/src/tint/reader/wgsl/parser_impl_struct_member_attribute_test.cc
index d185f37..df75f96 100644
--- a/src/tint/reader/wgsl/parser_impl_struct_member_attribute_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_struct_member_attribute_test.cc
@@ -33,6 +33,22 @@
EXPECT_EQ(o->size, 4u);
}
+TEST_F(ParserImplTest, Attribute_Size_TrailingComma) {
+ auto p = parser("size(4,)");
+ auto attr = p->attribute();
+ EXPECT_TRUE(attr.matched);
+ EXPECT_FALSE(attr.errored);
+ ASSERT_NE(attr.value, nullptr);
+ ASSERT_FALSE(p->has_error());
+
+ auto* member_attr = attr.value->As<ast::Attribute>();
+ ASSERT_NE(member_attr, nullptr);
+ ASSERT_TRUE(member_attr->Is<ast::StructMemberSizeAttribute>());
+
+ auto* o = member_attr->As<ast::StructMemberSizeAttribute>();
+ EXPECT_EQ(o->size, 4u);
+}
+
TEST_F(ParserImplTest, Attribute_Size_MissingLeftParen) {
auto p = parser("size 4)");
auto attr = p->attribute();
@@ -89,6 +105,22 @@
EXPECT_EQ(o->align, 4u);
}
+TEST_F(ParserImplTest, Attribute_Align_TrailingComma) {
+ auto p = parser("align(4,)");
+ auto attr = p->attribute();
+ EXPECT_TRUE(attr.matched);
+ EXPECT_FALSE(attr.errored);
+ ASSERT_NE(attr.value, nullptr);
+ ASSERT_FALSE(p->has_error());
+
+ auto* member_attr = attr.value->As<ast::Attribute>();
+ ASSERT_NE(member_attr, nullptr);
+ ASSERT_TRUE(member_attr->Is<ast::StructMemberAlignAttribute>());
+
+ auto* o = member_attr->As<ast::StructMemberAlignAttribute>();
+ EXPECT_EQ(o->align, 4u);
+}
+
TEST_F(ParserImplTest, Attribute_Align_MissingLeftParen) {
auto p = parser("align 4)");
auto attr = p->attribute();
diff --git a/src/tint/reader/wgsl/parser_impl_test_helper.h b/src/tint/reader/wgsl/parser_impl_test_helper.h
index f6ee8ea..1b14039 100644
--- a/src/tint/reader/wgsl/parser_impl_test_helper.h
+++ b/src/tint/reader/wgsl/parser_impl_test_helper.h
@@ -38,6 +38,7 @@
std::unique_ptr<ParserImpl> parser(const std::string& str) {
auto file = std::make_unique<Source::File>("test.wgsl", str);
auto impl = std::make_unique<ParserImpl>(file.get());
+ impl->InitializeLex();
files_.emplace_back(std::move(file));
return impl;
}
@@ -60,6 +61,7 @@
std::unique_ptr<ParserImpl> parser(const std::string& str) {
auto file = std::make_unique<Source::File>("test.wgsl", str);
auto impl = std::make_unique<ParserImpl>(file.get());
+ impl->InitializeLex();
files_.emplace_back(std::move(file));
return impl;
}
diff --git a/src/tint/reader/wgsl/parser_impl_variable_attribute_test.cc b/src/tint/reader/wgsl/parser_impl_variable_attribute_test.cc
index 8833273..01e4260 100644
--- a/src/tint/reader/wgsl/parser_impl_variable_attribute_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_variable_attribute_test.cc
@@ -32,6 +32,21 @@
EXPECT_EQ(loc->value, 4u);
}
+TEST_F(ParserImplTest, Attribute_Location_TrailingComma) {
+ auto p = parser("location(4,)");
+ auto attr = p->attribute();
+ EXPECT_TRUE(attr.matched);
+ EXPECT_FALSE(attr.errored);
+ ASSERT_NE(attr.value, nullptr);
+ auto* var_attr = attr.value->As<ast::Attribute>();
+ ASSERT_NE(var_attr, nullptr);
+ ASSERT_FALSE(p->has_error());
+ ASSERT_TRUE(var_attr->Is<ast::LocationAttribute>());
+
+ auto* loc = var_attr->As<ast::LocationAttribute>();
+ EXPECT_EQ(loc->value, 4u);
+}
+
TEST_F(ParserImplTest, Attribute_Location_MissingLeftParen) {
auto p = parser("location 4)");
auto attr = p->attribute();
@@ -99,6 +114,22 @@
auto* builtin = var_attr->As<ast::BuiltinAttribute>();
EXPECT_EQ(builtin->builtin, params.result);
}
+TEST_P(BuiltinTest, Attribute_Builtin_TrailingComma) {
+ auto params = GetParam();
+ auto p = parser(std::string("builtin(") + params.input + ",)");
+
+ auto attr = p->attribute();
+ EXPECT_TRUE(attr.matched);
+ EXPECT_FALSE(attr.errored);
+ ASSERT_NE(attr.value, nullptr);
+ auto* var_attr = attr.value->As<ast::Attribute>();
+ ASSERT_FALSE(p->has_error()) << p->error();
+ ASSERT_NE(var_attr, nullptr);
+ ASSERT_TRUE(var_attr->Is<ast::BuiltinAttribute>());
+
+ auto* builtin = var_attr->As<ast::BuiltinAttribute>();
+ EXPECT_EQ(builtin->builtin, params.result);
+}
INSTANTIATE_TEST_SUITE_P(
ParserImplTest,
BuiltinTest,
@@ -182,6 +213,32 @@
EXPECT_EQ(interp->sampling, ast::InterpolationSampling::kNone);
}
+TEST_F(ParserImplTest, Attribute_Interpolate_Single_TrailingComma) {
+ auto p = parser("interpolate(flat,)");
+ auto attr = p->attribute();
+ EXPECT_TRUE(attr.matched);
+ EXPECT_FALSE(attr.errored);
+ ASSERT_NE(attr.value, nullptr);
+ auto* var_attr = attr.value->As<ast::Attribute>();
+ ASSERT_NE(var_attr, nullptr);
+ ASSERT_FALSE(p->has_error());
+ ASSERT_TRUE(var_attr->Is<ast::InterpolateAttribute>());
+
+ auto* interp = var_attr->As<ast::InterpolateAttribute>();
+ EXPECT_EQ(interp->type, ast::InterpolationType::kFlat);
+ EXPECT_EQ(interp->sampling, ast::InterpolationSampling::kNone);
+}
+
+TEST_F(ParserImplTest, Attribute_Interpolate_Single_DoubleTrailingComma) {
+ auto p = parser("interpolate(flat,,)");
+ auto attr = p->attribute();
+ EXPECT_FALSE(attr.matched);
+ EXPECT_TRUE(attr.errored);
+ EXPECT_EQ(attr.value, nullptr);
+ EXPECT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), "1:18: invalid interpolation sampling");
+}
+
TEST_F(ParserImplTest, Attribute_Interpolate_Perspective_Center) {
auto p = parser("interpolate(perspective, center)");
auto attr = p->attribute();
@@ -198,6 +255,22 @@
EXPECT_EQ(interp->sampling, ast::InterpolationSampling::kCenter);
}
+TEST_F(ParserImplTest, Attribute_Interpolate_Double_TrailingComma) {
+ auto p = parser("interpolate(perspective, center,)");
+ auto attr = p->attribute();
+ EXPECT_TRUE(attr.matched);
+ EXPECT_FALSE(attr.errored);
+ ASSERT_NE(attr.value, nullptr);
+ auto* var_attr = attr.value->As<ast::Attribute>();
+ ASSERT_NE(var_attr, nullptr);
+ ASSERT_FALSE(p->has_error());
+ ASSERT_TRUE(var_attr->Is<ast::InterpolateAttribute>());
+
+ auto* interp = var_attr->As<ast::InterpolateAttribute>();
+ EXPECT_EQ(interp->type, ast::InterpolationType::kPerspective);
+ EXPECT_EQ(interp->sampling, ast::InterpolationSampling::kCenter);
+}
+
TEST_F(ParserImplTest, Attribute_Interpolate_Perspective_Centroid) {
auto p = parser("interpolate(perspective, centroid)");
auto attr = p->attribute();
@@ -270,16 +343,6 @@
EXPECT_EQ(p->error(), "1:13: invalid interpolation type");
}
-TEST_F(ParserImplTest, Attribute_Interpolate_MissingSecondValue) {
- auto p = parser("interpolate(perspective,)");
- auto attr = p->attribute();
- EXPECT_FALSE(attr.matched);
- EXPECT_TRUE(attr.errored);
- EXPECT_EQ(attr.value, nullptr);
- EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:25: invalid interpolation sampling");
-}
-
TEST_F(ParserImplTest, Attribute_Interpolate_InvalidSecondValue) {
auto p = parser("interpolate(perspective, nope)");
auto attr = p->attribute();
@@ -305,6 +368,21 @@
EXPECT_EQ(binding->value, 4u);
}
+TEST_F(ParserImplTest, Attribute_Binding_TrailingComma) {
+ auto p = parser("binding(4,)");
+ auto attr = p->attribute();
+ EXPECT_TRUE(attr.matched);
+ EXPECT_FALSE(attr.errored);
+ ASSERT_NE(attr.value, nullptr);
+ auto* var_attr = attr.value->As<ast::Attribute>();
+ ASSERT_NE(var_attr, nullptr);
+ ASSERT_FALSE(p->has_error());
+ ASSERT_TRUE(var_attr->Is<ast::BindingAttribute>());
+
+ auto* binding = var_attr->As<ast::BindingAttribute>();
+ EXPECT_EQ(binding->value, 4u);
+}
+
TEST_F(ParserImplTest, Attribute_Binding_MissingLeftParen) {
auto p = parser("binding 4)");
auto attr = p->attribute();
@@ -360,6 +438,21 @@
EXPECT_EQ(group->value, 4u);
}
+TEST_F(ParserImplTest, Attribute_group_TrailingComma) {
+ auto p = parser("group(4,)");
+ auto attr = p->attribute();
+ EXPECT_TRUE(attr.matched);
+ EXPECT_FALSE(attr.errored);
+ ASSERT_NE(attr.value, nullptr);
+ auto* var_attr = attr.value->As<ast::Attribute>();
+ ASSERT_FALSE(p->has_error());
+ ASSERT_NE(var_attr, nullptr);
+ ASSERT_TRUE(var_attr->Is<ast::GroupAttribute>());
+
+ auto* group = var_attr->As<ast::GroupAttribute>();
+ EXPECT_EQ(group->value, 4u);
+}
+
TEST_F(ParserImplTest, Attribute_Group_MissingLeftParen) {
auto p = parser("group 2)");
auto attr = p->attribute();
diff --git a/src/tint/reader/wgsl/parser_impl_variable_decl_test.cc b/src/tint/reader/wgsl/parser_impl_variable_decl_test.cc
index 39f8b24..3ed0948 100644
--- a/src/tint/reader/wgsl/parser_impl_variable_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_variable_decl_test.cc
@@ -68,7 +68,7 @@
EXPECT_FALSE(v.errored);
EXPECT_FALSE(p->has_error());
- auto t = p->next();
+ auto& t = p->next();
ASSERT_TRUE(t.IsIdentifier());
}
diff --git a/src/tint/reader/wgsl/parser_impl_variable_qualifier_test.cc b/src/tint/reader/wgsl/parser_impl_variable_qualifier_test.cc
index aefd2b5..0dc0a1a 100644
--- a/src/tint/reader/wgsl/parser_impl_variable_qualifier_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_variable_qualifier_test.cc
@@ -40,7 +40,7 @@
EXPECT_EQ(sc->storage_class, params.storage_class);
EXPECT_EQ(sc->access, params.access);
- auto t = p->next();
+ auto& t = p->next();
EXPECT_TRUE(t.IsEof());
}
INSTANTIATE_TEST_SUITE_P(
@@ -83,7 +83,7 @@
EXPECT_FALSE(sc.errored);
EXPECT_FALSE(sc.matched);
- auto t = p->next();
+ auto& t = p->next();
ASSERT_TRUE(t.Is(Token::Type::kIdentifier));
}
@@ -94,7 +94,7 @@
EXPECT_FALSE(sc.errored);
EXPECT_FALSE(sc.matched);
- auto t = p->next();
+ auto& t = p->next();
ASSERT_TRUE(t.Is(Token::Type::kIdentifier));
}
diff --git a/src/tint/reader/wgsl/token.cc b/src/tint/reader/wgsl/token.cc
index 44255d3..a7b437f 100644
--- a/src/tint/reader/wgsl/token.cc
+++ b/src/tint/reader/wgsl/token.cc
@@ -37,6 +37,8 @@
return "'i'-suffixed integer literal";
case Token::Type::kIntLiteral_U:
return "'u'-suffixed integer literal";
+ case Token::Type::kPlaceholder:
+ return "placeholder";
case Token::Type::kUninitialized:
return "uninitialized";
@@ -285,13 +287,9 @@
Token::Token(Token&&) = default;
-Token::Token(const Token&) = default;
-
Token::~Token() = default;
-Token& Token::operator=(const Token& rhs) = default;
-
-bool Token::operator==(std::string_view ident) {
+bool Token::operator==(std::string_view ident) const {
if (type_ != Type::kIdentifier) {
return false;
}
diff --git a/src/tint/reader/wgsl/token.h b/src/tint/reader/wgsl/token.h
index 473588d..352c18f 100644
--- a/src/tint/reader/wgsl/token.h
+++ b/src/tint/reader/wgsl/token.h
@@ -32,6 +32,8 @@
kError = -2,
/// Uninitialized token
kUninitialized = 0,
+ /// Placeholder token which maybe fillled in later
+ kPlaceholder = 1,
/// End of input string reached
kEOF,
@@ -310,19 +312,16 @@
Token(Type type, const Source& source, double val);
/// Move constructor
Token(Token&&);
- /// Copy constructor
- Token(const Token&);
~Token();
- /// Assignment operator
- /// @param b the token to copy
- /// @return Token
- Token& operator=(const Token& b);
-
/// Equality operator with an identifier
/// @param ident the identifier string
/// @return true if this token is an identifier and is equal to ident.
- bool operator==(std::string_view ident);
+ bool operator==(std::string_view ident) const;
+
+ /// Sets the token to the given type
+ /// @param type the type to set
+ void SetType(Token::Type type) { type_ = type; }
/// Returns true if the token is of the given type
/// @param t the type to check against.
@@ -331,6 +330,8 @@
/// @returns true if the token is uninitialized
bool IsUninitialized() const { return type_ == Type::kUninitialized; }
+ /// @returns true if the token is a placeholder
+ bool IsPlaceholder() const { return type_ == Type::kPlaceholder; }
/// @returns true if the token is EOF
bool IsEof() const { return type_ == Type::kEOF; }
/// @returns true if the token is Error
@@ -372,6 +373,11 @@
return type_ == Type::kVec2 || type_ == Type::kVec3 || type_ == Type::kVec4;
}
+ /// @returns true if the token can be split during parse into component tokens
+ bool IsSplittable() const {
+ return Is(Token::Type::kShiftRight) || Is(Token::Type::kGreaterThanEqual);
+ }
+
/// @returns the source information for this token
Source source() const { return source_; }
diff --git a/src/tint/resolver/builtin_test.cc b/src/tint/resolver/builtin_test.cc
index fa773b3..9d8d11e 100644
--- a/src/tint/resolver/builtin_test.cc
+++ b/src/tint/resolver/builtin_test.cc
@@ -1593,7 +1593,8 @@
ResolverBuiltinTest_Texture,
testing::ValuesIn(ast::builtin::test::TextureOverloadCase::ValidCases()));
-std::string to_str(const std::string& function, const sem::ParameterList& params) {
+std::string to_str(const std::string& function,
+ utils::ConstVectorRef<const sem::Parameter*> params) {
std::stringstream out;
out << function << "(";
bool first = true;
diff --git a/src/tint/resolver/const_eval.cc b/src/tint/resolver/const_eval.cc
index c5ee387..e1c3416 100644
--- a/src/tint/resolver/const_eval.cc
+++ b/src/tint/resolver/const_eval.cc
@@ -119,7 +119,7 @@
// Forward declaration
const Constant* CreateComposite(ProgramBuilder& builder,
const sem::Type* type,
- std::vector<const sem::Constant*> elements);
+ utils::VectorRef<const sem::Constant*> elements);
/// Element holds a single scalar or abstract-numeric value.
/// Element implements the Constant interface.
@@ -251,13 +251,16 @@
/// implementation. Use CreateComposite() to create the appropriate Constant type.
/// Composite implements the Constant interface.
struct Composite : Constant {
- Composite(const sem::Type* t, std::vector<const sem::Constant*> els, bool all_0, bool any_0)
+ Composite(const sem::Type* t,
+ utils::VectorRef<const sem::Constant*> els,
+ bool all_0,
+ bool any_0)
: type(t), elements(std::move(els)), all_zero(all_0), any_zero(any_0), hash(CalcHash()) {}
~Composite() override = default;
const sem::Type* Type() const override { return type; }
std::variant<std::monostate, AInt, AFloat> Value() const override { return {}; }
const sem::Constant* Index(size_t i) const override {
- return i < elements.size() ? elements[i] : nullptr;
+ return i < elements.Length() ? elements[i] : nullptr;
}
bool AllZero() const override { return all_zero; }
bool AnyZero() const override { return any_zero; }
@@ -269,8 +272,8 @@
const Source& source) const override {
// Convert each of the composite element types.
auto* el_ty = sem::Type::ElementOf(target_ty);
- std::vector<const sem::Constant*> conv_els;
- conv_els.reserve(elements.size());
+ utils::Vector<const sem::Constant*, 4> conv_els;
+ conv_els.Reserve(elements.Length());
for (auto* el : elements) {
// Note: This file is the only place where `sem::Constant`s are created, so this
// static_cast is safe.
@@ -281,7 +284,7 @@
if (!conv_el.Get()) {
return nullptr;
}
- conv_els.emplace_back(conv_el.Get());
+ conv_els.Push(conv_el.Get());
}
return CreateComposite(builder, target_ty, std::move(conv_els));
}
@@ -295,7 +298,7 @@
}
sem::Type const* const type;
- const std::vector<const sem::Constant*> elements;
+ const utils::Vector<const sem::Constant*, 8> elements;
const bool all_zero;
const bool any_zero;
const size_t hash;
@@ -327,15 +330,15 @@
},
[&](const sem::Struct* s) -> const Constant* {
std::unordered_map<sem::Type*, const Constant*> zero_by_type;
- std::vector<const sem::Constant*> zeros;
- zeros.reserve(s->Members().size());
+ utils::Vector<const sem::Constant*, 4> zeros;
+ zeros.Reserve(s->Members().size());
for (auto* member : s->Members()) {
auto* zero = utils::GetOrCreate(zero_by_type, member->Type(),
[&] { return ZeroValue(builder, member->Type()); });
if (!zero) {
return nullptr;
}
- zeros.emplace_back(zero);
+ zeros.Push(zero);
}
if (zero_by_type.size() == 1) {
// All members were of the same type, so the zero value is the same for all members.
@@ -392,14 +395,14 @@
/// depending on the element types and values.
const Constant* CreateComposite(ProgramBuilder& builder,
const sem::Type* type,
- std::vector<const sem::Constant*> elements) {
- if (elements.size() == 0) {
+ utils::VectorRef<const sem::Constant*> elements) {
+ if (elements.IsEmpty()) {
return nullptr;
}
bool any_zero = false;
bool all_zero = true;
bool all_equal = true;
- auto* first = elements.front();
+ auto* first = elements.Front();
for (auto* el : elements) {
if (!el) {
return nullptr;
@@ -417,7 +420,7 @@
}
}
if (all_equal) {
- return builder.create<Splat>(type, elements[0], elements.size());
+ return builder.create<Splat>(type, elements[0], elements.Length());
} else {
return builder.create<Composite>(type, std::move(elements), all_zero, any_zero);
}
@@ -433,9 +436,10 @@
if (el_ty == ty) {
return f(c);
}
- std::vector<const sem::Constant*> els(n);
+ utils::Vector<const sem::Constant*, 8> els;
+ els.Reserve(n);
for (uint32_t i = 0; i < n; i++) {
- els[i] = TransformElements(builder, c->Index(i), f);
+ els.Push(TransformElements(builder, c->Index(i), f));
}
return CreateComposite(builder, c->Type(), std::move(els));
}
@@ -475,27 +479,29 @@
});
}
-const sem::Constant* ConstEval::ArrayOrStructCtor(const sem::Type* ty,
- const std::vector<const sem::Expression*>& args) {
- if (args.empty()) {
+const sem::Constant* ConstEval::ArrayOrStructCtor(
+ const sem::Type* ty,
+ utils::ConstVectorRef<const sem::Expression*> args) {
+ if (args.IsEmpty()) {
return ZeroValue(builder, ty);
}
- if (args.size() == 1 && args[0]->Type() == ty) {
+ if (args.Length() == 1 && args[0]->Type() == ty) {
// Identity constructor.
return args[0]->ConstantValue();
}
// Multiple arguments. Must be a type constructor.
- std::vector<const sem::Constant*> els;
- els.reserve(args.size());
+ utils::Vector<const sem::Constant*, 4> els;
+ els.Reserve(args.Length());
for (auto* arg : args) {
- els.emplace_back(arg->ConstantValue());
+ els.Push(arg->ConstantValue());
}
return CreateComposite(builder, ty, std::move(els));
}
-const sem::Constant* ConstEval::Conv(const sem::Type* ty, ArgumentList args, size_t) {
+const sem::Constant* ConstEval::Conv(const sem::Type* ty,
+ utils::ConstVectorRef<const sem::Expression*> args) {
uint32_t el_count = 0;
auto* el_ty = sem::Type::ElementOf(ty, &el_count);
if (!el_ty) {
@@ -515,79 +521,79 @@
return nullptr;
}
-const sem::Constant* ConstEval::Zero(const sem::Type* ty, ArgumentList, size_t) {
+const sem::Constant* ConstEval::Zero(const sem::Type* ty,
+ utils::ConstVectorRef<const sem::Expression*>) {
return ZeroValue(builder, ty);
}
-const sem::Constant* ConstEval::Identity(const sem::Type*, ArgumentList args, size_t) {
+const sem::Constant* ConstEval::Identity(const sem::Type*,
+ utils::ConstVectorRef<const sem::Expression*> args) {
return args[0]->ConstantValue();
}
const sem::Constant* ConstEval::VecSplat(const sem::Type* ty,
- sem::Expression const* const* args,
- size_t) {
+ utils::ConstVectorRef<const sem::Expression*> args) {
if (auto* arg = args[0]->ConstantValue()) {
return builder.create<Splat>(ty, arg, static_cast<const sem::Vector*>(ty)->Width());
}
return nullptr;
}
-const sem::Constant* ConstEval::VecCtorS(const sem::Type* ty, ArgumentList args, size_t num_args) {
- std::vector<const sem::Constant*> els;
- els.reserve(num_args);
- for (size_t i = 0; i < num_args; i++) {
- els.emplace_back(args[i]->ConstantValue());
+const sem::Constant* ConstEval::VecCtorS(const sem::Type* ty,
+ utils::ConstVectorRef<const sem::Expression*> args) {
+ utils::Vector<const sem::Constant*, 4> els;
+ for (auto* arg : args) {
+ els.Push(arg->ConstantValue());
}
return CreateComposite(builder, ty, std::move(els));
}
-const sem::Constant* ConstEval::VecCtorM(const sem::Type* ty, ArgumentList args, size_t num_args) {
- std::vector<const sem::Constant*> els;
- els.reserve(num_args);
- for (size_t i = 0; i < num_args; i++) {
- auto* arg = args[i]->ConstantValue();
- if (!arg) {
+const sem::Constant* ConstEval::VecCtorM(const sem::Type* ty,
+ utils::ConstVectorRef<const sem::Expression*> args) {
+ utils::Vector<const sem::Constant*, 4> els;
+ for (auto* arg : args) {
+ auto* val = arg->ConstantValue();
+ if (!val) {
return nullptr;
}
auto* arg_ty = arg->Type();
if (auto* arg_vec = arg_ty->As<sem::Vector>()) {
// Extract out vector elements.
for (uint32_t j = 0; j < arg_vec->Width(); j++) {
- auto* el = arg->Index(j);
+ auto* el = val->Index(j);
if (!el) {
return nullptr;
}
- els.emplace_back(el);
+ els.Push(el);
}
} else {
- els.emplace_back(arg);
+ els.Push(val);
}
}
return CreateComposite(builder, ty, std::move(els));
}
-const sem::Constant* ConstEval::MatCtorS(const sem::Type* ty, ArgumentList args, size_t num_args) {
+const sem::Constant* ConstEval::MatCtorS(const sem::Type* ty,
+ utils::ConstVectorRef<const sem::Expression*> args) {
auto* m = static_cast<const sem::Matrix*>(ty);
- std::vector<const sem::Constant*> els;
- els.reserve(num_args);
+ utils::Vector<const sem::Constant*, 4> els;
for (uint32_t c = 0; c < m->columns(); c++) {
- std::vector<const sem::Constant*> column;
- column.reserve(m->rows());
+ utils::Vector<const sem::Constant*, 4> column;
for (uint32_t r = 0; r < m->rows(); r++) {
auto i = r + c * m->rows();
- column.emplace_back(args[i]->ConstantValue());
+ column.Push(args[i]->ConstantValue());
}
- els.push_back(CreateComposite(builder, m->ColumnType(), std::move(column)));
+ els.Push(CreateComposite(builder, m->ColumnType(), std::move(column)));
}
return CreateComposite(builder, ty, std::move(els));
}
-const sem::Constant* ConstEval::MatCtorV(const sem::Type* ty, ArgumentList args, size_t num_args) {
- std::vector<const sem::Constant*> els;
- els.reserve(num_args);
- for (size_t i = 0; i < num_args; i++) {
- els.emplace_back(args[i]->ConstantValue());
+const sem::Constant* ConstEval::MatCtorV(const sem::Type* ty,
+ utils::ConstVectorRef<const sem::Expression*> args) {
+ utils::Vector<const sem::Constant*, 4> els;
+ for (auto* arg : args) {
+ els.Push(arg->ConstantValue());
}
return CreateComposite(builder, ty, std::move(els));
}
@@ -631,15 +637,15 @@
const sem::Constant* ConstEval::Swizzle(const sem::Type* ty,
const sem::Expression* vec_expr,
- const std::vector<uint32_t>& indices) {
+ utils::ConstVectorRef<uint32_t> indices) {
auto* vec_val = vec_expr->ConstantValue();
if (!vec_val) {
return nullptr;
}
- if (indices.size() == 1) {
+ if (indices.Length() == 1) {
return vec_val->Index(static_cast<size_t>(indices[0]));
} else {
- auto values = utils::Transform(
+ auto values = utils::Transform<4>(
indices, [&](uint32_t i) { return vec_val->Index(static_cast<size_t>(i)); });
return CreateComposite(builder, ty, std::move(values));
}
@@ -651,8 +657,7 @@
}
const sem::Constant* ConstEval::OpComplement(const sem::Type*,
- sem::Expression const* const* args,
- size_t) {
+ utils::ConstVectorRef<const sem::Expression*> args) {
return TransformElements(builder, args[0]->ConstantValue(), [&](const sem::Constant* c) {
return Dispatch_ia_iu32(c, [&](auto i) { //
return CreateElement(builder, c->Type(), decltype(i)(~i.value));
@@ -661,8 +666,7 @@
}
const sem::Constant* ConstEval::OpMinus(const sem::Type*,
- sem::Expression const* const* args,
- size_t) {
+ utils::ConstVectorRef<const sem::Expression*> args) {
return TransformElements(builder, args[0]->ConstantValue(), [&](const sem::Constant* c) {
return Dispatch_fia_fi32_f16(c, [&](auto i) { //
// For signed integrals, avoid C++ UB by not negating the smallest negative number. In
diff --git a/src/tint/resolver/const_eval.h b/src/tint/resolver/const_eval.h
index 6a85c85..9b8a3b1 100644
--- a/src/tint/resolver/const_eval.h
+++ b/src/tint/resolver/const_eval.h
@@ -17,9 +17,9 @@
#include <stddef.h>
#include <string>
-#include <vector>
#include "src/tint/utils/result.h"
+#include "src/tint/utils/vector.h"
// Forward declarations
namespace tint {
@@ -44,14 +44,9 @@
/// before calling a method to evaluate an expression's value.
class ConstEval {
public:
- /// Typedef for a pointer to an array of `const sem::Expression*`, where each expression is an
- /// argument to the function.
- using ArgumentList = sem::Expression const* const*;
-
/// Typedef for a constant evaluation function
- using Function = const sem::Constant* (ConstEval::*)(const sem::Type* result_ty,
- ArgumentList args,
- size_t num_args);
+ using Function = const sem::Constant* (
+ ConstEval::*)(const sem::Type* result_ty, utils::ConstVectorRef<const sem::Expression*>);
/// The result type of a method that may raise a diagnostic error and the caller should abort
/// resolving. Can be one of three distinct values:
@@ -76,7 +71,7 @@
/// @param args the input arguments
/// @return the constructed value, or null if the value cannot be calculated
const sem::Constant* ArrayOrStructCtor(const sem::Type* ty,
- const std::vector<const sem::Expression*>& args);
+ utils::ConstVectorRef<const sem::Expression*> args);
/// @param ty the target type
/// @param expr the input expression
@@ -105,7 +100,7 @@
/// @return the result of the swizzle, or null if the value cannot be calculated
const sem::Constant* Swizzle(const sem::Type* ty,
const sem::Expression* vector,
- const std::vector<uint32_t>& indices);
+ utils::ConstVectorRef<uint32_t> indices);
/// Convert the `value` to `target_type`
/// @param ty the result type
@@ -121,58 +116,58 @@
/// Type conversion
/// @param ty the result type
/// @param args the input arguments
- /// @param num_args the number of input arguments
/// @return the converted value, or null if the value cannot be calculated
- const sem::Constant* Conv(const sem::Type* ty, ArgumentList args, size_t num_args);
+ const sem::Constant* Conv(const sem::Type* ty,
+ utils::ConstVectorRef<const sem::Expression*> args);
/// Zero value type constructor
/// @param ty the result type
/// @param args the input arguments (no arguments provided)
- /// @param num_args the number of input arguments (no arguments provided)
/// @return the constructed value, or null if the value cannot be calculated
- const sem::Constant* Zero(const sem::Type* ty, ArgumentList args, size_t num_args);
+ const sem::Constant* Zero(const sem::Type* ty,
+ utils::ConstVectorRef<const sem::Expression*> args);
/// Identity value type constructor
/// @param ty the result type
/// @param args the input arguments
- /// @param num_args the number of input arguments (must be 1)
/// @return the constructed value, or null if the value cannot be calculated
- const sem::Constant* Identity(const sem::Type* ty, ArgumentList args, size_t num_args);
+ const sem::Constant* Identity(const sem::Type* ty,
+ utils::ConstVectorRef<const sem::Expression*> args);
/// Vector splat constructor
/// @param ty the vector type
/// @param args the input arguments
- /// @param num_args the number of input arguments (must be 1)
/// @return the constructed value, or null if the value cannot be calculated
- const sem::Constant* VecSplat(const sem::Type* ty, ArgumentList args, size_t num_args);
+ const sem::Constant* VecSplat(const sem::Type* ty,
+ utils::ConstVectorRef<const sem::Expression*> args);
/// Vector constructor using scalars
/// @param ty the vector type
/// @param args the input arguments
- /// @param num_args the number of input arguments (must be equal to vector width)
/// @return the constructed value, or null if the value cannot be calculated
- const sem::Constant* VecCtorS(const sem::Type* ty, ArgumentList args, size_t num_args);
+ const sem::Constant* VecCtorS(const sem::Type* ty,
+ utils::ConstVectorRef<const sem::Expression*> args);
/// Vector constructor using a mix of scalars and smaller vectors
/// @param ty the vector type
/// @param args the input arguments
- /// @param num_args the number of input arguments
/// @return the constructed value, or null if the value cannot be calculated
- const sem::Constant* VecCtorM(const sem::Type* ty, ArgumentList args, size_t num_args);
+ const sem::Constant* VecCtorM(const sem::Type* ty,
+ utils::ConstVectorRef<const sem::Expression*> args);
/// Matrix constructor using scalar values
/// @param ty the matrix type
/// @param args the input arguments
- /// @param num_args the number of input arguments (must equal num-columns * num-rows)
/// @return the constructed value, or null if the value cannot be calculated
- const sem::Constant* MatCtorS(const sem::Type* ty, ArgumentList args, size_t num_args);
+ const sem::Constant* MatCtorS(const sem::Type* ty,
+ utils::ConstVectorRef<const sem::Expression*> args);
/// Matrix constructor using column vectors
/// @param ty the matrix type
/// @param args the input arguments
- /// @param num_args the number of input arguments (must equal num-columns)
/// @return the constructed value, or null if the value cannot be calculated
- const sem::Constant* MatCtorV(const sem::Type* ty, ArgumentList args, size_t num_args);
+ const sem::Constant* MatCtorV(const sem::Type* ty,
+ utils::ConstVectorRef<const sem::Expression*> args);
////////////////////////////////////////////////////////////////////////////
// Operators
@@ -181,20 +176,16 @@
/// Complement operator '~'
/// @param ty the integer type
/// @param args the input arguments
- /// @param num_args the number of input arguments (must be 1)
/// @return the result value, or null if the value cannot be calculated
const sem::Constant* OpComplement(const sem::Type* ty,
- sem::Expression const* const* args,
- size_t num_args);
+ utils::ConstVectorRef<const sem::Expression*> args);
/// Minus operator '-'
/// @param ty the expression type
/// @param args the input arguments
- /// @param num_args the number of input arguments (must be 1)
/// @return the result value, or null if the value cannot be calculated
const sem::Constant* OpMinus(const sem::Type* ty,
- sem::Expression const* const* args,
- size_t num_args);
+ utils::ConstVectorRef<const sem::Expression*> args);
private:
/// Adds the given error message to the diagnostics
diff --git a/src/tint/resolver/intrinsic_table.cc b/src/tint/resolver/intrinsic_table.cc
index b874df1..8e6d72d 100644
--- a/src/tint/resolver/intrinsic_table.cc
+++ b/src/tint/resolver/intrinsic_table.cc
@@ -48,6 +48,12 @@
class NumberMatcher;
class TypeMatcher;
+/// The utils::Vector `N` template argument value for arrays of parameters.
+constexpr static const size_t kNumFixedParams = 8;
+
+/// The utils::Vector `N` template argument value for arrays of overload candidates.
+constexpr static const size_t kNumFixedCandidates = 8;
+
/// A special type that matches all TypeMatchers
class Any final : public Castable<Any, sem::Type> {
public:
@@ -882,7 +888,7 @@
/// @param i the IntrinsicPrototype to create a hash for
/// @return the hash value
inline std::size_t operator()(const IntrinsicPrototype& i) const {
- size_t hash = utils::Hash(i.parameters.size());
+ size_t hash = utils::Hash(i.parameters.Length());
for (auto& p : i.parameters) {
utils::HashCombine(&hash, p.type, p.usage);
}
@@ -892,16 +898,16 @@
const OverloadInfo* overload = nullptr;
sem::Type const* return_type = nullptr;
- std::vector<Parameter> parameters;
+ utils::Vector<Parameter, kNumFixedParams> parameters;
};
/// Equality operator for IntrinsicPrototype
bool operator==(const IntrinsicPrototype& a, const IntrinsicPrototype& b) {
if (a.overload != b.overload || a.return_type != b.return_type ||
- a.parameters.size() != b.parameters.size()) {
+ a.parameters.Length() != b.parameters.Length()) {
return false;
}
- for (size_t i = 0; i < a.parameters.size(); i++) {
+ for (size_t i = 0; i < a.parameters.Length(); i++) {
auto& pa = a.parameters[i];
auto& pb = b.parameters[i];
if (pa.type != pb.type || pa.usage != pb.usage) {
@@ -917,7 +923,7 @@
explicit Impl(ProgramBuilder& builder);
Builtin Lookup(sem::BuiltinType builtin_type,
- const std::vector<const sem::Type*>& args,
+ utils::VectorRef<const sem::Type*> args,
const Source& source) override;
UnaryOperator Lookup(ast::UnaryOp op, const sem::Type* arg, const Source& source) override;
@@ -930,7 +936,7 @@
CtorOrConv Lookup(CtorConvIntrinsic type,
const sem::Type* template_arg,
- const std::vector<const sem::Type*>& args,
+ utils::VectorRef<const sem::Type*> args,
const Source& source) override;
private:
@@ -941,7 +947,7 @@
/// The template types and numbers
TemplateState templates;
/// The parameter types for the candidate overload
- std::vector<IntrinsicPrototype::Parameter> parameters;
+ utils::Vector<IntrinsicPrototype::Parameter, kNumFixedParams> parameters;
/// The match-score of the candidate overload.
/// A score of zero indicates an exact match.
/// Non-zero scores are used for diagnostics when no overload matches.
@@ -950,10 +956,10 @@
};
/// A list of candidates
- using Candidates = std::vector<Candidate>;
+ using Candidates = utils::Vector<Candidate, kNumFixedCandidates>;
/// Callback function when no overloads match.
- using OnNoMatch = std::function<void(Candidates)>;
+ using OnNoMatch = std::function<void(utils::VectorRef<Candidate>)>;
/// Sorts the candidates based on their score, with the lowest (best-ranking) scores first.
static inline void SortCandidates(Candidates& candidates) {
@@ -975,7 +981,7 @@
/// IntrinsicPrototype::return_type.
IntrinsicPrototype MatchIntrinsic(const IntrinsicInfo& intrinsic,
const char* intrinsic_name,
- const std::vector<const sem::Type*>& args,
+ utils::VectorRef<const sem::Type*> args,
TemplateState templates,
OnNoMatch on_no_match) const;
@@ -987,7 +993,7 @@
/// template as `f32`.
/// @returns the evaluated Candidate information.
Candidate ScoreOverload(const OverloadInfo* overload,
- const std::vector<const sem::Type*>& args,
+ utils::VectorRef<const sem::Type*> args,
TemplateState templates) const;
/// Performs overload resolution given the list of candidates, by ranking the conversions of
@@ -1002,7 +1008,7 @@
/// @returns the resolved Candidate.
Candidate ResolveCandidate(Candidates&& candidates,
const char* intrinsic_name,
- const std::vector<const sem::Type*>& args,
+ utils::VectorRef<const sem::Type*> args,
TemplateState templates) const;
/// Match constructs a new MatchState
@@ -1020,14 +1026,14 @@
// Prints the list of candidates for emitting diagnostics
void PrintCandidates(std::ostream& ss,
- const Candidates& candidates,
+ utils::ConstVectorRef<Candidate> candidates,
const char* intrinsic_name) const;
/// Raises an error when no overload is a clear winner of overload resolution
void ErrAmbiguousOverload(const char* intrinsic_name,
- const std::vector<const sem::Type*>& args,
+ utils::ConstVectorRef<const sem::Type*> args,
TemplateState templates,
- Candidates candidates) const;
+ utils::ConstVectorRef<Candidate> candidates) const;
ProgramBuilder& builder;
Matchers matchers;
@@ -1042,7 +1048,7 @@
/// types.
std::string CallSignature(ProgramBuilder& builder,
const char* intrinsic_name,
- const std::vector<const sem::Type*>& args,
+ utils::VectorRef<const sem::Type*> args,
const sem::Type* template_arg = nullptr) {
std::stringstream ss;
ss << intrinsic_name;
@@ -1076,18 +1082,18 @@
Impl::Impl(ProgramBuilder& b) : builder(b) {}
Impl::Builtin Impl::Lookup(sem::BuiltinType builtin_type,
- const std::vector<const sem::Type*>& args,
+ utils::VectorRef<const sem::Type*> args,
const Source& source) {
const char* intrinsic_name = sem::str(builtin_type);
// Generates an error when no overloads match the provided arguments
- auto on_no_match = [&](Candidates candidates) {
+ auto on_no_match = [&](utils::VectorRef<Candidate> candidates) {
std::stringstream ss;
ss << "no matching call to " << CallSignature(builder, intrinsic_name, args) << std::endl;
- if (!candidates.empty()) {
+ if (!candidates.IsEmpty()) {
ss << std::endl
- << candidates.size() << " candidate function" << (candidates.size() > 1 ? "s:" : ":")
- << std::endl;
+ << candidates.Length() << " candidate function"
+ << (candidates.Length() > 1 ? "s:" : ":") << std::endl;
PrintCandidates(ss, candidates, intrinsic_name);
}
builder.Diagnostics().add_error(diag::System::Resolver, ss.str(), source);
@@ -1102,11 +1108,11 @@
// De-duplicate builtins that are identical.
auto* sem = utils::GetOrCreate(builtins, match, [&] {
- std::vector<sem::Parameter*> params;
- params.reserve(match.parameters.size());
+ utils::Vector<sem::Parameter*, kNumFixedParams> params;
+ params.Reserve(match.parameters.Length());
for (auto& p : match.parameters) {
- params.emplace_back(builder.create<sem::Parameter>(
- nullptr, static_cast<uint32_t>(params.size()), p.type, ast::StorageClass::kNone,
+ params.Push(builder.create<sem::Parameter>(
+ nullptr, static_cast<uint32_t>(params.Length()), p.type, ast::StorageClass::kNone,
ast::Access::kUndefined, p.usage));
}
sem::PipelineStageSet supported_stages;
@@ -1144,21 +1150,23 @@
}
}();
+ utils::Vector args{arg};
+
// Generates an error when no overloads match the provided arguments
- auto on_no_match = [&, name = intrinsic_name](Candidates candidates) {
+ auto on_no_match = [&, name = intrinsic_name](utils::VectorRef<Candidate> candidates) {
std::stringstream ss;
- ss << "no matching overload for " << CallSignature(builder, name, {arg}) << std::endl;
- if (!candidates.empty()) {
+ ss << "no matching overload for " << CallSignature(builder, name, args) << std::endl;
+ if (!candidates.IsEmpty()) {
ss << std::endl
- << candidates.size() << " candidate operator" << (candidates.size() > 1 ? "s:" : ":")
- << std::endl;
+ << candidates.Length() << " candidate operator"
+ << (candidates.Length() > 1 ? "s:" : ":") << std::endl;
PrintCandidates(ss, candidates, name);
}
builder.Diagnostics().add_error(diag::System::Resolver, ss.str(), source);
};
// Resolve the intrinsic overload
- auto match = MatchIntrinsic(kUnaryOperators[intrinsic_index], intrinsic_name, {arg},
+ auto match = MatchIntrinsic(kUnaryOperators[intrinsic_index], intrinsic_name, args,
TemplateState{}, on_no_match);
if (!match.overload) {
return {};
@@ -1219,21 +1227,23 @@
}
}();
+ utils::Vector args{lhs, rhs};
+
// Generates an error when no overloads match the provided arguments
- auto on_no_match = [&, name = intrinsic_name](Candidates candidates) {
+ auto on_no_match = [&, name = intrinsic_name](utils::VectorRef<Candidate> candidates) {
std::stringstream ss;
- ss << "no matching overload for " << CallSignature(builder, name, {lhs, rhs}) << std::endl;
- if (!candidates.empty()) {
+ ss << "no matching overload for " << CallSignature(builder, name, args) << std::endl;
+ if (!candidates.IsEmpty()) {
ss << std::endl
- << candidates.size() << " candidate operator" << (candidates.size() > 1 ? "s:" : ":")
- << std::endl;
+ << candidates.Length() << " candidate operator"
+ << (candidates.Length() > 1 ? "s:" : ":") << std::endl;
PrintCandidates(ss, candidates, name);
}
builder.Diagnostics().add_error(diag::System::Resolver, ss.str(), source);
};
// Resolve the intrinsic overload
- auto match = MatchIntrinsic(kBinaryOperators[intrinsic_index], intrinsic_name, {lhs, rhs},
+ auto match = MatchIntrinsic(kBinaryOperators[intrinsic_index], intrinsic_name, args,
TemplateState{}, on_no_match);
if (!match.overload) {
return {};
@@ -1249,32 +1259,32 @@
IntrinsicTable::CtorOrConv Impl::Lookup(CtorConvIntrinsic type,
const sem::Type* template_arg,
- const std::vector<const sem::Type*>& args,
+ utils::VectorRef<const sem::Type*> args,
const Source& source) {
auto name = str(type);
// Generates an error when no overloads match the provided arguments
- auto on_no_match = [&](Candidates candidates) {
+ auto on_no_match = [&](utils::VectorRef<Candidate> candidates) {
std::stringstream ss;
ss << "no matching constructor for " << CallSignature(builder, name, args, template_arg)
<< std::endl;
Candidates ctor, conv;
for (auto candidate : candidates) {
if (candidate.overload->flags.Contains(OverloadFlag::kIsConstructor)) {
- ctor.emplace_back(candidate);
+ ctor.Push(candidate);
} else {
- conv.emplace_back(candidate);
+ conv.Push(candidate);
}
}
- if (!ctor.empty()) {
+ if (!ctor.IsEmpty()) {
ss << std::endl
- << ctor.size() << " candidate constructor" << (ctor.size() > 1 ? "s:" : ":")
+ << ctor.Length() << " candidate constructor" << (ctor.Length() > 1 ? "s:" : ":")
<< std::endl;
PrintCandidates(ss, ctor, name);
}
- if (!conv.empty()) {
+ if (!conv.IsEmpty()) {
ss << std::endl
- << conv.size() << " candidate conversion" << (conv.size() > 1 ? "s:" : ":")
+ << conv.Length() << " candidate conversion" << (conv.Length() > 1 ? "s:" : ":")
<< std::endl;
PrintCandidates(ss, conv, name);
}
@@ -1296,11 +1306,11 @@
// Was this overload a constructor or conversion?
if (match.overload->flags.Contains(OverloadFlag::kIsConstructor)) {
- sem::ParameterList params;
- params.reserve(match.parameters.size());
+ utils::Vector<const sem::Parameter*, 8> params;
+ params.Reserve(match.parameters.Length());
for (auto& p : match.parameters) {
- params.emplace_back(builder.create<sem::Parameter>(
- nullptr, static_cast<uint32_t>(params.size()), p.type, ast::StorageClass::kNone,
+ params.Push(builder.create<sem::Parameter>(
+ nullptr, static_cast<uint32_t>(params.Length()), p.type, ast::StorageClass::kNone,
ast::Access::kUndefined, p.usage));
}
auto eval_stage = match.overload->const_eval_fn ? sem::EvaluationStage::kConstant
@@ -1326,13 +1336,13 @@
IntrinsicPrototype Impl::MatchIntrinsic(const IntrinsicInfo& intrinsic,
const char* intrinsic_name,
- const std::vector<const sem::Type*>& args,
+ utils::VectorRef<const sem::Type*> args,
TemplateState templates,
OnNoMatch on_no_match) const {
size_t num_matched = 0;
size_t match_idx = 0;
- Candidates candidates;
- candidates.reserve(intrinsic.num_overloads);
+ utils::Vector<Candidate, kNumFixedCandidates> candidates;
+ candidates.Reserve(intrinsic.num_overloads);
for (size_t overload_idx = 0; overload_idx < static_cast<size_t>(intrinsic.num_overloads);
overload_idx++) {
auto candidate = ScoreOverload(&intrinsic.overloads[overload_idx], args, templates);
@@ -1340,7 +1350,7 @@
match_idx = overload_idx;
num_matched++;
}
- candidates.emplace_back(std::move(candidate));
+ candidates.Push(std::move(candidate));
}
// How many candidates matched?
@@ -1380,7 +1390,7 @@
}
Impl::Candidate Impl::ScoreOverload(const OverloadInfo* overload,
- const std::vector<const sem::Type*>& args,
+ utils::VectorRef<const sem::Type*> args,
TemplateState templates) const {
// Penalty weights for overload mismatching.
// This scoring is used to order the suggested overloads in diagnostic on overload mismatch, and
@@ -1392,7 +1402,7 @@
constexpr int kMismatchedTemplateNumberPenalty = 1;
size_t num_parameters = static_cast<size_t>(overload->num_parameters);
- size_t num_arguments = static_cast<size_t>(args.size());
+ size_t num_arguments = static_cast<size_t>(args.Length());
size_t score = 0;
@@ -1459,14 +1469,14 @@
}
// Now that all the template types have been finalized, we can construct the parameters.
- std::vector<IntrinsicPrototype::Parameter> parameters;
+ utils::Vector<IntrinsicPrototype::Parameter, kNumFixedParams> parameters;
if (score == 0) {
- parameters.reserve(num_params);
+ parameters.Reserve(num_params);
for (size_t p = 0; p < num_params; p++) {
auto& parameter = overload->parameters[p];
auto* indices = parameter.matcher_indices;
auto* ty = Match(templates, overload, indices).Type(args[p]->UnwrapRef());
- parameters.emplace_back(IntrinsicPrototype::Parameter{ty, parameter.usage});
+ parameters.Emplace(ty, parameter.usage);
}
}
@@ -1475,9 +1485,10 @@
Impl::Candidate Impl::ResolveCandidate(Impl::Candidates&& candidates,
const char* intrinsic_name,
- const std::vector<const sem::Type*>& args,
+ utils::VectorRef<const sem::Type*> args,
TemplateState templates) const {
- std::vector<uint32_t> best_ranks(args.size(), 0xffffffff);
+ utils::Vector<uint32_t, kNumFixedParams> best_ranks;
+ best_ranks.Resize(args.Length(), 0xffffffff);
size_t num_matched = 0;
Candidate* best = nullptr;
for (auto& candidate : candidates) {
@@ -1486,7 +1497,7 @@
}
bool some_won = false; // An argument ranked less than the 'best' overload's argument
bool some_lost = false; // An argument ranked more than the 'best' overload's argument
- for (size_t i = 0; i < args.size(); i++) {
+ for (size_t i = 0; i < args.Length(); i++) {
auto rank = sem::Type::ConversionRank(args[i], candidate.parameters[i].type);
if (best_ranks[i] > rank) {
best_ranks[i] = rank;
@@ -1593,7 +1604,7 @@
}
void Impl::PrintCandidates(std::ostream& ss,
- const Candidates& candidates,
+ utils::ConstVectorRef<Candidate> candidates,
const char* intrinsic_name) const {
for (auto& candidate : candidates) {
ss << " ";
@@ -1627,9 +1638,9 @@
}
void Impl::ErrAmbiguousOverload(const char* intrinsic_name,
- const std::vector<const sem::Type*>& args,
+ utils::ConstVectorRef<const sem::Type*> args,
TemplateState templates,
- Candidates candidates) const {
+ utils::ConstVectorRef<Candidate> candidates) const {
std::stringstream ss;
ss << "ambiguous overload while attempting to match " << intrinsic_name;
for (size_t i = 0; i < std::numeric_limits<size_t>::max(); i++) {
diff --git a/src/tint/resolver/intrinsic_table.h b/src/tint/resolver/intrinsic_table.h
index 79eb82f..b36f8d8 100644
--- a/src/tint/resolver/intrinsic_table.h
+++ b/src/tint/resolver/intrinsic_table.h
@@ -17,13 +17,13 @@
#include <memory>
#include <string>
-#include <vector>
#include "src/tint/ast/binary_expression.h"
#include "src/tint/ast/unary_op.h"
#include "src/tint/resolver/const_eval.h"
#include "src/tint/resolver/ctor_conv_intrinsic.h"
#include "src/tint/sem/builtin.h"
+#include "src/tint/utils/vector.h"
// Forward declarations
namespace tint {
@@ -87,7 +87,7 @@
/// @param source the source of the builtin call
/// @return the semantic builtin if found, otherwise nullptr
virtual Builtin Lookup(sem::BuiltinType type,
- const std::vector<const sem::Type*>& args,
+ utils::VectorRef<const sem::Type*> args,
const Source& source) = 0;
/// Lookup looks for the unary op overload with the given signature, raising an error
@@ -123,7 +123,7 @@
/// @return a sem::TypeConstructor, sem::TypeConversion or nullptr if nothing matched
virtual CtorOrConv Lookup(CtorConvIntrinsic type,
const sem::Type* template_arg,
- const std::vector<const sem::Type*>& args,
+ utils::VectorRef<const sem::Type*> args,
const Source& source) = 0;
};
diff --git a/src/tint/resolver/intrinsic_table_test.cc b/src/tint/resolver/intrinsic_table_test.cc
index c246dd9..e8cb206 100644
--- a/src/tint/resolver/intrinsic_table_test.cc
+++ b/src/tint/resolver/intrinsic_table_test.cc
@@ -53,18 +53,18 @@
TEST_F(IntrinsicTableTest, MatchF32) {
auto* f32 = create<sem::F32>();
- auto result = table->Lookup(BuiltinType::kCos, {f32}, Source{});
+ auto result = table->Lookup(BuiltinType::kCos, utils::Vector{f32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result.sem->Type(), BuiltinType::kCos);
EXPECT_EQ(result.sem->ReturnType(), f32);
- ASSERT_EQ(result.sem->Parameters().size(), 1u);
+ ASSERT_EQ(result.sem->Parameters().Length(), 1u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), f32);
}
TEST_F(IntrinsicTableTest, MismatchF32) {
auto* i32 = create<sem::I32>();
- auto result = table->Lookup(BuiltinType::kCos, {i32}, Source{});
+ auto result = table->Lookup(BuiltinType::kCos, utils::Vector{i32}, Source{});
ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
@@ -73,18 +73,18 @@
auto* f32 = create<sem::F32>();
auto* u32 = create<sem::U32>();
auto* vec2_f32 = create<sem::Vector>(f32, 2u);
- auto result = table->Lookup(BuiltinType::kUnpack2x16float, {u32}, Source{});
+ auto result = table->Lookup(BuiltinType::kUnpack2x16float, utils::Vector{u32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result.sem->Type(), BuiltinType::kUnpack2x16float);
EXPECT_EQ(result.sem->ReturnType(), vec2_f32);
- ASSERT_EQ(result.sem->Parameters().size(), 1u);
+ ASSERT_EQ(result.sem->Parameters().Length(), 1u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), u32);
}
TEST_F(IntrinsicTableTest, MismatchU32) {
auto* f32 = create<sem::F32>();
- auto result = table->Lookup(BuiltinType::kUnpack2x16float, {f32}, Source{});
+ auto result = table->Lookup(BuiltinType::kUnpack2x16float, utils::Vector{f32}, Source{});
ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
@@ -94,12 +94,12 @@
auto* i32 = create<sem::I32>();
auto* vec4_f32 = create<sem::Vector>(f32, 4u);
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k1d, f32);
- auto result = table->Lookup(BuiltinType::kTextureLoad, {tex, i32, i32}, Source{});
+ auto result = table->Lookup(BuiltinType::kTextureLoad, utils::Vector{tex, i32, i32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureLoad);
EXPECT_EQ(result.sem->ReturnType(), vec4_f32);
- ASSERT_EQ(result.sem->Parameters().size(), 3u);
+ ASSERT_EQ(result.sem->Parameters().Length(), 3u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), tex);
EXPECT_EQ(result.sem->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), i32);
@@ -111,48 +111,48 @@
TEST_F(IntrinsicTableTest, MismatchI32) {
auto* f32 = create<sem::F32>();
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k1d, f32);
- auto result = table->Lookup(BuiltinType::kTextureLoad, {tex, f32}, Source{});
+ auto result = table->Lookup(BuiltinType::kTextureLoad, utils::Vector{tex, f32}, Source{});
ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
TEST_F(IntrinsicTableTest, MatchIU32AsI32) {
auto* i32 = create<sem::I32>();
- auto result = table->Lookup(BuiltinType::kCountOneBits, {i32}, Source{});
+ auto result = table->Lookup(BuiltinType::kCountOneBits, utils::Vector{i32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result.sem->Type(), BuiltinType::kCountOneBits);
EXPECT_EQ(result.sem->ReturnType(), i32);
- ASSERT_EQ(result.sem->Parameters().size(), 1u);
+ ASSERT_EQ(result.sem->Parameters().Length(), 1u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), i32);
}
TEST_F(IntrinsicTableTest, MatchIU32AsU32) {
auto* u32 = create<sem::U32>();
- auto result = table->Lookup(BuiltinType::kCountOneBits, {u32}, Source{});
+ auto result = table->Lookup(BuiltinType::kCountOneBits, utils::Vector{u32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result.sem->Type(), BuiltinType::kCountOneBits);
EXPECT_EQ(result.sem->ReturnType(), u32);
- ASSERT_EQ(result.sem->Parameters().size(), 1u);
+ ASSERT_EQ(result.sem->Parameters().Length(), 1u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), u32);
}
TEST_F(IntrinsicTableTest, MismatchIU32) {
auto* f32 = create<sem::F32>();
- auto result = table->Lookup(BuiltinType::kCountOneBits, {f32}, Source{});
+ auto result = table->Lookup(BuiltinType::kCountOneBits, utils::Vector{f32}, Source{});
ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
TEST_F(IntrinsicTableTest, MatchFIU32AsI32) {
auto* i32 = create<sem::I32>();
- auto result = table->Lookup(BuiltinType::kClamp, {i32, i32, i32}, Source{});
+ auto result = table->Lookup(BuiltinType::kClamp, utils::Vector{i32, i32, i32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result.sem->Type(), BuiltinType::kClamp);
EXPECT_EQ(result.sem->ReturnType(), i32);
- ASSERT_EQ(result.sem->Parameters().size(), 3u);
+ ASSERT_EQ(result.sem->Parameters().Length(), 3u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), i32);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), i32);
EXPECT_EQ(result.sem->Parameters()[2]->Type(), i32);
@@ -160,12 +160,12 @@
TEST_F(IntrinsicTableTest, MatchFIU32AsU32) {
auto* u32 = create<sem::U32>();
- auto result = table->Lookup(BuiltinType::kClamp, {u32, u32, u32}, Source{});
+ auto result = table->Lookup(BuiltinType::kClamp, utils::Vector{u32, u32, u32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result.sem->Type(), BuiltinType::kClamp);
EXPECT_EQ(result.sem->ReturnType(), u32);
- ASSERT_EQ(result.sem->Parameters().size(), 3u);
+ ASSERT_EQ(result.sem->Parameters().Length(), 3u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), u32);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), u32);
EXPECT_EQ(result.sem->Parameters()[2]->Type(), u32);
@@ -173,12 +173,12 @@
TEST_F(IntrinsicTableTest, MatchFIU32AsF32) {
auto* f32 = create<sem::F32>();
- auto result = table->Lookup(BuiltinType::kClamp, {f32, f32, f32}, Source{});
+ auto result = table->Lookup(BuiltinType::kClamp, utils::Vector{f32, f32, f32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result.sem->Type(), BuiltinType::kClamp);
EXPECT_EQ(result.sem->ReturnType(), f32);
- ASSERT_EQ(result.sem->Parameters().size(), 3u);
+ ASSERT_EQ(result.sem->Parameters().Length(), 3u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), f32);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), f32);
EXPECT_EQ(result.sem->Parameters()[2]->Type(), f32);
@@ -186,7 +186,7 @@
TEST_F(IntrinsicTableTest, MismatchFIU32) {
auto* bool_ = create<sem::Bool>();
- auto result = table->Lookup(BuiltinType::kClamp, {bool_, bool_, bool_}, Source{});
+ auto result = table->Lookup(BuiltinType::kClamp, utils::Vector{bool_, bool_, bool_}, Source{});
ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
@@ -194,12 +194,12 @@
TEST_F(IntrinsicTableTest, MatchBool) {
auto* f32 = create<sem::F32>();
auto* bool_ = create<sem::Bool>();
- auto result = table->Lookup(BuiltinType::kSelect, {f32, f32, bool_}, Source{});
+ auto result = table->Lookup(BuiltinType::kSelect, utils::Vector{f32, f32, bool_}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result.sem->Type(), BuiltinType::kSelect);
EXPECT_EQ(result.sem->ReturnType(), f32);
- ASSERT_EQ(result.sem->Parameters().size(), 3u);
+ ASSERT_EQ(result.sem->Parameters().Length(), 3u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), f32);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), f32);
EXPECT_EQ(result.sem->Parameters()[2]->Type(), bool_);
@@ -207,7 +207,7 @@
TEST_F(IntrinsicTableTest, MismatchBool) {
auto* f32 = create<sem::F32>();
- auto result = table->Lookup(BuiltinType::kSelect, {f32, f32, f32}, Source{});
+ auto result = table->Lookup(BuiltinType::kSelect, utils::Vector{f32, f32, f32}, Source{});
ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
@@ -217,19 +217,19 @@
auto* atomicI32 = create<sem::Atomic>(i32);
auto* ptr =
create<sem::Pointer>(atomicI32, ast::StorageClass::kWorkgroup, ast::Access::kReadWrite);
- auto result = table->Lookup(BuiltinType::kAtomicLoad, {ptr}, Source{});
+ auto result = table->Lookup(BuiltinType::kAtomicLoad, utils::Vector{ptr}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result.sem->Type(), BuiltinType::kAtomicLoad);
EXPECT_EQ(result.sem->ReturnType(), i32);
- ASSERT_EQ(result.sem->Parameters().size(), 1u);
+ ASSERT_EQ(result.sem->Parameters().Length(), 1u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), ptr);
}
TEST_F(IntrinsicTableTest, MismatchPointer) {
auto* i32 = create<sem::I32>();
auto* atomicI32 = create<sem::Atomic>(i32);
- auto result = table->Lookup(BuiltinType::kAtomicLoad, {atomicI32}, Source{});
+ auto result = table->Lookup(BuiltinType::kAtomicLoad, utils::Vector{atomicI32}, Source{});
ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
@@ -237,12 +237,12 @@
TEST_F(IntrinsicTableTest, MatchArray) {
auto* arr = create<sem::Array>(create<sem::U32>(), 0u, 4u, 4u, 4u, 4u);
auto* arr_ptr = create<sem::Pointer>(arr, ast::StorageClass::kStorage, ast::Access::kReadWrite);
- auto result = table->Lookup(BuiltinType::kArrayLength, {arr_ptr}, Source{});
+ auto result = table->Lookup(BuiltinType::kArrayLength, utils::Vector{arr_ptr}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result.sem->Type(), BuiltinType::kArrayLength);
EXPECT_TRUE(result.sem->ReturnType()->Is<sem::U32>());
- ASSERT_EQ(result.sem->Parameters().size(), 1u);
+ ASSERT_EQ(result.sem->Parameters().Length(), 1u);
auto* param_type = result.sem->Parameters()[0]->Type();
ASSERT_TRUE(param_type->Is<sem::Pointer>());
EXPECT_TRUE(param_type->As<sem::Pointer>()->StoreType()->Is<sem::Array>());
@@ -250,7 +250,7 @@
TEST_F(IntrinsicTableTest, MismatchArray) {
auto* f32 = create<sem::F32>();
- auto result = table->Lookup(BuiltinType::kArrayLength, {f32}, Source{});
+ auto result = table->Lookup(BuiltinType::kArrayLength, utils::Vector{f32}, Source{});
ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
@@ -261,12 +261,13 @@
auto* vec4_f32 = create<sem::Vector>(f32, 4u);
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k2d, f32);
auto* sampler = create<sem::Sampler>(ast::SamplerKind::kSampler);
- auto result = table->Lookup(BuiltinType::kTextureSample, {tex, sampler, vec2_f32}, Source{});
+ auto result =
+ table->Lookup(BuiltinType::kTextureSample, utils::Vector{tex, sampler, vec2_f32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureSample);
EXPECT_EQ(result.sem->ReturnType(), vec4_f32);
- ASSERT_EQ(result.sem->Parameters().size(), 3u);
+ ASSERT_EQ(result.sem->Parameters().Length(), 3u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), tex);
EXPECT_EQ(result.sem->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), sampler);
@@ -279,7 +280,8 @@
auto* f32 = create<sem::F32>();
auto* vec2_f32 = create<sem::Vector>(f32, 2u);
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k2d, f32);
- auto result = table->Lookup(BuiltinType::kTextureSample, {tex, f32, vec2_f32}, Source{});
+ auto result =
+ table->Lookup(BuiltinType::kTextureSample, utils::Vector{tex, f32, vec2_f32}, Source{});
ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
@@ -290,12 +292,13 @@
auto* vec2_i32 = create<sem::Vector>(i32, 2u);
auto* vec4_f32 = create<sem::Vector>(f32, 4u);
auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k2d, f32);
- auto result = table->Lookup(BuiltinType::kTextureLoad, {tex, vec2_i32, i32}, Source{});
+ auto result =
+ table->Lookup(BuiltinType::kTextureLoad, utils::Vector{tex, vec2_i32, i32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureLoad);
EXPECT_EQ(result.sem->ReturnType(), vec4_f32);
- ASSERT_EQ(result.sem->Parameters().size(), 3u);
+ ASSERT_EQ(result.sem->Parameters().Length(), 3u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), tex);
EXPECT_EQ(result.sem->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), vec2_i32);
@@ -310,12 +313,13 @@
auto* vec2_i32 = create<sem::Vector>(i32, 2u);
auto* vec4_f32 = create<sem::Vector>(f32, 4u);
auto* tex = create<sem::MultisampledTexture>(ast::TextureDimension::k2d, f32);
- auto result = table->Lookup(BuiltinType::kTextureLoad, {tex, vec2_i32, i32}, Source{});
+ auto result =
+ table->Lookup(BuiltinType::kTextureLoad, utils::Vector{tex, vec2_i32, i32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureLoad);
EXPECT_EQ(result.sem->ReturnType(), vec4_f32);
- ASSERT_EQ(result.sem->Parameters().size(), 3u);
+ ASSERT_EQ(result.sem->Parameters().Length(), 3u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), tex);
EXPECT_EQ(result.sem->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), vec2_i32);
@@ -329,12 +333,13 @@
auto* i32 = create<sem::I32>();
auto* vec2_i32 = create<sem::Vector>(i32, 2u);
auto* tex = create<sem::DepthTexture>(ast::TextureDimension::k2d);
- auto result = table->Lookup(BuiltinType::kTextureLoad, {tex, vec2_i32, i32}, Source{});
+ auto result =
+ table->Lookup(BuiltinType::kTextureLoad, utils::Vector{tex, vec2_i32, i32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureLoad);
EXPECT_EQ(result.sem->ReturnType(), f32);
- ASSERT_EQ(result.sem->Parameters().size(), 3u);
+ ASSERT_EQ(result.sem->Parameters().Length(), 3u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), tex);
EXPECT_EQ(result.sem->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), vec2_i32);
@@ -348,12 +353,13 @@
auto* i32 = create<sem::I32>();
auto* vec2_i32 = create<sem::Vector>(i32, 2u);
auto* tex = create<sem::DepthMultisampledTexture>(ast::TextureDimension::k2d);
- auto result = table->Lookup(BuiltinType::kTextureLoad, {tex, vec2_i32, i32}, Source{});
+ auto result =
+ table->Lookup(BuiltinType::kTextureLoad, utils::Vector{tex, vec2_i32, i32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureLoad);
EXPECT_EQ(result.sem->ReturnType(), f32);
- ASSERT_EQ(result.sem->Parameters().size(), 3u);
+ ASSERT_EQ(result.sem->Parameters().Length(), 3u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), tex);
EXPECT_EQ(result.sem->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), vec2_i32);
@@ -368,12 +374,12 @@
auto* vec2_i32 = create<sem::Vector>(i32, 2u);
auto* vec4_f32 = create<sem::Vector>(f32, 4u);
auto* tex = create<sem::ExternalTexture>();
- auto result = table->Lookup(BuiltinType::kTextureLoad, {tex, vec2_i32}, Source{});
+ auto result = table->Lookup(BuiltinType::kTextureLoad, utils::Vector{tex, vec2_i32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureLoad);
EXPECT_EQ(result.sem->ReturnType(), vec4_f32);
- ASSERT_EQ(result.sem->Parameters().size(), 2u);
+ ASSERT_EQ(result.sem->Parameters().Length(), 2u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), tex);
EXPECT_EQ(result.sem->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), vec2_i32);
@@ -389,12 +395,13 @@
auto* tex = create<sem::StorageTexture>(ast::TextureDimension::k2d, ast::TexelFormat::kR32Float,
ast::Access::kWrite, subtype);
- auto result = table->Lookup(BuiltinType::kTextureStore, {tex, vec2_i32, vec4_f32}, Source{});
+ auto result =
+ table->Lookup(BuiltinType::kTextureStore, utils::Vector{tex, vec2_i32, vec4_f32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureStore);
EXPECT_TRUE(result.sem->ReturnType()->Is<sem::Void>());
- ASSERT_EQ(result.sem->Parameters().size(), 3u);
+ ASSERT_EQ(result.sem->Parameters().Length(), 3u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), tex);
EXPECT_EQ(result.sem->Parameters()[0]->Usage(), ParameterUsage::kTexture);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), vec2_i32);
@@ -407,7 +414,7 @@
auto* f32 = create<sem::F32>();
auto* i32 = create<sem::I32>();
auto* vec2_i32 = create<sem::Vector>(i32, 2u);
- auto result = table->Lookup(BuiltinType::kTextureLoad, {f32, vec2_i32}, Source{});
+ auto result = table->Lookup(BuiltinType::kTextureLoad, utils::Vector{f32, vec2_i32}, Source{});
ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
@@ -416,19 +423,21 @@
auto* f32 = create<sem::F32>();
auto result = table->Lookup(
BuiltinType::kCos,
- {create<sem::Reference>(f32, ast::StorageClass::kFunction, ast::Access::kReadWrite)},
+ utils::Vector{
+ create<sem::Reference>(f32, ast::StorageClass::kFunction, ast::Access::kReadWrite),
+ },
Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result.sem->Type(), BuiltinType::kCos);
EXPECT_EQ(result.sem->ReturnType(), f32);
- ASSERT_EQ(result.sem->Parameters().size(), 1u);
+ ASSERT_EQ(result.sem->Parameters().Length(), 1u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), f32);
}
TEST_F(IntrinsicTableTest, MatchTemplateType) {
auto* f32 = create<sem::F32>();
- auto result = table->Lookup(BuiltinType::kClamp, {f32, f32, f32}, Source{});
+ auto result = table->Lookup(BuiltinType::kClamp, utils::Vector{f32, f32, f32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result.sem->Type(), BuiltinType::kClamp);
@@ -441,7 +450,7 @@
TEST_F(IntrinsicTableTest, MismatchTemplateType) {
auto* f32 = create<sem::F32>();
auto* u32 = create<sem::U32>();
- auto result = table->Lookup(BuiltinType::kClamp, {f32, u32, f32}, Source{});
+ auto result = table->Lookup(BuiltinType::kClamp, utils::Vector{f32, u32, f32}, Source{});
ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
@@ -449,12 +458,13 @@
TEST_F(IntrinsicTableTest, MatchOpenSizeVector) {
auto* f32 = create<sem::F32>();
auto* vec2_f32 = create<sem::Vector>(f32, 2u);
- auto result = table->Lookup(BuiltinType::kClamp, {vec2_f32, vec2_f32, vec2_f32}, Source{});
+ auto result =
+ table->Lookup(BuiltinType::kClamp, utils::Vector{vec2_f32, vec2_f32, vec2_f32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result.sem->Type(), BuiltinType::kClamp);
EXPECT_EQ(result.sem->ReturnType(), vec2_f32);
- ASSERT_EQ(result.sem->Parameters().size(), 3u);
+ ASSERT_EQ(result.sem->Parameters().Length(), 3u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), vec2_f32);
EXPECT_EQ(result.sem->Parameters()[1]->Type(), vec2_f32);
EXPECT_EQ(result.sem->Parameters()[2]->Type(), vec2_f32);
@@ -464,7 +474,8 @@
auto* f32 = create<sem::F32>();
auto* u32 = create<sem::U32>();
auto* vec2_f32 = create<sem::Vector>(f32, 2u);
- auto result = table->Lookup(BuiltinType::kClamp, {vec2_f32, u32, vec2_f32}, Source{});
+ auto result =
+ table->Lookup(BuiltinType::kClamp, utils::Vector{vec2_f32, u32, vec2_f32}, Source{});
ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
@@ -473,12 +484,12 @@
auto* f32 = create<sem::F32>();
auto* vec3_f32 = create<sem::Vector>(f32, 3u);
auto* mat3_f32 = create<sem::Matrix>(vec3_f32, 3u);
- auto result = table->Lookup(BuiltinType::kDeterminant, {mat3_f32}, Source{});
+ auto result = table->Lookup(BuiltinType::kDeterminant, utils::Vector{mat3_f32}, Source{});
ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
EXPECT_EQ(result.sem->Type(), BuiltinType::kDeterminant);
EXPECT_EQ(result.sem->ReturnType(), f32);
- ASSERT_EQ(result.sem->Parameters().size(), 1u);
+ ASSERT_EQ(result.sem->Parameters().Length(), 1u);
EXPECT_EQ(result.sem->Parameters()[0]->Type(), mat3_f32);
}
@@ -486,7 +497,7 @@
auto* f32 = create<sem::F32>();
auto* vec2_f32 = create<sem::Vector>(f32, 2u);
auto* mat3x2_f32 = create<sem::Matrix>(vec2_f32, 3u);
- auto result = table->Lookup(BuiltinType::kDeterminant, {mat3x2_f32}, Source{});
+ auto result = table->Lookup(BuiltinType::kDeterminant, utils::Vector{mat3x2_f32}, Source{});
ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
@@ -495,7 +506,7 @@
// None of the arguments match, so expect the overloads with 2 parameters to
// come first
auto* bool_ = create<sem::Bool>();
- table->Lookup(BuiltinType::kTextureDimensions, {bool_, bool_}, Source{});
+ table->Lookup(BuiltinType::kTextureDimensions, utils::Vector{bool_, bool_}, Source{});
ASSERT_EQ(Diagnostics().str(),
R"(error: no matching call to textureDimensions(bool, bool)
@@ -533,7 +544,7 @@
TEST_F(IntrinsicTableTest, OverloadOrderByMatchingParameter) {
auto* tex = create<sem::DepthTexture>(ast::TextureDimension::k2d);
auto* bool_ = create<sem::Bool>();
- table->Lookup(BuiltinType::kTextureDimensions, {tex, bool_}, Source{});
+ table->Lookup(BuiltinType::kTextureDimensions, utils::Vector{tex, bool_}, Source{});
ASSERT_EQ(Diagnostics().str(),
R"(error: no matching call to textureDimensions(texture_depth_2d, bool)
@@ -572,14 +583,15 @@
auto* f32 = create<sem::F32>();
auto* vec2_f32 = create<sem::Vector>(create<sem::F32>(), 2u);
auto* bool_ = create<sem::Bool>();
- auto a = table->Lookup(BuiltinType::kSelect, {f32, f32, bool_}, Source{});
+ auto a = table->Lookup(BuiltinType::kSelect, utils::Vector{f32, f32, bool_}, Source{});
ASSERT_NE(a.sem, nullptr) << Diagnostics().str();
- auto b = table->Lookup(BuiltinType::kSelect, {f32, f32, bool_}, Source{});
+ auto b = table->Lookup(BuiltinType::kSelect, utils::Vector{f32, f32, bool_}, Source{});
ASSERT_NE(b.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
- auto c = table->Lookup(BuiltinType::kSelect, {vec2_f32, vec2_f32, bool_}, Source{});
+ auto c =
+ table->Lookup(BuiltinType::kSelect, utils::Vector{vec2_f32, vec2_f32, bool_}, Source{});
ASSERT_NE(c.sem, nullptr) << Diagnostics().str();
ASSERT_EQ(Diagnostics().str(), "");
@@ -676,12 +688,12 @@
TEST_F(IntrinsicTableTest, MatchTypeConstructorImplicit) {
auto* i32 = create<sem::I32>();
auto* vec3_i32 = create<sem::Vector>(i32, 3u);
- auto result =
- table->Lookup(CtorConvIntrinsic::kVec3, nullptr, {i32, i32, i32}, Source{{12, 34}});
+ auto result = table->Lookup(CtorConvIntrinsic::kVec3, nullptr, utils::Vector{i32, i32, i32},
+ Source{{12, 34}});
ASSERT_NE(result.target, nullptr);
EXPECT_EQ(result.target->ReturnType(), vec3_i32);
EXPECT_TRUE(result.target->Is<sem::TypeConstructor>());
- ASSERT_EQ(result.target->Parameters().size(), 3u);
+ ASSERT_EQ(result.target->Parameters().Length(), 3u);
EXPECT_EQ(result.target->Parameters()[0]->Type(), i32);
EXPECT_EQ(result.target->Parameters()[1]->Type(), i32);
EXPECT_EQ(result.target->Parameters()[2]->Type(), i32);
@@ -691,11 +703,12 @@
TEST_F(IntrinsicTableTest, MatchTypeConstructorExplicit) {
auto* i32 = create<sem::I32>();
auto* vec3_i32 = create<sem::Vector>(i32, 3u);
- auto result = table->Lookup(CtorConvIntrinsic::kVec3, i32, {i32, i32, i32}, Source{{12, 34}});
+ auto result = table->Lookup(CtorConvIntrinsic::kVec3, i32, utils::Vector{i32, i32, i32},
+ Source{{12, 34}});
ASSERT_NE(result.target, nullptr);
EXPECT_EQ(result.target->ReturnType(), vec3_i32);
EXPECT_TRUE(result.target->Is<sem::TypeConstructor>());
- ASSERT_EQ(result.target->Parameters().size(), 3u);
+ ASSERT_EQ(result.target->Parameters().Length(), 3u);
EXPECT_EQ(result.target->Parameters()[0]->Type(), i32);
EXPECT_EQ(result.target->Parameters()[1]->Type(), i32);
EXPECT_EQ(result.target->Parameters()[2]->Type(), i32);
@@ -705,10 +718,11 @@
TEST_F(IntrinsicTableTest, MismatchTypeConstructorImplicit) {
auto* i32 = create<sem::I32>();
auto* f32 = create<sem::F32>();
- auto result =
- table->Lookup(CtorConvIntrinsic::kVec3, nullptr, {i32, f32, i32}, Source{{12, 34}});
+ auto result = table->Lookup(CtorConvIntrinsic::kVec3, nullptr, utils::Vector{i32, f32, i32},
+ Source{{12, 34}});
ASSERT_EQ(result.target, nullptr);
- EXPECT_EQ(Diagnostics().str(), R"(12:34 error: no matching constructor for vec3(i32, f32, i32)
+ EXPECT_EQ(Diagnostics().str(),
+ R"(12:34 error: no matching constructor for vec3(i32, f32, i32)
6 candidate constructors:
vec3(x: T, y: T, z: T) -> vec3<T> where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
@@ -730,7 +744,8 @@
TEST_F(IntrinsicTableTest, MismatchTypeConstructorExplicit) {
auto* i32 = create<sem::I32>();
auto* f32 = create<sem::F32>();
- auto result = table->Lookup(CtorConvIntrinsic::kVec3, i32, {i32, f32, i32}, Source{{12, 34}});
+ auto result = table->Lookup(CtorConvIntrinsic::kVec3, i32, utils::Vector{i32, f32, i32},
+ Source{{12, 34}});
ASSERT_EQ(result.target, nullptr);
EXPECT_EQ(Diagnostics().str(),
R"(12:34 error: no matching constructor for vec3<i32>(i32, f32, i32)
@@ -757,18 +772,20 @@
auto* vec3_i32 = create<sem::Vector>(i32, 3u);
auto* f32 = create<sem::F32>();
auto* vec3_f32 = create<sem::Vector>(f32, 3u);
- auto result = table->Lookup(CtorConvIntrinsic::kVec3, i32, {vec3_f32}, Source{{12, 34}});
+ auto result =
+ table->Lookup(CtorConvIntrinsic::kVec3, i32, utils::Vector{vec3_f32}, Source{{12, 34}});
ASSERT_NE(result.target, nullptr);
EXPECT_EQ(result.target->ReturnType(), vec3_i32);
EXPECT_TRUE(result.target->Is<sem::TypeConversion>());
- ASSERT_EQ(result.target->Parameters().size(), 1u);
+ ASSERT_EQ(result.target->Parameters().Length(), 1u);
EXPECT_EQ(result.target->Parameters()[0]->Type(), vec3_f32);
}
TEST_F(IntrinsicTableTest, MismatchTypeConversion) {
auto* arr = create<sem::Array>(create<sem::U32>(), 0u, 4u, 4u, 4u, 4u);
auto* f32 = create<sem::F32>();
- auto result = table->Lookup(CtorConvIntrinsic::kVec3, f32, {arr}, Source{{12, 34}});
+ auto result =
+ table->Lookup(CtorConvIntrinsic::kVec3, f32, utils::Vector{arr}, Source{{12, 34}});
ASSERT_EQ(result.target, nullptr);
EXPECT_EQ(Diagnostics().str(),
R"(12:34 error: no matching constructor for vec3<f32>(array<u32>)
@@ -792,7 +809,8 @@
TEST_F(IntrinsicTableTest, Err257Arguments) { // crbug.com/1323605
auto* f32 = create<sem::F32>();
- std::vector<const sem::Type*> arg_tys(257, f32);
+ utils::Vector<const sem::Type*, 0> arg_tys;
+ arg_tys.Resize(257, f32);
auto result = table->Lookup(BuiltinType::kAbs, std::move(arg_tys), Source{});
ASSERT_EQ(result.sem, nullptr);
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
@@ -805,10 +823,10 @@
// The first should win overload resolution.
auto* ai = create<sem::AbstractInt>();
auto* i32 = create<sem::I32>();
- auto result = table->Lookup(CtorConvIntrinsic::kI32, nullptr, {ai}, Source{});
+ auto result = table->Lookup(CtorConvIntrinsic::kI32, nullptr, utils::Vector{ai}, Source{});
ASSERT_NE(result.target, nullptr);
EXPECT_EQ(result.target->ReturnType(), i32);
- EXPECT_EQ(result.target->Parameters().size(), 1u);
+ EXPECT_EQ(result.target->Parameters().Length(), 1u);
EXPECT_EQ(result.target->Parameters()[0]->Type(), i32);
}
@@ -816,7 +834,6 @@
// AbstractBinaryTests
////////////////////////////////////////////////////////////////////////////////
namespace AbstractBinaryTests {
-
struct Case {
template <typename RESULT,
typename PARAM_LHS,
@@ -991,7 +1008,6 @@
// AbstractTernaryTests
////////////////////////////////////////////////////////////////////////////////
namespace AbstractTernaryTests {
-
struct Case {
template <typename RESULT,
typename PARAM_A,
@@ -1030,7 +1046,8 @@
auto* arg_a = GetParam().arg_a(*this);
auto* arg_b = GetParam().arg_b(*this);
auto* arg_c = GetParam().arg_c(*this);
- auto builtin = table->Lookup(sem::BuiltinType::kClamp, {arg_a, arg_b, arg_c}, Source{{12, 34}});
+ auto builtin = table->Lookup(sem::BuiltinType::kClamp, utils::Vector{arg_a, arg_b, arg_c},
+ Source{{12, 34}});
bool matched = builtin.sem != nullptr;
bool expected_match = GetParam().expected_match;
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index b418deb..b7b7586 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -85,6 +85,7 @@
#include "src/tint/utils/reverse.h"
#include "src/tint/utils/scoped_assignment.h"
#include "src/tint/utils/transform.h"
+#include "src/tint/utils/vector.h"
namespace tint::resolver {
@@ -720,7 +721,7 @@
sem::Function* Resolver::Function(const ast::Function* decl) {
uint32_t parameter_index = 0;
std::unordered_map<Symbol, Source> parameter_names;
- std::vector<sem::Parameter*> parameters;
+ utils::Vector<sem::Parameter*, 8> parameters;
// Resolve all the parameters
for (auto* param : decl->params) {
@@ -745,7 +746,7 @@
return nullptr;
}
- parameters.emplace_back(p);
+ parameters.Push(p);
auto* p_ty = const_cast<sem::Type*>(p->Type());
if (auto* str = p_ty->As<sem::Struct>()) {
@@ -799,7 +800,7 @@
}
}
- auto* func = builder_->create<sem::Function>(decl, return_type, parameters);
+ auto* func = builder_->create<sem::Function>(decl, return_type, std::move(parameters));
builder_->Sem().Add(decl, func);
TINT_SCOPED_ASSIGNMENT(current_function_, func);
@@ -1229,7 +1230,7 @@
}
sem::Expression* Resolver::Expression(const ast::Expression* root) {
- std::vector<const ast::Expression*> sorted;
+ utils::Vector<const ast::Expression*, 128> sorted;
constexpr size_t kMaxExpressionDepth = 512U;
bool failed = false;
if (!ast::TraverseExpressions<ast::TraverseOrder::RightToLeft>(
@@ -1245,7 +1246,7 @@
failed = true;
return ast::TraverseAction::Stop;
}
- sorted.emplace_back(expr);
+ sorted.Push(expr);
return ast::TraverseAction::Descend;
})) {
return nullptr;
@@ -1377,9 +1378,9 @@
[&](Default) { return expr; });
}
-bool Resolver::MaterializeArguments(std::vector<const sem::Expression*>& args,
+bool Resolver::MaterializeArguments(utils::VectorRef<const sem::Expression*> args,
const sem::CallTarget* target) {
- for (size_t i = 0, n = std::min(args.size(), target->Parameters().size()); i < n; i++) {
+ for (size_t i = 0, n = std::min(args.Length(), target->Parameters().Length()); i < n; i++) {
const auto* param_ty = target->Parameters()[i]->Type();
if (ShouldMaterializeArgument(param_ty)) {
auto* materialized = Materialize(args[i], param_ty);
@@ -1480,7 +1481,8 @@
// * A type conversion.
// Resolve all of the arguments, their types and the set of behaviors.
- std::vector<const sem::Expression*> args(expr->args.size());
+ utils::Vector<const sem::Expression*, 8> args;
+ args.Reserve(expr->args.size());
auto args_stage = sem::EvaluationStage::kConstant;
sem::Behaviors arg_behaviors;
for (size_t i = 0; i < expr->args.size(); i++) {
@@ -1488,7 +1490,7 @@
if (!arg) {
return nullptr;
}
- args[i] = arg;
+ args.Push(arg);
args_stage = sem::EarliestStage(args_stage, arg->Stage());
arg_behaviors.Add(arg->Behaviors());
}
@@ -1512,8 +1514,8 @@
const sem::Constant* value = nullptr;
auto stage = sem::EarliestStage(ctor_or_conv.target->Stage(), args_stage);
if (stage == sem::EvaluationStage::kConstant) {
- value = (const_eval_.*ctor_or_conv.const_eval_fn)(ctor_or_conv.target->ReturnType(),
- args.data(), args.size());
+ value =
+ (const_eval_.*ctor_or_conv.const_eval_fn)(ctor_or_conv.target->ReturnType(), args);
}
return builder_->create<sem::Call>(expr, ctor_or_conv.target, stage, std::move(args),
current_statement_, value, has_side_effects);
@@ -1563,16 +1565,17 @@
[&](const sem::Bool*) { return ct_ctor_or_conv(CtorConvIntrinsic::kBool, nullptr); },
[&](const sem::Array* arr) -> sem::Call* {
auto* call_target = utils::GetOrCreate(
- array_ctors_, ArrayConstructorSig{{arr, args.size(), args_stage}},
+ array_ctors_, ArrayConstructorSig{{arr, args.Length(), args_stage}},
[&]() -> sem::TypeConstructor* {
- sem::ParameterList params(args.size());
- for (size_t i = 0; i < args.size(); i++) {
- params[i] = builder_->create<sem::Parameter>(
- nullptr, // declaration
- static_cast<uint32_t>(i), // index
- arr->ElemType(), // type
- ast::StorageClass::kNone, // storage_class
- ast::Access::kUndefined); // access
+ utils::Vector<const sem::Parameter*, 8> params;
+ params.Reserve(args.Length());
+ for (size_t i = 0; i < args.Length(); i++) {
+ params.Push(builder_->create<sem::Parameter>(
+ nullptr, // declaration
+ static_cast<uint32_t>(i), // index
+ arr->ElemType(), // type
+ ast::StorageClass::kNone, // storage_class
+ ast::Access::kUndefined)); // access
}
return builder_->create<sem::TypeConstructor>(arr, std::move(params),
args_stage);
@@ -1581,10 +1584,11 @@
},
[&](const sem::Struct* str) -> sem::Call* {
auto* call_target = utils::GetOrCreate(
- struct_ctors_, StructConstructorSig{{str, args.size(), args_stage}},
+ struct_ctors_, StructConstructorSig{{str, args.Length(), args_stage}},
[&]() -> sem::TypeConstructor* {
- sem::ParameterList params(std::min(args.size(), str->Members().size()));
- for (size_t i = 0, n = params.size(); i < n; i++) {
+ utils::Vector<const sem::Parameter*, 8> params;
+ params.Resize(std::min(args.Length(), str->Members().size()));
+ for (size_t i = 0, n = params.Length(); i < n; i++) {
params[i] = builder_->create<sem::Parameter>(
nullptr, // declaration
static_cast<uint32_t>(i), // index
@@ -1705,10 +1709,10 @@
sem::Call* Resolver::BuiltinCall(const ast::CallExpression* expr,
sem::BuiltinType builtin_type,
- std::vector<const sem::Expression*> args) {
+ utils::VectorRef<const sem::Expression*> args) {
IntrinsicTable::Builtin builtin;
{
- auto arg_tys = utils::Transform(args, [](auto* arg) { return arg->Type(); });
+ auto arg_tys = utils::Transform<8>(args, [](auto* arg) { return arg->Type(); });
builtin = intrinsic_table_->Lookup(builtin_type, arg_tys, expr->source);
if (!builtin.sem) {
return nullptr;
@@ -1738,8 +1742,7 @@
// If the builtin is @const, and all arguments have constant values, evaluate the builtin now.
const sem::Constant* value = nullptr;
if (stage == sem::EvaluationStage::kConstant) {
- value = (const_eval_.*builtin.const_eval_fn)(builtin.sem->ReturnType(), args.data(),
- args.size());
+ value = (const_eval_.*builtin.const_eval_fn)(builtin.sem->ReturnType(), args);
}
bool has_side_effects =
@@ -1771,8 +1774,9 @@
return call;
}
-void Resolver::CollectTextureSamplerPairs(const sem::Builtin* builtin,
- const std::vector<const sem::Expression*>& args) const {
+void Resolver::CollectTextureSamplerPairs(
+ const sem::Builtin* builtin,
+ utils::ConstVectorRef<const sem::Expression*> args) const {
// Collect a texture/sampler pair for this builtin.
const auto& signature = builtin->Signature();
int texture_index = signature.IndexOf(sem::ParameterUsage::kTexture);
@@ -1792,7 +1796,7 @@
sem::Call* Resolver::FunctionCall(const ast::CallExpression* expr,
sem::Function* target,
- std::vector<const sem::Expression*> args,
+ utils::VectorRef<const sem::Expression*> args,
sem::Behaviors arg_behaviors) {
auto sym = expr->target.name->symbol;
auto name = builder_->Symbols().NameFor(sym);
@@ -1838,8 +1842,9 @@
return call;
}
-void Resolver::CollectTextureSamplerPairs(sem::Function* func,
- const std::vector<const sem::Expression*>& args) const {
+void Resolver::CollectTextureSamplerPairs(
+ sem::Function* func,
+ utils::ConstVectorRef<const sem::Expression*> args) const {
// Map all texture/sampler pairs from the target function to the
// current function. These can only be global or parameter
// variables. Resolve any parameter variables to the corresponding
@@ -1992,7 +1997,7 @@
auto* source_var = object->SourceVariable();
const sem::Type* ret = nullptr;
- std::vector<uint32_t> swizzle;
+ utils::Vector<uint32_t, 4> swizzle;
// Object may be a side-effecting expression (e.g. function call).
bool has_side_effects = object && object->HasSideEffects();
@@ -2030,33 +2035,33 @@
Mark(expr->member);
std::string s = builder_->Symbols().NameFor(expr->member->symbol);
auto size = s.size();
- swizzle.reserve(s.size());
+ swizzle.Reserve(s.size());
for (auto c : s) {
switch (c) {
case 'x':
case 'r':
- swizzle.emplace_back(0);
+ swizzle.Push(0u);
break;
case 'y':
case 'g':
- swizzle.emplace_back(1);
+ swizzle.Push(1u);
break;
case 'z':
case 'b':
- swizzle.emplace_back(2);
+ swizzle.Push(2u);
break;
case 'w':
case 'a':
- swizzle.emplace_back(3);
+ swizzle.Push(3u);
break;
default:
AddError("invalid vector swizzle character",
- expr->member->source.Begin() + swizzle.size());
+ expr->member->source.Begin() + swizzle.Length());
return nullptr;
}
- if (swizzle.back() >= vec->Width()) {
+ if (swizzle.Back() >= vec->Width()) {
AddError("invalid vector swizzle member", expr->member->source);
return nullptr;
}
@@ -2126,8 +2131,7 @@
const sem::Constant* value = nullptr;
if (op.const_eval_fn) {
- const sem::Expression* args[] = {lhs, rhs};
- value = (const_eval_.*op.const_eval_fn)(op.result, args, 2u);
+ value = (const_eval_.*op.const_eval_fn)(op.result, utils::Vector{lhs, rhs});
}
bool has_side_effects = lhs->HasSideEffects() || rhs->HasSideEffects();
@@ -2203,7 +2207,7 @@
stage = expr->Stage();
if (stage == sem::EvaluationStage::kConstant) {
if (op.const_eval_fn) {
- value = (const_eval_.*op.const_eval_fn)(ty, &expr, 1u);
+ value = (const_eval_.*op.const_eval_fn)(ty, utils::Vector{expr});
} else {
stage = sem::EvaluationStage::kRuntime;
}
diff --git a/src/tint/resolver/resolver.h b/src/tint/resolver/resolver.h
index 4a1c250..03f1b9e 100644
--- a/src/tint/resolver/resolver.h
+++ b/src/tint/resolver/resolver.h
@@ -195,12 +195,12 @@
sem::Function* Function(const ast::Function*);
sem::Call* FunctionCall(const ast::CallExpression*,
sem::Function* target,
- std::vector<const sem::Expression*> args,
+ utils::VectorRef<const sem::Expression*> args,
sem::Behaviors arg_behaviors);
sem::Expression* Identifier(const ast::IdentifierExpression*);
sem::Call* BuiltinCall(const ast::CallExpression*,
sem::BuiltinType,
- std::vector<const sem::Expression*> args);
+ utils::VectorRef<const sem::Expression*> args);
sem::Expression* Literal(const ast::LiteralExpression*);
sem::Expression* MemberAccessor(const ast::MemberAccessorExpression*);
sem::Expression* UnaryOp(const ast::UnaryOpExpression*);
@@ -223,7 +223,7 @@
/// Materializes all the arguments in `args` to the parameter types of `target`.
/// @returns true on success, false on failure.
- bool MaterializeArguments(std::vector<const sem::Expression*>& args,
+ bool MaterializeArguments(utils::VectorRef<const sem::Expression*> args,
const sem::CallTarget* target);
/// @returns true if an argument of an abstract numeric type, passed to a parameter of type
@@ -257,9 +257,9 @@
// CollectTextureSamplerPairs() collects all the texture/sampler pairs from the target function
// / builtin, and records these on the current function by calling AddTextureSamplerPair().
void CollectTextureSamplerPairs(sem::Function* func,
- const std::vector<const sem::Expression*>& args) const;
+ utils::ConstVectorRef<const sem::Expression*> args) const;
void CollectTextureSamplerPairs(const sem::Builtin* builtin,
- const std::vector<const sem::Expression*>& args) const;
+ utils::ConstVectorRef<const sem::Expression*> args) const;
/// Resolves the WorkgroupSize for the given function, assigning it to
/// current_function_
diff --git a/src/tint/resolver/resolver_test.cc b/src/tint/resolver/resolver_test.cc
index 9b56c31..bacbcd6 100644
--- a/src/tint/resolver/resolver_test.cc
+++ b/src/tint/resolver/resolver_test.cc
@@ -763,7 +763,7 @@
auto* func_sem = Sem().Get(func);
ASSERT_NE(func_sem, nullptr);
- EXPECT_EQ(func_sem->Parameters().size(), 3u);
+ EXPECT_EQ(func_sem->Parameters().Length(), 3u);
EXPECT_TRUE(func_sem->Parameters()[0]->Type()->Is<sem::F32>());
EXPECT_TRUE(func_sem->Parameters()[1]->Type()->Is<sem::I32>());
EXPECT_TRUE(func_sem->Parameters()[2]->Type()->Is<sem::U32>());
@@ -796,7 +796,7 @@
auto* func_sem = Sem().Get(func);
ASSERT_NE(func_sem, nullptr);
- EXPECT_EQ(func_sem->Parameters().size(), 0u);
+ EXPECT_EQ(func_sem->Parameters().Length(), 0u);
EXPECT_TRUE(func_sem->ReturnType()->Is<sem::Void>());
const auto& vars = func_sem->TransitivelyReferencedGlobals();
@@ -832,7 +832,7 @@
auto* func2_sem = Sem().Get(func2);
ASSERT_NE(func2_sem, nullptr);
- EXPECT_EQ(func2_sem->Parameters().size(), 0u);
+ EXPECT_EQ(func2_sem->Parameters().Length(), 0u);
const auto& vars = func2_sem->TransitivelyReferencedGlobals();
ASSERT_EQ(vars.size(), 3u);
@@ -1867,9 +1867,9 @@
ASSERT_NE(ep_1_sem, nullptr);
ASSERT_NE(ep_2_sem, nullptr);
- EXPECT_EQ(func_b_sem->Parameters().size(), 0u);
- EXPECT_EQ(func_a_sem->Parameters().size(), 0u);
- EXPECT_EQ(func_c_sem->Parameters().size(), 0u);
+ EXPECT_EQ(func_b_sem->Parameters().Length(), 0u);
+ EXPECT_EQ(func_a_sem->Parameters().Length(), 0u);
+ EXPECT_EQ(func_c_sem->Parameters().Length(), 0u);
const auto& b_eps = func_b_sem->AncestorEntryPoints();
ASSERT_EQ(2u, b_eps.size());
diff --git a/src/tint/resolver/type_constructor_validation_test.cc b/src/tint/resolver/type_constructor_validation_test.cc
index 41cc698..0fb01bc 100644
--- a/src/tint/resolver/type_constructor_validation_test.cc
+++ b/src/tint/resolver/type_constructor_validation_test.cc
@@ -371,7 +371,7 @@
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 1u);
+ ASSERT_EQ(ctor->Parameters().Length(), 1u);
EXPECT_EQ(ctor->Parameters()[0]->Type(), TypeOf(arg));
break;
}
@@ -379,7 +379,7 @@
auto* conv = call->Target()->As<sem::TypeConversion>();
ASSERT_NE(conv, nullptr);
EXPECT_EQ(call->Type(), conv->ReturnType());
- ASSERT_EQ(conv->Parameters().size(), 1u);
+ ASSERT_EQ(conv->Parameters().Length(), 1u);
EXPECT_EQ(conv->Parameters()[0]->Type(), TypeOf(arg));
break;
}
@@ -499,7 +499,7 @@
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 0u);
+ ASSERT_EQ(ctor->Parameters().Length(), 0u);
}
TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Array_type_match) {
@@ -515,7 +515,7 @@
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 3u);
+ ASSERT_EQ(ctor->Parameters().Length(), 3u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::U32>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::U32>());
EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::U32>());
@@ -686,7 +686,7 @@
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 1u);
+ ASSERT_EQ(ctor->Parameters().Length(), 1u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::I32>());
}
@@ -704,7 +704,7 @@
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 1u);
+ ASSERT_EQ(ctor->Parameters().Length(), 1u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::U32>());
}
@@ -722,7 +722,7 @@
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 1u);
+ ASSERT_EQ(ctor->Parameters().Length(), 1u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::F32>());
}
@@ -742,7 +742,7 @@
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 1u);
+ ASSERT_EQ(ctor->Parameters().Length(), 1u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::F16>());
}
@@ -760,7 +760,7 @@
auto* ctor = call->Target()->As<sem::TypeConversion>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 1u);
+ ASSERT_EQ(ctor->Parameters().Length(), 1u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::F32>());
}
@@ -778,7 +778,7 @@
auto* ctor = call->Target()->As<sem::TypeConversion>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 1u);
+ ASSERT_EQ(ctor->Parameters().Length(), 1u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::I32>());
}
@@ -798,7 +798,7 @@
auto* ctor = call->Target()->As<sem::TypeConversion>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 1u);
+ ASSERT_EQ(ctor->Parameters().Length(), 1u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::U32>());
}
@@ -818,7 +818,7 @@
auto* ctor = call->Target()->As<sem::TypeConversion>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 1u);
+ ASSERT_EQ(ctor->Parameters().Length(), 1u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::F16>());
}
@@ -941,7 +941,7 @@
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 0u);
+ ASSERT_EQ(ctor->Parameters().Length(), 0u);
}
TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec2F32_Success_Scalar) {
@@ -960,7 +960,7 @@
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 2u);
+ ASSERT_EQ(ctor->Parameters().Length(), 2u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::F32>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::F32>());
}
@@ -983,7 +983,7 @@
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 2u);
+ ASSERT_EQ(ctor->Parameters().Length(), 2u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::F16>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::F16>());
}
@@ -1004,7 +1004,7 @@
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 2u);
+ ASSERT_EQ(ctor->Parameters().Length(), 2u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::U32>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::U32>());
}
@@ -1025,7 +1025,7 @@
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 2u);
+ ASSERT_EQ(ctor->Parameters().Length(), 2u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::I32>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::I32>());
}
@@ -1046,7 +1046,7 @@
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 2u);
+ ASSERT_EQ(ctor->Parameters().Length(), 2u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Bool>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::Bool>());
}
@@ -1067,7 +1067,7 @@
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 1u);
+ ASSERT_EQ(ctor->Parameters().Length(), 1u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Vector>());
}
@@ -1087,7 +1087,7 @@
auto* ctor = call->Target()->As<sem::TypeConversion>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 1u);
+ ASSERT_EQ(ctor->Parameters().Length(), 1u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Vector>());
}
@@ -1223,7 +1223,7 @@
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 0u);
+ ASSERT_EQ(ctor->Parameters().Length(), 0u);
}
TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec3F32_Success_Scalar) {
@@ -1242,7 +1242,7 @@
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 3u);
+ ASSERT_EQ(ctor->Parameters().Length(), 3u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::F32>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::F32>());
EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::F32>());
@@ -1266,7 +1266,7 @@
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 3u);
+ ASSERT_EQ(ctor->Parameters().Length(), 3u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::F16>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::F16>());
EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::F16>());
@@ -1288,7 +1288,7 @@
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 3u);
+ ASSERT_EQ(ctor->Parameters().Length(), 3u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::U32>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::U32>());
EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::U32>());
@@ -1310,7 +1310,7 @@
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 3u);
+ ASSERT_EQ(ctor->Parameters().Length(), 3u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::I32>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::I32>());
EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::I32>());
@@ -1332,7 +1332,7 @@
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 3u);
+ ASSERT_EQ(ctor->Parameters().Length(), 3u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Bool>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::Bool>());
EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::Bool>());
@@ -1354,7 +1354,7 @@
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 2u);
+ ASSERT_EQ(ctor->Parameters().Length(), 2u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Vector>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::F32>());
}
@@ -1375,7 +1375,7 @@
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 2u);
+ ASSERT_EQ(ctor->Parameters().Length(), 2u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::F32>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::Vector>());
}
@@ -1396,7 +1396,7 @@
auto* ctor = call->Target()->As<sem::TypeConstructor>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 1u);
+ ASSERT_EQ(ctor->Parameters().Length(), 1u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Vector>());
}
@@ -1416,7 +1416,7 @@
auto* ctor = call->Target()->As<sem::TypeConversion>();
ASSERT_NE(ctor, nullptr);
EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 1u);
+ ASSERT_EQ(ctor->Parameters().Length(), 1u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Vector>());
}
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index f962e3b..b7776fa 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -1602,9 +1602,14 @@
bool Validator::BuiltinCall(const sem::Call* call) const {
if (call->Type()->Is<sem::Void>()) {
bool is_call_statement = false;
- if (auto* call_stmt = As<ast::CallStatement>(call->Stmt()->Declaration())) {
- if (call_stmt->expr == call->Declaration()) {
- is_call_statement = true;
+ // Some built-in call are not owned by a statement, e.g. a built-in called in global
+ // variable declaration. Calling no-return-value built-in in these context is invalid as
+ // well.
+ if (auto* call_stmt = call->Stmt()) {
+ if (auto* call_stmt_ast = As<ast::CallStatement>(call_stmt->Declaration())) {
+ if (call_stmt_ast->expr == call->Declaration()) {
+ is_call_statement = true;
+ }
}
}
if (!is_call_statement) {
@@ -1734,17 +1739,17 @@
return false;
}
- if (decl->args.size() != target->Parameters().size()) {
- bool more = decl->args.size() > target->Parameters().size();
+ if (decl->args.size() != target->Parameters().Length()) {
+ bool more = decl->args.size() > target->Parameters().Length();
AddError("too " + (more ? std::string("many") : std::string("few")) +
" arguments in call to '" + name + "', expected " +
- std::to_string(target->Parameters().size()) + ", got " +
- std::to_string(call->Arguments().size()),
+ std::to_string(target->Parameters().Length()) + ", got " +
+ std::to_string(call->Arguments().Length()),
decl->source);
return false;
}
- for (size_t i = 0; i < call->Arguments().size(); ++i) {
+ for (size_t i = 0; i < call->Arguments().Length(); ++i) {
const sem::Variable* param = target->Parameters()[i];
const ast::Expression* arg_expr = decl->args[i];
auto* param_type = param->Type();
diff --git a/src/tint/resolver/variable_validation_test.cc b/src/tint/resolver/variable_validation_test.cc
index 5625f00..e13585f 100644
--- a/src/tint/resolver/variable_validation_test.cc
+++ b/src/tint/resolver/variable_validation_test.cc
@@ -40,6 +40,24 @@
EXPECT_EQ(r()->error(), "12:34 error: var declaration requires a type or initializer");
}
+TEST_F(ResolverVariableValidationTest, VarInitializerNoReturnValueBuiltin) {
+ // fn f() { var a = storageBarrier(); }
+ auto* NoReturnValueBuiltin = Call(Source{{12, 34}}, "storageBarrier");
+ WrapInFunction(Var("a", nullptr, ast::StorageClass::kNone, NoReturnValueBuiltin));
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: builtin 'storageBarrier' does not return a value");
+}
+
+TEST_F(ResolverVariableValidationTest, GlobalVarInitializerNoReturnValueBuiltin) {
+ // var a = storageBarrier();
+ auto* NoReturnValueBuiltin = Call(Source{{12, 34}}, "storageBarrier");
+ GlobalVar("a", nullptr, ast::StorageClass::kNone, NoReturnValueBuiltin);
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: builtin 'storageBarrier' does not return a value");
+}
+
TEST_F(ResolverVariableValidationTest, GlobalVarUsedAtModuleScope) {
// var<private> a : i32;
// var<private> b : i32 = a;
diff --git a/src/tint/sem/builtin.cc b/src/tint/sem/builtin.cc
index c688c4c..ee5a02b 100644
--- a/src/tint/sem/builtin.cc
+++ b/src/tint/sem/builtin.cc
@@ -17,13 +17,25 @@
#include "src/tint/sem/builtin.h"
+#include <utility>
#include <vector>
-#include "src/tint/utils/to_const_ptr_vec.h"
+#include "src/tint/utils/transform.h"
TINT_INSTANTIATE_TYPEINFO(tint::sem::Builtin);
namespace tint::sem {
+namespace {
+
+utils::VectorRef<const Parameter*> SetOwner(utils::VectorRef<Parameter*> parameters,
+ const tint::sem::CallTarget* owner) {
+ for (auto* parameter : parameters) {
+ parameter->SetOwner(owner);
+ }
+ return parameters;
+}
+
+} // namespace
const char* Builtin::str() const {
return sem::str(type_);
@@ -89,18 +101,14 @@
Builtin::Builtin(BuiltinType type,
const sem::Type* return_type,
- std::vector<Parameter*> parameters,
+ utils::VectorRef<Parameter*> parameters,
EvaluationStage eval_stage,
PipelineStageSet supported_stages,
bool is_deprecated)
- : Base(return_type, utils::ToConstPtrVec(parameters), eval_stage),
+ : Base(return_type, SetOwner(std::move(parameters), this), eval_stage),
type_(type),
supported_stages_(supported_stages),
- is_deprecated_(is_deprecated) {
- for (auto* parameter : parameters) {
- parameter->SetOwner(this);
- }
-}
+ is_deprecated_(is_deprecated) {}
Builtin::~Builtin() = default;
diff --git a/src/tint/sem/builtin.h b/src/tint/sem/builtin.h
index 783e6ca..b63bece 100644
--- a/src/tint/sem/builtin.h
+++ b/src/tint/sem/builtin.h
@@ -90,7 +90,7 @@
/// deprecated
Builtin(BuiltinType type,
const sem::Type* return_type,
- std::vector<Parameter*> parameters,
+ utils::VectorRef<Parameter*> parameters,
EvaluationStage eval_stage,
PipelineStageSet supported_stages,
bool is_deprecated);
diff --git a/src/tint/sem/call.cc b/src/tint/sem/call.cc
index a20649d..1fb7de4 100644
--- a/src/tint/sem/call.cc
+++ b/src/tint/sem/call.cc
@@ -24,7 +24,7 @@
Call::Call(const ast::CallExpression* declaration,
const CallTarget* target,
EvaluationStage stage,
- std::vector<const sem::Expression*> arguments,
+ utils::VectorRef<const sem::Expression*> arguments,
const Statement* statement,
const Constant* constant,
bool has_side_effects)
diff --git a/src/tint/sem/call.h b/src/tint/sem/call.h
index 2d82306..1213c6d 100644
--- a/src/tint/sem/call.h
+++ b/src/tint/sem/call.h
@@ -20,6 +20,7 @@
#include "src/tint/ast/call_expression.h"
#include "src/tint/sem/builtin.h"
#include "src/tint/sem/expression.h"
+#include "src/tint/utils/vector.h"
namespace tint::sem {
@@ -38,7 +39,7 @@
Call(const ast::CallExpression* declaration,
const CallTarget* target,
EvaluationStage stage,
- std::vector<const sem::Expression*> arguments,
+ utils::VectorRef<const sem::Expression*> arguments,
const Statement* statement,
const Constant* constant,
bool has_side_effects);
@@ -50,7 +51,7 @@
const CallTarget* Target() const { return target_; }
/// @return the call arguments
- const std::vector<const sem::Expression*>& Arguments() const { return arguments_; }
+ const auto& Arguments() const { return arguments_; }
/// @returns the AST node
const ast::CallExpression* Declaration() const {
@@ -59,7 +60,7 @@
private:
CallTarget const* const target_;
- std::vector<const sem::Expression*> arguments_;
+ utils::Vector<const sem::Expression*, 8> arguments_;
};
} // namespace tint::sem
diff --git a/src/tint/sem/call_target.cc b/src/tint/sem/call_target.cc
index f8bcdd8..362482c 100644
--- a/src/tint/sem/call_target.cc
+++ b/src/tint/sem/call_target.cc
@@ -14,6 +14,8 @@
#include "src/tint/sem/call_target.h"
+#include <utility>
+
#include "src/tint/symbol_table.h"
#include "src/tint/utils/hash.h"
@@ -22,22 +24,23 @@
namespace tint::sem {
CallTarget::CallTarget(const sem::Type* return_type,
- const ParameterList& parameters,
+ utils::VectorRef<const Parameter*> parameters,
EvaluationStage stage)
- : signature_{return_type, parameters}, stage_(stage) {
+ : signature_{return_type, std::move(parameters)}, stage_(stage) {
TINT_ASSERT(Semantic, return_type);
}
CallTarget::CallTarget(const CallTarget&) = default;
CallTarget::~CallTarget() = default;
-CallTargetSignature::CallTargetSignature(const sem::Type* ret_ty, const ParameterList& params)
- : return_type(ret_ty), parameters(params) {}
+CallTargetSignature::CallTargetSignature(const sem::Type* ret_ty,
+ utils::VectorRef<const Parameter*> params)
+ : return_type(ret_ty), parameters(std::move(params)) {}
CallTargetSignature::CallTargetSignature(const CallTargetSignature&) = default;
CallTargetSignature::~CallTargetSignature() = default;
int CallTargetSignature::IndexOf(ParameterUsage usage) const {
- for (size_t i = 0; i < parameters.size(); i++) {
+ for (size_t i = 0; i < parameters.Length(); i++) {
if (parameters[i]->Usage() == usage) {
return static_cast<int>(i);
}
@@ -46,10 +49,10 @@
}
bool CallTargetSignature::operator==(const CallTargetSignature& other) const {
- if (return_type != other.return_type || parameters.size() != other.parameters.size()) {
+ if (return_type != other.return_type || parameters.Length() != other.parameters.Length()) {
return false;
}
- for (size_t i = 0; i < parameters.size(); i++) {
+ for (size_t i = 0; i < parameters.Length(); i++) {
auto* a = parameters[i];
auto* b = other.parameters[i];
if (a->Type() != b->Type() || a->Usage() != b->Usage()) {
@@ -65,7 +68,7 @@
std::size_t hash<tint::sem::CallTargetSignature>::operator()(
const tint::sem::CallTargetSignature& sig) const {
- size_t hash = tint::utils::Hash(sig.parameters.size());
+ size_t hash = tint::utils::Hash(sig.parameters.Length());
for (auto* p : sig.parameters) {
tint::utils::HashCombine(&hash, p->Type(), p->Usage());
}
diff --git a/src/tint/sem/call_target.h b/src/tint/sem/call_target.h
index be1b96a..3f7fec1 100644
--- a/src/tint/sem/call_target.h
+++ b/src/tint/sem/call_target.h
@@ -21,6 +21,7 @@
#include "src/tint/sem/sampler.h"
#include "src/tint/sem/variable.h"
#include "src/tint/utils/hash.h"
+#include "src/tint/utils/vector.h"
// Forward declarations
namespace tint::sem {
@@ -34,7 +35,7 @@
/// Constructor
/// @param ret_ty the call target return type
/// @param params the call target parameters
- CallTargetSignature(const sem::Type* ret_ty, const ParameterList& params);
+ CallTargetSignature(const sem::Type* ret_ty, utils::VectorRef<const Parameter*> params);
/// Copy constructor
CallTargetSignature(const CallTargetSignature&);
@@ -45,7 +46,7 @@
/// The type of the call target return value
const sem::Type* const return_type = nullptr;
/// The parameters of the call target
- const ParameterList parameters;
+ const utils::Vector<const sem::Parameter*, 8> parameters;
/// Equality operator
/// @param other the signature to compare this to
@@ -67,7 +68,7 @@
/// @param return_type the return type of the call target
/// @param parameters the parameters for the call target
CallTarget(const sem::Type* return_type,
- const ParameterList& parameters,
+ utils::VectorRef<const Parameter*> parameters,
EvaluationStage stage);
/// Copy constructor
@@ -80,7 +81,7 @@
const sem::Type* ReturnType() const { return signature_.return_type; }
/// @return the parameters of the call target
- const ParameterList& Parameters() const { return signature_.parameters; }
+ auto& Parameters() const { return signature_.parameters; }
/// @return the signature of the call target
const CallTargetSignature& Signature() const { return signature_; }
diff --git a/src/tint/sem/function.cc b/src/tint/sem/function.cc
index dcc80b0..ff3a2a7 100644
--- a/src/tint/sem/function.cc
+++ b/src/tint/sem/function.cc
@@ -21,22 +21,29 @@
#include "src/tint/sem/sampled_texture.h"
#include "src/tint/sem/storage_texture.h"
#include "src/tint/sem/variable.h"
-#include "src/tint/utils/to_const_ptr_vec.h"
+#include "src/tint/utils/transform.h"
TINT_INSTANTIATE_TYPEINFO(tint::sem::Function);
namespace tint::sem {
+namespace {
+
+utils::VectorRef<const Parameter*> SetOwner(utils::VectorRef<Parameter*> parameters,
+ const tint::sem::CallTarget* owner) {
+ for (auto* parameter : parameters) {
+ parameter->SetOwner(owner);
+ }
+ return parameters;
+}
+
+} // namespace
Function::Function(const ast::Function* declaration,
Type* return_type,
- std::vector<Parameter*> parameters)
- : Base(return_type, utils::ToConstPtrVec(parameters), EvaluationStage::kRuntime),
+ utils::VectorRef<Parameter*> parameters)
+ : Base(return_type, SetOwner(std::move(parameters), this), EvaluationStage::kRuntime),
declaration_(declaration),
- workgroup_size_{WorkgroupDimension{1}, WorkgroupDimension{1}, WorkgroupDimension{1}} {
- for (auto* parameter : parameters) {
- parameter->SetOwner(this);
- }
-}
+ workgroup_size_{WorkgroupDimension{1}, WorkgroupDimension{1}, WorkgroupDimension{1}} {}
Function::~Function() = default;
diff --git a/src/tint/sem/function.h b/src/tint/sem/function.h
index f95920a..c90d749 100644
--- a/src/tint/sem/function.h
+++ b/src/tint/sem/function.h
@@ -22,6 +22,7 @@
#include "src/tint/ast/variable.h"
#include "src/tint/sem/call.h"
#include "src/tint/utils/unique_vector.h"
+#include "src/tint/utils/vector.h"
// Forward declarations
namespace tint::ast {
@@ -62,7 +63,7 @@
/// @param parameters the parameters to the function
Function(const ast::Function* declaration,
Type* return_type,
- std::vector<Parameter*> parameters);
+ utils::VectorRef<Parameter*> parameters);
/// Destructor
~Function() override;
diff --git a/src/tint/sem/member_accessor_expression.cc b/src/tint/sem/member_accessor_expression.cc
index 74b4833..4d194a9 100644
--- a/src/tint/sem/member_accessor_expression.cc
+++ b/src/tint/sem/member_accessor_expression.cc
@@ -61,7 +61,7 @@
const Statement* statement,
const Constant* constant,
const Expression* object,
- std::vector<uint32_t> indices,
+ utils::VectorRef<uint32_t> indices,
bool has_side_effects,
const Variable* source_var /* = nullptr */)
: Base(declaration,
diff --git a/src/tint/sem/member_accessor_expression.h b/src/tint/sem/member_accessor_expression.h
index 43e1466..b8144f1 100644
--- a/src/tint/sem/member_accessor_expression.h
+++ b/src/tint/sem/member_accessor_expression.h
@@ -15,9 +15,8 @@
#ifndef SRC_TINT_SEM_MEMBER_ACCESSOR_EXPRESSION_H_
#define SRC_TINT_SEM_MEMBER_ACCESSOR_EXPRESSION_H_
-#include <vector>
-
#include "src/tint/sem/expression.h"
+#include "src/tint/utils/vector.h"
// Forward declarations
namespace tint::ast {
@@ -113,7 +112,7 @@
const Statement* statement,
const Constant* constant,
const Expression* object,
- std::vector<uint32_t> indices,
+ utils::VectorRef<uint32_t> indices,
bool has_side_effects,
const Variable* source_var = nullptr);
@@ -121,10 +120,10 @@
~Swizzle() override;
/// @return the swizzle indices, if this is a vector swizzle
- const std::vector<uint32_t>& Indices() const { return indices_; }
+ const auto& Indices() const { return indices_; }
private:
- std::vector<uint32_t> const indices_;
+ utils::Vector<uint32_t, 4> const indices_;
};
} // namespace tint::sem
diff --git a/src/tint/sem/type_constructor.cc b/src/tint/sem/type_constructor.cc
index d85e1be..2acf74f 100644
--- a/src/tint/sem/type_constructor.cc
+++ b/src/tint/sem/type_constructor.cc
@@ -14,14 +14,16 @@
#include "src/tint/sem/type_constructor.h"
+#include <utility>
+
TINT_INSTANTIATE_TYPEINFO(tint::sem::TypeConstructor);
namespace tint::sem {
TypeConstructor::TypeConstructor(const sem::Type* type,
- const ParameterList& parameters,
+ utils::VectorRef<const Parameter*> parameters,
EvaluationStage stage)
- : Base(type, parameters, stage) {}
+ : Base(type, std::move(parameters), stage) {}
TypeConstructor::~TypeConstructor() = default;
diff --git a/src/tint/sem/type_constructor.h b/src/tint/sem/type_constructor.h
index 74b7858..1995db8 100644
--- a/src/tint/sem/type_constructor.h
+++ b/src/tint/sem/type_constructor.h
@@ -16,6 +16,7 @@
#define SRC_TINT_SEM_TYPE_CONSTRUCTOR_H_
#include "src/tint/sem/call_target.h"
+#include "src/tint/utils/vector.h"
namespace tint::sem {
@@ -26,7 +27,9 @@
/// @param type the type that's being constructed
/// @param parameters the type constructor parameters
/// @param stage the earliest evaluation stage for the expression
- TypeConstructor(const sem::Type* type, const ParameterList& parameters, EvaluationStage stage);
+ TypeConstructor(const sem::Type* type,
+ utils::VectorRef<const Parameter*> parameters,
+ EvaluationStage stage);
/// Destructor
~TypeConstructor() override;
diff --git a/src/tint/sem/type_conversion.cc b/src/tint/sem/type_conversion.cc
index 262e3a0..42fa2e0 100644
--- a/src/tint/sem/type_conversion.cc
+++ b/src/tint/sem/type_conversion.cc
@@ -21,7 +21,7 @@
TypeConversion::TypeConversion(const sem::Type* type,
const sem::Parameter* parameter,
EvaluationStage stage)
- : Base(type, ParameterList{parameter}, stage) {}
+ : Base(type, utils::Vector<const sem::Parameter*, 1>{parameter}, stage) {}
TypeConversion::~TypeConversion() = default;
diff --git a/src/tint/sem/variable.h b/src/tint/sem/variable.h
index fe54f50..ecc536b 100644
--- a/src/tint/sem/variable.h
+++ b/src/tint/sem/variable.h
@@ -222,9 +222,6 @@
const sem::Node* shadows_ = nullptr;
};
-/// ParameterList is a list of Parameter
-using ParameterList = std::vector<const Parameter*>;
-
/// VariableUser holds the semantic information for an identifier expression
/// node that resolves to a variable.
class VariableUser final : public Castable<VariableUser, Expression> {
diff --git a/src/tint/text/unicode.cc b/src/tint/text/unicode.cc
index 7339297..e23f3dd 100644
--- a/src/tint/text/unicode.cc
+++ b/src/tint/text/unicode.cc
@@ -306,15 +306,26 @@
} // namespace
bool CodePoint::IsXIDStart() const {
- // Short circuit ascii. It will end up being at the end of the binary search
- // but is our, currently, common case.
+ // Short circuit ASCII. The binary search will find these last, but most
+ // of our current source is ASCII, so handle them quicker.
if ((value >= 'a' && value <= 'z') || (value >= 'A' && value <= 'Z')) {
return true;
}
+ // With [a-zA-Z] handled, nothing less then the next sequence start can be
+ // XIDStart, so filter them all out. This catches most of the common symbols
+ // that are used in ASCII.
+ if (value < 0x000aa) {
+ return false;
+ }
return std::binary_search(kXIDStartRanges, kXIDStartRanges + kNumXIDStartRanges, *this);
}
bool CodePoint::IsXIDContinue() const {
+ // Short circuit ASCII. The binary search will find these last, but most
+ // of our current source is ASCII, so handle them quicker.
+ if ((value >= '0' && value <= '9') || value == '_') {
+ return true;
+ }
return IsXIDStart() || std::binary_search(kXIDContinueRanges,
kXIDContinueRanges + kNumXIDContinueRanges, *this);
}
diff --git a/src/tint/transform/canonicalize_entry_point_io.cc b/src/tint/transform/canonicalize_entry_point_io.cc
index 6f84029..a24aa0a 100644
--- a/src/tint/transform/canonicalize_entry_point_io.cc
+++ b/src/tint/transform/canonicalize_entry_point_io.cc
@@ -509,7 +509,7 @@
}
// Exit early if there is no shader IO to handle.
- if (func_sem->Parameters().size() == 0 && func_sem->ReturnType()->Is<sem::Void>() &&
+ if (func_sem->Parameters().Length() == 0 && func_sem->ReturnType()->Is<sem::Void>() &&
!needs_fixed_sample_mask && !needs_vertex_point_size &&
cfg.shader_style != ShaderStyle::kGlsl) {
return;
@@ -517,7 +517,7 @@
// Process the entry point parameters, collecting those that need to be
// aggregated into a single structure.
- if (!func_sem->Parameters().empty()) {
+ if (!func_sem->Parameters().IsEmpty()) {
for (auto* param : func_sem->Parameters()) {
if (param->Type()->Is<sem::Struct>()) {
ProcessStructParameter(param);
diff --git a/src/tint/transform/decompose_memory_access.cc b/src/tint/transform/decompose_memory_access.cc
index a714581..135a42a 100644
--- a/src/tint/transform/decompose_memory_access.cc
+++ b/src/tint/transform/decompose_memory_access.cc
@@ -635,7 +635,7 @@
};
// Other parameters are copied as-is:
- for (size_t i = 1; i < intrinsic->Parameters().size(); i++) {
+ for (size_t i = 1; i < intrinsic->Parameters().Length(); i++) {
auto* param = intrinsic->Parameters()[i];
auto* ty = CreateASTTypeFor(ctx, param->Type());
params.emplace_back(b.Param("param_" + std::to_string(i), ty));
@@ -834,7 +834,7 @@
// X.Y
auto* accessor_sem = sem.Get(accessor);
if (auto* swizzle = accessor_sem->As<sem::Swizzle>()) {
- if (swizzle->Indices().size() == 1) {
+ if (swizzle->Indices().Length() == 1) {
if (auto access = state.TakeAccess(accessor->structure)) {
auto* vec_ty = access.type->As<sem::Vector>();
auto* offset = state.Mul(vec_ty->type()->Size(), swizzle->Indices()[0u]);
diff --git a/src/tint/transform/multiplanar_external_texture.cc b/src/tint/transform/multiplanar_external_texture.cc
index c3aba9b..a554616 100644
--- a/src/tint/transform/multiplanar_external_texture.cc
+++ b/src/tint/transform/multiplanar_external_texture.cc
@@ -187,7 +187,7 @@
auto* call = sem.Get(expr)->UnwrapMaterialize()->As<sem::Call>();
auto* builtin = call->Target()->As<sem::Builtin>();
- if (builtin && !builtin->Parameters().empty() &&
+ if (builtin && !builtin->Parameters().IsEmpty() &&
builtin->Parameters()[0]->Type()->Is<sem::ExternalTexture>() &&
builtin->Type() != sem::BuiltinType::kTextureDimensions) {
if (auto* var_user = sem.Get<sem::VariableUser>(expr->args[0])) {
diff --git a/src/tint/transform/vectorize_scalar_matrix_constructors.cc b/src/tint/transform/vectorize_scalar_matrix_constructors.cc
index e7c76e3..3bba57c 100644
--- a/src/tint/transform/vectorize_scalar_matrix_constructors.cc
+++ b/src/tint/transform/vectorize_scalar_matrix_constructors.cc
@@ -36,7 +36,7 @@
if (auto* call = program->Sem().Get<sem::Call>(node)) {
if (call->Target()->Is<sem::TypeConstructor>() && call->Type()->Is<sem::Matrix>()) {
auto& args = call->Arguments();
- if (args.size() > 0 && args[0]->Type()->UnwrapRef()->is_scalar()) {
+ if (!args.IsEmpty() && args[0]->Type()->UnwrapRef()->is_scalar()) {
return true;
}
}
@@ -61,7 +61,7 @@
}
auto& args = call->Arguments();
- if (args.size() == 0) {
+ if (args.IsEmpty()) {
return nullptr;
}
if (!args[0]->Type()->UnwrapRef()->is_scalar()) {
@@ -85,7 +85,7 @@
return ctx.dst->Construct(CreateASTTypeFor(ctx, mat_type), columns);
};
- if (args.size() == 1) {
+ if (args.Length() == 1) {
// Generate a helper function for constructing the matrix.
// This is done to ensure that the single argument value is only evaluated once, and
// with the correct expression evaluation order.
@@ -109,7 +109,7 @@
return ctx.dst->Call(fn, ctx.Clone(args[0]->Declaration()));
}
- if (args.size() == mat_type->columns() * mat_type->rows()) {
+ if (args.Length() == mat_type->columns() * mat_type->rows()) {
return build_mat([&](uint32_t c, uint32_t r) {
return ctx.Clone(args[c * mat_type->rows() + r]->Declaration());
});
diff --git a/src/tint/utils/hash_test.cc b/src/tint/utils/hash_test.cc
index 9e60a66..6ce6820 100644
--- a/src/tint/utils/hash_test.cc
+++ b/src/tint/utils/hash_test.cc
@@ -44,10 +44,10 @@
}
TEST(HashTests, TintVector) {
- EXPECT_EQ(Hash(Vector<int>({})), Hash(Vector<int>({})));
- EXPECT_EQ(Hash(Vector<int>({1, 2, 3})), Hash(Vector<int>({1, 2, 3})));
- EXPECT_NE(Hash(Vector<int>({1, 2, 3})), Hash(Vector<int>({1, 2, 4})));
- EXPECT_NE(Hash(Vector<int>({1, 2, 3})), Hash(Vector<int>({1, 2, 3, 4})));
+ EXPECT_EQ(Hash(Vector<int, 0>({})), Hash(Vector<int, 0>({})));
+ EXPECT_EQ(Hash(Vector<int, 0>({1, 2, 3})), Hash(Vector<int, 0>({1, 2, 3})));
+ EXPECT_NE(Hash(Vector<int, 0>({1, 2, 3})), Hash(Vector<int, 0>({1, 2, 4})));
+ EXPECT_NE(Hash(Vector<int, 0>({1, 2, 3})), Hash(Vector<int, 0>({1, 2, 3, 4})));
EXPECT_EQ(Hash(Vector<int, 3>({1, 2, 3})), Hash(Vector<int, 4>({1, 2, 3})));
EXPECT_EQ(Hash(Vector<int, 3>({1, 2, 3})), Hash(Vector<int, 2>({1, 2, 3})));
}
diff --git a/src/tint/utils/to_const_ptr_vec.h b/src/tint/utils/to_const_ptr_vec.h
deleted file mode 100644
index 02cc984..0000000
--- a/src/tint/utils/to_const_ptr_vec.h
+++ /dev/null
@@ -1,37 +0,0 @@
-
-// 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_TINT_UTILS_TO_CONST_PTR_VEC_H_
-#define SRC_TINT_UTILS_TO_CONST_PTR_VEC_H_
-
-#include <vector>
-
-namespace tint::utils {
-
-/// @param in a vector of `T*`
-/// @returns a vector of `const T*` with the content of `in`.
-template <typename T>
-std::vector<const T*> ToConstPtrVec(const std::vector<T*>& in) {
- std::vector<const T*> out;
- out.reserve(in.size());
- for (auto* ptr : in) {
- out.emplace_back(ptr);
- }
- return out;
-}
-
-} // namespace tint::utils
-
-#endif // SRC_TINT_UTILS_TO_CONST_PTR_VEC_H_
diff --git a/src/tint/utils/transform.h b/src/tint/utils/transform.h
index 0c91263..412dbed 100644
--- a/src/tint/utils/transform.h
+++ b/src/tint/utils/transform.h
@@ -59,10 +59,12 @@
/// @returns a new vector with each element of the source vector transformed by `transform`.
template <typename IN, size_t N, typename TRANSFORMER>
auto Transform(const Vector<IN, N>& in, TRANSFORMER&& transform)
- -> Vector<decltype(transform(in[0]))> {
- Vector<decltype(transform(in[0])), N> result(in.Length());
- for (size_t i = 0; i < result.Length(); ++i) {
- result[i] = transform(in[i]);
+ -> Vector<decltype(transform(in[0])), N> {
+ const auto count = in.Length();
+ Vector<decltype(transform(in[0])), N> result;
+ result.Reserve(count);
+ for (size_t i = 0; i < count; ++i) {
+ result.Push(transform(in[i]));
}
return result;
}
@@ -74,9 +76,11 @@
template <typename IN, size_t N, typename TRANSFORMER>
auto Transform(const Vector<IN, N>& in, TRANSFORMER&& transform)
-> Vector<decltype(transform(in[0], 1u)), N> {
- Vector<decltype(transform(in[0], 1u)), N> result(in.Length());
- for (size_t i = 0; i < result.Length(); ++i) {
- result[i] = transform(in[i], i);
+ const auto count = in.Length();
+ Vector<decltype(transform(in[0], 1u)), N> result;
+ result.Reserve(count);
+ for (size_t i = 0; i < count; ++i) {
+ result.Push(transform(in[i], i));
}
return result;
}
@@ -89,9 +93,11 @@
template <size_t N, typename IN, typename TRANSFORMER>
auto Transform(const VectorRef<IN>& in, TRANSFORMER&& transform)
-> Vector<decltype(transform(in[0])), N> {
- Vector<decltype(transform(in[0])), N> result(in.Length());
- for (size_t i = 0; i < result.Length(); ++i) {
- result[i] = transform(in[i]);
+ const auto count = in.Length();
+ Vector<decltype(transform(in[0])), N> result;
+ result.Reserve(count);
+ for (size_t i = 0; i < count; ++i) {
+ result.Push(transform(in[i]));
}
return result;
}
@@ -104,9 +110,11 @@
template <size_t N, typename IN, typename TRANSFORMER>
auto Transform(const VectorRef<IN>& in, TRANSFORMER&& transform)
-> Vector<decltype(transform(in[0], 1u)), N> {
- Vector<decltype(transform(in[0], 1u)), N> result(in.Length());
- for (size_t i = 0; i < result.Length(); ++i) {
- result[i] = transform(in[i], i);
+ const auto count = in.Length();
+ Vector<decltype(transform(in[0], 1u)), N> result;
+ result.Reserve(count);
+ for (size_t i = 0; i < count; ++i) {
+ result.Push(transform(in[i], i));
}
return result;
}
@@ -119,9 +127,11 @@
template <size_t N, typename IN, typename TRANSFORMER>
auto Transform(ConstVectorRef<IN> in, TRANSFORMER&& transform)
-> Vector<decltype(transform(in[0])), N> {
- Vector<decltype(transform(in[0])), N> result(in.Length());
- for (size_t i = 0; i < result.Length(); ++i) {
- result[i] = transform(in[i]);
+ const auto count = in.Length();
+ Vector<decltype(transform(in[0])), N> result;
+ result.Reserve(count);
+ for (size_t i = 0; i < count; ++i) {
+ result.Push(transform(in[i]));
}
return result;
}
@@ -134,9 +144,11 @@
template <size_t N, typename IN, typename TRANSFORMER>
auto Transform(ConstVectorRef<IN> in, TRANSFORMER&& transform)
-> Vector<decltype(transform(in[0], 1u)), N> {
- Vector<decltype(transform(in[0], 1u)), N> result(in.Length());
- for (size_t i = 0; i < result.Length(); ++i) {
- result[i] = transform(in[i], i);
+ const auto count = in.Length();
+ Vector<decltype(transform(in[0], 1u)), N> result;
+ result.Reserve(count);
+ for (size_t i = 0; i < count; ++i) {
+ result.Push(transform(in[i], i));
}
return result;
}
diff --git a/src/tint/utils/vector.h b/src/tint/utils/vector.h
index daac42c..d339a49 100644
--- a/src/tint/utils/vector.h
+++ b/src/tint/utils/vector.h
@@ -22,6 +22,8 @@
#include <utility>
#include <vector>
+#include "src/tint/castable.h"
+#include "src/tint/traits.h"
#include "src/tint/utils/bitcast.h"
namespace tint::utils {
@@ -36,8 +38,6 @@
namespace tint::utils {
-namespace detail {
-
/// A slice represents a contigious array of elements of type T.
template <typename T>
struct Slice {
@@ -97,7 +97,42 @@
auto rend() const { return std::reverse_iterator<const T*>(begin()); }
};
-} // namespace detail
+/// Evaluates whether a `vector<FROM>` and be reinterpreted as a `vector<TO>`.
+/// Vectors can be reinterpreted if both `FROM` and `TO` are pointers to a type that derives from
+/// CastableBase, and the pointee type of `TO` is of the same type as, or is an ancestor of the
+/// pointee type of `FROM`. Vectors of non-`const` Castable pointers can be converted to a vector of
+/// `const` Castable pointers.
+template <typename TO, typename FROM>
+static constexpr bool CanReinterpretSlice =
+ // TO and FROM are both pointer types
+ std::is_pointer_v<TO> && std::is_pointer_v<FROM> && //
+ // const can only be applied, not removed
+ (std::is_const_v<std::remove_pointer_t<TO>> ||
+ !std::is_const_v<std::remove_pointer_t<FROM>>)&& //
+ // TO and FROM are both Castable
+ IsCastable<std::remove_pointer_t<FROM>, std::remove_pointer_t<TO>> &&
+ // FROM is of, or derives from TO
+ traits::IsTypeOrDerived<std::remove_pointer_t<FROM>, std::remove_pointer_t<TO>>;
+
+/// Reinterprets `const Slice<FROM>*` as `const Slice<TO>*`
+/// @param slice a pointer to the slice to reinterpret
+/// @returns the reinterpreted slice
+/// @see CanReinterpretSlice
+template <typename TO, typename FROM>
+const Slice<TO>* ReinterpretSlice(const Slice<FROM>* slice) {
+ static_assert(CanReinterpretSlice<TO, FROM>);
+ return Bitcast<const Slice<TO>*>(slice);
+}
+
+/// Reinterprets `Slice<FROM>*` as `Slice<TO>*`
+/// @param slice a pointer to the slice to reinterpret
+/// @returns the reinterpreted slice
+/// @see CanReinterpretSlice
+template <typename TO, typename FROM>
+Slice<TO>* ReinterpretSlice(Slice<FROM>* slice) {
+ static_assert(CanReinterpretSlice<TO, FROM>);
+ return Bitcast<Slice<TO>*>(slice);
+}
/// Vector is a small-object-optimized, dynamically-sized vector of contigious elements of type T.
///
@@ -118,39 +153,20 @@
/// array'. This reduces memory copying, but may incur additional memory usage.
/// * Resizing, or popping elements from a vector that has spilled to a heap allocation does not
/// revert back to using the 'small array'. Again, this is to reduce memory copying.
-template <typename T, size_t N = 0>
+template <typename T, size_t N>
class Vector {
public:
/// Type of `T`.
using value_type = T;
+ /// Value of `N`
+ static constexpr size_t static_length = N;
/// Constructor
Vector() = default;
/// Constructor
- /// @param length the initial length of the vector. Elements will be zero-initialized.
- explicit Vector(size_t length) {
- Reserve(length);
- for (size_t i = 0; i < length; i++) {
- new (&impl_.slice.data[i]) T{};
- }
- impl_.slice.len = length;
- }
-
- /// Constructor
- /// @param length the initial length of the vector
- /// @param value the value to copy into each element of the vector
- Vector(size_t length, const T& value) {
- Reserve(length);
- for (size_t i = 0; i < length; i++) {
- new (&impl_.slice.data[i]) T{value};
- }
- impl_.slice.len = length;
- }
-
- /// Constructor
/// @param elements the elements to place into the vector
- explicit Vector(std::initializer_list<T> elements) {
+ Vector(std::initializer_list<T> elements) {
Reserve(elements.size());
for (auto& el : elements) {
new (&impl_.slice.data[impl_.slice.len++]) T{el};
@@ -179,17 +195,32 @@
MoveOrCopy(VectorRef<T>(std::move(other)));
}
+ /// Copy constructor with covariance / const conversion
+ /// @param other the vector to copy
+ /// @see CanReinterpretSlice for rules about conversion
+ template <typename U, size_t N2, typename = std::enable_if_t<CanReinterpretSlice<T, U>>>
+ Vector(const Vector<U, N2>& other) { // NOLINT(runtime/explicit)
+ Copy(*ReinterpretSlice<T>(&other.impl_.slice));
+ }
+
+ /// Move constructor with covariance / const conversion
+ /// @param other the vector to move
+ /// @see CanReinterpretSlice for rules about conversion
+ template <typename U, size_t N2, typename = std::enable_if_t<CanReinterpretSlice<T, U>>>
+ Vector(Vector<U, N2>&& other) { // NOLINT(runtime/explicit)
+ MoveOrCopy(VectorRef<T>(std::move(other)));
+ }
+
/// Move constructor from a mutable vector reference
/// @param other the vector reference to move
- Vector(VectorRef<T>&& other) { // NOLINT(runtime/explicit)
- MoveOrCopy(std::move(other));
- }
+ explicit Vector(VectorRef<T>&& other) { MoveOrCopy(std::move(other)); }
/// Copy constructor from an immutable vector reference
/// @param other the vector reference to copy
- Vector(const ConstVectorRef<T>& other) { // NOLINT(runtime/explicit)
- Copy(other.slice_);
- }
+ explicit Vector(const ConstVectorRef<T>& other) { Copy(other.slice_); }
+
+ /// Move constructor from an immutable vector reference (invalid)
+ Vector(ConstVectorRef<T>&&) = delete; // NOLINT(runtime/explicit)
/// Destructor
~Vector() { ClearAndFree(); }
@@ -277,6 +308,20 @@
impl_.slice.len = new_len;
}
+ /// Resizes the vector to the given length, expanding capacity if necessary.
+ /// @param new_len the new vector length
+ /// @param value the value to copy into the new elements
+ void Resize(size_t new_len, const T& value) {
+ Reserve(new_len);
+ for (size_t i = impl_.slice.len; i > new_len; i--) { // Shrink
+ impl_.slice.data[i - 1].~T();
+ }
+ for (size_t i = impl_.slice.len; i < new_len; i++) { // Grow
+ new (&impl_.slice.data[i]) T{value};
+ }
+ impl_.slice.len = new_len;
+ }
+
/// Copies all the elements from `other` to this vector, replacing the content of this vector.
/// @param other the
template <typename T2, size_t N2>
@@ -317,7 +362,7 @@
if (impl_.slice.len >= impl_.slice.cap) {
Grow();
}
- new (&impl_.slice.data[impl_.slice.len++]) T(std::forward<ARGS>(args)...);
+ new (&impl_.slice.data[impl_.slice.len++]) T{std::forward<ARGS>(args)...};
}
/// Removes and returns the last element from the vector.
@@ -377,7 +422,12 @@
friend class ConstVectorRef;
/// The slice type used by this vector
- using Slice = detail::Slice<T>;
+ using Slice = utils::Slice<T>;
+
+ template <typename... Ts>
+ void AppendVariadic(Ts&&... args) {
+ ((new (&impl_.slice.data[impl_.slice.len++]) T(std::forward<Ts>(args))), ...);
+ }
/// Expands the capacity of the vector
void Grow() { Reserve(impl_.slice.cap * 2); }
@@ -484,6 +534,43 @@
std::conditional_t<HasSmallArray, ImplWithSmallArray, ImplWithoutSmallArray> impl_;
};
+namespace detail {
+
+/// Helper for determining the Vector element type (`T`) from the vector's constuctor arguments
+/// @tparam IS_CASTABLE true if the types of `Ts` derive from CastableBase
+/// @tparam Ts the vector constructor argument types to infer the vector element type from.
+template <bool IS_CASTABLE, typename... Ts>
+struct VectorCommonType;
+
+/// VectorCommonType specialization for non-castable types.
+template <typename... Ts>
+struct VectorCommonType</*IS_CASTABLE*/ false, Ts...> {
+ /// The common T type to use for the vector
+ using type = std::common_type_t<Ts...>;
+};
+
+/// VectorCommonType specialization for castable types.
+template <typename... Ts>
+struct VectorCommonType</*IS_CASTABLE*/ true, Ts...> {
+ /// The common Castable type (excluding pointer)
+ using common_ty = CastableCommonBase<std::remove_pointer_t<Ts>...>;
+ /// The common T type to use for the vector
+ using type = std::conditional_t<(std::is_const_v<std::remove_pointer_t<Ts>> || ...),
+ const common_ty*,
+ common_ty*>;
+};
+
+} // namespace detail
+
+/// Helper for determining the Vector element type (`T`) from the vector's constuctor arguments
+template <typename... Ts>
+using VectorCommonType =
+ typename detail::VectorCommonType<IsCastable<std::remove_pointer_t<Ts>...>, Ts...>::type;
+
+/// Deduction guide for Vector
+template <typename... Ts>
+Vector(Ts...) -> Vector<VectorCommonType<Ts...>, sizeof...(Ts)>;
+
/// VectorRef is a weak reference to a Vector, used to pass vectors as parameters, avoiding copies
/// between the caller and the callee. VectorRef can accept a Vector of any 'N' value, decoupling
/// the caller's vector internal size from the callee's vector size.
@@ -507,16 +594,16 @@
template <typename T>
class VectorRef {
/// The slice type used by this vector reference
- using Slice = detail::Slice<T>;
+ using Slice = utils::Slice<T>;
public:
- /// Constructor from a Vector.
- /// @param vector the vector reference
+ /// Constructor from a Vector
+ /// @param vector the vector to create a reference of
template <size_t N>
VectorRef(Vector<T, N>& vector) // NOLINT(runtime/explicit)
: slice_(vector.impl_.slice), can_move_(false) {}
- /// Constructor from a std::move()'d Vector
+ /// Constructor from a moved Vector
/// @param vector the vector being moved
template <size_t N>
VectorRef(Vector<T, N>&& vector) // NOLINT(runtime/explicit)
@@ -530,6 +617,32 @@
/// @param other the vector reference
VectorRef(VectorRef&& other) = default;
+ /// Copy constructor with covariance / const conversion
+ /// @param other the other vector reference
+ template <typename U, typename = std::enable_if_t<CanReinterpretSlice<T, U>>>
+ VectorRef(const VectorRef<U>& other) // NOLINT(runtime/explicit)
+ : slice_(*ReinterpretSlice<T>(&other.slice_)), can_move_(false) {}
+
+ /// Move constructor with covariance / const conversion
+ /// @param other the vector reference
+ template <typename U, typename = std::enable_if_t<CanReinterpretSlice<T, U>>>
+ VectorRef(VectorRef<U>&& other) // NOLINT(runtime/explicit)
+ : slice_(*ReinterpretSlice<T>(&other.slice_)), can_move_(other.can_move_) {}
+
+ /// Constructor from a Vector with covariance / const conversion
+ /// @param vector the vector to create a reference of
+ /// @see CanReinterpretSlice for rules about conversion
+ template <typename U, size_t N, typename = std::enable_if_t<CanReinterpretSlice<T, U>>>
+ VectorRef(Vector<U, N>& vector) // NOLINT(runtime/explicit)
+ : slice_(*ReinterpretSlice<T>(&vector.impl_.slice)), can_move_(false) {}
+
+ /// Constructor from a moved Vector with covariance / const conversion
+ /// @param vector the vector to create a reference of
+ /// @see CanReinterpretSlice for rules about conversion
+ template <typename U, size_t N, typename = std::enable_if_t<CanReinterpretSlice<T, U>>>
+ VectorRef(Vector<U, N>&& vector) // NOLINT(runtime/explicit)
+ : slice_(*ReinterpretSlice<T>(&vector.impl_.slice)), can_move_(vector.impl_.CanMove()) {}
+
/// Index operator
/// @param i the element index. Must be less than `len`.
/// @returns a reference to the i'th element.
@@ -587,10 +700,18 @@
auto rend() const { return slice_.rend(); }
private:
- /// Friend classes
+ /// Friend class
template <typename, size_t>
friend class Vector;
+ /// Friend class
+ template <typename>
+ friend class VectorRef;
+
+ /// Friend class
+ template <typename>
+ friend class ConstVectorRef;
+
/// The slice of the vector being referenced.
Slice& slice_;
/// Whether the slice data is passed by r-value reference, and can be moved.
@@ -603,7 +724,7 @@
template <typename T>
class ConstVectorRef {
/// The slice type used by this vector reference
- using Slice = detail::Slice<T>;
+ using Slice = utils::Slice<T>;
public:
/// Constructor from a Vector.
@@ -616,6 +737,34 @@
/// @param other the vector reference
ConstVectorRef(const ConstVectorRef& other) = default;
+ /// Conversion constructor to convert from a non-const to const vector reference
+ /// @param other the vector reference
+ ConstVectorRef(const VectorRef<T>& other) : slice_(other.slice_) {} // NOLINT(runtime/explicit)
+
+ /// Move constructor. Deleted as this won't move anything.
+ ConstVectorRef(ConstVectorRef&&) = delete;
+
+ /// Constructor from a Vector with covariance / const conversion
+ /// @param vector the vector to create a reference of
+ /// @see CanReinterpretSlice for rules about conversion
+ template <typename U, size_t N, typename = std::enable_if_t<CanReinterpretSlice<T, U>>>
+ ConstVectorRef(const Vector<U, N>& vector) // NOLINT(runtime/explicit)
+ : slice_(*ReinterpretSlice<T>(&vector.impl_.slice)) {}
+
+ /// Constructor from a VectorRef with covariance / const conversion
+ /// @param other the vector reference
+ /// @see CanReinterpretSlice for rules about conversion
+ template <typename U, typename = std::enable_if_t<CanReinterpretSlice<T, U>>>
+ ConstVectorRef(const VectorRef<U>& other) // NOLINT(runtime/explicit)
+ : slice_(*ReinterpretSlice<T>(&other.slice_)) {}
+
+ /// Constructor from a ConstVectorRef with covariance / const conversion
+ /// @param other the vector reference
+ /// @see CanReinterpretSlice for rules about conversion
+ template <typename U, typename = std::enable_if_t<CanReinterpretSlice<T, U>>>
+ ConstVectorRef(const ConstVectorRef<U>& other) // NOLINT(runtime/explicit)
+ : slice_(*ReinterpretSlice<T>(&other.slice_)) {}
+
/// Index operator
/// @param i the element index. Must be less than `len`.
/// @returns a reference to the i'th element.
@@ -650,10 +799,14 @@
auto rend() const { return slice_.rend(); }
private:
- /// Friend classes
+ /// Friend class
template <typename, size_t>
friend class Vector;
+ /// Friend class
+ template <typename>
+ friend class ConstVectorRef;
+
/// The slice of the vector being referenced.
const Slice& slice_;
};
@@ -682,14 +835,6 @@
return out;
}
-/// Helper for constructing a Vector from a set of elements.
-/// The returned Vector's small-array size (`N`) is equal to the number of provided elements.
-/// @param elements the elements used to construct the vector.
-template <typename T, typename... Ts>
-auto MakeVector(Ts&&... elements) {
- return Vector<T, sizeof...(Ts)>({std::forward<Ts>(elements)...});
-}
-
} // namespace tint::utils
#endif // SRC_TINT_UTILS_VECTOR_H_
diff --git a/src/tint/utils/vector_test.cc b/src/tint/utils/vector_test.cc
index 8c2c0cf..6e529dc 100644
--- a/src/tint/utils/vector_test.cc
+++ b/src/tint/utils/vector_test.cc
@@ -24,6 +24,11 @@
namespace tint::utils {
namespace {
+class C0 : public Castable<C0> {};
+class C1 : public Castable<C1, C0> {};
+class C2a : public Castable<C2a, C1> {};
+class C2b : public Castable<C2b, C1> {};
+
/// @returns true if the address of el is within the memory of the vector vec.
template <typename T, size_t N, typename E>
bool IsInternal(Vector<T, N>& vec, E& el) {
@@ -54,6 +59,46 @@
return true;
}
+////////////////////////////////////////////////////////////////////////////////
+// Static asserts
+////////////////////////////////////////////////////////////////////////////////
+static_assert(std::is_same_v<VectorCommonType<int>, int>);
+static_assert(std::is_same_v<VectorCommonType<int, int>, int>);
+static_assert(std::is_same_v<VectorCommonType<int, float>, float>);
+
+static_assert(std::is_same_v<VectorCommonType<C0*>, C0*>);
+static_assert(std::is_same_v<VectorCommonType<const C0*>, const C0*>);
+
+static_assert(std::is_same_v<VectorCommonType<C0*, C1*>, C0*>);
+static_assert(std::is_same_v<VectorCommonType<const C0*, C1*>, const C0*>);
+static_assert(std::is_same_v<VectorCommonType<C0*, const C1*>, const C0*>);
+static_assert(std::is_same_v<VectorCommonType<const C0*, const C1*>, const C0*>);
+
+static_assert(std::is_same_v<VectorCommonType<C2a*, C2b*>, C1*>);
+static_assert(std::is_same_v<VectorCommonType<const C2a*, C2b*>, const C1*>);
+static_assert(std::is_same_v<VectorCommonType<C2a*, const C2b*>, const C1*>);
+static_assert(std::is_same_v<VectorCommonType<const C2a*, const C2b*>, const C1*>);
+
+static_assert(CanReinterpretSlice<const C0*, C0*>, "apply const");
+static_assert(!CanReinterpretSlice<C0*, const C0*>, "remove const");
+static_assert(CanReinterpretSlice<C0*, C1*>, "up cast");
+static_assert(CanReinterpretSlice<const C0*, const C1*>, "up cast");
+static_assert(CanReinterpretSlice<const C0*, C1*>, "up cast, apply const");
+static_assert(!CanReinterpretSlice<C0*, const C1*>, "up cast, remove const");
+static_assert(!CanReinterpretSlice<C1*, C0*>, "down cast");
+static_assert(!CanReinterpretSlice<const C1*, const C0*>, "down cast");
+static_assert(!CanReinterpretSlice<const C1*, C0*>, "down cast, apply const");
+static_assert(!CanReinterpretSlice<C1*, const C0*>, "down cast, remove const");
+static_assert(!CanReinterpretSlice<const C1*, C0*>, "down cast, apply const");
+static_assert(!CanReinterpretSlice<C1*, const C0*>, "down cast, remove const");
+static_assert(!CanReinterpretSlice<C2a*, C2b*>, "sideways cast");
+static_assert(!CanReinterpretSlice<const C2a*, const C2b*>, "sideways cast");
+static_assert(!CanReinterpretSlice<const C2a*, C2b*>, "sideways cast, apply const");
+static_assert(!CanReinterpretSlice<C2a*, const C2b*>, "sideways cast, remove const");
+
+////////////////////////////////////////////////////////////////////////////////
+// TintVectorTest
+////////////////////////////////////////////////////////////////////////////////
TEST(TintVectorTest, SmallArray_Empty) {
Vector<int, 2> vec;
EXPECT_EQ(vec.Length(), 0u);
@@ -61,59 +106,12 @@
}
TEST(TintVectorTest, Empty_NoSmallArray) {
- Vector<int> vec;
+ Vector<int, 0> vec;
EXPECT_EQ(vec.Length(), 0u);
EXPECT_EQ(vec.Capacity(), 0u);
}
-TEST(TintVectorTest, SmallArray_ConstructLength_NoSpill) {
- Vector<int, 2> vec(2);
- EXPECT_EQ(vec.Length(), 2u);
- EXPECT_EQ(vec.Capacity(), 2u);
- EXPECT_EQ(vec[0], 0);
- EXPECT_EQ(vec[1], 0);
- EXPECT_TRUE(AllInternallyHeld(vec));
-}
-
-TEST(TintVectorTest, SmallArray_ConstructLength_WithSpill) {
- Vector<int, 2> vec(3);
- EXPECT_EQ(vec.Length(), 3u);
- EXPECT_EQ(vec.Capacity(), 3u);
- EXPECT_EQ(vec[0], 0);
- EXPECT_EQ(vec[1], 0);
- EXPECT_EQ(vec[2], 0);
- EXPECT_TRUE(AllExternallyHeld(vec));
-}
-
-TEST(TintVectorTest, SmallArray_ConstructLengthValue_NoSpill) {
- Vector<std::string, 2> vec(2, "abc");
- EXPECT_EQ(vec.Length(), 2u);
- EXPECT_EQ(vec.Capacity(), 2u);
- EXPECT_EQ(vec[0], "abc");
- EXPECT_EQ(vec[1], "abc");
- EXPECT_TRUE(AllInternallyHeld(vec));
-}
-
-TEST(TintVectorTest, SmallArray_ConstructLengthValue_WithSpill) {
- Vector<std::string, 2> vec(3, "abc");
- EXPECT_EQ(vec.Length(), 3u);
- EXPECT_EQ(vec.Capacity(), 3u);
- EXPECT_EQ(vec[0], "abc");
- EXPECT_EQ(vec[1], "abc");
- EXPECT_EQ(vec[2], "abc");
- EXPECT_TRUE(AllExternallyHeld(vec));
-}
-
-TEST(TintVectorTest, ConstructLength_NoSmallArray) {
- Vector<int> vec(2);
- EXPECT_EQ(vec.Length(), 2u);
- EXPECT_EQ(vec.Capacity(), 2u);
- EXPECT_EQ(vec[0], 0);
- EXPECT_EQ(vec[1], 0);
- EXPECT_TRUE(AllExternallyHeld(vec));
-}
-
-TEST(TintVectorTest, ConstructInitializerList_NoSpill) {
+TEST(TintVectorTest, InitializerList_NoSpill) {
Vector<std::string, 2> vec{"one", "two"};
EXPECT_EQ(vec.Length(), 2u);
EXPECT_EQ(vec.Capacity(), 2u);
@@ -122,7 +120,7 @@
EXPECT_TRUE(AllInternallyHeld(vec));
}
-TEST(TintVectorTest, ConstructInitializerList_WithSpill) {
+TEST(TintVectorTest, InitializerList_WithSpill) {
Vector<std::string, 2> vec{"one", "two", "three"};
EXPECT_EQ(vec.Length(), 3u);
EXPECT_EQ(vec.Capacity(), 3u);
@@ -132,8 +130,8 @@
EXPECT_TRUE(AllExternallyHeld(vec));
}
-TEST(TintVectorTest, ConstructInitializerList_NoSmallArray) {
- Vector<std::string> vec{"one", "two"};
+TEST(TintVectorTest, InitializerList_NoSmallArray) {
+ Vector<std::string, 0> vec{"one", "two"};
EXPECT_EQ(vec.Length(), 2u);
EXPECT_EQ(vec.Capacity(), 2u);
EXPECT_EQ(vec[0], "one");
@@ -141,9 +139,180 @@
EXPECT_TRUE(AllExternallyHeld(vec));
}
-TEST(TintVectorTest, CopyCtor_NoSpill_N2_to_N2) {
+TEST(TintVectorTest, InferTN_1CString) {
+ auto vec = Vector{"one"};
+ static_assert(std::is_same_v<decltype(vec)::value_type, const char*>);
+ static_assert(decltype(vec)::static_length == 1u);
+ EXPECT_EQ(vec.Length(), 1u);
+ EXPECT_EQ(vec.Capacity(), 1u);
+ EXPECT_STREQ(vec[0], "one");
+ EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_2CStrings) {
+ auto vec = Vector{"one", "two"};
+ static_assert(std::is_same_v<decltype(vec)::value_type, const char*>);
+ static_assert(decltype(vec)::static_length == 2u);
+ EXPECT_EQ(vec.Length(), 2u);
+ EXPECT_EQ(vec.Capacity(), 2u);
+ EXPECT_STREQ(vec[0], "one");
+ EXPECT_STREQ(vec[1], "two");
+ EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_IntFloat) {
+ auto vec = Vector{1, 2.0f};
+ static_assert(std::is_same_v<decltype(vec)::value_type, float>);
+ static_assert(decltype(vec)::static_length == 2u);
+ EXPECT_EQ(vec.Length(), 2u);
+ EXPECT_EQ(vec.Capacity(), 2u);
+ EXPECT_EQ(vec[0], 1.0f);
+ EXPECT_EQ(vec[1], 2.0f);
+ EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_IntDoubleIntDouble) {
+ auto vec = Vector{1, 2.0, 3, 4.0};
+ static_assert(std::is_same_v<decltype(vec)::value_type, double>);
+ static_assert(decltype(vec)::static_length == 4u);
+ EXPECT_EQ(vec.Length(), 4u);
+ EXPECT_EQ(vec.Capacity(), 4u);
+ EXPECT_EQ(vec[0], 1.0);
+ EXPECT_EQ(vec[1], 2.0);
+ EXPECT_EQ(vec[2], 3.0);
+ EXPECT_EQ(vec[3], 4.0);
+ EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_C0) {
+ C0 c0;
+ auto vec = Vector{&c0};
+ static_assert(std::is_same_v<decltype(vec)::value_type, C0*>);
+ static_assert(decltype(vec)::static_length == 1u);
+ EXPECT_EQ(vec.Length(), 1u);
+ EXPECT_EQ(vec.Capacity(), 1u);
+ EXPECT_EQ(vec[0], &c0);
+ EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_ConstC0) {
+ const C0 c0;
+ auto vec = Vector{&c0};
+ static_assert(std::is_same_v<decltype(vec)::value_type, const C0*>);
+ static_assert(decltype(vec)::static_length == 1u);
+ EXPECT_EQ(vec.Length(), 1u);
+ EXPECT_EQ(vec.Capacity(), 1u);
+ EXPECT_EQ(vec[0], &c0);
+ EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_C0C1) {
+ C0 c0;
+ C1 c1;
+ auto vec = Vector{&c0, &c1};
+ static_assert(std::is_same_v<decltype(vec)::value_type, C0*>);
+ static_assert(decltype(vec)::static_length == 2u);
+ EXPECT_EQ(vec.Length(), 2u);
+ EXPECT_EQ(vec.Capacity(), 2u);
+ EXPECT_EQ(vec[0], &c0);
+ EXPECT_EQ(vec[1], &c1);
+ EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_ConstC0C1) {
+ const C0 c0;
+ C1 c1;
+ auto vec = Vector{&c0, &c1};
+ static_assert(std::is_same_v<decltype(vec)::value_type, const C0*>);
+ static_assert(decltype(vec)::static_length == 2u);
+ EXPECT_EQ(vec.Length(), 2u);
+ EXPECT_EQ(vec.Capacity(), 2u);
+ EXPECT_EQ(vec[0], &c0);
+ EXPECT_EQ(vec[1], &c1);
+ EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_C0ConstC1) {
+ C0 c0;
+ const C1 c1;
+ auto vec = Vector{&c0, &c1};
+ static_assert(std::is_same_v<decltype(vec)::value_type, const C0*>);
+ static_assert(decltype(vec)::static_length == 2u);
+ EXPECT_EQ(vec.Length(), 2u);
+ EXPECT_EQ(vec.Capacity(), 2u);
+ EXPECT_EQ(vec[0], &c0);
+ EXPECT_EQ(vec[1], &c1);
+ EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_ConstC0ConstC1) {
+ const C0 c0;
+ const C1 c1;
+ auto vec = Vector{&c0, &c1};
+ static_assert(std::is_same_v<decltype(vec)::value_type, const C0*>);
+ static_assert(decltype(vec)::static_length == 2u);
+ EXPECT_EQ(vec.Length(), 2u);
+ EXPECT_EQ(vec.Capacity(), 2u);
+ EXPECT_EQ(vec[0], &c0);
+ EXPECT_EQ(vec[1], &c1);
+ EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_C2aC2b) {
+ C2a c2a;
+ C2b c2b;
+ auto vec = Vector{&c2a, &c2b};
+ static_assert(std::is_same_v<decltype(vec)::value_type, C1*>);
+ static_assert(decltype(vec)::static_length == 2u);
+ EXPECT_EQ(vec.Length(), 2u);
+ EXPECT_EQ(vec.Capacity(), 2u);
+ EXPECT_EQ(vec[0], &c2a);
+ EXPECT_EQ(vec[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_ConstC2aC2b) {
+ const C2a c2a;
+ C2b c2b;
+ auto vec = Vector{&c2a, &c2b};
+ static_assert(std::is_same_v<decltype(vec)::value_type, const C1*>);
+ static_assert(decltype(vec)::static_length == 2u);
+ EXPECT_EQ(vec.Length(), 2u);
+ EXPECT_EQ(vec.Capacity(), 2u);
+ EXPECT_EQ(vec[0], &c2a);
+ EXPECT_EQ(vec[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_C2aConstC2b) {
+ C2a c2a;
+ const C2b c2b;
+ auto vec = Vector{&c2a, &c2b};
+ static_assert(std::is_same_v<decltype(vec)::value_type, const C1*>);
+ static_assert(decltype(vec)::static_length == 2u);
+ EXPECT_EQ(vec.Length(), 2u);
+ EXPECT_EQ(vec.Capacity(), 2u);
+ EXPECT_EQ(vec[0], &c2a);
+ EXPECT_EQ(vec[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_ConstC2aConstC2b) {
+ const C2a c2a;
+ const C2b c2b;
+ auto vec = Vector{&c2a, &c2b};
+ static_assert(std::is_same_v<decltype(vec)::value_type, const C1*>);
+ static_assert(decltype(vec)::static_length == 2u);
+ EXPECT_EQ(vec.Length(), 2u);
+ EXPECT_EQ(vec.Capacity(), 2u);
+ EXPECT_EQ(vec[0], &c2a);
+ EXPECT_EQ(vec[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, CopyVector_NoSpill_N2_to_N2) {
Vector<std::string, 2> vec_a{"hello", "world"};
- Vector<std::string, 2> vec_b{vec_a};
+ Vector<std::string, 2> vec_b(vec_a);
EXPECT_EQ(vec_b.Length(), 2u);
EXPECT_EQ(vec_b.Capacity(), 2u);
EXPECT_EQ(vec_b[0], "hello");
@@ -151,9 +320,9 @@
EXPECT_TRUE(AllInternallyHeld(vec_b));
}
-TEST(TintVectorTest, CopyCtor_WithSpill_N2_to_N2) {
+TEST(TintVectorTest, CopyVector_WithSpill_N2_to_N2) {
Vector<std::string, 2> vec_a{"hello", "world", "spill"};
- Vector<std::string, 2> vec_b{vec_a};
+ Vector<std::string, 2> vec_b(vec_a);
EXPECT_EQ(vec_b.Length(), 3u);
EXPECT_EQ(vec_b.Capacity(), 3u);
EXPECT_EQ(vec_b[0], "hello");
@@ -162,30 +331,9 @@
EXPECT_TRUE(AllExternallyHeld(vec_b));
}
-TEST(TintVectorTest, MoveCtor_NoSpill_N2_to_N2) {
+TEST(TintVectorTest, CopyVector_NoSpill_N2_to_N1) {
Vector<std::string, 2> vec_a{"hello", "world"};
- Vector<std::string, 2> vec_b{std::move(vec_a)};
- EXPECT_EQ(vec_b.Length(), 2u);
- EXPECT_EQ(vec_b.Capacity(), 2u);
- EXPECT_EQ(vec_b[0], "hello");
- EXPECT_EQ(vec_b[1], "world");
- EXPECT_TRUE(AllInternallyHeld(vec_b));
-}
-
-TEST(TintVectorTest, MoveCtor_WithSpill_N2_to_N2) {
- Vector<std::string, 2> vec_a{"hello", "world", "spill"};
- Vector<std::string, 2> vec_b{std::move(vec_a)};
- EXPECT_EQ(vec_b.Length(), 3u);
- EXPECT_EQ(vec_b.Capacity(), 3u);
- EXPECT_EQ(vec_b[0], "hello");
- EXPECT_EQ(vec_b[1], "world");
- EXPECT_EQ(vec_b[2], "spill");
- EXPECT_TRUE(AllExternallyHeld(vec_b));
-}
-
-TEST(TintVectorTest, CopyCtor_NoSpill_N2_to_N1) {
- Vector<std::string, 2> vec_a{"hello", "world"};
- Vector<std::string, 1> vec_b{vec_a};
+ Vector<std::string, 1> vec_b(vec_a);
EXPECT_EQ(vec_b.Length(), 2u);
EXPECT_EQ(vec_b.Capacity(), 2u);
EXPECT_EQ(vec_b[0], "hello");
@@ -193,9 +341,9 @@
EXPECT_TRUE(AllExternallyHeld(vec_b));
}
-TEST(TintVectorTest, CopyCtor_WithSpill_N2_to_N1) {
+TEST(TintVectorTest, CopyVector_WithSpill_N2_to_N1) {
Vector<std::string, 2> vec_a{"hello", "world", "spill"};
- Vector<std::string, 1> vec_b{vec_a};
+ Vector<std::string, 1> vec_b(vec_a);
EXPECT_EQ(vec_b.Length(), 3u);
EXPECT_EQ(vec_b.Capacity(), 3u);
EXPECT_EQ(vec_b[0], "hello");
@@ -204,9 +352,111 @@
EXPECT_TRUE(AllExternallyHeld(vec_b));
}
-TEST(TintVectorTest, MoveCtor_NoSpill_N2_to_N1) {
+TEST(TintVectorTest, CopyVector_NoSpill_N2_to_N3) {
Vector<std::string, 2> vec_a{"hello", "world"};
- Vector<std::string, 1> vec_b{std::move(vec_a)};
+ Vector<std::string, 3> vec_b(vec_a);
+ EXPECT_EQ(vec_b.Length(), 2u);
+ EXPECT_EQ(vec_b.Capacity(), 3u);
+ EXPECT_EQ(vec_b[0], "hello");
+ EXPECT_EQ(vec_b[1], "world");
+ EXPECT_TRUE(AllInternallyHeld(vec_b));
+}
+
+TEST(TintVectorTest, CopyVector_WithSpill_N2_to_N3) {
+ Vector<std::string, 2> vec_a{"hello", "world", "spill"};
+ Vector<std::string, 3> vec_b(vec_a);
+ EXPECT_EQ(vec_b.Length(), 3u);
+ EXPECT_EQ(vec_b.Capacity(), 3u);
+ EXPECT_EQ(vec_b[0], "hello");
+ EXPECT_EQ(vec_b[1], "world");
+ EXPECT_EQ(vec_b[2], "spill");
+ EXPECT_TRUE(AllInternallyHeld(vec_b));
+}
+
+TEST(TintVectorTest, CopyVector_NoMoveUpcast_NoSpill) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 2> vec_a{&c2a, &c2b};
+ Vector<C0*, 2> vec_b(vec_a); // No move
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
+}
+
+TEST(TintVectorTest, CopyVector_NoMoveUpcast_WithSpill) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ Vector<C0*, 2> vec_b(vec_a); // No move
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
+}
+
+TEST(TintVectorTest, CopyVector_NoMoveAddConst_NoSpill) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 2> vec_a{&c2a, &c2b};
+ Vector<const C1*, 2> vec_b(vec_a); // No move
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
+}
+
+TEST(TintVectorTest, CopyVector_NoMoveAddConst_WithSpill) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ Vector<const C1*, 2> vec_b(vec_a); // No move
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
+}
+
+TEST(TintVectorTest, CopyVector_NoMoveUpcastAndAddConst_NoSpill) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 2> vec_a{&c2a, &c2b};
+ Vector<const C0*, 2> vec_b(vec_a); // No move
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
+}
+
+TEST(TintVectorTest, CopyVector_NoMoveUpcastAndAddConst_WithSpill) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ Vector<const C0*, 2> vec_b(vec_a); // No move
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
+}
+
+TEST(TintVectorTest, MoveVector_NoSpill_N2_to_N2) {
+ Vector<std::string, 2> vec_a{"hello", "world"};
+ Vector<std::string, 2> vec_b(std::move(vec_a));
+ EXPECT_EQ(vec_b.Length(), 2u);
+ EXPECT_EQ(vec_b.Capacity(), 2u);
+ EXPECT_EQ(vec_b[0], "hello");
+ EXPECT_EQ(vec_b[1], "world");
+ EXPECT_TRUE(AllInternallyHeld(vec_b));
+}
+
+TEST(TintVectorTest, MoveVector_WithSpill_N2_to_N2) {
+ Vector<std::string, 2> vec_a{"hello", "world", "spill"};
+ Vector<std::string, 2> vec_b(std::move(vec_a));
+ EXPECT_EQ(vec_b.Length(), 3u);
+ EXPECT_EQ(vec_b.Capacity(), 3u);
+ EXPECT_EQ(vec_b[0], "hello");
+ EXPECT_EQ(vec_b[1], "world");
+ EXPECT_EQ(vec_b[2], "spill");
+ EXPECT_TRUE(AllExternallyHeld(vec_b));
+}
+
+TEST(TintVectorTest, MoveVector_NoSpill_N2_to_N1) {
+ Vector<std::string, 2> vec_a{"hello", "world"};
+ Vector<std::string, 1> vec_b(std::move(vec_a));
EXPECT_EQ(vec_b.Length(), 2u);
EXPECT_EQ(vec_b.Capacity(), 2u);
EXPECT_EQ(vec_b[0], "hello");
@@ -214,9 +464,9 @@
EXPECT_TRUE(AllExternallyHeld(vec_b));
}
-TEST(TintVectorTest, MoveCtor_WithSpill_N2_to_N1) {
+TEST(TintVectorTest, MoveVector_WithSpill_N2_to_N1) {
Vector<std::string, 2> vec_a{"hello", "world", "spill"};
- Vector<std::string, 1> vec_b{std::move(vec_a)};
+ Vector<std::string, 1> vec_b(std::move(vec_a));
EXPECT_EQ(vec_b.Length(), 3u);
EXPECT_EQ(vec_b.Capacity(), 3u);
EXPECT_EQ(vec_b[0], "hello");
@@ -225,9 +475,9 @@
EXPECT_TRUE(AllExternallyHeld(vec_b));
}
-TEST(TintVectorTest, CopyCtor_NoSpill_N2_to_N3) {
+TEST(TintVectorTest, MoveVector_NoSpill_N2_to_N3) {
Vector<std::string, 2> vec_a{"hello", "world"};
- Vector<std::string, 3> vec_b{vec_a};
+ Vector<std::string, 3> vec_b(std::move(vec_a));
EXPECT_EQ(vec_b.Length(), 2u);
EXPECT_EQ(vec_b.Capacity(), 3u);
EXPECT_EQ(vec_b[0], "hello");
@@ -235,30 +485,9 @@
EXPECT_TRUE(AllInternallyHeld(vec_b));
}
-TEST(TintVectorTest, CopyCtor_WithSpill_N2_to_N3) {
+TEST(TintVectorTest, MoveVector_WithSpill_N2_to_N3) {
Vector<std::string, 2> vec_a{"hello", "world", "spill"};
- Vector<std::string, 3> vec_b{vec_a};
- EXPECT_EQ(vec_b.Length(), 3u);
- EXPECT_EQ(vec_b.Capacity(), 3u);
- EXPECT_EQ(vec_b[0], "hello");
- EXPECT_EQ(vec_b[1], "world");
- EXPECT_EQ(vec_b[2], "spill");
- EXPECT_TRUE(AllInternallyHeld(vec_b));
-}
-
-TEST(TintVectorTest, MoveCtor_NoSpill_N2_to_N3) {
- Vector<std::string, 2> vec_a{"hello", "world"};
- Vector<std::string, 3> vec_b{std::move(vec_a)};
- EXPECT_EQ(vec_b.Length(), 2u);
- EXPECT_EQ(vec_b.Capacity(), 3u);
- EXPECT_EQ(vec_b[0], "hello");
- EXPECT_EQ(vec_b[1], "world");
- EXPECT_TRUE(AllInternallyHeld(vec_b));
-}
-
-TEST(TintVectorTest, MoveCtor_WithSpill_N2_to_N3) {
- Vector<std::string, 2> vec_a{"hello", "world", "spill"};
- Vector<std::string, 3> vec_b{std::move(vec_a)};
+ Vector<std::string, 3> vec_b(std::move(vec_a));
EXPECT_EQ(vec_b.Length(), 3u);
EXPECT_EQ(vec_b.Capacity(), 3u);
EXPECT_EQ(vec_b[0], "hello");
@@ -267,6 +496,66 @@
EXPECT_TRUE(AllExternallyHeld(vec_b));
}
+TEST(TintVectorTest, MoveVector_Upcast_NoSpill) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 2> vec_a{&c2a, &c2b};
+ Vector<C0*, 2> vec_b(std::move(vec_a)); // Move
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
+}
+
+TEST(TintVectorTest, MoveVector_Upcast_WithSpill) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ Vector<C0*, 2> vec_b(std::move(vec_a)); // Move
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllExternallyHeld(vec_b)); // Moved, not copied
+}
+
+TEST(TintVectorTest, MoveVector_AddConst_NoSpill) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 2> vec_a{&c2a, &c2b};
+ Vector<const C1*, 2> vec_b(std::move(vec_a)); // Move
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
+}
+
+TEST(TintVectorTest, MoveVector_AddConst_WithSpill) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ Vector<const C1*, 2> vec_b(std::move(vec_a)); // Move
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllExternallyHeld(vec_b)); // Moved, not copied
+}
+
+TEST(TintVectorTest, MoveVector_UpcastAndAddConst_NoSpill) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 2> vec_a{&c2a, &c2b};
+ Vector<const C0*, 2> vec_b(std::move(vec_a)); // Move
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
+}
+
+TEST(TintVectorTest, MoveVector_UpcastAndAddConst_WithSpill) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ Vector<const C0*, 2> vec_b(std::move(vec_a)); // Move
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllExternallyHeld(vec_b)); // Moved, not copied
+}
+
TEST(TintVectorTest, CopyAssign_NoSpill_N2_to_N2) {
Vector<std::string, 2> vec_a{"hello", "world"};
Vector<std::string, 2> vec_b;
@@ -290,29 +579,6 @@
EXPECT_TRUE(AllExternallyHeld(vec_b));
}
-TEST(TintVectorTest, MoveAssign_NoSpill_N2_to_N2) {
- Vector<std::string, 2> vec_a{"hello", "world"};
- Vector<std::string, 2> vec_b;
- vec_b = std::move(vec_a);
- EXPECT_EQ(vec_b.Length(), 2u);
- EXPECT_EQ(vec_b.Capacity(), 2u);
- EXPECT_EQ(vec_b[0], "hello");
- EXPECT_EQ(vec_b[1], "world");
- EXPECT_TRUE(AllInternallyHeld(vec_b));
-}
-
-TEST(TintVectorTest, MoveAssign_WithSpill_N2_to_N2) {
- Vector<std::string, 2> vec_a{"hello", "world", "spill"};
- Vector<std::string, 2> vec_b;
- vec_b = std::move(vec_a);
- EXPECT_EQ(vec_b.Length(), 3u);
- EXPECT_EQ(vec_b.Capacity(), 3u);
- EXPECT_EQ(vec_b[0], "hello");
- EXPECT_EQ(vec_b[1], "world");
- EXPECT_EQ(vec_b[2], "spill");
- EXPECT_TRUE(AllExternallyHeld(vec_b));
-}
-
TEST(TintVectorTest, CopyAssign_NoSpill_N2_to_N1) {
Vector<std::string, 2> vec_a{"hello", "world"};
Vector<std::string, 1> vec_b;
@@ -336,29 +602,6 @@
EXPECT_TRUE(AllExternallyHeld(vec_b));
}
-TEST(TintVectorTest, MoveAssign_NoSpill_N2_to_N1) {
- Vector<std::string, 2> vec_a{"hello", "world"};
- Vector<std::string, 1> vec_b;
- vec_b = std::move(vec_a);
- EXPECT_EQ(vec_b.Length(), 2u);
- EXPECT_EQ(vec_b.Capacity(), 2u);
- EXPECT_EQ(vec_b[0], "hello");
- EXPECT_EQ(vec_b[1], "world");
- EXPECT_TRUE(AllExternallyHeld(vec_b));
-}
-
-TEST(TintVectorTest, MoveAssign_SpillSpill_N2_to_N1) {
- Vector<std::string, 2> vec_a{"hello", "world", "spill"};
- Vector<std::string, 1> vec_b;
- vec_b = std::move(vec_a);
- EXPECT_EQ(vec_b.Length(), 3u);
- EXPECT_EQ(vec_b.Capacity(), 3u);
- EXPECT_EQ(vec_b[0], "hello");
- EXPECT_EQ(vec_b[1], "world");
- EXPECT_EQ(vec_b[2], "spill");
- EXPECT_TRUE(AllExternallyHeld(vec_b));
-}
-
TEST(TintVectorTest, CopyAssign_NoSpill_N2_to_N3) {
Vector<std::string, 2> vec_a{"hello", "world"};
Vector<std::string, 3> vec_b;
@@ -382,32 +625,9 @@
EXPECT_TRUE(AllInternallyHeld(vec_b));
}
-TEST(TintVectorTest, MoveAssign_NoSpill_N2_to_N3) {
- Vector<std::string, 2> vec_a{"hello", "world"};
- Vector<std::string, 3> vec_b;
- vec_b = std::move(vec_a);
- EXPECT_EQ(vec_b.Length(), 2u);
- EXPECT_EQ(vec_b.Capacity(), 3u);
- EXPECT_EQ(vec_b[0], "hello");
- EXPECT_EQ(vec_b[1], "world");
- EXPECT_TRUE(AllInternallyHeld(vec_b));
-}
-
-TEST(TintVectorTest, MoveAssign_WithSpill_N2_to_N3) {
- Vector<std::string, 2> vec_a{"hello", "world", "spill"};
- Vector<std::string, 3> vec_b;
- vec_b = std::move(vec_a);
- EXPECT_EQ(vec_b.Length(), 3u);
- EXPECT_EQ(vec_b.Capacity(), 3u);
- EXPECT_EQ(vec_b[0], "hello");
- EXPECT_EQ(vec_b[1], "world");
- EXPECT_EQ(vec_b[2], "spill");
- EXPECT_TRUE(AllExternallyHeld(vec_b));
-}
-
TEST(TintVectorTest, CopyAssign_NoSpill_N2_to_N0) {
Vector<std::string, 2> vec_a{"hello", "world"};
- Vector<std::string> vec_b;
+ Vector<std::string, 0> vec_b;
vec_b = vec_a;
EXPECT_EQ(vec_b.Length(), 2u);
EXPECT_EQ(vec_b.Capacity(), 2u);
@@ -418,7 +638,7 @@
TEST(TintVectorTest, CopyAssign_WithSpill_N2_to_N0) {
Vector<std::string, 2> vec_a{"hello", "world", "spill"};
- Vector<std::string> vec_b;
+ Vector<std::string, 0> vec_b;
vec_b = vec_a;
EXPECT_EQ(vec_b.Length(), 3u);
EXPECT_EQ(vec_b.Capacity(), 3u);
@@ -428,29 +648,6 @@
EXPECT_TRUE(AllExternallyHeld(vec_b));
}
-TEST(TintVectorTest, MoveAssign_NoSpill_N2_to_N0) {
- Vector<std::string, 2> vec_a{"hello", "world"};
- Vector<std::string> vec_b;
- vec_b = std::move(vec_a);
- EXPECT_EQ(vec_b.Length(), 2u);
- EXPECT_EQ(vec_b.Capacity(), 2u);
- EXPECT_EQ(vec_b[0], "hello");
- EXPECT_EQ(vec_b[1], "world");
- EXPECT_TRUE(AllExternallyHeld(vec_b));
-}
-
-TEST(TintVectorTest, MoveAssign_WithSpill_N2_to_N0) {
- Vector<std::string, 2> vec_a{"hello", "world", "spill"};
- Vector<std::string> vec_b;
- vec_b = std::move(vec_a);
- EXPECT_EQ(vec_b.Length(), 3u);
- EXPECT_EQ(vec_b.Capacity(), 3u);
- EXPECT_EQ(vec_b[0], "hello");
- EXPECT_EQ(vec_b[1], "world");
- EXPECT_EQ(vec_b[2], "spill");
- EXPECT_TRUE(AllExternallyHeld(vec_b));
-}
-
TEST(TintVectorTest, CopyAssign_Self_NoSpill) {
Vector<std::string, 2> vec{"hello", "world"};
auto* vec_ptr = &vec; // Used to avoid -Wself-assign-overloaded
@@ -473,6 +670,98 @@
EXPECT_TRUE(AllExternallyHeld(vec));
}
+TEST(TintVectorTest, MoveAssign_NoSpill_N2_to_N2) {
+ Vector<std::string, 2> vec_a{"hello", "world"};
+ Vector<std::string, 2> vec_b;
+ vec_b = std::move(vec_a);
+ EXPECT_EQ(vec_b.Length(), 2u);
+ EXPECT_EQ(vec_b.Capacity(), 2u);
+ EXPECT_EQ(vec_b[0], "hello");
+ EXPECT_EQ(vec_b[1], "world");
+ EXPECT_TRUE(AllInternallyHeld(vec_b));
+}
+
+TEST(TintVectorTest, MoveAssign_WithSpill_N2_to_N2) {
+ Vector<std::string, 2> vec_a{"hello", "world", "spill"};
+ Vector<std::string, 2> vec_b;
+ vec_b = std::move(vec_a);
+ EXPECT_EQ(vec_b.Length(), 3u);
+ EXPECT_EQ(vec_b.Capacity(), 3u);
+ EXPECT_EQ(vec_b[0], "hello");
+ EXPECT_EQ(vec_b[1], "world");
+ EXPECT_EQ(vec_b[2], "spill");
+ EXPECT_TRUE(AllExternallyHeld(vec_b));
+}
+
+TEST(TintVectorTest, MoveAssign_NoSpill_N2_to_N1) {
+ Vector<std::string, 2> vec_a{"hello", "world"};
+ Vector<std::string, 1> vec_b;
+ vec_b = std::move(vec_a);
+ EXPECT_EQ(vec_b.Length(), 2u);
+ EXPECT_EQ(vec_b.Capacity(), 2u);
+ EXPECT_EQ(vec_b[0], "hello");
+ EXPECT_EQ(vec_b[1], "world");
+ EXPECT_TRUE(AllExternallyHeld(vec_b));
+}
+
+TEST(TintVectorTest, MoveAssign_SpillSpill_N2_to_N1) {
+ Vector<std::string, 2> vec_a{"hello", "world", "spill"};
+ Vector<std::string, 1> vec_b;
+ vec_b = std::move(vec_a);
+ EXPECT_EQ(vec_b.Length(), 3u);
+ EXPECT_EQ(vec_b.Capacity(), 3u);
+ EXPECT_EQ(vec_b[0], "hello");
+ EXPECT_EQ(vec_b[1], "world");
+ EXPECT_EQ(vec_b[2], "spill");
+ EXPECT_TRUE(AllExternallyHeld(vec_b));
+}
+
+TEST(TintVectorTest, MoveAssign_NoSpill_N2_to_N3) {
+ Vector<std::string, 2> vec_a{"hello", "world"};
+ Vector<std::string, 3> vec_b;
+ vec_b = std::move(vec_a);
+ EXPECT_EQ(vec_b.Length(), 2u);
+ EXPECT_EQ(vec_b.Capacity(), 3u);
+ EXPECT_EQ(vec_b[0], "hello");
+ EXPECT_EQ(vec_b[1], "world");
+ EXPECT_TRUE(AllInternallyHeld(vec_b));
+}
+
+TEST(TintVectorTest, MoveAssign_WithSpill_N2_to_N3) {
+ Vector<std::string, 2> vec_a{"hello", "world", "spill"};
+ Vector<std::string, 3> vec_b;
+ vec_b = std::move(vec_a);
+ EXPECT_EQ(vec_b.Length(), 3u);
+ EXPECT_EQ(vec_b.Capacity(), 3u);
+ EXPECT_EQ(vec_b[0], "hello");
+ EXPECT_EQ(vec_b[1], "world");
+ EXPECT_EQ(vec_b[2], "spill");
+ EXPECT_TRUE(AllExternallyHeld(vec_b));
+}
+
+TEST(TintVectorTest, MoveAssign_NoSpill_N2_to_N0) {
+ Vector<std::string, 2> vec_a{"hello", "world"};
+ Vector<std::string, 0> vec_b;
+ vec_b = std::move(vec_a);
+ EXPECT_EQ(vec_b.Length(), 2u);
+ EXPECT_EQ(vec_b.Capacity(), 2u);
+ EXPECT_EQ(vec_b[0], "hello");
+ EXPECT_EQ(vec_b[1], "world");
+ EXPECT_TRUE(AllExternallyHeld(vec_b));
+}
+
+TEST(TintVectorTest, MoveAssign_WithSpill_N2_to_N0) {
+ Vector<std::string, 2> vec_a{"hello", "world", "spill"};
+ Vector<std::string, 0> vec_b;
+ vec_b = std::move(vec_a);
+ EXPECT_EQ(vec_b.Length(), 3u);
+ EXPECT_EQ(vec_b.Capacity(), 3u);
+ EXPECT_EQ(vec_b[0], "hello");
+ EXPECT_EQ(vec_b[1], "world");
+ EXPECT_EQ(vec_b[2], "spill");
+ EXPECT_TRUE(AllExternallyHeld(vec_b));
+}
+
TEST(TintVectorTest, MoveAssign_Self_NoSpill) {
Vector<std::string, 2> vec{"hello", "world"};
auto* vec_ptr = &vec; // Used to avoid -Wself-move
@@ -541,7 +830,7 @@
EXPECT_EQ(vec[1], "world");
}
-TEST(TintVectorTest, SmallArray_Reserve_NoSpill) {
+TEST(TintVectorTest, Reserve_NoSpill) {
Vector<std::string, 2> vec;
EXPECT_EQ(vec.Length(), 0u);
EXPECT_EQ(vec.Capacity(), 2u);
@@ -562,7 +851,7 @@
EXPECT_TRUE(AllInternallyHeld(vec));
}
-TEST(TintVectorTest, SmallArray_Reserve_WithSpill) {
+TEST(TintVectorTest, Reserve_WithSpill) {
Vector<std::string, 1> vec;
EXPECT_EQ(vec.Length(), 0u);
EXPECT_EQ(vec.Capacity(), 1u);
@@ -584,7 +873,7 @@
EXPECT_TRUE(AllExternallyHeld(vec));
}
-TEST(TintVectorTest, SmallArray_Resize_NoSpill) {
+TEST(TintVectorTest, ResizeZero_NoSpill) {
Vector<std::string, 2> vec;
EXPECT_EQ(vec.Length(), 0u);
EXPECT_EQ(vec.Capacity(), 2u);
@@ -614,7 +903,7 @@
EXPECT_TRUE(AllInternallyHeld(vec));
}
-TEST(TintVectorTest, SmallArray_Resize_WithSpill) {
+TEST(TintVectorTest, ResizeZero_WithSpill) {
Vector<std::string, 1> vec;
EXPECT_EQ(vec.Length(), 0u);
EXPECT_EQ(vec.Capacity(), 1u);
@@ -644,8 +933,68 @@
EXPECT_TRUE(AllExternallyHeld(vec));
}
+TEST(TintVectorTest, ResizeValue_NoSpill) {
+ Vector<std::string, 2> vec;
+ EXPECT_EQ(vec.Length(), 0u);
+ EXPECT_EQ(vec.Capacity(), 2u);
+ vec.Resize(1, "meow");
+ EXPECT_EQ(vec.Length(), 1u);
+ EXPECT_EQ(vec.Capacity(), 2u);
+ EXPECT_EQ(vec[0], "meow");
+ EXPECT_TRUE(AllInternallyHeld(vec));
+ vec[0] = "hello";
+ vec.Resize(2, "woof");
+ EXPECT_EQ(vec.Length(), 2u);
+ EXPECT_EQ(vec.Capacity(), 2u);
+ EXPECT_EQ(vec[0], "hello");
+ EXPECT_EQ(vec[1], "woof");
+ EXPECT_TRUE(AllInternallyHeld(vec));
+ vec[1] = "world";
+ vec.Resize(1, "quack");
+ EXPECT_EQ(vec.Length(), 1u);
+ EXPECT_EQ(vec.Capacity(), 2u);
+ EXPECT_EQ(vec[0], "hello");
+ EXPECT_TRUE(AllInternallyHeld(vec));
+ vec.Resize(2, "hiss");
+ EXPECT_EQ(vec.Length(), 2u);
+ EXPECT_EQ(vec.Capacity(), 2u);
+ EXPECT_EQ(vec[0], "hello");
+ EXPECT_EQ(vec[1], "hiss");
+ EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, ResizeValue_WithSpill) {
+ Vector<std::string, 1> vec;
+ EXPECT_EQ(vec.Length(), 0u);
+ EXPECT_EQ(vec.Capacity(), 1u);
+ vec.Resize(1, "meow");
+ EXPECT_EQ(vec.Length(), 1u);
+ EXPECT_EQ(vec.Capacity(), 1u);
+ EXPECT_EQ(vec[0], "meow");
+ EXPECT_TRUE(AllInternallyHeld(vec));
+ vec[0] = "hello";
+ vec.Resize(2, "woof");
+ EXPECT_EQ(vec.Length(), 2u);
+ EXPECT_EQ(vec.Capacity(), 2u);
+ EXPECT_EQ(vec[0], "hello");
+ EXPECT_EQ(vec[1], "woof");
+ EXPECT_TRUE(AllExternallyHeld(vec));
+ vec[1] = "world";
+ vec.Resize(1, "quack");
+ EXPECT_EQ(vec.Length(), 1u);
+ EXPECT_EQ(vec.Capacity(), 2u);
+ EXPECT_EQ(vec[0], "hello");
+ EXPECT_TRUE(AllExternallyHeld(vec));
+ vec.Resize(2, "hiss");
+ EXPECT_EQ(vec.Length(), 2u);
+ EXPECT_EQ(vec.Capacity(), 2u);
+ EXPECT_EQ(vec[0], "hello");
+ EXPECT_EQ(vec[1], "hiss");
+ EXPECT_TRUE(AllExternallyHeld(vec));
+}
+
TEST(TintVectorTest, Reserve_NoSmallArray) {
- Vector<std::string> vec;
+ Vector<std::string, 0> vec;
EXPECT_EQ(vec.Length(), 0u);
EXPECT_EQ(vec.Capacity(), 0u);
vec.Reserve(1);
@@ -667,7 +1016,7 @@
}
TEST(TintVectorTest, Resize_NoSmallArray) {
- Vector<std::string> vec;
+ Vector<std::string, 0> vec;
EXPECT_EQ(vec.Length(), 0u);
EXPECT_EQ(vec.Capacity(), 0u);
vec.Resize(1);
@@ -925,60 +1274,67 @@
}
TEST(TintVectorTest, PushPop_StringNoSpill) {
+ const std::string hello = "hello";
+ const std::string world = "world";
+
Vector<std::string, 2> vec;
EXPECT_EQ(vec.Length(), 0u);
EXPECT_TRUE(AllInternallyHeld(vec));
- vec.Push("hello");
+ vec.Push(hello);
EXPECT_EQ(vec.Length(), 1u);
EXPECT_TRUE(AllInternallyHeld(vec));
- vec.Push("world");
+ vec.Push(world);
EXPECT_EQ(vec.Length(), 2u);
EXPECT_TRUE(AllInternallyHeld(vec));
- EXPECT_EQ(vec.Pop(), "world");
+ EXPECT_EQ(vec.Pop(), world);
EXPECT_EQ(vec.Length(), 1u);
EXPECT_TRUE(AllInternallyHeld(vec));
- EXPECT_EQ(vec.Pop(), "hello");
+ EXPECT_EQ(vec.Pop(), hello);
EXPECT_EQ(vec.Length(), 0u);
EXPECT_TRUE(AllInternallyHeld(vec));
}
TEST(TintVectorTest, PushPop_StringWithSpill) {
+ const std::string hello = "hello";
+ const std::string world = "world";
+
Vector<std::string, 1> vec;
EXPECT_EQ(vec.Length(), 0u);
EXPECT_TRUE(AllInternallyHeld(vec));
- vec.Push("hello");
+ vec.Push(hello);
EXPECT_EQ(vec.Length(), 1u);
EXPECT_TRUE(AllInternallyHeld(vec));
- vec.Push("world");
+ vec.Push(world);
EXPECT_EQ(vec.Length(), 2u);
EXPECT_TRUE(AllExternallyHeld(vec));
- EXPECT_EQ(vec.Pop(), "world");
+ EXPECT_EQ(vec.Pop(), world);
EXPECT_EQ(vec.Length(), 1u);
EXPECT_TRUE(AllExternallyHeld(vec));
- EXPECT_EQ(vec.Pop(), "hello");
+ EXPECT_EQ(vec.Pop(), hello);
EXPECT_EQ(vec.Length(), 0u);
EXPECT_TRUE(AllExternallyHeld(vec));
}
TEST(TintVectorTest, PushPop_StringMoveNoSpill) {
+ std::string hello = "hello";
+ std::string world = "world";
+
Vector<std::string, 2> vec;
EXPECT_EQ(vec.Length(), 0u);
EXPECT_TRUE(AllInternallyHeld(vec));
- std::string hello = "hello";
vec.Push(std::move(hello));
EXPECT_EQ(vec.Length(), 1u);
EXPECT_TRUE(AllInternallyHeld(vec));
- std::string world = "world";
vec.Push(std::move(world));
EXPECT_EQ(vec.Length(), 2u);
EXPECT_TRUE(AllInternallyHeld(vec));
@@ -993,15 +1349,18 @@
}
TEST(TintVectorTest, PushPop_StringMoveWithSpill) {
+ std::string hello = "hello";
+ std::string world = "world";
+
Vector<std::string, 1> vec;
EXPECT_EQ(vec.Length(), 0u);
EXPECT_TRUE(AllInternallyHeld(vec));
- vec.Push("hello");
+ vec.Push(std::move(hello));
EXPECT_EQ(vec.Length(), 1u);
EXPECT_TRUE(AllInternallyHeld(vec));
- vec.Push("world");
+ vec.Push(std::move(world));
EXPECT_EQ(vec.Length(), 2u);
EXPECT_TRUE(AllExternallyHeld(vec));
@@ -1131,42 +1490,195 @@
EXPECT_EQ(vec.end(), &vec[0] + 3);
}
-TEST(TintVectorRefTest, CtorVectorNoMove) {
- Vector<std::string, 1> vec_a{"one", "two"};
- VectorRef<std::string> vec_ref(vec_a); // No move
- Vector<std::string, 2> vec_b(std::move(vec_ref));
- EXPECT_EQ(vec_b[0], "one");
- EXPECT_EQ(vec_b[1], "two");
- EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copy, no move
-}
-
-TEST(TintVectorRefTest, CtorVectorMove) {
- Vector<std::string, 1> vec_a{"one", "two"};
- VectorRef<std::string> vec_ref(std::move(vec_a)); // Move
- Vector<std::string, 2> vec_b(std::move(vec_ref));
- EXPECT_EQ(vec_b[0], "one");
- EXPECT_EQ(vec_b[1], "two");
- EXPECT_TRUE(AllExternallyHeld(vec_b)); // Move, no copy
-}
-
-TEST(TintVectorRefTest, CopyCtor) {
+////////////////////////////////////////////////////////////////////////////////
+// TintVectorRefTest
+////////////////////////////////////////////////////////////////////////////////
+TEST(TintVectorRefTest, CopyVectorRef) {
Vector<std::string, 1> vec_a{"one", "two"};
VectorRef<std::string> vec_ref_a(std::move(vec_a));
VectorRef<std::string> vec_ref_b(vec_ref_a); // No move
Vector<std::string, 2> vec_b(std::move(vec_ref_b));
EXPECT_EQ(vec_b[0], "one");
EXPECT_EQ(vec_b[1], "two");
- EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copy, no move
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
}
-TEST(TintVectorRefTest, MoveCtor) {
+TEST(TintVectorRefTest, CopyVectorRef_Upcast) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ VectorRef<C1*> vec_ref_a(std::move(vec_a));
+ VectorRef<C0*> vec_ref_b(vec_ref_a); // No-move. Up-cast
+ Vector<C0*, 2> vec_b(std::move(vec_ref_b));
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
+}
+
+TEST(TintVectorRefTest, CopyVectorRef_AddConst) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ VectorRef<C1*> vec_ref_a(std::move(vec_a));
+ VectorRef<const C1*> vec_ref_b(vec_ref_a); // No-move. Up-cast
+ Vector<const C1*, 2> vec_b(std::move(vec_ref_b));
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
+}
+
+TEST(TintVectorRefTest, CopyVectorRef_UpcastAndAddConst) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ VectorRef<C1*> vec_ref_a(std::move(vec_a));
+ VectorRef<const C0*> vec_ref_b(vec_ref_a); // No-move. Up-cast
+ Vector<const C0*, 2> vec_b(std::move(vec_ref_b));
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
+}
+
+TEST(TintVectorRefTest, MoveVectorRef) {
Vector<std::string, 1> vec_a{"one", "two"};
VectorRef<std::string> vec_ref_a(std::move(vec_a)); // Move
VectorRef<std::string> vec_ref_b(std::move(vec_ref_a));
Vector<std::string, 2> vec_b(std::move(vec_ref_b));
EXPECT_EQ(vec_b[0], "one");
EXPECT_EQ(vec_b[1], "two");
- EXPECT_TRUE(AllExternallyHeld(vec_b)); // Move, no copy
+ EXPECT_TRUE(AllExternallyHeld(vec_b)); // Moved, not copied
+}
+
+TEST(TintVectorRefTest, MoveVectorRef_Upcast) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ VectorRef<C1*> vec_ref_a(std::move(vec_a));
+ VectorRef<C0*> vec_ref_b(std::move(vec_ref_a)); // Moved. Up-cast
+ Vector<C0*, 2> vec_b(std::move(vec_ref_b));
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllExternallyHeld(vec_b)); // Moved, not copied
+}
+
+TEST(TintVectorRefTest, MoveVectorRef_AddConst) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ VectorRef<C1*> vec_ref_a(std::move(vec_a));
+ VectorRef<const C1*> vec_ref_b(std::move(vec_ref_a)); // Moved. Up-cast
+ Vector<const C1*, 2> vec_b(std::move(vec_ref_b));
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllExternallyHeld(vec_b)); // Moved, not copied
+}
+
+TEST(TintVectorRefTest, MoveVectorRef_UpcastAndAddConst) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ VectorRef<C1*> vec_ref_a(std::move(vec_a));
+ VectorRef<const C0*> vec_ref_b(std::move(vec_ref_a)); // Moved. Up-cast
+ Vector<const C0*, 2> vec_b(std::move(vec_ref_b));
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllExternallyHeld(vec_b)); // Moved, not copied
+}
+
+TEST(TintVectorRefTest, CopyVector) {
+ Vector<std::string, 1> vec_a{"one", "two"};
+ VectorRef<std::string> vec_ref(vec_a); // No move
+ Vector<std::string, 2> vec_b(std::move(vec_ref));
+ EXPECT_EQ(vec_b[0], "one");
+ EXPECT_EQ(vec_b[1], "two");
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
+}
+
+TEST(TintVectorRefTest, CopyVector_Upcast) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ VectorRef<C0*> vec_ref(vec_a); // No move
+ EXPECT_EQ(vec_ref[0], &c2a);
+ EXPECT_EQ(vec_ref[1], &c2b);
+ Vector<C0*, 2> vec_b(std::move(vec_ref));
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
+}
+
+TEST(TintVectorRefTest, CopyVector_AddConst) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ VectorRef<const C1*> vec_ref(vec_a); // No move
+ EXPECT_EQ(vec_ref[0], &c2a);
+ EXPECT_EQ(vec_ref[1], &c2b);
+ Vector<const C1*, 2> vec_b(std::move(vec_ref));
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
+}
+
+TEST(TintVectorRefTest, CopyVector_UpcastAndAddConst) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ VectorRef<const C0*> vec_ref(vec_a); // No move
+ EXPECT_EQ(vec_ref[0], &c2a);
+ EXPECT_EQ(vec_ref[1], &c2b);
+ Vector<const C0*, 2> vec_b(std::move(vec_ref));
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
+}
+
+TEST(TintVectorRefTest, MoveVector) {
+ Vector<std::string, 1> vec_a{"one", "two"};
+ VectorRef<std::string> vec_ref(std::move(vec_a)); // Move
+ Vector<std::string, 2> vec_b(std::move(vec_ref));
+ EXPECT_EQ(vec_b[0], "one");
+ EXPECT_EQ(vec_b[1], "two");
+ EXPECT_TRUE(AllExternallyHeld(vec_b)); // Moved, not copied
+}
+
+TEST(TintVectorRefTest, MoveVector_Upcast) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ VectorRef<C0*> vec_ref(std::move(vec_a)); // Move
+ EXPECT_EQ(vec_ref[0], &c2a);
+ EXPECT_EQ(vec_ref[1], &c2b);
+ Vector<C0*, 2> vec_b(std::move(vec_ref));
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllExternallyHeld(vec_b)); // Moved, not copied
+}
+
+TEST(TintVectorRefTest, MoveVector_AddConst) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ VectorRef<const C1*> vec_ref(std::move(vec_a)); // Move
+ EXPECT_EQ(vec_ref[0], &c2a);
+ EXPECT_EQ(vec_ref[1], &c2b);
+ Vector<const C1*, 2> vec_b(std::move(vec_ref));
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllExternallyHeld(vec_b)); // Moved, not copied
+}
+
+TEST(TintVectorRefTest, MoveVector_UpcastAndAddConst) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ VectorRef<const C0*> vec_ref(std::move(vec_a)); // Move
+ EXPECT_EQ(vec_ref[0], &c2a);
+ EXPECT_EQ(vec_ref[1], &c2b);
+ Vector<const C0*, 2> vec_b(std::move(vec_ref));
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllExternallyHeld(vec_b)); // Moved, not copied
}
TEST(TintVectorRefTest, Index) {
@@ -1243,42 +1755,130 @@
EXPECT_EQ(vec_ref.end(), &vec[0] + 3);
}
-TEST(TintVectorConstRefTest, CtorVectorNoMove) {
+////////////////////////////////////////////////////////////////////////////////
+// TintVectorConstRefTest
+////////////////////////////////////////////////////////////////////////////////
+TEST(TintVectorConstRefTest, CopyVectorConstRef) {
Vector<std::string, 1> vec_a{"one", "two"};
- ConstVectorRef<std::string> vec_ref(vec_a); // No move
- Vector<std::string, 2> vec_b(std::move(vec_ref));
+ ConstVectorRef<std::string> vec_ref_a(vec_a);
+ ConstVectorRef<std::string> vec_ref_b(vec_ref_a);
+ Vector<std::string, 2> vec_b(vec_ref_b);
EXPECT_EQ(vec_b[0], "one");
EXPECT_EQ(vec_b[1], "two");
- EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copy, no move
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
}
-TEST(TintVectorConstRefTest, CtorVectorMove) {
- Vector<std::string, 1> vec_a{"one", "two"};
- ConstVectorRef<std::string> vec_ref(std::move(vec_a)); // Move
- Vector<std::string, 2> vec_b(std::move(vec_ref));
- EXPECT_EQ(vec_b[0], "one");
- EXPECT_EQ(vec_b[1], "two");
- EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copy, no move
+TEST(TintVectorConstRefTest, CopyVectorConstRef_Upcast) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ ConstVectorRef<C1*> vec_ref_a(vec_a);
+ ConstVectorRef<C0*> vec_ref_b(vec_ref_a); // Up-cast
+ Vector<C0*, 2> vec_b(vec_ref_b);
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
}
-TEST(TintVectorConstRefTest, CopyCtor) {
- Vector<std::string, 1> vec_a{"one", "two"};
- ConstVectorRef<std::string> vec_ref_a(std::move(vec_a));
- ConstVectorRef<std::string> vec_ref_b(vec_ref_a); // No move
- Vector<std::string, 2> vec_b(std::move(vec_ref_b));
- EXPECT_EQ(vec_b[0], "one");
- EXPECT_EQ(vec_b[1], "two");
- EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copy, no move
+TEST(TintVectorConstRefTest, CopyVectorConstRef_AddConst) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ ConstVectorRef<C1*> vec_ref_a(vec_a);
+ ConstVectorRef<const C1*> vec_ref_b(vec_ref_a); // Up-cast
+ Vector<const C1*, 2> vec_b(vec_ref_b);
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
}
-TEST(TintVectorConstRefTest, MoveCtor) {
+TEST(TintVectorConstRefTest, CopyVectorConstRef_UpcastAndAddConst) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ ConstVectorRef<C1*> vec_ref_a(vec_a);
+ ConstVectorRef<const C0*> vec_ref_b(vec_ref_a); // Up-cast
+ Vector<const C0*, 2> vec_b(vec_ref_b);
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
+}
+
+TEST(TintVectorConstRefTest, CopyVector) {
Vector<std::string, 1> vec_a{"one", "two"};
- ConstVectorRef<std::string> vec_ref_a(std::move(vec_a)); // Move
- ConstVectorRef<std::string> vec_ref_b(std::move(vec_ref_a));
- Vector<std::string, 2> vec_b(std::move(vec_ref_b));
+ ConstVectorRef<std::string> vec_ref(vec_a);
+ Vector<std::string, 2> vec_b(vec_ref);
EXPECT_EQ(vec_b[0], "one");
EXPECT_EQ(vec_b[1], "two");
- EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copy, no move
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
+}
+
+TEST(TintVectorConstRefTest, CopyVector_Upcast) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ ConstVectorRef<C0*> vec_ref(vec_a);
+ EXPECT_EQ(vec_ref[0], &c2a);
+ EXPECT_EQ(vec_ref[1], &c2b);
+ Vector<C0*, 2> vec_b(vec_ref);
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
+}
+
+TEST(TintVectorConstRefTest, CopyVector_AddConst) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ ConstVectorRef<const C1*> vec_ref(vec_a);
+ EXPECT_EQ(vec_ref[0], &c2a);
+ EXPECT_EQ(vec_ref[1], &c2b);
+ Vector<const C1*, 2> vec_b(vec_ref);
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
+}
+
+TEST(TintVectorConstRefTest, CopyVectorRef_Upcast) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ VectorRef<C1*> vec_ref_a(vec_a);
+ ConstVectorRef<C0*> vec_ref_b(vec_ref_a);
+ EXPECT_EQ(vec_ref_b[0], &c2a);
+ EXPECT_EQ(vec_ref_b[1], &c2b);
+ Vector<C0*, 2> vec_b(vec_ref_b);
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
+}
+
+TEST(TintVectorConstRefTest, CopyVectorRef_AddConst) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ VectorRef<C1*> vec_ref_a(vec_a);
+ ConstVectorRef<const C1*> vec_ref_b(vec_ref_a);
+ EXPECT_EQ(vec_ref_b[0], &c2a);
+ EXPECT_EQ(vec_ref_b[1], &c2b);
+ Vector<const C1*, 2> vec_b(vec_ref_b);
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
+}
+
+TEST(TintVectorConstRefTest, CopyVectorRef_UpcastAndAddConst) {
+ C2a c2a;
+ C2b c2b;
+ Vector<C1*, 1> vec_a{&c2a, &c2b};
+ VectorRef<C1*> vec_ref_a(vec_a);
+ ConstVectorRef<const C0*> vec_ref_b(vec_ref_a);
+ EXPECT_EQ(vec_ref_b[0], &c2a);
+ EXPECT_EQ(vec_ref_b[1], &c2b);
+ Vector<const C0*, 2> vec_b(vec_ref_b);
+ EXPECT_EQ(vec_b[0], &c2a);
+ EXPECT_EQ(vec_b[1], &c2b);
+ EXPECT_TRUE(AllInternallyHeld(vec_b)); // Copied, not moved
}
TEST(TintVectorConstRefTest, Index) {
@@ -1357,3 +1957,8 @@
} // namespace
} // namespace tint::utils
+
+TINT_INSTANTIATE_TYPEINFO(tint::utils::C0);
+TINT_INSTANTIATE_TYPEINFO(tint::utils::C1);
+TINT_INSTANTIATE_TYPEINFO(tint::utils::C2a);
+TINT_INSTANTIATE_TYPEINFO(tint::utils::C2b);
diff --git a/src/tint/writer/append_vector.cc b/src/tint/writer/append_vector.cc
index 28845d9..d6eb017 100644
--- a/src/tint/writer/append_vector.cc
+++ b/src/tint/writer/append_vector.cc
@@ -112,23 +112,23 @@
// to convert a vector of a different type, e.g. vec2<i32>(vec2<u32>()).
// In that case, preserve the original argument, or you'll get a type error.
- std::vector<const sem::Expression*> packed;
+ utils::Vector<const sem::Expression*, 4> packed;
if (auto vc = AsVectorConstructor(vector_sem)) {
- const auto num_supplied = vc.call->Arguments().size();
+ const auto num_supplied = vc.call->Arguments().Length();
if (num_supplied == 0) {
// Zero-value vector constructor. Populate with zeros
for (uint32_t i = 0; i < packed_size - 1; i++) {
auto* zero = Zero(*b, packed_el_sem_ty, statement);
- packed.emplace_back(zero);
+ packed.Push(zero);
}
} else if (num_supplied + 1 == packed_size) {
// All vector components were supplied as scalars. Pass them through.
packed = vc.call->Arguments();
}
}
- if (packed.empty()) {
+ if (packed.IsEmpty()) {
// The special cases didn't occur. Use the vector argument as-is.
- packed.emplace_back(vector_sem);
+ packed.Push(vector_sem);
}
if (packed_el_sem_ty != scalar_sem->Type()->UnwrapRef()) {
@@ -141,17 +141,18 @@
sem::EvaluationStage::kRuntime);
auto* scalar_cast_sem = b->create<sem::Call>(
scalar_cast_ast, scalar_cast_target, sem::EvaluationStage::kRuntime,
- std::vector<const sem::Expression*>{scalar_sem}, statement,
+ utils::Vector<const sem::Expression*, 1>{scalar_sem}, statement,
/* constant_value */ nullptr, /* has_side_effects */ false);
b->Sem().Add(scalar_cast_ast, scalar_cast_sem);
- packed.emplace_back(scalar_cast_sem);
+ packed.Push(scalar_cast_sem);
} else {
- packed.emplace_back(scalar_sem);
+ packed.Push(scalar_sem);
}
auto* constructor_ast = b->Construct(
- packed_ast_ty,
- utils::Transform(packed, [&](const sem::Expression* expr) { return expr->Declaration(); }));
+ packed_ast_ty, utils::ToStdVector(utils::Transform(packed, [&](const sem::Expression* expr) {
+ return expr->Declaration();
+ })));
auto* constructor_target = b->create<sem::TypeConstructor>(
packed_sem_ty,
utils::Transform(packed,
@@ -163,7 +164,8 @@
sem::EvaluationStage::kRuntime);
auto* constructor_sem =
b->create<sem::Call>(constructor_ast, constructor_target, sem::EvaluationStage::kRuntime,
- packed, statement, /* constant_value */ nullptr,
+ std::move(packed), statement,
+ /* constant_value */ nullptr,
/* has_side_effects */ false);
b->Sem().Add(constructor_ast, constructor_sem);
return constructor_sem;
diff --git a/src/tint/writer/append_vector_test.cc b/src/tint/writer/append_vector_test.cc
index 2231003..b4ab0c7 100644
--- a/src/tint/writer/append_vector_test.cc
+++ b/src/tint/writer/append_vector_test.cc
@@ -48,7 +48,7 @@
auto* call = Sem().Get<sem::Call>(vec_123);
ASSERT_NE(call, nullptr);
- ASSERT_EQ(call->Arguments().size(), 3u);
+ ASSERT_EQ(call->Arguments().Length(), 3u);
EXPECT_EQ(call->Arguments()[0], Sem().Get(scalar_1));
EXPECT_EQ(call->Arguments()[1], Sem().Get(scalar_2));
EXPECT_EQ(call->Arguments()[2], Sem().Get(scalar_3));
@@ -60,7 +60,7 @@
EXPECT_TRUE(ctor->ReturnType()->As<sem::Vector>()->type()->Is<sem::I32>());
EXPECT_EQ(ctor->ReturnType(), call->Type());
- ASSERT_EQ(ctor->Parameters().size(), 3u);
+ ASSERT_EQ(ctor->Parameters().Length(), 3u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::I32>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::I32>());
EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::I32>());
@@ -92,7 +92,7 @@
auto* call = Sem().Get<sem::Call>(vec_123);
ASSERT_NE(call, nullptr);
- ASSERT_EQ(call->Arguments().size(), 3u);
+ ASSERT_EQ(call->Arguments().Length(), 3u);
EXPECT_EQ(call->Arguments()[0], Sem().Get(scalar_1));
EXPECT_EQ(call->Arguments()[1], Sem().Get(scalar_2));
EXPECT_EQ(call->Arguments()[2], Sem().Get(u32_to_i32));
@@ -104,7 +104,7 @@
EXPECT_TRUE(ctor->ReturnType()->As<sem::Vector>()->type()->Is<sem::I32>());
EXPECT_EQ(ctor->ReturnType(), call->Type());
- ASSERT_EQ(ctor->Parameters().size(), 3u);
+ ASSERT_EQ(ctor->Parameters().Length(), 3u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::I32>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::I32>());
EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::I32>());
@@ -144,7 +144,7 @@
auto* call = Sem().Get<sem::Call>(vec_123);
ASSERT_NE(call, nullptr);
- ASSERT_EQ(call->Arguments().size(), 2u);
+ ASSERT_EQ(call->Arguments().Length(), 2u);
EXPECT_EQ(call->Arguments()[0], Sem().Get(vec_12));
EXPECT_EQ(call->Arguments()[1], Sem().Get(u32_to_i32));
@@ -155,7 +155,7 @@
EXPECT_TRUE(ctor->ReturnType()->As<sem::Vector>()->type()->Is<sem::I32>());
EXPECT_EQ(ctor->ReturnType(), call->Type());
- ASSERT_EQ(ctor->Parameters().size(), 2u);
+ ASSERT_EQ(ctor->Parameters().Length(), 2u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Vector>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::I32>());
}
@@ -186,7 +186,7 @@
auto* call = Sem().Get<sem::Call>(vec_123);
ASSERT_NE(call, nullptr);
- ASSERT_EQ(call->Arguments().size(), 3u);
+ ASSERT_EQ(call->Arguments().Length(), 3u);
EXPECT_EQ(call->Arguments()[0], Sem().Get(scalar_1));
EXPECT_EQ(call->Arguments()[1], Sem().Get(scalar_2));
EXPECT_EQ(call->Arguments()[2], Sem().Get(f32_to_i32));
@@ -198,7 +198,7 @@
EXPECT_TRUE(ctor->ReturnType()->As<sem::Vector>()->type()->Is<sem::I32>());
EXPECT_EQ(ctor->ReturnType(), call->Type());
- ASSERT_EQ(ctor->Parameters().size(), 3u);
+ ASSERT_EQ(ctor->Parameters().Length(), 3u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::I32>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::I32>());
EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::I32>());
@@ -228,7 +228,7 @@
auto* call = Sem().Get<sem::Call>(vec_1234);
ASSERT_NE(call, nullptr);
- ASSERT_EQ(call->Arguments().size(), 4u);
+ ASSERT_EQ(call->Arguments().Length(), 4u);
EXPECT_EQ(call->Arguments()[0], Sem().Get(scalar_1));
EXPECT_EQ(call->Arguments()[1], Sem().Get(scalar_2));
EXPECT_EQ(call->Arguments()[2], Sem().Get(scalar_3));
@@ -241,7 +241,7 @@
EXPECT_TRUE(ctor->ReturnType()->As<sem::Vector>()->type()->Is<sem::I32>());
EXPECT_EQ(ctor->ReturnType(), call->Type());
- ASSERT_EQ(ctor->Parameters().size(), 4u);
+ ASSERT_EQ(ctor->Parameters().Length(), 4u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::I32>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::I32>());
EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::I32>());
@@ -268,7 +268,7 @@
auto* call = Sem().Get<sem::Call>(vec_123);
ASSERT_NE(call, nullptr);
- ASSERT_EQ(call->Arguments().size(), 2u);
+ ASSERT_EQ(call->Arguments().Length(), 2u);
EXPECT_EQ(call->Arguments()[0], Sem().Get(vec_12));
EXPECT_EQ(call->Arguments()[1], Sem().Get(scalar_3));
@@ -279,7 +279,7 @@
EXPECT_TRUE(ctor->ReturnType()->As<sem::Vector>()->type()->Is<sem::I32>());
EXPECT_EQ(ctor->ReturnType(), call->Type());
- ASSERT_EQ(ctor->Parameters().size(), 2u);
+ ASSERT_EQ(ctor->Parameters().Length(), 2u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Vector>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::I32>());
}
@@ -307,7 +307,7 @@
auto* call = Sem().Get<sem::Call>(vec_123);
ASSERT_NE(call, nullptr);
- ASSERT_EQ(call->Arguments().size(), 3u);
+ ASSERT_EQ(call->Arguments().Length(), 3u);
EXPECT_EQ(call->Arguments()[0], Sem().Get(scalar_1));
EXPECT_EQ(call->Arguments()[1], Sem().Get(scalar_2));
EXPECT_EQ(call->Arguments()[2], Sem().Get(scalar_3));
@@ -319,7 +319,7 @@
EXPECT_TRUE(ctor->ReturnType()->As<sem::Vector>()->type()->Is<sem::I32>());
EXPECT_EQ(ctor->ReturnType(), call->Type());
- ASSERT_EQ(ctor->Parameters().size(), 3u);
+ ASSERT_EQ(ctor->Parameters().Length(), 3u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::I32>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::I32>());
EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::I32>());
@@ -346,7 +346,7 @@
auto* call = Sem().Get<sem::Call>(vec_123);
ASSERT_NE(call, nullptr);
- ASSERT_EQ(call->Arguments().size(), 2u);
+ ASSERT_EQ(call->Arguments().Length(), 2u);
EXPECT_EQ(call->Arguments()[0], Sem().Get(vec_12));
EXPECT_EQ(call->Arguments()[1], Sem().Get(scalar_3));
@@ -357,7 +357,7 @@
EXPECT_TRUE(ctor->ReturnType()->As<sem::Vector>()->type()->Is<sem::I32>());
EXPECT_EQ(ctor->ReturnType(), call->Type());
- ASSERT_EQ(ctor->Parameters().size(), 2u);
+ ASSERT_EQ(ctor->Parameters().Length(), 2u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Vector>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::I32>());
}
@@ -387,7 +387,7 @@
auto* call = Sem().Get<sem::Call>(vec_123);
ASSERT_NE(call, nullptr);
- ASSERT_EQ(call->Arguments().size(), 2u);
+ ASSERT_EQ(call->Arguments().Length(), 2u);
EXPECT_EQ(call->Arguments()[0], Sem().Get(vec_12));
EXPECT_EQ(call->Arguments()[1], Sem().Get(f32_to_i32));
@@ -398,7 +398,7 @@
EXPECT_TRUE(ctor->ReturnType()->As<sem::Vector>()->type()->Is<sem::I32>());
EXPECT_EQ(ctor->ReturnType(), call->Type());
- ASSERT_EQ(ctor->Parameters().size(), 2u);
+ ASSERT_EQ(ctor->Parameters().Length(), 2u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Vector>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::I32>());
}
@@ -424,7 +424,7 @@
auto* call = Sem().Get<sem::Call>(vec_123);
ASSERT_NE(call, nullptr);
- ASSERT_EQ(call->Arguments().size(), 2u);
+ ASSERT_EQ(call->Arguments().Length(), 2u);
EXPECT_EQ(call->Arguments()[0], Sem().Get(vec_12));
EXPECT_EQ(call->Arguments()[1], Sem().Get(scalar_3));
@@ -435,7 +435,7 @@
EXPECT_TRUE(ctor->ReturnType()->As<sem::Vector>()->type()->Is<sem::Bool>());
EXPECT_EQ(ctor->ReturnType(), call->Type());
- ASSERT_EQ(ctor->Parameters().size(), 2u);
+ ASSERT_EQ(ctor->Parameters().Length(), 2u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Vector>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::Bool>());
}
@@ -463,7 +463,7 @@
auto* call = Sem().Get<sem::Call>(vec_0004);
ASSERT_NE(call, nullptr);
- ASSERT_EQ(call->Arguments().size(), 4u);
+ ASSERT_EQ(call->Arguments().Length(), 4u);
EXPECT_EQ(call->Arguments()[0], Sem().Get(vec_0004->args[0]));
EXPECT_EQ(call->Arguments()[1], Sem().Get(vec_0004->args[1]));
EXPECT_EQ(call->Arguments()[2], Sem().Get(vec_0004->args[2]));
@@ -476,7 +476,7 @@
EXPECT_TRUE(ctor->ReturnType()->As<sem::Vector>()->type()->Is<sem::I32>());
EXPECT_EQ(ctor->ReturnType(), call->Type());
- ASSERT_EQ(ctor->Parameters().size(), 4u);
+ ASSERT_EQ(ctor->Parameters().Length(), 4u);
EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::I32>());
EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::I32>());
EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::I32>());
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index e9c0c4a..9abe6ce 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -861,7 +861,7 @@
// If the type constructor is empty then we need to construct with the zero
// value for all components.
- if (call->Arguments().empty()) {
+ if (call->Arguments().IsEmpty()) {
return EmitZeroValue(out, type);
}
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index 4516a98..a9fddc3 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -1120,13 +1120,13 @@
// If the type constructor is empty then we need to construct with the zero
// value for all components.
- if (call->Arguments().empty()) {
+ if (call->Arguments().IsEmpty()) {
return EmitZeroValue(out, type);
}
// Single parameter matrix initializers must be identity constructor.
// It could also be conversions between f16 and f32 matrix when f16 is properly supported.
- if (type->Is<sem::Matrix>() && call->Arguments().size() == 1) {
+ if (type->Is<sem::Matrix>() && call->Arguments().Length() == 1) {
if (!ctor->Parameters()[0]->Type()->UnwrapRef()->is_float_matrix()) {
TINT_UNREACHABLE(Writer, diagnostics_)
<< "found a single-parameter matrix constructor that is not identity constructor";
@@ -1139,7 +1139,7 @@
// For single-value vector initializers, swizzle the scalar to the right
// vector dimension using .x
const bool is_single_value_vector_init = type->is_scalar_vector() &&
- call->Arguments().size() == 1 &&
+ call->Arguments().Length() == 1 &&
ctor->Parameters()[0]->Type()->is_scalar();
if (brackets) {
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index 68151e1..626a11d 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -2304,7 +2304,7 @@
// For multi-element swizzles, we need to cast to a regular vector type
// first. Note that we do not currently allow assignments to swizzles, so
// the casting which will convert the l-value to r-value is fine.
- if (swizzle->Indices().size() == 1) {
+ if (swizzle->Indices().Length() == 1) {
if (!write_lhs()) {
return false;
}
diff --git a/src/tint/writer/spirv/builder.cc b/src/tint/writer/spirv/builder.cc
index 31ed539..934520a 100644
--- a/src/tint/writer/spirv/builder.cc
+++ b/src/tint/writer/spirv/builder.cc
@@ -1023,7 +1023,7 @@
if (auto* swizzle = expr_sem->As<sem::Swizzle>()) {
// Single element swizzle is either an access chain or a composite extract
auto& indices = swizzle->Indices();
- if (indices.size() == 1) {
+ if (indices.Length() == 1) {
if (info->source_type->Is<sem::Reference>()) {
auto idx_id = GenerateConstantIfNeeded(ScalarConstant::U32(indices[0]));
if (idx_id == 0) {
@@ -1338,7 +1338,7 @@
auto* result_type = call->Type();
// Generate the zero initializer if there are no values provided.
- if (args.empty()) {
+ if (args.IsEmpty()) {
if (global_var && global_var->Declaration()->Is<ast::Override>()) {
auto constant_id = global_var->ConstantId();
if (result_type->Is<sem::I32>()) {
@@ -1491,7 +1491,7 @@
// For a single-value vector initializer, splat the initializer value.
auto* const init_result_type = call->Type()->UnwrapRef();
- if (args.size() == 1 && init_result_type->is_scalar_vector() &&
+ if (args.Length() == 1 && init_result_type->is_scalar_vector() &&
args[0]->Type()->UnwrapRef()->is_scalar()) {
size_t vec_size = init_result_type->As<sem::Vector>()->Width();
for (size_t i = 0; i < (vec_size - 1); ++i) {
@@ -2667,7 +2667,7 @@
return 0;
}
- for (size_t i = 0; i < call->Arguments().size(); i++) {
+ for (size_t i = 0; i < call->Arguments().Length(); i++) {
if (auto val_id = get_arg_as_value_id(i)) {
params.emplace_back(Operand(val_id));
} else {
@@ -3172,8 +3172,8 @@
}
uint32_t value_id = 0;
- if (call->Arguments().size() > 1) {
- value_id = GenerateExpressionWithLoadIfNeeded(call->Arguments().back());
+ if (call->Arguments().Length() > 1) {
+ value_id = GenerateExpressionWithLoadIfNeeded(call->Arguments().Back());
if (value_id == 0) {
return false;
}