wgsl parser: use new TypesBuilder factory functions, and set Source for ast::Type nodes
* ProgramBuilder: added a bunch of overloads that take Source
* Added MultiTokenSource RAII helper to build source ranges for
multi-token types
* Added comparison operators to Source::Range and Source::Location to
make it easier to write tests to compare Source ranges
* Moved CombineSourceRange from resolver.cc to a static function in
Source named Source::Combine()
* Added Source tests for all ast type nodes returned by the wgsl parser
Bug: tint:724
Change-Id: I6fb6211a3c42c14693df8746af6a30f5aa56f2af
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/48963
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/program_builder.h b/src/program_builder.h
index 1b5da81..94f410c 100644
--- a/src/program_builder.h
+++ b/src/program_builder.h
@@ -28,6 +28,7 @@
#include "src/ast/call_expression.h"
#include "src/ast/case_statement.h"
#include "src/ast/depth_texture.h"
+#include "src/ast/external_texture.h"
#include "src/ast/f32.h"
#include "src/ast/float_literal.h"
#include "src/ast/i32.h"
@@ -62,6 +63,7 @@
#include "src/sem/array_type.h"
#include "src/sem/bool_type.h"
#include "src/sem/depth_texture_type.h"
+#include "src/sem/external_texture_type.h"
#include "src/sem/f32_type.h"
#include "src/sem/i32_type.h"
#include "src/sem/matrix_type.h"
@@ -352,26 +354,56 @@
return {builder->create<ast::Bool>(), builder->create<sem::Bool>()};
}
+ /// @param source the Source of the node
+ /// @returns a boolean type
+ typ::Bool bool_(const Source& source) const {
+ return {builder->create<ast::Bool>(source), builder->create<sem::Bool>()};
+ }
+
/// @returns a f32 type
typ::F32 f32() const {
return {builder->create<ast::F32>(), builder->create<sem::F32>()};
}
+ /// @param source the Source of the node
+ /// @returns a f32 type
+ typ::F32 f32(const Source& source) const {
+ return {builder->create<ast::F32>(source), builder->create<sem::F32>()};
+ }
+
/// @returns a i32 type
typ::I32 i32() const {
return {builder->create<ast::I32>(), builder->create<sem::I32>()};
}
+ /// @param source the Source of the node
+ /// @returns a i32 type
+ typ::I32 i32(const Source& source) const {
+ return {builder->create<ast::I32>(source), builder->create<sem::I32>()};
+ }
+
/// @returns a u32 type
typ::U32 u32() const {
return {builder->create<ast::U32>(), builder->create<sem::U32>()};
}
+ /// @param source the Source of the node
+ /// @returns a u32 type
+ typ::U32 u32(const Source& source) const {
+ return {builder->create<ast::U32>(source), builder->create<sem::U32>()};
+ }
+
/// @returns a void type
typ::Void void_() const {
return {builder->create<ast::Void>(), builder->create<sem::Void>()};
}
+ /// @param source the Source of the node
+ /// @returns a void type
+ typ::Void void_(const Source& source) const {
+ return {builder->create<ast::Void>(source), builder->create<sem::Void>()};
+ }
+
/// @param type vector subtype
/// @param n vector width in elements
/// @return the tint AST type for a `n`-element vector of `type`.
@@ -380,6 +412,15 @@
builder->create<sem::Vector>(type, n)};
}
+ /// @param source the Source of the node
+ /// @param type vector subtype
+ /// @param n vector width in elements
+ /// @return the tint AST type for a `n`-element vector of `type`.
+ typ::Vector vec(const Source& source, typ::Type type, uint32_t n) const {
+ return {builder->create<ast::Vector>(source, type, n),
+ builder->create<sem::Vector>(type, n)};
+ }
+
/// @param type vector subtype
/// @return the tint AST type for a 2-element vector of `type`.
typ::Vector vec2(typ::Type type) const { return vec(type, 2u); }
@@ -426,6 +467,19 @@
builder->create<sem::Matrix>(type, rows, columns)};
}
+ /// @param source the Source of the node
+ /// @param type matrix subtype
+ /// @param columns number of columns for the matrix
+ /// @param rows number of rows for the matrix
+ /// @return the tint AST type for a matrix of `type`
+ typ::Matrix mat(const Source& source,
+ typ::Type type,
+ uint32_t columns,
+ uint32_t rows) const {
+ return {builder->create<ast::Matrix>(source, type, rows, columns),
+ builder->create<sem::Matrix>(type, rows, columns)};
+ }
+
/// @param type matrix subtype
/// @return the tint AST type for a 2x3 matrix of `type`.
typ::Matrix mat2x2(typ::Type type) const {
@@ -560,7 +614,21 @@
ast::DecorationList decos = {}) const {
subtype = MaybeCreateTypename(subtype);
return {builder->create<ast::Array>(subtype, n, decos),
- builder->create<sem::ArrayType>(subtype, n, decos)};
+ builder->create<sem::ArrayType>(subtype, n, std::move(decos))};
+ }
+
+ /// @param source the Source of the node
+ /// @param subtype the array element type
+ /// @param n the array size. 0 represents a runtime-array
+ /// @param decos the optional decorations for the array
+ /// @return the tint AST type for a array of size `n` of type `T`
+ typ::Array array(const Source& source,
+ typ::Type subtype,
+ uint32_t n = 0,
+ ast::DecorationList decos = {}) const {
+ subtype = MaybeCreateTypename(subtype);
+ return {builder->create<ast::Array>(source, subtype, n, decos),
+ builder->create<sem::ArrayType>(subtype, n, std::move(decos))};
}
/// @param subtype the array element type
@@ -600,6 +668,21 @@
};
}
+ /// Creates an alias type
+ /// @param source the Source of the node
+ /// @param name the alias name
+ /// @param type the alias type
+ /// @returns the alias pointer
+ template <typename NAME>
+ typ::Alias alias(const Source& source, NAME&& name, typ::Type type) const {
+ type = MaybeCreateTypename(type);
+ auto sym = builder->Sym(std::forward<NAME>(name));
+ return {
+ builder->create<ast::Alias>(source, sym, type),
+ builder->create<sem::Alias>(sym, type),
+ };
+ }
+
/// Creates an access control qualifier type
/// @param access the access control
/// @param type the inner type
@@ -611,6 +694,19 @@
builder->create<sem::AccessControl>(access, type)};
}
+ /// Creates an access control qualifier type
+ /// @param source the Source of the node
+ /// @param access the access control
+ /// @param type the inner type
+ /// @returns the access control qualifier type
+ typ::AccessControl access(const Source& source,
+ ast::AccessControl::Access access,
+ typ::Type type) const {
+ type = MaybeCreateTypename(type);
+ return {builder->create<ast::AccessControl>(source, access, type),
+ builder->create<sem::AccessControl>(access, type)};
+ }
+
/// @param type the type of the pointer
/// @param storage_class the storage class of the pointer
/// @return the pointer to `type` with the given ast::StorageClass
@@ -621,6 +717,18 @@
builder->create<sem::Pointer>(type, storage_class)};
}
+ /// @param source the Source of the node
+ /// @param type the type of the pointer
+ /// @param storage_class the storage class of the pointer
+ /// @return the pointer to `type` with the given ast::StorageClass
+ typ::Pointer pointer(const Source& source,
+ typ::Type type,
+ ast::StorageClass storage_class) const {
+ type = MaybeCreateTypename(type);
+ return {builder->create<ast::Pointer>(source, type, storage_class),
+ builder->create<sem::Pointer>(type, storage_class)};
+ }
+
/// @param storage_class the storage class of the pointer
/// @return the pointer to type `T` with the given ast::StorageClass.
template <typename T>
@@ -641,6 +749,14 @@
builder->create<sem::Sampler>(kind)};
}
+ /// @param source the Source of the node
+ /// @param kind the kind of sampler
+ /// @returns the sampler
+ typ::Sampler sampler(const Source& source, ast::SamplerKind kind) const {
+ return {builder->create<ast::Sampler>(source, kind),
+ builder->create<sem::Sampler>(kind)};
+ }
+
/// @param dims the dimensionality of the texture
/// @returns the depth texture
typ::DepthTexture depth_texture(ast::TextureDimension dims) const {
@@ -648,6 +764,15 @@
builder->create<sem::DepthTexture>(dims)};
}
+ /// @param source the Source of the node
+ /// @param dims the dimensionality of the texture
+ /// @returns the depth texture
+ typ::DepthTexture depth_texture(const Source& source,
+ ast::TextureDimension dims) const {
+ return {builder->create<ast::DepthTexture>(source, dims),
+ builder->create<sem::DepthTexture>(dims)};
+ }
+
/// @param dims the dimensionality of the texture
/// @param subtype the texture subtype.
/// @returns the sampled texture
@@ -657,6 +782,17 @@
builder->create<sem::SampledTexture>(dims, subtype)};
}
+ /// @param source the Source of the node
+ /// @param dims the dimensionality of the texture
+ /// @param subtype the texture subtype.
+ /// @returns the sampled texture
+ typ::SampledTexture sampled_texture(const Source& source,
+ ast::TextureDimension dims,
+ typ::Type subtype) const {
+ return {builder->create<ast::SampledTexture>(source, dims, subtype),
+ builder->create<sem::SampledTexture>(dims, subtype)};
+ }
+
/// @param dims the dimensionality of the texture
/// @param subtype the texture subtype.
/// @returns the multisampled texture
@@ -666,6 +802,17 @@
builder->create<sem::MultisampledTexture>(dims, subtype)};
}
+ /// @param source the Source of the node
+ /// @param dims the dimensionality of the texture
+ /// @param subtype the texture subtype.
+ /// @returns the multisampled texture
+ typ::MultisampledTexture multisampled_texture(const Source& source,
+ ast::TextureDimension dims,
+ typ::Type subtype) const {
+ return {builder->create<ast::MultisampledTexture>(source, dims, subtype),
+ builder->create<sem::MultisampledTexture>(dims, subtype)};
+ }
+
/// @param dims the dimensionality of the texture
/// @param format the image format of the texture
/// @returns the storage texture
@@ -678,6 +825,28 @@
builder->create<sem::StorageTexture>(dims, format, sem_subtype)};
}
+ /// @param source the Source of the node
+ /// @param dims the dimensionality of the texture
+ /// @param format the image format of the texture
+ /// @returns the storage texture
+ typ::StorageTexture storage_texture(const Source& source,
+ ast::TextureDimension dims,
+ ast::ImageFormat format) const {
+ auto* ast_subtype = ast::StorageTexture::SubtypeFor(format, *builder);
+ auto* sem_subtype =
+ sem::StorageTexture::SubtypeFor(format, builder->Types());
+ return {builder->create<ast::StorageTexture>(source, dims, format,
+ ast_subtype),
+ builder->create<sem::StorageTexture>(dims, format, sem_subtype)};
+ }
+
+ /// @param source the Source of the node
+ /// @returns the external texture
+ typ::ExternalTexture external_texture(const Source& source) const {
+ return {builder->create<ast::ExternalTexture>(source),
+ builder->create<sem::ExternalTexture>()};
+ }
+
/// If ty is a ast::Struct or ast::Alias, the returned type is an
/// ast::TypeName of the given type's name, otherwise type is returned.
/// @param type the type
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index 7536cc4..c44cd2d 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -177,9 +177,38 @@
return 0;
}
};
-
} // namespace
+/// RAII helper that combines a Source on construction with the last token's
+/// source when implicitly converted to `Source`.
+class ParserImpl::MultiTokenSource {
+ public:
+ /// Constructor that starts with Source at the current peek position
+ /// @param parser the parser
+ explicit MultiTokenSource(ParserImpl* parser)
+ : MultiTokenSource(parser, parser->peek().source().Begin()) {}
+
+ /// Constructor that starts with the input `start` Source
+ /// @param parser the parser
+ /// @param start the start source of the range
+ MultiTokenSource(ParserImpl* parser, const Source& start)
+ : parser_(parser), start_(start) {}
+
+ /// 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();
+ if (end < start_) {
+ end = start_;
+ }
+ return Source::Combine(start_, end);
+ }
+
+ private:
+ ParserImpl* parser_;
+ Source start_;
+};
+
ParserImpl::TypedIdentifier::TypedIdentifier() = default;
ParserImpl::TypedIdentifier::TypedIdentifier(const TypedIdentifier&) = default;
@@ -266,15 +295,16 @@
if (!token_queue_.empty()) {
auto t = token_queue_.front();
token_queue_.pop_front();
- return t;
+ last_token_ = t;
+ return last_token_;
}
- return lexer_->next();
+ last_token_ = lexer_->next();
+ return last_token_;
}
Token ParserImpl::peek(size_t idx) {
while (token_queue_.size() < (idx + 1))
token_queue_.push_back(lexer_->next());
-
return token_queue_[idx];
}
@@ -282,6 +312,10 @@
return peek(0);
}
+Token ParserImpl::last_token() const {
+ return last_token_;
+}
+
void ParserImpl::register_constructed(const std::string& name,
sem::Type* type) {
registered_constructs_[name] = type;
@@ -560,6 +594,8 @@
if (type.matched)
return type.value;
+ auto source_range = make_source_range();
+
auto dim = sampled_texture_type();
if (dim.matched) {
const char* use = "sampled texture type";
@@ -568,9 +604,7 @@
if (subtype.errored)
return Failure::kErrored;
- return typ::Type{
- builder_.create<ast::SampledTexture>(dim.value, subtype.value),
- builder_.create<sem::SampledTexture>(dim.value, subtype.value)};
+ return builder_.ty.sampled_texture(source_range, dim.value, subtype.value);
}
auto ms_dim = multisampled_texture_type();
@@ -581,9 +615,8 @@
if (subtype.errored)
return Failure::kErrored;
- return typ::Type{
- builder_.create<ast::MultisampledTexture>(ms_dim.value, subtype.value),
- builder_.create<sem::MultisampledTexture>(ms_dim.value, subtype.value)};
+ return builder_.ty.multisampled_texture(source_range, ms_dim.value,
+ subtype.value);
}
auto storage = storage_texture_type();
@@ -596,14 +629,8 @@
if (format.errored)
return Failure::kErrored;
- auto* subtype = ast::StorageTexture::SubtypeFor(format.value, builder_);
- auto* subtype_sem =
- sem::StorageTexture::SubtypeFor(format.value, builder_.Types());
-
- return typ::Type{builder_.create<ast::StorageTexture>(
- storage.value, format.value, subtype),
- builder_.create<sem::StorageTexture>(
- storage.value, format.value, subtype_sem)};
+ return builder_.ty.storage_texture(source_range, storage.value,
+ format.value);
}
return Failure::kNoMatch;
@@ -613,14 +640,12 @@
// : SAMPLER
// | SAMPLER_COMPARISON
Maybe<typ::Type> ParserImpl::sampler_type() {
- if (match(Token::Type::kSampler))
- return typ::Type{builder_.create<ast::Sampler>(ast::SamplerKind::kSampler),
- builder_.create<sem::Sampler>(ast::SamplerKind::kSampler)};
+ Source source;
+ if (match(Token::Type::kSampler, &source))
+ return builder_.ty.sampler(source, ast::SamplerKind::kSampler);
- if (match(Token::Type::kComparisonSampler))
- return typ::Type{
- builder_.create<ast::Sampler>(ast::SamplerKind::kComparisonSampler),
- builder_.create<sem::Sampler>(ast::SamplerKind::kComparisonSampler)};
+ if (match(Token::Type::kComparisonSampler, &source))
+ return builder_.ty.sampler(source, ast::SamplerKind::kComparisonSampler);
return Failure::kNoMatch;
}
@@ -657,9 +682,9 @@
// external_texture_type
// : TEXTURE_EXTERNAL
Maybe<typ::Type> ParserImpl::external_texture_type() {
- if (match(Token::Type::kTextureExternal)) {
- return typ::Type{builder_.create<ast::ExternalTexture>(),
- builder_.create<sem::ExternalTexture>()};
+ Source source;
+ if (match(Token::Type::kTextureExternal, &source)) {
+ return builder_.ty.external_texture(source);
}
return Failure::kNoMatch;
@@ -698,25 +723,19 @@
// | TEXTURE_DEPTH_CUBE
// | TEXTURE_DEPTH_CUBE_ARRAY
Maybe<typ::Type> ParserImpl::depth_texture_type() {
- if (match(Token::Type::kTextureDepth2d))
- return typ::Type{
- builder_.create<ast::DepthTexture>(ast::TextureDimension::k2d),
- builder_.create<sem::DepthTexture>(ast::TextureDimension::k2d)};
+ Source source;
- if (match(Token::Type::kTextureDepth2dArray))
- return typ::Type{
- builder_.create<ast::DepthTexture>(ast::TextureDimension::k2dArray),
- builder_.create<sem::DepthTexture>(ast::TextureDimension::k2dArray)};
+ if (match(Token::Type::kTextureDepth2d, &source))
+ return builder_.ty.depth_texture(source, ast::TextureDimension::k2d);
- if (match(Token::Type::kTextureDepthCube))
- return typ::Type{
- builder_.create<ast::DepthTexture>(ast::TextureDimension::kCube),
- builder_.create<sem::DepthTexture>(ast::TextureDimension::kCube)};
+ if (match(Token::Type::kTextureDepth2dArray, &source))
+ return builder_.ty.depth_texture(source, ast::TextureDimension::k2dArray);
- if (match(Token::Type::kTextureDepthCubeArray))
- return typ::Type{
- builder_.create<ast::DepthTexture>(ast::TextureDimension::kCubeArray),
- builder_.create<sem::DepthTexture>(ast::TextureDimension::kCubeArray)};
+ if (match(Token::Type::kTextureDepthCube, &source))
+ return builder_.ty.depth_texture(source, ast::TextureDimension::kCube);
+
+ if (match(Token::Type::kTextureDepthCubeArray, &source))
+ return builder_.ty.depth_texture(source, ast::TextureDimension::kCubeArray);
return Failure::kNoMatch;
}
@@ -897,26 +916,15 @@
if (access_decos.size() > 1)
return add_error(ident.source, "multiple access decorations not allowed");
- // TODO(crbug.com/tint/724): Remove
- auto* sem_ty = type.value.sem;
+ typ::Type ty = type.value;
+
for (auto* deco : access_decos) {
// If we have an access control decoration then we take it and wrap our
// type up with that decoration
- sem_ty = builder_.create<sem::AccessControl>(
- deco->As<ast::AccessDecoration>()->value(), sem_ty);
+ ty = builder_.ty.access(deco->source(),
+ deco->As<ast::AccessDecoration>()->value(), ty);
}
-
- auto* ty = type.value.ast;
- // TODO(crbug.com/tint/724): Remove 'if'
- if (ty) {
- for (auto* deco : access_decos) {
- // If we have an access control decoration then we take it and wrap our
- // type up with that decoration
- ty = builder_.create<ast::AccessControl>(
- deco->As<ast::AccessDecoration>()->value(), ty);
- }
- }
- return TypedIdentifier{typ::Type{ty, sem_ty}, ident.value, ident.source};
+ return TypedIdentifier{ty, ident.value, ident.source};
}
Expect<ast::AccessControl::Access> ParserImpl::expect_access_type() {
@@ -974,14 +982,10 @@
if (!type.matched)
return add_error(peek(), "invalid type alias");
- // TODO(crbug.com/tint/724): remove
- auto* alias = builder_.create<sem::Alias>(
- builder_.Symbols().Register(name.value), type.value);
+ auto alias = builder_.ty.alias(make_source_range_from(t.source()), name.value,
+ type.value);
register_constructed(name.value, alias);
-
- return typ::Type{builder_.create<ast::Alias>(
- builder_.Symbols().Register(name.value), type.value),
- alias};
+ return alias;
}
// type_decl
@@ -1027,29 +1031,29 @@
Maybe<typ::Type> ParserImpl::type_decl(ast::DecorationList& decos) {
auto t = peek();
- if (match(Token::Type::kIdentifier)) {
+ Source source;
+ if (match(Token::Type::kIdentifier, &source)) {
// TODO(crbug.com/tint/697): Remove
auto* ty = get_constructed(t.to_str());
if (ty == nullptr)
return add_error(t, "unknown constructed type '" + t.to_str() + "'");
- return typ::Type{
- builder_.create<ast::TypeName>(builder_.Symbols().Register(t.to_str())),
- ty};
+ return typ::Type{builder_.create<ast::TypeName>(
+ source, builder_.Symbols().Register(t.to_str())),
+ ty};
}
- if (match(Token::Type::kBool))
- return typ::Type{builder_.create<ast::Bool>(),
- builder_.create<sem::Bool>()};
+ if (match(Token::Type::kBool, &source))
+ return builder_.ty.bool_(source);
- if (match(Token::Type::kF32))
- return typ::Type{builder_.create<ast::F32>(), builder_.create<sem::F32>()};
+ if (match(Token::Type::kF32, &source))
+ return builder_.ty.f32(source);
- if (match(Token::Type::kI32))
- return typ::Type{builder_.create<ast::I32>(), builder_.create<sem::I32>()};
+ if (match(Token::Type::kI32, &source))
+ return builder_.ty.i32(source);
- if (match(Token::Type::kU32))
- return typ::Type{builder_.create<ast::U32>(), builder_.create<sem::U32>()};
+ if (match(Token::Type::kU32, &source))
+ return builder_.ty.u32(source);
if (t.IsVec2() || t.IsVec3() || t.IsVec4()) {
next(); // Consume the peek
@@ -1057,10 +1061,10 @@
}
if (match(Token::Type::kPtr))
- return expect_type_decl_pointer();
+ return expect_type_decl_pointer(t);
- if (match(Token::Type::kArray)) {
- return expect_type_decl_array(std::move(decos));
+ if (match(Token::Type::kArray, &source)) {
+ return expect_type_decl_array(t, std::move(decos));
}
if (t.IsMat2x2() || t.IsMat2x3() || t.IsMat2x4() || t.IsMat3x2() ||
@@ -1088,24 +1092,33 @@
return type.value;
}
-Expect<typ::Type> ParserImpl::expect_type_decl_pointer() {
+Expect<typ::Type> ParserImpl::expect_type_decl_pointer(Token t) {
const char* use = "ptr declaration";
- return expect_lt_gt_block(use, [&]() -> Expect<typ::Type> {
+ ast::StorageClass storage_class = ast::StorageClass::kNone;
+
+ auto subtype = expect_lt_gt_block(use, [&]() -> Expect<typ::Type> {
auto sc = expect_storage_class(use);
if (sc.errored)
return Failure::kErrored;
+ storage_class = sc.value;
if (!expect(use, Token::Type::kComma))
return Failure::kErrored;
- auto subtype = expect_type(use);
- if (subtype.errored)
+ auto type = expect_type(use);
+ if (type.errored)
return Failure::kErrored;
- return typ::Type{builder_.create<ast::Pointer>(subtype.value, sc.value),
- builder_.create<sem::Pointer>(subtype.value, sc.value)};
+ return type.value;
});
+
+ if (subtype.errored) {
+ return Failure::kErrored;
+ }
+
+ return builder_.ty.pointer(make_source_range_from(t.source()), subtype.value,
+ storage_class);
}
Expect<typ::Type> ParserImpl::expect_type_decl_vector(Token t) {
@@ -1121,20 +1134,22 @@
if (subtype.errored)
return Failure::kErrored;
- return typ::Type{builder_.create<ast::Vector>(subtype.value.ast, count),
- builder_.create<sem::Vector>(subtype.value.sem, count)};
+ return builder_.ty.vec(make_source_range_from(t.source()), subtype.value,
+ count);
}
Expect<typ::Type> ParserImpl::expect_type_decl_array(
+ Token t,
ast::DecorationList decos) {
const char* use = "array declaration";
- return expect_lt_gt_block(use, [&]() -> Expect<typ::Type> {
- auto subtype = expect_type(use);
- if (subtype.errored)
+ uint32_t size = 0;
+
+ auto subtype = expect_lt_gt_block(use, [&]() -> Expect<typ::Type> {
+ auto type = expect_type(use);
+ if (type.errored)
return Failure::kErrored;
- uint32_t size = 0;
if (match(Token::Type::kComma)) {
auto val = expect_nonzero_positive_sint("array size");
if (val.errored)
@@ -1142,10 +1157,15 @@
size = val.value;
}
- return typ::Type{
- create<ast::Array>(subtype.value, size, decos),
- create<sem::ArrayType>(subtype.value, size, std::move(decos))};
+ return type.value;
});
+
+ if (subtype.errored) {
+ return Failure::kErrored;
+ }
+
+ return builder_.ty.array(make_source_range_from(t.source()), subtype.value,
+ size, std::move(decos));
}
Expect<typ::Type> ParserImpl::expect_type_decl_matrix(Token t) {
@@ -1168,8 +1188,8 @@
if (subtype.errored)
return Failure::kErrored;
- return typ::Type{builder_.create<ast::Matrix>(subtype.value, rows, columns),
- builder_.create<sem::Matrix>(subtype.value, rows, columns)};
+ return builder_.ty.mat(make_source_range_from(t.source()), subtype.value,
+ columns, rows);
}
// storage_class
@@ -1321,9 +1341,9 @@
// : type_decl
// | VOID
Maybe<typ::Type> ParserImpl::function_type_decl() {
- if (match(Token::Type::kVoid))
- return typ::Type{builder_.create<ast::Void>(),
- builder_.create<sem::Void>()};
+ Source source;
+ if (match(Token::Type::kVoid, &source))
+ return builder_.ty.void_(source);
return type_decl();
}
@@ -3385,6 +3405,15 @@
return result;
}
+ParserImpl::MultiTokenSource ParserImpl::make_source_range() {
+ return MultiTokenSource(this);
+}
+
+ParserImpl::MultiTokenSource ParserImpl::make_source_range_from(
+ const Source& start) {
+ return MultiTokenSource(this, start);
+}
+
} // namespace wgsl
} // namespace reader
} // namespace tint
diff --git a/src/reader/wgsl/parser_impl.h b/src/reader/wgsl/parser_impl.h
index 7ae13ba..e01cf25 100644
--- a/src/reader/wgsl/parser_impl.h
+++ b/src/reader/wgsl/parser_impl.h
@@ -331,6 +331,8 @@
/// @param idx the index of the token to return
/// @returns the token `idx` positions ahead without advancing
Token peek(size_t idx);
+ /// @returns the last token that was returned by `next()`
+ Token last_token() const;
/// Appends an error at `t` with the message `msg`
/// @param t the token to associate the error with
/// @param msg the error message
@@ -821,9 +823,9 @@
/// Used to ensure that all decorations are consumed.
bool expect_decorations_consumed(const ast::DecorationList& list);
- Expect<typ::Type> expect_type_decl_pointer();
+ Expect<typ::Type> expect_type_decl_pointer(Token t);
Expect<typ::Type> expect_type_decl_vector(Token t);
- Expect<typ::Type> expect_type_decl_array(ast::DecorationList decos);
+ Expect<typ::Type> expect_type_decl_array(Token t, ast::DecorationList decos);
Expect<typ::Type> expect_type_decl_matrix(Token t);
Expect<typ::Type> expect_type(const std::string& use);
@@ -832,6 +834,10 @@
Maybe<ast::Statement*> for_header_initializer();
Maybe<ast::Statement*> for_header_continuing();
+ class MultiTokenSource;
+ MultiTokenSource make_source_range();
+ MultiTokenSource make_source_range_from(const Source& start);
+
/// Creates a new `ast::Node` owned by the Module. When the Module is
/// destructed, the `ast::Node` will also be destructed.
/// @param args the arguments to pass to the type constructor
@@ -843,6 +849,7 @@
std::unique_ptr<Lexer> lexer_;
std::deque<Token> token_queue_;
+ Token last_token_;
bool synchronized_ = true;
uint32_t sync_depth_ = 0;
std::vector<Token::Type> sync_tokens_;
diff --git a/src/reader/wgsl/parser_impl_depth_texture_type_test.cc b/src/reader/wgsl/parser_impl_depth_texture_type_test.cc
index cc06e7a..82b73c1 100644
--- a/src/reader/wgsl/parser_impl_depth_texture_type_test.cc
+++ b/src/reader/wgsl/parser_impl_depth_texture_type_test.cc
@@ -38,6 +38,7 @@
ASSERT_TRUE(t->Is<sem::DepthTexture>());
EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k2d);
EXPECT_FALSE(p->has_error());
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 17u}}));
}
TEST_F(ParserImplTest, DepthTextureType_2dArray) {
@@ -50,6 +51,7 @@
ASSERT_TRUE(t->Is<sem::DepthTexture>());
EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k2dArray);
EXPECT_FALSE(p->has_error());
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 23u}}));
}
TEST_F(ParserImplTest, DepthTextureType_Cube) {
@@ -62,6 +64,7 @@
ASSERT_TRUE(t->Is<sem::DepthTexture>());
EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::kCube);
EXPECT_FALSE(p->has_error());
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 19u}}));
}
TEST_F(ParserImplTest, DepthTextureType_CubeArray) {
@@ -74,6 +77,7 @@
ASSERT_TRUE(t->Is<sem::DepthTexture>());
EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::kCubeArray);
EXPECT_FALSE(p->has_error());
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 25u}}));
}
} // namespace
diff --git a/src/reader/wgsl/parser_impl_external_texture_type_test.cc b/src/reader/wgsl/parser_impl_external_texture_type_test.cc
index 92a8738..9f46112 100644
--- a/src/reader/wgsl/parser_impl_external_texture_type_test.cc
+++ b/src/reader/wgsl/parser_impl_external_texture_type_test.cc
@@ -32,6 +32,7 @@
auto t = p->external_texture_type();
EXPECT_TRUE(t.matched);
EXPECT_FALSE(t.errored);
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 17u}}));
}
} // namespace
diff --git a/src/reader/wgsl/parser_impl_function_type_decl_test.cc b/src/reader/wgsl/parser_impl_function_type_decl_test.cc
index f876f5c..ed4eb5a 100644
--- a/src/reader/wgsl/parser_impl_function_type_decl_test.cc
+++ b/src/reader/wgsl/parser_impl_function_type_decl_test.cc
@@ -29,6 +29,7 @@
EXPECT_FALSE(e.errored);
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_EQ(e.value, v);
+ EXPECT_EQ(e.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 5u}}));
}
TEST_F(ParserImplTest, FunctionTypeDecl_Type) {
@@ -42,6 +43,7 @@
EXPECT_FALSE(e.errored);
EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_EQ(e.value, vec2);
+ EXPECT_EQ(e.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 10u}}));
}
TEST_F(ParserImplTest, FunctionTypeDecl_InvalidType) {
diff --git a/src/reader/wgsl/parser_impl_sampler_type_test.cc b/src/reader/wgsl/parser_impl_sampler_type_test.cc
index 4ec0c01..9ac9f6e 100644
--- a/src/reader/wgsl/parser_impl_sampler_type_test.cc
+++ b/src/reader/wgsl/parser_impl_sampler_type_test.cc
@@ -37,6 +37,7 @@
ASSERT_TRUE(t->Is<sem::Sampler>());
EXPECT_FALSE(t->As<sem::Sampler>()->IsComparison());
EXPECT_FALSE(p->has_error());
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 8u}}));
}
TEST_F(ParserImplTest, SamplerType_ComparisonSampler) {
@@ -48,6 +49,7 @@
ASSERT_TRUE(t->Is<sem::Sampler>());
EXPECT_TRUE(t->As<sem::Sampler>()->IsComparison());
EXPECT_FALSE(p->has_error());
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 19u}}));
}
} // namespace
diff --git a/src/reader/wgsl/parser_impl_struct_member_test.cc b/src/reader/wgsl/parser_impl_struct_member_test.cc
index f92eb00..d1c8e4b 100644
--- a/src/reader/wgsl/parser_impl_struct_member_test.cc
+++ b/src/reader/wgsl/parser_impl_struct_member_test.cc
@@ -39,10 +39,8 @@
EXPECT_EQ(m->type(), i32);
EXPECT_EQ(m->decorations().size(), 0u);
- ASSERT_EQ(m->source().range.begin.line, 1u);
- ASSERT_EQ(m->source().range.begin.column, 1u);
- ASSERT_EQ(m->source().range.end.line, 1u);
- ASSERT_EQ(m->source().range.end.column, 2u);
+ EXPECT_EQ(m->source().range, (Source::Range{{1u, 1u}, {1u, 2u}}));
+ EXPECT_EQ(m->type().ast->source().range, (Source::Range{{1u, 5u}, {1u, 8u}}));
}
TEST_F(ParserImplTest, StructMember_ParsesWithOffsetDecoration_DEPRECATED) {
@@ -69,10 +67,9 @@
m->decorations()[0]->As<ast::StructMemberOffsetDecoration>()->offset(),
2u);
- ASSERT_EQ(m->source().range.begin.line, 1u);
- ASSERT_EQ(m->source().range.begin.column, 15u);
- ASSERT_EQ(m->source().range.end.line, 1u);
- ASSERT_EQ(m->source().range.end.column, 16u);
+ EXPECT_EQ(m->source().range, (Source::Range{{1u, 15u}, {1u, 16u}}));
+ EXPECT_EQ(m->type().ast->source().range,
+ (Source::Range{{1u, 19u}, {1u, 22u}}));
}
TEST_F(ParserImplTest, StructMember_ParsesWithAlignDecoration) {
@@ -98,10 +95,9 @@
EXPECT_EQ(
m->decorations()[0]->As<ast::StructMemberAlignDecoration>()->align(), 2u);
- ASSERT_EQ(m->source().range.begin.line, 1u);
- ASSERT_EQ(m->source().range.begin.column, 14u);
- ASSERT_EQ(m->source().range.end.line, 1u);
- ASSERT_EQ(m->source().range.end.column, 15u);
+ EXPECT_EQ(m->source().range, (Source::Range{{1u, 14u}, {1u, 15u}}));
+ EXPECT_EQ(m->type().ast->source().range,
+ (Source::Range{{1u, 18u}, {1u, 21u}}));
}
TEST_F(ParserImplTest, StructMember_ParsesWithSizeDecoration) {
@@ -127,10 +123,9 @@
EXPECT_EQ(m->decorations()[0]->As<ast::StructMemberSizeDecoration>()->size(),
2u);
- ASSERT_EQ(m->source().range.begin.line, 1u);
- ASSERT_EQ(m->source().range.begin.column, 13u);
- ASSERT_EQ(m->source().range.end.line, 1u);
- ASSERT_EQ(m->source().range.end.column, 14u);
+ EXPECT_EQ(m->source().range, (Source::Range{{1u, 13u}, {1u, 14u}}));
+ EXPECT_EQ(m->type().ast->source().range,
+ (Source::Range{{1u, 17u}, {1u, 20u}}));
}
TEST_F(ParserImplTest, StructMember_ParsesWithDecoration) {
@@ -156,10 +151,9 @@
EXPECT_EQ(m->decorations()[0]->As<ast::StructMemberSizeDecoration>()->size(),
2u);
- ASSERT_EQ(m->source().range.begin.line, 1u);
- ASSERT_EQ(m->source().range.begin.column, 13u);
- ASSERT_EQ(m->source().range.end.line, 1u);
- ASSERT_EQ(m->source().range.end.column, 14u);
+ EXPECT_EQ(m->source().range, (Source::Range{{1u, 13u}, {1u, 14u}}));
+ EXPECT_EQ(m->type().ast->source().range,
+ (Source::Range{{1u, 17u}, {1u, 20u}}));
}
TEST_F(ParserImplTest, StructMember_ParsesWithMultipleDecorations) {
@@ -189,10 +183,9 @@
EXPECT_EQ(
m->decorations()[1]->As<ast::StructMemberAlignDecoration>()->align(), 4u);
- ASSERT_EQ(m->source().range.begin.line, 2u);
- ASSERT_EQ(m->source().range.begin.column, 14u);
- ASSERT_EQ(m->source().range.end.line, 2u);
- ASSERT_EQ(m->source().range.end.column, 15u);
+ EXPECT_EQ(m->source().range, (Source::Range{{2u, 14u}, {2u, 15u}}));
+ EXPECT_EQ(m->type().ast->source().range,
+ (Source::Range{{2u, 18u}, {2u, 21u}}));
}
TEST_F(ParserImplTest, StructMember_InvalidDecoration) {
diff --git a/src/reader/wgsl/parser_impl_texture_sampler_types_test.cc b/src/reader/wgsl/parser_impl_texture_sampler_types_test.cc
index 8b5118e..646100d 100644
--- a/src/reader/wgsl/parser_impl_texture_sampler_types_test.cc
+++ b/src/reader/wgsl/parser_impl_texture_sampler_types_test.cc
@@ -40,6 +40,7 @@
ASSERT_NE(t.value, nullptr);
ASSERT_TRUE(t->Is<sem::Sampler>());
ASSERT_FALSE(t->As<sem::Sampler>()->IsComparison());
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 8u}}));
}
TEST_F(ParserImplTest, TextureSamplerTypes_SamplerComparison) {
@@ -51,6 +52,7 @@
ASSERT_NE(t.value, nullptr);
ASSERT_TRUE(t->Is<sem::Sampler>());
ASSERT_TRUE(t->As<sem::Sampler>()->IsComparison());
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 19u}}));
}
TEST_F(ParserImplTest, TextureSamplerTypes_DepthTexture) {
@@ -63,6 +65,7 @@
ASSERT_TRUE(t->Is<sem::Texture>());
ASSERT_TRUE(t->Is<sem::DepthTexture>());
EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k2d);
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 17u}}));
}
TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_F32) {
@@ -76,6 +79,7 @@
ASSERT_TRUE(t->Is<sem::SampledTexture>());
ASSERT_TRUE(t->As<sem::SampledTexture>()->type()->Is<sem::F32>());
EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k1d);
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 16u}}));
}
TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_I32) {
@@ -89,6 +93,7 @@
ASSERT_TRUE(t->Is<sem::SampledTexture>());
ASSERT_TRUE(t->As<sem::SampledTexture>()->type()->Is<sem::I32>());
EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k2d);
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 16u}}));
}
TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_U32) {
@@ -102,6 +107,7 @@
ASSERT_TRUE(t->Is<sem::SampledTexture>());
ASSERT_TRUE(t->As<sem::SampledTexture>()->type()->Is<sem::U32>());
EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k3d);
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 16u}}));
}
TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_Invalid) {
@@ -155,6 +161,7 @@
ASSERT_TRUE(t->Is<sem::MultisampledTexture>());
ASSERT_TRUE(t->As<sem::MultisampledTexture>()->type()->Is<sem::I32>());
EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k2d);
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 29u}}));
}
TEST_F(ParserImplTest, TextureSamplerTypes_MultisampledTexture_Invalid) {
@@ -210,6 +217,7 @@
EXPECT_EQ(t->As<sem::StorageTexture>()->image_format(),
ast::ImageFormat::kR8Unorm);
EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k1d);
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 28u}}));
}
TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_Writeonly2dR16Float) {
@@ -225,6 +233,7 @@
EXPECT_EQ(t->As<sem::StorageTexture>()->image_format(),
ast::ImageFormat::kR16Float);
EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k2d);
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 29u}}));
}
TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_InvalidType) {
diff --git a/src/reader/wgsl/parser_impl_type_alias_test.cc b/src/reader/wgsl/parser_impl_type_alias_test.cc
index 82cca4d..ad3e45e 100644
--- a/src/reader/wgsl/parser_impl_type_alias_test.cc
+++ b/src/reader/wgsl/parser_impl_type_alias_test.cc
@@ -33,6 +33,8 @@
auto* alias = t->As<sem::Alias>();
ASSERT_TRUE(alias->type()->Is<sem::I32>());
ASSERT_EQ(alias->type(), i32);
+
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 13u}}));
}
TEST_F(ParserImplTest, TypeDecl_ParsesStruct_Ident) {
@@ -54,6 +56,8 @@
auto* s = alias->type()->As<sem::StructType>();
EXPECT_EQ(s->impl()->name(), p->builder().Symbols().Get("B"));
EXPECT_EQ(s->impl()->name(), p->builder().Symbols().Get("B"));
+
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 11u}}));
}
TEST_F(ParserImplTest, TypeDecl_MissingIdent) {
diff --git a/src/reader/wgsl/parser_impl_type_decl_test.cc b/src/reader/wgsl/parser_impl_type_decl_test.cc
index 185ac69..8d7ebb1 100644
--- a/src/reader/wgsl/parser_impl_type_decl_test.cc
+++ b/src/reader/wgsl/parser_impl_type_decl_test.cc
@@ -50,6 +50,7 @@
auto* alias = t->As<sem::Alias>();
EXPECT_EQ(p->builder().Symbols().NameFor(alias->symbol()), "A");
EXPECT_EQ(alias->type(), int_type);
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 2u}}));
}
TEST_F(ParserImplTest, TypeDecl_Identifier_NotFound) {
@@ -75,6 +76,7 @@
ASSERT_NE(t.value, nullptr) << p->error();
EXPECT_EQ(t.value, bool_type);
ASSERT_TRUE(t->Is<sem::Bool>());
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 5u}}));
}
TEST_F(ParserImplTest, TypeDecl_F32) {
@@ -89,6 +91,7 @@
ASSERT_NE(t.value, nullptr) << p->error();
EXPECT_EQ(t.value, float_type);
ASSERT_TRUE(t->Is<sem::F32>());
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 4u}}));
}
TEST_F(ParserImplTest, TypeDecl_I32) {
@@ -103,6 +106,7 @@
ASSERT_NE(t.value, nullptr) << p->error();
EXPECT_EQ(t.value, int_type);
ASSERT_TRUE(t->Is<sem::I32>());
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 4u}}));
}
TEST_F(ParserImplTest, TypeDecl_U32) {
@@ -117,11 +121,13 @@
ASSERT_NE(t.value, nullptr) << p->error();
EXPECT_EQ(t.value, uint_type);
ASSERT_TRUE(t->Is<sem::U32>());
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 4u}}));
}
struct VecData {
const char* input;
size_t count;
+ Source::Range range;
};
inline std::ostream& operator<<(std::ostream& out, VecData data) {
out << std::string(data.input);
@@ -140,12 +146,16 @@
ASSERT_FALSE(p->has_error());
EXPECT_TRUE(t->Is<sem::Vector>());
EXPECT_EQ(t->As<sem::Vector>()->size(), params.count);
+ EXPECT_EQ(t.value.ast->source().range, params.range);
}
-INSTANTIATE_TEST_SUITE_P(ParserImplTest,
- VecTest,
- testing::Values(VecData{"vec2<f32>", 2},
- VecData{"vec3<f32>", 3},
- VecData{"vec4<f32>", 4}));
+INSTANTIATE_TEST_SUITE_P(
+ ParserImplTest,
+ VecTest,
+ testing::Values(VecData{"vec2<f32>", 2, Source::Range{{1u, 1u}, {1u, 10u}}},
+ VecData{"vec3<f32>", 3, Source::Range{{1u, 1u}, {1u, 10u}}},
+ VecData{"vec4<f32>", 4, Source::Range{{1u, 1u}, {1u, 10u}}}
+
+ ));
class VecMissingGreaterThanTest : public ParserImplTestWithParam<VecData> {};
@@ -161,9 +171,9 @@
}
INSTANTIATE_TEST_SUITE_P(ParserImplTest,
VecMissingGreaterThanTest,
- testing::Values(VecData{"vec2<f32", 2},
- VecData{"vec3<f32", 3},
- VecData{"vec4<f32", 4}));
+ testing::Values(VecData{"vec2<f32", 2, {}},
+ VecData{"vec3<f32", 3, {}},
+ VecData{"vec4<f32", 4, {}}));
class VecMissingLessThanTest : public ParserImplTestWithParam<VecData> {};
@@ -179,9 +189,9 @@
}
INSTANTIATE_TEST_SUITE_P(ParserImplTest,
VecMissingLessThanTest,
- testing::Values(VecData{"vec2", 2},
- VecData{"vec3", 3},
- VecData{"vec4", 4}));
+ testing::Values(VecData{"vec2", 2, {}},
+ VecData{"vec3", 3, {}},
+ VecData{"vec4", 4, {}}));
class VecBadType : public ParserImplTestWithParam<VecData> {};
@@ -197,9 +207,9 @@
}
INSTANTIATE_TEST_SUITE_P(ParserImplTest,
VecBadType,
- testing::Values(VecData{"vec2<unknown", 2},
- VecData{"vec3<unknown", 3},
- VecData{"vec4<unknown", 4}));
+ testing::Values(VecData{"vec2<unknown", 2, {}},
+ VecData{"vec3<unknown", 3, {}},
+ VecData{"vec4<unknown", 4, {}}));
class VecMissingType : public ParserImplTestWithParam<VecData> {};
@@ -215,9 +225,9 @@
}
INSTANTIATE_TEST_SUITE_P(ParserImplTest,
VecMissingType,
- testing::Values(VecData{"vec2<>", 2},
- VecData{"vec3<>", 3},
- VecData{"vec4<>", 4}));
+ testing::Values(VecData{"vec2<>", 2, {}},
+ VecData{"vec3<>", 3, {}},
+ VecData{"vec4<>", 4, {}}));
TEST_F(ParserImplTest, TypeDecl_Ptr) {
auto p = parser("ptr<function, f32>");
@@ -231,6 +241,7 @@
auto* ptr = t->As<sem::Pointer>();
ASSERT_TRUE(ptr->type()->Is<sem::F32>());
ASSERT_EQ(ptr->storage_class(), ast::StorageClass::kFunction);
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 19u}}));
}
TEST_F(ParserImplTest, TypeDecl_Ptr_ToVec) {
@@ -249,6 +260,7 @@
auto* vec = ptr->type()->As<sem::Vector>();
ASSERT_EQ(vec->size(), 2u);
ASSERT_TRUE(vec->type()->Is<sem::F32>());
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 25}}));
}
TEST_F(ParserImplTest, TypeDecl_Ptr_MissingLessThan) {
@@ -345,6 +357,7 @@
ASSERT_EQ(a->size(), 5u);
ASSERT_TRUE(a->type()->Is<sem::F32>());
EXPECT_EQ(a->decorations().size(), 0u);
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 14u}}));
}
TEST_F(ParserImplTest, TypeDecl_Array_Stride) {
@@ -365,6 +378,7 @@
auto* stride = a->decorations()[0];
ASSERT_TRUE(stride->Is<ast::StrideDecoration>());
ASSERT_EQ(stride->As<ast::StrideDecoration>()->stride(), 16u);
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 16u}, {1u, 29u}}));
}
TEST_F(ParserImplTest, TypeDecl_Array_Runtime_Stride) {
@@ -384,6 +398,7 @@
auto* stride = a->decorations()[0];
ASSERT_TRUE(stride->Is<ast::StrideDecoration>());
ASSERT_EQ(stride->As<ast::StrideDecoration>()->stride(), 16u);
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 16u}, {1u, 26u}}));
}
TEST_F(ParserImplTest, TypeDecl_Array_MultipleDecorations_OneBlock) {
@@ -405,6 +420,7 @@
EXPECT_EQ(decos[0]->As<ast::StrideDecoration>()->stride(), 16u);
EXPECT_TRUE(decos[1]->Is<ast::StrideDecoration>());
EXPECT_EQ(decos[1]->As<ast::StrideDecoration>()->stride(), 32u);
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 28u}, {1u, 38u}}));
}
TEST_F(ParserImplTest, TypeDecl_Array_MultipleDecorations_MultipleBlocks) {
@@ -426,6 +442,7 @@
EXPECT_EQ(decos[0]->As<ast::StrideDecoration>()->stride(), 16u);
EXPECT_TRUE(decos[1]->Is<ast::StrideDecoration>());
EXPECT_EQ(decos[1]->As<ast::StrideDecoration>()->stride(), 32u);
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 31u}, {1u, 41u}}));
}
TEST_F(ParserImplTest, TypeDecl_Array_Decoration_MissingArray) {
@@ -522,6 +539,7 @@
auto* a = t->As<sem::ArrayType>();
ASSERT_TRUE(a->IsRuntimeArray());
ASSERT_TRUE(a->type()->Is<sem::U32>());
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 11u}}));
}
TEST_F(ParserImplTest, TypeDecl_Array_Runtime_Vec) {
@@ -536,6 +554,7 @@
auto* a = t->As<sem::ArrayType>();
ASSERT_TRUE(a->IsRuntimeArray());
ASSERT_TRUE(a->type()->is_unsigned_integer_vector());
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 17u}}));
}
TEST_F(ParserImplTest, TypeDecl_Array_BadType) {
@@ -612,6 +631,7 @@
const char* input;
size_t columns;
size_t rows;
+ Source::Range range;
};
inline std::ostream& operator<<(std::ostream& out, MatrixData data) {
out << std::string(data.input);
@@ -632,18 +652,21 @@
auto* mat = t->As<sem::Matrix>();
EXPECT_EQ(mat->rows(), params.rows);
EXPECT_EQ(mat->columns(), params.columns);
+ EXPECT_EQ(t.value.ast->source().range, params.range);
}
-INSTANTIATE_TEST_SUITE_P(ParserImplTest,
- MatrixTest,
- testing::Values(MatrixData{"mat2x2<f32>", 2, 2},
- MatrixData{"mat2x3<f32>", 2, 3},
- MatrixData{"mat2x4<f32>", 2, 4},
- MatrixData{"mat3x2<f32>", 3, 2},
- MatrixData{"mat3x3<f32>", 3, 3},
- MatrixData{"mat3x4<f32>", 3, 4},
- MatrixData{"mat4x2<f32>", 4, 2},
- MatrixData{"mat4x3<f32>", 4, 3},
- MatrixData{"mat4x4<f32>", 4, 4}));
+INSTANTIATE_TEST_SUITE_P(
+ ParserImplTest,
+ MatrixTest,
+ testing::Values(
+ MatrixData{"mat2x2<f32>", 2, 2, Source::Range{{1u, 1u}, {1u, 12u}}},
+ MatrixData{"mat2x3<f32>", 2, 3, Source::Range{{1u, 1u}, {1u, 12u}}},
+ MatrixData{"mat2x4<f32>", 2, 4, Source::Range{{1u, 1u}, {1u, 12u}}},
+ MatrixData{"mat3x2<f32>", 3, 2, Source::Range{{1u, 1u}, {1u, 12u}}},
+ MatrixData{"mat3x3<f32>", 3, 3, Source::Range{{1u, 1u}, {1u, 12u}}},
+ MatrixData{"mat3x4<f32>", 3, 4, Source::Range{{1u, 1u}, {1u, 12u}}},
+ MatrixData{"mat4x2<f32>", 4, 2, Source::Range{{1u, 1u}, {1u, 12u}}},
+ MatrixData{"mat4x3<f32>", 4, 3, Source::Range{{1u, 1u}, {1u, 12u}}},
+ MatrixData{"mat4x4<f32>", 4, 4, Source::Range{{1u, 1u}, {1u, 12u}}}));
class MatrixMissingGreaterThanTest
: public ParserImplTestWithParam<MatrixData> {};
@@ -660,15 +683,15 @@
}
INSTANTIATE_TEST_SUITE_P(ParserImplTest,
MatrixMissingGreaterThanTest,
- testing::Values(MatrixData{"mat2x2<f32", 2, 2},
- MatrixData{"mat2x3<f32", 2, 3},
- MatrixData{"mat2x4<f32", 2, 4},
- MatrixData{"mat3x2<f32", 3, 2},
- MatrixData{"mat3x3<f32", 3, 3},
- MatrixData{"mat3x4<f32", 3, 4},
- MatrixData{"mat4x2<f32", 4, 2},
- MatrixData{"mat4x3<f32", 4, 3},
- MatrixData{"mat4x4<f32", 4, 4}));
+ testing::Values(MatrixData{"mat2x2<f32", 2, 2, {}},
+ MatrixData{"mat2x3<f32", 2, 3, {}},
+ MatrixData{"mat2x4<f32", 2, 4, {}},
+ MatrixData{"mat3x2<f32", 3, 2, {}},
+ MatrixData{"mat3x3<f32", 3, 3, {}},
+ MatrixData{"mat3x4<f32", 3, 4, {}},
+ MatrixData{"mat4x2<f32", 4, 2, {}},
+ MatrixData{"mat4x3<f32", 4, 3, {}},
+ MatrixData{"mat4x4<f32", 4, 4, {}}));
class MatrixMissingLessThanTest : public ParserImplTestWithParam<MatrixData> {};
@@ -684,15 +707,15 @@
}
INSTANTIATE_TEST_SUITE_P(ParserImplTest,
MatrixMissingLessThanTest,
- testing::Values(MatrixData{"mat2x2 f32>", 2, 2},
- MatrixData{"mat2x3 f32>", 2, 3},
- MatrixData{"mat2x4 f32>", 2, 4},
- MatrixData{"mat3x2 f32>", 3, 2},
- MatrixData{"mat3x3 f32>", 3, 3},
- MatrixData{"mat3x4 f32>", 3, 4},
- MatrixData{"mat4x2 f32>", 4, 2},
- MatrixData{"mat4x3 f32>", 4, 3},
- MatrixData{"mat4x4 f32>", 4, 4}));
+ testing::Values(MatrixData{"mat2x2 f32>", 2, 2, {}},
+ MatrixData{"mat2x3 f32>", 2, 3, {}},
+ MatrixData{"mat2x4 f32>", 2, 4, {}},
+ MatrixData{"mat3x2 f32>", 3, 2, {}},
+ MatrixData{"mat3x3 f32>", 3, 3, {}},
+ MatrixData{"mat3x4 f32>", 3, 4, {}},
+ MatrixData{"mat4x2 f32>", 4, 2, {}},
+ MatrixData{"mat4x3 f32>", 4, 3, {}},
+ MatrixData{"mat4x4 f32>", 4, 4, {}}));
class MatrixBadType : public ParserImplTestWithParam<MatrixData> {};
@@ -706,17 +729,18 @@
ASSERT_TRUE(p->has_error());
ASSERT_EQ(p->error(), "1:8: unknown constructed type 'unknown'");
}
-INSTANTIATE_TEST_SUITE_P(ParserImplTest,
- MatrixBadType,
- testing::Values(MatrixData{"mat2x2<unknown>", 2, 2},
- MatrixData{"mat2x3<unknown>", 2, 3},
- MatrixData{"mat2x4<unknown>", 2, 4},
- MatrixData{"mat3x2<unknown>", 3, 2},
- MatrixData{"mat3x3<unknown>", 3, 3},
- MatrixData{"mat3x4<unknown>", 3, 4},
- MatrixData{"mat4x2<unknown>", 4, 2},
- MatrixData{"mat4x3<unknown>", 4, 3},
- MatrixData{"mat4x4<unknown>", 4, 4}));
+INSTANTIATE_TEST_SUITE_P(
+ ParserImplTest,
+ MatrixBadType,
+ testing::Values(MatrixData{"mat2x2<unknown>", 2, 2, {}},
+ MatrixData{"mat2x3<unknown>", 2, 3, {}},
+ MatrixData{"mat2x4<unknown>", 2, 4, {}},
+ MatrixData{"mat3x2<unknown>", 3, 2, {}},
+ MatrixData{"mat3x3<unknown>", 3, 3, {}},
+ MatrixData{"mat3x4<unknown>", 3, 4, {}},
+ MatrixData{"mat4x2<unknown>", 4, 2, {}},
+ MatrixData{"mat4x3<unknown>", 4, 3, {}},
+ MatrixData{"mat4x4<unknown>", 4, 4, {}}));
class MatrixMissingType : public ParserImplTestWithParam<MatrixData> {};
@@ -732,15 +756,15 @@
}
INSTANTIATE_TEST_SUITE_P(ParserImplTest,
MatrixMissingType,
- testing::Values(MatrixData{"mat2x2<>", 2, 2},
- MatrixData{"mat2x3<>", 2, 3},
- MatrixData{"mat2x4<>", 2, 4},
- MatrixData{"mat3x2<>", 3, 2},
- MatrixData{"mat3x3<>", 3, 3},
- MatrixData{"mat3x4<>", 3, 4},
- MatrixData{"mat4x2<>", 4, 2},
- MatrixData{"mat4x3<>", 4, 3},
- MatrixData{"mat4x4<>", 4, 4}));
+ testing::Values(MatrixData{"mat2x2<>", 2, 2, {}},
+ MatrixData{"mat2x3<>", 2, 3, {}},
+ MatrixData{"mat2x4<>", 2, 4, {}},
+ MatrixData{"mat3x2<>", 3, 2, {}},
+ MatrixData{"mat3x3<>", 3, 3, {}},
+ MatrixData{"mat3x4<>", 3, 4, {}},
+ MatrixData{"mat4x2<>", 4, 2, {}},
+ MatrixData{"mat4x3<>", 4, 3, {}},
+ MatrixData{"mat4x4<>", 4, 4, {}}));
TEST_F(ParserImplTest, TypeDecl_Sampler) {
auto p = parser("sampler");
@@ -755,6 +779,7 @@
EXPECT_EQ(t.value, type);
ASSERT_TRUE(t->Is<sem::Sampler>());
ASSERT_FALSE(t->As<sem::Sampler>()->IsComparison());
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 8u}}));
}
TEST_F(ParserImplTest, TypeDecl_Texture) {
@@ -772,6 +797,7 @@
ASSERT_TRUE(t->Is<sem::Texture>());
ASSERT_TRUE(t->Is<sem::SampledTexture>());
ASSERT_TRUE(t->As<sem::SampledTexture>()->type()->Is<sem::F32>());
+ EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 18u}}));
}
} // namespace
diff --git a/src/reader/wgsl/parser_impl_variable_decl_test.cc b/src/reader/wgsl/parser_impl_variable_decl_test.cc
index 19ed638..d932bd1 100644
--- a/src/reader/wgsl/parser_impl_variable_decl_test.cc
+++ b/src/reader/wgsl/parser_impl_variable_decl_test.cc
@@ -29,10 +29,8 @@
EXPECT_NE(v->type, nullptr);
EXPECT_TRUE(v->type->Is<sem::F32>());
- EXPECT_EQ(v->source.range.begin.line, 1u);
- EXPECT_EQ(v->source.range.begin.column, 5u);
- EXPECT_EQ(v->source.range.end.line, 1u);
- EXPECT_EQ(v->source.range.end.column, 11u);
+ EXPECT_EQ(v->source.range, (Source::Range{{1u, 5u}, {1u, 11u}}));
+ EXPECT_EQ(v->type.ast->source().range, (Source::Range{{1u, 14u}, {1u, 17u}}));
}
TEST_F(ParserImplTest, VariableDecl_MissingVar) {
diff --git a/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc b/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc
index 6802fb3..8d4ac62 100644
--- a/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc
+++ b/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc
@@ -30,10 +30,9 @@
ASSERT_NE(decl->type, nullptr);
ASSERT_TRUE(decl->type->Is<sem::F32>());
- ASSERT_EQ(decl->source.range.begin.line, 1u);
- ASSERT_EQ(decl->source.range.begin.column, 1u);
- ASSERT_EQ(decl->source.range.end.line, 1u);
- ASSERT_EQ(decl->source.range.end.column, 7u);
+ EXPECT_EQ(decl->source.range, (Source::Range{{1u, 1u}, {1u, 7u}}));
+ EXPECT_EQ(decl->type.ast->source().range,
+ (Source::Range{{1u, 10u}, {1u, 13u}}));
}
TEST_F(ParserImplTest, VariableIdentDecl_MissingIdent) {
diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc
index ebb18c0..19acd68 100644
--- a/src/resolver/resolver.cc
+++ b/src/resolver/resolver.cc
@@ -84,13 +84,6 @@
T old_value_;
};
-// Helper function that returns the range union of two source locations. The
-// `start` and `end` locations are assumed to refer to the same source file.
-Source CombineSourceRange(const Source& start, const Source& end) {
- return Source(Source::Range(start.range.begin, end.range.end),
- start.file_path, start.file_content);
-}
-
bool IsValidStorageTextureDimension(ast::TextureDimension dim) {
switch (dim) {
case ast::TextureDimension::k1d:
@@ -1392,7 +1385,7 @@
"attempted to construct '" +
vec_type->FriendlyName(builder_->Symbols()) + "' with " +
std::to_string(value_cardinality_sum) + " component(s)",
- CombineSourceRange(values_start, values_end));
+ Source::Combine(values_start, values_end));
return false;
}
return true;
@@ -1414,7 +1407,7 @@
VectorPretty(matrix_type->rows(), elem_type) + "' arguments in '" +
matrix_type->FriendlyName(builder_->Symbols()) +
"' constructor, found " + std::to_string(values.size()),
- CombineSourceRange(values_start, values_end));
+ Source::Combine(values_start, values_end));
return false;
}
diff --git a/src/source.h b/src/source.h
index 07146da..b74d20e 100644
--- a/src/source.h
+++ b/src/source.h
@@ -18,6 +18,7 @@
#include <iostream>
#include <string>
+#include <tuple>
#include <vector>
namespace tint {
@@ -65,6 +66,27 @@
size_t line = 0;
/// the 1-based column number. 0 represents no column information.
size_t column = 0;
+
+ /// Returns true of `this` location is lexicographically less than `rhs`
+ /// @param rhs location to compare against
+ /// @returns true if `this` < `rhs`
+ inline bool operator<(const Source::Location& rhs) {
+ return std::tie(line, column) < std::tie(rhs.line, rhs.column);
+ }
+
+ /// Returns true of `this` location is equal to `rhs`
+ /// @param rhs location to compare against
+ /// @returns true if `this` == `rhs`
+ inline bool operator==(const Location& rhs) const {
+ return line == rhs.line && column == rhs.column;
+ }
+
+ /// Returns true of `this` location is not equal to `rhs`
+ /// @param rhs location to compare against
+ /// @returns true if `this` != `rhs`
+ inline bool operator!=(const Location& rhs) const {
+ return !(*this == rhs);
+ }
};
/// Range holds a Location interval described by [begin, end).
@@ -75,12 +97,14 @@
/// Constructs a zero-length Range starting at `loc`
/// @param loc the start and end location for the range
- inline explicit Range(const Location& loc) : begin(loc), end(loc) {}
+ inline constexpr explicit Range(const Location& loc)
+ : begin(loc), end(loc) {}
/// Constructs the Range beginning at `b` and ending at `e`
/// @param b the range start location
/// @param e the range end location
- inline Range(const Location& b, const Location& e) : begin(b), end(e) {}
+ inline constexpr Range(const Location& b, const Location& e)
+ : begin(b), end(e) {}
/// Return a column-shifted Range
/// @param n the number of characters to shift by
@@ -89,6 +113,18 @@
return Range{{begin.line, begin.column + n}, {end.line, end.column + n}};
}
+ /// Returns true of `this` range is not equal to `rhs`
+ /// @param rhs range to compare against
+ /// @returns true if `this` != `rhs`
+ inline bool operator==(const Range& rhs) const {
+ return begin == rhs.begin && end == rhs.end;
+ }
+
+ /// Returns true of `this` range is equal to `rhs`
+ /// @param rhs range to compare against
+ /// @returns true if `this` == `rhs`
+ inline bool operator!=(const Range& rhs) const { return !(*this == rhs); }
+
/// The location of the first character in the range.
Location begin;
/// The location of one-past the last character in the range.
@@ -139,6 +175,29 @@
return Source(range + n, file_path, file_content);
}
+ /// Returns true of `this` Source is lexicographically less than `rhs`
+ /// @param rhs source to compare against
+ /// @returns true if `this` < `rhs`
+ inline bool operator<(const Source& rhs) {
+ if (file_path != rhs.file_path) {
+ return false;
+ }
+ if (file_content != rhs.file_content) {
+ return false;
+ }
+ return range.begin < rhs.range.begin;
+ }
+
+ /// Helper function that returns the range union of two source locations. The
+ /// `start` and `end` locations are assumed to refer to the same source file.
+ /// @param start the start source of the range
+ /// @param end the end source of the range
+ /// @returns the combined source
+ inline static Source Combine(const Source& start, const Source& end) {
+ return Source(Source::Range(start.range.begin, end.range.end),
+ start.file_path, start.file_content);
+ }
+
/// range is the span of text this source refers to in #file_path
Range range;
/// file is the optional file path this source refers to
@@ -147,6 +206,25 @@
const FileContent* file_content = nullptr;
};
+/// Writes the Source::Location to the std::ostream.
+/// @param out the std::ostream to write to
+/// @param loc the location to write
+/// @returns out so calls can be chained
+inline std::ostream& operator<<(std::ostream& out,
+ const Source::Location& loc) {
+ out << loc.line << ":" << loc.column;
+ return out;
+}
+
+/// Writes the Source::Range to the std::ostream.
+/// @param out the std::ostream to write to
+/// @param range the range to write
+/// @returns out so calls can be chained
+inline std::ostream& operator<<(std::ostream& out, const Source::Range& range) {
+ out << "[" << range.begin << ", " << range.end << "]";
+ return out;
+}
+
/// Writes the Source to the std::ostream.
/// @param out the std::ostream to write to
/// @param source the source to write
diff --git a/src/typepair.h b/src/typepair.h
index cc1c724..7e53800 100644
--- a/src/typepair.h
+++ b/src/typepair.h
@@ -33,6 +33,7 @@
class Array;
class Bool;
class DepthTexture;
+class ExternalTexture;
class F32;
class I32;
class Matrix;
@@ -54,6 +55,7 @@
class ArrayType;
class Bool;
class DepthTexture;
+class ExternalTexture;
class F32;
class I32;
class Matrix;
@@ -145,6 +147,7 @@
using Array = TypePair<ast::Array, sem::ArrayType>;
using Bool = TypePair<ast::Bool, sem::Bool>;
using DepthTexture = TypePair<ast::DepthTexture, sem::DepthTexture>;
+using ExternalTexture = TypePair<ast::ExternalTexture, sem::ExternalTexture>;
using F32 = TypePair<ast::F32, sem::F32>;
using I32 = TypePair<ast::I32, sem::I32>;
using Matrix = TypePair<ast::Matrix, sem::Matrix>;