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>
This commit is contained in:
Antonio Maiorano 2021-04-27 17:32:37 +00:00 committed by Commit Bot service account
parent a810d71df5
commit 4b16a160d5
16 changed files with 532 additions and 215 deletions

View File

@ -28,6 +28,7 @@
#include "src/ast/call_expression.h" #include "src/ast/call_expression.h"
#include "src/ast/case_statement.h" #include "src/ast/case_statement.h"
#include "src/ast/depth_texture.h" #include "src/ast/depth_texture.h"
#include "src/ast/external_texture.h"
#include "src/ast/f32.h" #include "src/ast/f32.h"
#include "src/ast/float_literal.h" #include "src/ast/float_literal.h"
#include "src/ast/i32.h" #include "src/ast/i32.h"
@ -62,6 +63,7 @@
#include "src/sem/array_type.h" #include "src/sem/array_type.h"
#include "src/sem/bool_type.h" #include "src/sem/bool_type.h"
#include "src/sem/depth_texture_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/f32_type.h"
#include "src/sem/i32_type.h" #include "src/sem/i32_type.h"
#include "src/sem/matrix_type.h" #include "src/sem/matrix_type.h"
@ -352,26 +354,56 @@ class ProgramBuilder {
return {builder->create<ast::Bool>(), builder->create<sem::Bool>()}; 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 /// @returns a f32 type
typ::F32 f32() const { typ::F32 f32() const {
return {builder->create<ast::F32>(), builder->create<sem::F32>()}; 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 /// @returns a i32 type
typ::I32 i32() const { typ::I32 i32() const {
return {builder->create<ast::I32>(), builder->create<sem::I32>()}; 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 /// @returns a u32 type
typ::U32 u32() const { typ::U32 u32() const {
return {builder->create<ast::U32>(), builder->create<sem::U32>()}; 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 /// @returns a void type
typ::Void void_() const { typ::Void void_() const {
return {builder->create<ast::Void>(), builder->create<sem::Void>()}; 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 type vector subtype
/// @param n vector width in elements /// @param n vector width in elements
/// @return the tint AST type for a `n`-element vector of `type`. /// @return the tint AST type for a `n`-element vector of `type`.
@ -380,6 +412,15 @@ class ProgramBuilder {
builder->create<sem::Vector>(type, n)}; 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 /// @param type vector subtype
/// @return the tint AST type for a 2-element vector of `type`. /// @return the tint AST type for a 2-element vector of `type`.
typ::Vector vec2(typ::Type type) const { return vec(type, 2u); } typ::Vector vec2(typ::Type type) const { return vec(type, 2u); }
@ -426,6 +467,19 @@ class ProgramBuilder {
builder->create<sem::Matrix>(type, rows, columns)}; 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 /// @param type matrix subtype
/// @return the tint AST type for a 2x3 matrix of `type`. /// @return the tint AST type for a 2x3 matrix of `type`.
typ::Matrix mat2x2(typ::Type type) const { typ::Matrix mat2x2(typ::Type type) const {
@ -560,7 +614,21 @@ class ProgramBuilder {
ast::DecorationList decos = {}) const { ast::DecorationList decos = {}) const {
subtype = MaybeCreateTypename(subtype); subtype = MaybeCreateTypename(subtype);
return {builder->create<ast::Array>(subtype, n, decos), 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 /// @param subtype the array element type
@ -600,6 +668,21 @@ class ProgramBuilder {
}; };
} }
/// 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 /// Creates an access control qualifier type
/// @param access the access control /// @param access the access control
/// @param type the inner type /// @param type the inner type
@ -611,6 +694,19 @@ class ProgramBuilder {
builder->create<sem::AccessControl>(access, type)}; 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 type the type of the pointer
/// @param storage_class the storage class of the pointer /// @param storage_class the storage class of the pointer
/// @return the pointer to `type` with the given ast::StorageClass /// @return the pointer to `type` with the given ast::StorageClass
@ -621,6 +717,18 @@ class ProgramBuilder {
builder->create<sem::Pointer>(type, storage_class)}; 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 /// @param storage_class the storage class of the pointer
/// @return the pointer to type `T` with the given ast::StorageClass. /// @return the pointer to type `T` with the given ast::StorageClass.
template <typename T> template <typename T>
@ -641,6 +749,14 @@ class ProgramBuilder {
builder->create<sem::Sampler>(kind)}; 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 /// @param dims the dimensionality of the texture
/// @returns the depth texture /// @returns the depth texture
typ::DepthTexture depth_texture(ast::TextureDimension dims) const { typ::DepthTexture depth_texture(ast::TextureDimension dims) const {
@ -648,6 +764,15 @@ class ProgramBuilder {
builder->create<sem::DepthTexture>(dims)}; 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 dims the dimensionality of the texture
/// @param subtype the texture subtype. /// @param subtype the texture subtype.
/// @returns the sampled texture /// @returns the sampled texture
@ -657,6 +782,17 @@ class ProgramBuilder {
builder->create<sem::SampledTexture>(dims, subtype)}; 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 dims the dimensionality of the texture
/// @param subtype the texture subtype. /// @param subtype the texture subtype.
/// @returns the multisampled texture /// @returns the multisampled texture
@ -666,6 +802,17 @@ class ProgramBuilder {
builder->create<sem::MultisampledTexture>(dims, subtype)}; 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 dims the dimensionality of the texture
/// @param format the image format of the texture /// @param format the image format of the texture
/// @returns the storage texture /// @returns the storage texture
@ -678,6 +825,28 @@ class ProgramBuilder {
builder->create<sem::StorageTexture>(dims, format, sem_subtype)}; 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 /// 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. /// ast::TypeName of the given type's name, otherwise type is returned.
/// @param type the type /// @param type the type

View File

@ -177,9 +177,38 @@ struct BlockCounters {
return 0; return 0;
} }
}; };
} // namespace } // 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() = default;
ParserImpl::TypedIdentifier::TypedIdentifier(const TypedIdentifier&) = default; ParserImpl::TypedIdentifier::TypedIdentifier(const TypedIdentifier&) = default;
@ -266,15 +295,16 @@ Token ParserImpl::next() {
if (!token_queue_.empty()) { if (!token_queue_.empty()) {
auto t = token_queue_.front(); auto t = token_queue_.front();
token_queue_.pop_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) { Token ParserImpl::peek(size_t idx) {
while (token_queue_.size() < (idx + 1)) while (token_queue_.size() < (idx + 1))
token_queue_.push_back(lexer_->next()); token_queue_.push_back(lexer_->next());
return token_queue_[idx]; return token_queue_[idx];
} }
@ -282,6 +312,10 @@ Token ParserImpl::peek() {
return peek(0); return peek(0);
} }
Token ParserImpl::last_token() const {
return last_token_;
}
void ParserImpl::register_constructed(const std::string& name, void ParserImpl::register_constructed(const std::string& name,
sem::Type* type) { sem::Type* type) {
registered_constructs_[name] = type; registered_constructs_[name] = type;
@ -560,6 +594,8 @@ Maybe<typ::Type> ParserImpl::texture_sampler_types() {
if (type.matched) if (type.matched)
return type.value; return type.value;
auto source_range = make_source_range();
auto dim = sampled_texture_type(); auto dim = sampled_texture_type();
if (dim.matched) { if (dim.matched) {
const char* use = "sampled texture type"; const char* use = "sampled texture type";
@ -568,9 +604,7 @@ Maybe<typ::Type> ParserImpl::texture_sampler_types() {
if (subtype.errored) if (subtype.errored)
return Failure::kErrored; return Failure::kErrored;
return typ::Type{ return builder_.ty.sampled_texture(source_range, dim.value, subtype.value);
builder_.create<ast::SampledTexture>(dim.value, subtype.value),
builder_.create<sem::SampledTexture>(dim.value, subtype.value)};
} }
auto ms_dim = multisampled_texture_type(); auto ms_dim = multisampled_texture_type();
@ -581,9 +615,8 @@ Maybe<typ::Type> ParserImpl::texture_sampler_types() {
if (subtype.errored) if (subtype.errored)
return Failure::kErrored; return Failure::kErrored;
return typ::Type{ return builder_.ty.multisampled_texture(source_range, ms_dim.value,
builder_.create<ast::MultisampledTexture>(ms_dim.value, subtype.value), subtype.value);
builder_.create<sem::MultisampledTexture>(ms_dim.value, subtype.value)};
} }
auto storage = storage_texture_type(); auto storage = storage_texture_type();
@ -596,14 +629,8 @@ Maybe<typ::Type> ParserImpl::texture_sampler_types() {
if (format.errored) if (format.errored)
return Failure::kErrored; return Failure::kErrored;
auto* subtype = ast::StorageTexture::SubtypeFor(format.value, builder_); return builder_.ty.storage_texture(source_range, storage.value,
auto* subtype_sem = format.value);
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 Failure::kNoMatch; return Failure::kNoMatch;
@ -613,14 +640,12 @@ Maybe<typ::Type> ParserImpl::texture_sampler_types() {
// : SAMPLER // : SAMPLER
// | SAMPLER_COMPARISON // | SAMPLER_COMPARISON
Maybe<typ::Type> ParserImpl::sampler_type() { Maybe<typ::Type> ParserImpl::sampler_type() {
if (match(Token::Type::kSampler)) Source source;
return typ::Type{builder_.create<ast::Sampler>(ast::SamplerKind::kSampler), if (match(Token::Type::kSampler, &source))
builder_.create<sem::Sampler>(ast::SamplerKind::kSampler)}; return builder_.ty.sampler(source, ast::SamplerKind::kSampler);
if (match(Token::Type::kComparisonSampler)) if (match(Token::Type::kComparisonSampler, &source))
return typ::Type{ return builder_.ty.sampler(source, ast::SamplerKind::kComparisonSampler);
builder_.create<ast::Sampler>(ast::SamplerKind::kComparisonSampler),
builder_.create<sem::Sampler>(ast::SamplerKind::kComparisonSampler)};
return Failure::kNoMatch; return Failure::kNoMatch;
} }
@ -657,9 +682,9 @@ Maybe<ast::TextureDimension> ParserImpl::sampled_texture_type() {
// external_texture_type // external_texture_type
// : TEXTURE_EXTERNAL // : TEXTURE_EXTERNAL
Maybe<typ::Type> ParserImpl::external_texture_type() { Maybe<typ::Type> ParserImpl::external_texture_type() {
if (match(Token::Type::kTextureExternal)) { Source source;
return typ::Type{builder_.create<ast::ExternalTexture>(), if (match(Token::Type::kTextureExternal, &source)) {
builder_.create<sem::ExternalTexture>()}; return builder_.ty.external_texture(source);
} }
return Failure::kNoMatch; return Failure::kNoMatch;
@ -698,25 +723,19 @@ Maybe<ast::TextureDimension> ParserImpl::storage_texture_type() {
// | TEXTURE_DEPTH_CUBE // | TEXTURE_DEPTH_CUBE
// | TEXTURE_DEPTH_CUBE_ARRAY // | TEXTURE_DEPTH_CUBE_ARRAY
Maybe<typ::Type> ParserImpl::depth_texture_type() { Maybe<typ::Type> ParserImpl::depth_texture_type() {
if (match(Token::Type::kTextureDepth2d)) Source source;
return typ::Type{
builder_.create<ast::DepthTexture>(ast::TextureDimension::k2d),
builder_.create<sem::DepthTexture>(ast::TextureDimension::k2d)};
if (match(Token::Type::kTextureDepth2dArray)) if (match(Token::Type::kTextureDepth2d, &source))
return typ::Type{ return builder_.ty.depth_texture(source, ast::TextureDimension::k2d);
builder_.create<ast::DepthTexture>(ast::TextureDimension::k2dArray),
builder_.create<sem::DepthTexture>(ast::TextureDimension::k2dArray)};
if (match(Token::Type::kTextureDepthCube)) if (match(Token::Type::kTextureDepth2dArray, &source))
return typ::Type{ return builder_.ty.depth_texture(source, ast::TextureDimension::k2dArray);
builder_.create<ast::DepthTexture>(ast::TextureDimension::kCube),
builder_.create<sem::DepthTexture>(ast::TextureDimension::kCube)};
if (match(Token::Type::kTextureDepthCubeArray)) if (match(Token::Type::kTextureDepthCube, &source))
return typ::Type{ return builder_.ty.depth_texture(source, ast::TextureDimension::kCube);
builder_.create<ast::DepthTexture>(ast::TextureDimension::kCubeArray),
builder_.create<sem::DepthTexture>(ast::TextureDimension::kCubeArray)}; if (match(Token::Type::kTextureDepthCubeArray, &source))
return builder_.ty.depth_texture(source, ast::TextureDimension::kCubeArray);
return Failure::kNoMatch; return Failure::kNoMatch;
} }
@ -897,26 +916,15 @@ Expect<ParserImpl::TypedIdentifier> ParserImpl::expect_variable_ident_decl(
if (access_decos.size() > 1) if (access_decos.size() > 1)
return add_error(ident.source, "multiple access decorations not allowed"); return add_error(ident.source, "multiple access decorations not allowed");
// TODO(crbug.com/tint/724): Remove typ::Type ty = type.value;
auto* sem_ty = type.value.sem;
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);
}
auto* ty = type.value.ast;
// TODO(crbug.com/tint/724): Remove 'if'
if (ty) {
for (auto* deco : access_decos) { for (auto* deco : access_decos) {
// If we have an access control decoration then we take it and wrap our // If we have an access control decoration then we take it and wrap our
// type up with that decoration // type up with that decoration
ty = builder_.create<ast::AccessControl>( ty = builder_.ty.access(deco->source(),
deco->As<ast::AccessDecoration>()->value(), ty); deco->As<ast::AccessDecoration>()->value(), ty);
} }
} return TypedIdentifier{ty, ident.value, ident.source};
return TypedIdentifier{typ::Type{ty, sem_ty}, ident.value, ident.source};
} }
Expect<ast::AccessControl::Access> ParserImpl::expect_access_type() { Expect<ast::AccessControl::Access> ParserImpl::expect_access_type() {
@ -974,14 +982,10 @@ Maybe<typ::Type> ParserImpl::type_alias() {
if (!type.matched) if (!type.matched)
return add_error(peek(), "invalid type alias"); return add_error(peek(), "invalid type alias");
// TODO(crbug.com/tint/724): remove auto alias = builder_.ty.alias(make_source_range_from(t.source()), name.value,
auto* alias = builder_.create<sem::Alias>( type.value);
builder_.Symbols().Register(name.value), type.value);
register_constructed(name.value, alias); register_constructed(name.value, alias);
return alias;
return typ::Type{builder_.create<ast::Alias>(
builder_.Symbols().Register(name.value), type.value),
alias};
} }
// type_decl // type_decl
@ -1027,29 +1031,29 @@ Maybe<typ::Type> ParserImpl::type_decl() {
Maybe<typ::Type> ParserImpl::type_decl(ast::DecorationList& decos) { Maybe<typ::Type> ParserImpl::type_decl(ast::DecorationList& decos) {
auto t = peek(); auto t = peek();
if (match(Token::Type::kIdentifier)) { Source source;
if (match(Token::Type::kIdentifier, &source)) {
// TODO(crbug.com/tint/697): Remove // TODO(crbug.com/tint/697): Remove
auto* ty = get_constructed(t.to_str()); auto* ty = get_constructed(t.to_str());
if (ty == nullptr) if (ty == nullptr)
return add_error(t, "unknown constructed type '" + t.to_str() + "'"); return add_error(t, "unknown constructed type '" + t.to_str() + "'");
return typ::Type{ return typ::Type{builder_.create<ast::TypeName>(
builder_.create<ast::TypeName>(builder_.Symbols().Register(t.to_str())), source, builder_.Symbols().Register(t.to_str())),
ty}; ty};
} }
if (match(Token::Type::kBool)) if (match(Token::Type::kBool, &source))
return typ::Type{builder_.create<ast::Bool>(), return builder_.ty.bool_(source);
builder_.create<sem::Bool>()};
if (match(Token::Type::kF32)) if (match(Token::Type::kF32, &source))
return typ::Type{builder_.create<ast::F32>(), builder_.create<sem::F32>()}; return builder_.ty.f32(source);
if (match(Token::Type::kI32)) if (match(Token::Type::kI32, &source))
return typ::Type{builder_.create<ast::I32>(), builder_.create<sem::I32>()}; return builder_.ty.i32(source);
if (match(Token::Type::kU32)) if (match(Token::Type::kU32, &source))
return typ::Type{builder_.create<ast::U32>(), builder_.create<sem::U32>()}; return builder_.ty.u32(source);
if (t.IsVec2() || t.IsVec3() || t.IsVec4()) { if (t.IsVec2() || t.IsVec3() || t.IsVec4()) {
next(); // Consume the peek next(); // Consume the peek
@ -1057,10 +1061,10 @@ Maybe<typ::Type> ParserImpl::type_decl(ast::DecorationList& decos) {
} }
if (match(Token::Type::kPtr)) if (match(Token::Type::kPtr))
return expect_type_decl_pointer(); return expect_type_decl_pointer(t);
if (match(Token::Type::kArray)) { if (match(Token::Type::kArray, &source)) {
return expect_type_decl_array(std::move(decos)); return expect_type_decl_array(t, std::move(decos));
} }
if (t.IsMat2x2() || t.IsMat2x3() || t.IsMat2x4() || t.IsMat3x2() || if (t.IsMat2x2() || t.IsMat2x3() || t.IsMat2x4() || t.IsMat3x2() ||
@ -1088,24 +1092,33 @@ Expect<typ::Type> ParserImpl::expect_type(const std::string& use) {
return type.value; 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"; 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); auto sc = expect_storage_class(use);
if (sc.errored) if (sc.errored)
return Failure::kErrored; return Failure::kErrored;
storage_class = sc.value;
if (!expect(use, Token::Type::kComma)) if (!expect(use, Token::Type::kComma))
return Failure::kErrored; return Failure::kErrored;
auto subtype = expect_type(use); auto type = expect_type(use);
if (subtype.errored) if (type.errored)
return Failure::kErrored; return Failure::kErrored;
return typ::Type{builder_.create<ast::Pointer>(subtype.value, sc.value), return type.value;
builder_.create<sem::Pointer>(subtype.value, sc.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) { Expect<typ::Type> ParserImpl::expect_type_decl_vector(Token t) {
@ -1121,20 +1134,22 @@ Expect<typ::Type> ParserImpl::expect_type_decl_vector(Token t) {
if (subtype.errored) if (subtype.errored)
return Failure::kErrored; return Failure::kErrored;
return typ::Type{builder_.create<ast::Vector>(subtype.value.ast, count), return builder_.ty.vec(make_source_range_from(t.source()), subtype.value,
builder_.create<sem::Vector>(subtype.value.sem, count)}; count);
} }
Expect<typ::Type> ParserImpl::expect_type_decl_array( Expect<typ::Type> ParserImpl::expect_type_decl_array(
Token t,
ast::DecorationList decos) { ast::DecorationList decos) {
const char* use = "array declaration"; const char* use = "array declaration";
return expect_lt_gt_block(use, [&]() -> Expect<typ::Type> { uint32_t size = 0;
auto subtype = expect_type(use);
if (subtype.errored) auto subtype = expect_lt_gt_block(use, [&]() -> Expect<typ::Type> {
auto type = expect_type(use);
if (type.errored)
return Failure::kErrored; return Failure::kErrored;
uint32_t size = 0;
if (match(Token::Type::kComma)) { if (match(Token::Type::kComma)) {
auto val = expect_nonzero_positive_sint("array size"); auto val = expect_nonzero_positive_sint("array size");
if (val.errored) if (val.errored)
@ -1142,10 +1157,15 @@ Expect<typ::Type> ParserImpl::expect_type_decl_array(
size = val.value; size = val.value;
} }
return typ::Type{ return type.value;
create<ast::Array>(subtype.value, size, decos),
create<sem::ArrayType>(subtype.value, size, std::move(decos))};
}); });
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) { Expect<typ::Type> ParserImpl::expect_type_decl_matrix(Token t) {
@ -1168,8 +1188,8 @@ Expect<typ::Type> ParserImpl::expect_type_decl_matrix(Token t) {
if (subtype.errored) if (subtype.errored)
return Failure::kErrored; return Failure::kErrored;
return typ::Type{builder_.create<ast::Matrix>(subtype.value, rows, columns), return builder_.ty.mat(make_source_range_from(t.source()), subtype.value,
builder_.create<sem::Matrix>(subtype.value, rows, columns)}; columns, rows);
} }
// storage_class // storage_class
@ -1321,9 +1341,9 @@ Maybe<ast::Function*> ParserImpl::function_decl(ast::DecorationList& decos) {
// : type_decl // : type_decl
// | VOID // | VOID
Maybe<typ::Type> ParserImpl::function_type_decl() { Maybe<typ::Type> ParserImpl::function_type_decl() {
if (match(Token::Type::kVoid)) Source source;
return typ::Type{builder_.create<ast::Void>(), if (match(Token::Type::kVoid, &source))
builder_.create<sem::Void>()}; return builder_.ty.void_(source);
return type_decl(); return type_decl();
} }
@ -3385,6 +3405,15 @@ T ParserImpl::without_error(F&& body) {
return result; 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 wgsl
} // namespace reader } // namespace reader
} // namespace tint } // namespace tint

View File

@ -331,6 +331,8 @@ class ParserImpl {
/// @param idx the index of the token to return /// @param idx the index of the token to return
/// @returns the token `idx` positions ahead without advancing /// @returns the token `idx` positions ahead without advancing
Token peek(size_t idx); 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` /// Appends an error at `t` with the message `msg`
/// @param t the token to associate the error with /// @param t the token to associate the error with
/// @param msg the error message /// @param msg the error message
@ -821,9 +823,9 @@ class ParserImpl {
/// Used to ensure that all decorations are consumed. /// Used to ensure that all decorations are consumed.
bool expect_decorations_consumed(const ast::DecorationList& list); 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_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_decl_matrix(Token t);
Expect<typ::Type> expect_type(const std::string& use); Expect<typ::Type> expect_type(const std::string& use);
@ -832,6 +834,10 @@ class ParserImpl {
Maybe<ast::Statement*> for_header_initializer(); Maybe<ast::Statement*> for_header_initializer();
Maybe<ast::Statement*> for_header_continuing(); 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 /// Creates a new `ast::Node` owned by the Module. When the Module is
/// destructed, the `ast::Node` will also be destructed. /// destructed, the `ast::Node` will also be destructed.
/// @param args the arguments to pass to the type constructor /// @param args the arguments to pass to the type constructor
@ -843,6 +849,7 @@ class ParserImpl {
std::unique_ptr<Lexer> lexer_; std::unique_ptr<Lexer> lexer_;
std::deque<Token> token_queue_; std::deque<Token> token_queue_;
Token last_token_;
bool synchronized_ = true; bool synchronized_ = true;
uint32_t sync_depth_ = 0; uint32_t sync_depth_ = 0;
std::vector<Token::Type> sync_tokens_; std::vector<Token::Type> sync_tokens_;

View File

@ -38,6 +38,7 @@ TEST_F(ParserImplTest, DepthTextureType_2d) {
ASSERT_TRUE(t->Is<sem::DepthTexture>()); ASSERT_TRUE(t->Is<sem::DepthTexture>());
EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k2d); EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k2d);
EXPECT_FALSE(p->has_error()); EXPECT_FALSE(p->has_error());
EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 17u}}));
} }
TEST_F(ParserImplTest, DepthTextureType_2dArray) { TEST_F(ParserImplTest, DepthTextureType_2dArray) {
@ -50,6 +51,7 @@ TEST_F(ParserImplTest, DepthTextureType_2dArray) {
ASSERT_TRUE(t->Is<sem::DepthTexture>()); ASSERT_TRUE(t->Is<sem::DepthTexture>());
EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k2dArray); EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k2dArray);
EXPECT_FALSE(p->has_error()); EXPECT_FALSE(p->has_error());
EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 23u}}));
} }
TEST_F(ParserImplTest, DepthTextureType_Cube) { TEST_F(ParserImplTest, DepthTextureType_Cube) {
@ -62,6 +64,7 @@ TEST_F(ParserImplTest, DepthTextureType_Cube) {
ASSERT_TRUE(t->Is<sem::DepthTexture>()); ASSERT_TRUE(t->Is<sem::DepthTexture>());
EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::kCube); EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::kCube);
EXPECT_FALSE(p->has_error()); EXPECT_FALSE(p->has_error());
EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 19u}}));
} }
TEST_F(ParserImplTest, DepthTextureType_CubeArray) { TEST_F(ParserImplTest, DepthTextureType_CubeArray) {
@ -74,6 +77,7 @@ TEST_F(ParserImplTest, DepthTextureType_CubeArray) {
ASSERT_TRUE(t->Is<sem::DepthTexture>()); ASSERT_TRUE(t->Is<sem::DepthTexture>());
EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::kCubeArray); EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::kCubeArray);
EXPECT_FALSE(p->has_error()); EXPECT_FALSE(p->has_error());
EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 25u}}));
} }
} // namespace } // namespace

View File

@ -32,6 +32,7 @@ TEST_F(ParserImplTest, ExternalTextureType) {
auto t = p->external_texture_type(); auto t = p->external_texture_type();
EXPECT_TRUE(t.matched); EXPECT_TRUE(t.matched);
EXPECT_FALSE(t.errored); EXPECT_FALSE(t.errored);
EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 17u}}));
} }
} // namespace } // namespace

View File

@ -29,6 +29,7 @@ TEST_F(ParserImplTest, FunctionTypeDecl_Void) {
EXPECT_FALSE(e.errored); EXPECT_FALSE(e.errored);
EXPECT_FALSE(p->has_error()) << p->error(); EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_EQ(e.value, v); ASSERT_EQ(e.value, v);
EXPECT_EQ(e.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 5u}}));
} }
TEST_F(ParserImplTest, FunctionTypeDecl_Type) { TEST_F(ParserImplTest, FunctionTypeDecl_Type) {
@ -42,6 +43,7 @@ TEST_F(ParserImplTest, FunctionTypeDecl_Type) {
EXPECT_FALSE(e.errored); EXPECT_FALSE(e.errored);
EXPECT_FALSE(p->has_error()) << p->error(); EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_EQ(e.value, vec2); ASSERT_EQ(e.value, vec2);
EXPECT_EQ(e.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 10u}}));
} }
TEST_F(ParserImplTest, FunctionTypeDecl_InvalidType) { TEST_F(ParserImplTest, FunctionTypeDecl_InvalidType) {

View File

@ -37,6 +37,7 @@ TEST_F(ParserImplTest, SamplerType_Sampler) {
ASSERT_TRUE(t->Is<sem::Sampler>()); ASSERT_TRUE(t->Is<sem::Sampler>());
EXPECT_FALSE(t->As<sem::Sampler>()->IsComparison()); EXPECT_FALSE(t->As<sem::Sampler>()->IsComparison());
EXPECT_FALSE(p->has_error()); EXPECT_FALSE(p->has_error());
EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 8u}}));
} }
TEST_F(ParserImplTest, SamplerType_ComparisonSampler) { TEST_F(ParserImplTest, SamplerType_ComparisonSampler) {
@ -48,6 +49,7 @@ TEST_F(ParserImplTest, SamplerType_ComparisonSampler) {
ASSERT_TRUE(t->Is<sem::Sampler>()); ASSERT_TRUE(t->Is<sem::Sampler>());
EXPECT_TRUE(t->As<sem::Sampler>()->IsComparison()); EXPECT_TRUE(t->As<sem::Sampler>()->IsComparison());
EXPECT_FALSE(p->has_error()); EXPECT_FALSE(p->has_error());
EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 19u}}));
} }
} // namespace } // namespace

View File

@ -39,10 +39,8 @@ TEST_F(ParserImplTest, StructMember_Parses) {
EXPECT_EQ(m->type(), i32); EXPECT_EQ(m->type(), i32);
EXPECT_EQ(m->decorations().size(), 0u); EXPECT_EQ(m->decorations().size(), 0u);
ASSERT_EQ(m->source().range.begin.line, 1u); EXPECT_EQ(m->source().range, (Source::Range{{1u, 1u}, {1u, 2u}}));
ASSERT_EQ(m->source().range.begin.column, 1u); EXPECT_EQ(m->type().ast->source().range, (Source::Range{{1u, 5u}, {1u, 8u}}));
ASSERT_EQ(m->source().range.end.line, 1u);
ASSERT_EQ(m->source().range.end.column, 2u);
} }
TEST_F(ParserImplTest, StructMember_ParsesWithOffsetDecoration_DEPRECATED) { TEST_F(ParserImplTest, StructMember_ParsesWithOffsetDecoration_DEPRECATED) {
@ -69,10 +67,9 @@ TEST_F(ParserImplTest, StructMember_ParsesWithOffsetDecoration_DEPRECATED) {
m->decorations()[0]->As<ast::StructMemberOffsetDecoration>()->offset(), m->decorations()[0]->As<ast::StructMemberOffsetDecoration>()->offset(),
2u); 2u);
ASSERT_EQ(m->source().range.begin.line, 1u); EXPECT_EQ(m->source().range, (Source::Range{{1u, 15u}, {1u, 16u}}));
ASSERT_EQ(m->source().range.begin.column, 15u); EXPECT_EQ(m->type().ast->source().range,
ASSERT_EQ(m->source().range.end.line, 1u); (Source::Range{{1u, 19u}, {1u, 22u}}));
ASSERT_EQ(m->source().range.end.column, 16u);
} }
TEST_F(ParserImplTest, StructMember_ParsesWithAlignDecoration) { TEST_F(ParserImplTest, StructMember_ParsesWithAlignDecoration) {
@ -98,10 +95,9 @@ TEST_F(ParserImplTest, StructMember_ParsesWithAlignDecoration) {
EXPECT_EQ( EXPECT_EQ(
m->decorations()[0]->As<ast::StructMemberAlignDecoration>()->align(), 2u); m->decorations()[0]->As<ast::StructMemberAlignDecoration>()->align(), 2u);
ASSERT_EQ(m->source().range.begin.line, 1u); EXPECT_EQ(m->source().range, (Source::Range{{1u, 14u}, {1u, 15u}}));
ASSERT_EQ(m->source().range.begin.column, 14u); EXPECT_EQ(m->type().ast->source().range,
ASSERT_EQ(m->source().range.end.line, 1u); (Source::Range{{1u, 18u}, {1u, 21u}}));
ASSERT_EQ(m->source().range.end.column, 15u);
} }
TEST_F(ParserImplTest, StructMember_ParsesWithSizeDecoration) { TEST_F(ParserImplTest, StructMember_ParsesWithSizeDecoration) {
@ -127,10 +123,9 @@ TEST_F(ParserImplTest, StructMember_ParsesWithSizeDecoration) {
EXPECT_EQ(m->decorations()[0]->As<ast::StructMemberSizeDecoration>()->size(), EXPECT_EQ(m->decorations()[0]->As<ast::StructMemberSizeDecoration>()->size(),
2u); 2u);
ASSERT_EQ(m->source().range.begin.line, 1u); EXPECT_EQ(m->source().range, (Source::Range{{1u, 13u}, {1u, 14u}}));
ASSERT_EQ(m->source().range.begin.column, 13u); EXPECT_EQ(m->type().ast->source().range,
ASSERT_EQ(m->source().range.end.line, 1u); (Source::Range{{1u, 17u}, {1u, 20u}}));
ASSERT_EQ(m->source().range.end.column, 14u);
} }
TEST_F(ParserImplTest, StructMember_ParsesWithDecoration) { TEST_F(ParserImplTest, StructMember_ParsesWithDecoration) {
@ -156,10 +151,9 @@ TEST_F(ParserImplTest, StructMember_ParsesWithDecoration) {
EXPECT_EQ(m->decorations()[0]->As<ast::StructMemberSizeDecoration>()->size(), EXPECT_EQ(m->decorations()[0]->As<ast::StructMemberSizeDecoration>()->size(),
2u); 2u);
ASSERT_EQ(m->source().range.begin.line, 1u); EXPECT_EQ(m->source().range, (Source::Range{{1u, 13u}, {1u, 14u}}));
ASSERT_EQ(m->source().range.begin.column, 13u); EXPECT_EQ(m->type().ast->source().range,
ASSERT_EQ(m->source().range.end.line, 1u); (Source::Range{{1u, 17u}, {1u, 20u}}));
ASSERT_EQ(m->source().range.end.column, 14u);
} }
TEST_F(ParserImplTest, StructMember_ParsesWithMultipleDecorations) { TEST_F(ParserImplTest, StructMember_ParsesWithMultipleDecorations) {
@ -189,10 +183,9 @@ TEST_F(ParserImplTest, StructMember_ParsesWithMultipleDecorations) {
EXPECT_EQ( EXPECT_EQ(
m->decorations()[1]->As<ast::StructMemberAlignDecoration>()->align(), 4u); m->decorations()[1]->As<ast::StructMemberAlignDecoration>()->align(), 4u);
ASSERT_EQ(m->source().range.begin.line, 2u); EXPECT_EQ(m->source().range, (Source::Range{{2u, 14u}, {2u, 15u}}));
ASSERT_EQ(m->source().range.begin.column, 14u); EXPECT_EQ(m->type().ast->source().range,
ASSERT_EQ(m->source().range.end.line, 2u); (Source::Range{{2u, 18u}, {2u, 21u}}));
ASSERT_EQ(m->source().range.end.column, 15u);
} }
TEST_F(ParserImplTest, StructMember_InvalidDecoration) { TEST_F(ParserImplTest, StructMember_InvalidDecoration) {

View File

@ -40,6 +40,7 @@ TEST_F(ParserImplTest, TextureSamplerTypes_Sampler) {
ASSERT_NE(t.value, nullptr); ASSERT_NE(t.value, nullptr);
ASSERT_TRUE(t->Is<sem::Sampler>()); ASSERT_TRUE(t->Is<sem::Sampler>());
ASSERT_FALSE(t->As<sem::Sampler>()->IsComparison()); 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) { TEST_F(ParserImplTest, TextureSamplerTypes_SamplerComparison) {
@ -51,6 +52,7 @@ TEST_F(ParserImplTest, TextureSamplerTypes_SamplerComparison) {
ASSERT_NE(t.value, nullptr); ASSERT_NE(t.value, nullptr);
ASSERT_TRUE(t->Is<sem::Sampler>()); ASSERT_TRUE(t->Is<sem::Sampler>());
ASSERT_TRUE(t->As<sem::Sampler>()->IsComparison()); 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) { TEST_F(ParserImplTest, TextureSamplerTypes_DepthTexture) {
@ -63,6 +65,7 @@ TEST_F(ParserImplTest, TextureSamplerTypes_DepthTexture) {
ASSERT_TRUE(t->Is<sem::Texture>()); ASSERT_TRUE(t->Is<sem::Texture>());
ASSERT_TRUE(t->Is<sem::DepthTexture>()); ASSERT_TRUE(t->Is<sem::DepthTexture>());
EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k2d); 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) { TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_F32) {
@ -76,6 +79,7 @@ TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_F32) {
ASSERT_TRUE(t->Is<sem::SampledTexture>()); ASSERT_TRUE(t->Is<sem::SampledTexture>());
ASSERT_TRUE(t->As<sem::SampledTexture>()->type()->Is<sem::F32>()); ASSERT_TRUE(t->As<sem::SampledTexture>()->type()->Is<sem::F32>());
EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k1d); 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) { TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_I32) {
@ -89,6 +93,7 @@ TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_I32) {
ASSERT_TRUE(t->Is<sem::SampledTexture>()); ASSERT_TRUE(t->Is<sem::SampledTexture>());
ASSERT_TRUE(t->As<sem::SampledTexture>()->type()->Is<sem::I32>()); ASSERT_TRUE(t->As<sem::SampledTexture>()->type()->Is<sem::I32>());
EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k2d); 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) { TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_U32) {
@ -102,6 +107,7 @@ TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_U32) {
ASSERT_TRUE(t->Is<sem::SampledTexture>()); ASSERT_TRUE(t->Is<sem::SampledTexture>());
ASSERT_TRUE(t->As<sem::SampledTexture>()->type()->Is<sem::U32>()); ASSERT_TRUE(t->As<sem::SampledTexture>()->type()->Is<sem::U32>());
EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k3d); 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) { TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_Invalid) {
@ -155,6 +161,7 @@ TEST_F(ParserImplTest, TextureSamplerTypes_MultisampledTexture_I32) {
ASSERT_TRUE(t->Is<sem::MultisampledTexture>()); ASSERT_TRUE(t->Is<sem::MultisampledTexture>());
ASSERT_TRUE(t->As<sem::MultisampledTexture>()->type()->Is<sem::I32>()); ASSERT_TRUE(t->As<sem::MultisampledTexture>()->type()->Is<sem::I32>());
EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k2d); 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) { TEST_F(ParserImplTest, TextureSamplerTypes_MultisampledTexture_Invalid) {
@ -210,6 +217,7 @@ TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_Readonly1dR8Unorm) {
EXPECT_EQ(t->As<sem::StorageTexture>()->image_format(), EXPECT_EQ(t->As<sem::StorageTexture>()->image_format(),
ast::ImageFormat::kR8Unorm); ast::ImageFormat::kR8Unorm);
EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k1d); 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) { TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_Writeonly2dR16Float) {
@ -225,6 +233,7 @@ TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_Writeonly2dR16Float) {
EXPECT_EQ(t->As<sem::StorageTexture>()->image_format(), EXPECT_EQ(t->As<sem::StorageTexture>()->image_format(),
ast::ImageFormat::kR16Float); ast::ImageFormat::kR16Float);
EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k2d); 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) { TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_InvalidType) {

View File

@ -33,6 +33,8 @@ TEST_F(ParserImplTest, TypeDecl_ParsesType) {
auto* alias = t->As<sem::Alias>(); auto* alias = t->As<sem::Alias>();
ASSERT_TRUE(alias->type()->Is<sem::I32>()); ASSERT_TRUE(alias->type()->Is<sem::I32>());
ASSERT_EQ(alias->type(), 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) { TEST_F(ParserImplTest, TypeDecl_ParsesStruct_Ident) {
@ -54,6 +56,8 @@ TEST_F(ParserImplTest, TypeDecl_ParsesStruct_Ident) {
auto* s = alias->type()->As<sem::StructType>(); 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(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) { TEST_F(ParserImplTest, TypeDecl_MissingIdent) {

View File

@ -50,6 +50,7 @@ TEST_F(ParserImplTest, TypeDecl_Identifier) {
auto* alias = t->As<sem::Alias>(); auto* alias = t->As<sem::Alias>();
EXPECT_EQ(p->builder().Symbols().NameFor(alias->symbol()), "A"); EXPECT_EQ(p->builder().Symbols().NameFor(alias->symbol()), "A");
EXPECT_EQ(alias->type(), int_type); 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) { TEST_F(ParserImplTest, TypeDecl_Identifier_NotFound) {
@ -75,6 +76,7 @@ TEST_F(ParserImplTest, TypeDecl_Bool) {
ASSERT_NE(t.value, nullptr) << p->error(); ASSERT_NE(t.value, nullptr) << p->error();
EXPECT_EQ(t.value, bool_type); EXPECT_EQ(t.value, bool_type);
ASSERT_TRUE(t->Is<sem::Bool>()); ASSERT_TRUE(t->Is<sem::Bool>());
EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 5u}}));
} }
TEST_F(ParserImplTest, TypeDecl_F32) { TEST_F(ParserImplTest, TypeDecl_F32) {
@ -89,6 +91,7 @@ TEST_F(ParserImplTest, TypeDecl_F32) {
ASSERT_NE(t.value, nullptr) << p->error(); ASSERT_NE(t.value, nullptr) << p->error();
EXPECT_EQ(t.value, float_type); EXPECT_EQ(t.value, float_type);
ASSERT_TRUE(t->Is<sem::F32>()); ASSERT_TRUE(t->Is<sem::F32>());
EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 4u}}));
} }
TEST_F(ParserImplTest, TypeDecl_I32) { TEST_F(ParserImplTest, TypeDecl_I32) {
@ -103,6 +106,7 @@ TEST_F(ParserImplTest, TypeDecl_I32) {
ASSERT_NE(t.value, nullptr) << p->error(); ASSERT_NE(t.value, nullptr) << p->error();
EXPECT_EQ(t.value, int_type); EXPECT_EQ(t.value, int_type);
ASSERT_TRUE(t->Is<sem::I32>()); ASSERT_TRUE(t->Is<sem::I32>());
EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 4u}}));
} }
TEST_F(ParserImplTest, TypeDecl_U32) { TEST_F(ParserImplTest, TypeDecl_U32) {
@ -117,11 +121,13 @@ TEST_F(ParserImplTest, TypeDecl_U32) {
ASSERT_NE(t.value, nullptr) << p->error(); ASSERT_NE(t.value, nullptr) << p->error();
EXPECT_EQ(t.value, uint_type); EXPECT_EQ(t.value, uint_type);
ASSERT_TRUE(t->Is<sem::U32>()); ASSERT_TRUE(t->Is<sem::U32>());
EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 4u}}));
} }
struct VecData { struct VecData {
const char* input; const char* input;
size_t count; size_t count;
Source::Range range;
}; };
inline std::ostream& operator<<(std::ostream& out, VecData data) { inline std::ostream& operator<<(std::ostream& out, VecData data) {
out << std::string(data.input); out << std::string(data.input);
@ -140,12 +146,16 @@ TEST_P(VecTest, Parse) {
ASSERT_FALSE(p->has_error()); ASSERT_FALSE(p->has_error());
EXPECT_TRUE(t->Is<sem::Vector>()); EXPECT_TRUE(t->Is<sem::Vector>());
EXPECT_EQ(t->As<sem::Vector>()->size(), params.count); EXPECT_EQ(t->As<sem::Vector>()->size(), params.count);
EXPECT_EQ(t.value.ast->source().range, params.range);
} }
INSTANTIATE_TEST_SUITE_P(ParserImplTest, INSTANTIATE_TEST_SUITE_P(
ParserImplTest,
VecTest, VecTest,
testing::Values(VecData{"vec2<f32>", 2}, testing::Values(VecData{"vec2<f32>", 2, Source::Range{{1u, 1u}, {1u, 10u}}},
VecData{"vec3<f32>", 3}, VecData{"vec3<f32>", 3, Source::Range{{1u, 1u}, {1u, 10u}}},
VecData{"vec4<f32>", 4})); VecData{"vec4<f32>", 4, Source::Range{{1u, 1u}, {1u, 10u}}}
));
class VecMissingGreaterThanTest : public ParserImplTestWithParam<VecData> {}; class VecMissingGreaterThanTest : public ParserImplTestWithParam<VecData> {};
@ -161,9 +171,9 @@ TEST_P(VecMissingGreaterThanTest, Handles_Missing_GreaterThan) {
} }
INSTANTIATE_TEST_SUITE_P(ParserImplTest, INSTANTIATE_TEST_SUITE_P(ParserImplTest,
VecMissingGreaterThanTest, VecMissingGreaterThanTest,
testing::Values(VecData{"vec2<f32", 2}, testing::Values(VecData{"vec2<f32", 2, {}},
VecData{"vec3<f32", 3}, VecData{"vec3<f32", 3, {}},
VecData{"vec4<f32", 4})); VecData{"vec4<f32", 4, {}}));
class VecMissingLessThanTest : public ParserImplTestWithParam<VecData> {}; class VecMissingLessThanTest : public ParserImplTestWithParam<VecData> {};
@ -179,9 +189,9 @@ TEST_P(VecMissingLessThanTest, Handles_Missing_GreaterThan) {
} }
INSTANTIATE_TEST_SUITE_P(ParserImplTest, INSTANTIATE_TEST_SUITE_P(ParserImplTest,
VecMissingLessThanTest, VecMissingLessThanTest,
testing::Values(VecData{"vec2", 2}, testing::Values(VecData{"vec2", 2, {}},
VecData{"vec3", 3}, VecData{"vec3", 3, {}},
VecData{"vec4", 4})); VecData{"vec4", 4, {}}));
class VecBadType : public ParserImplTestWithParam<VecData> {}; class VecBadType : public ParserImplTestWithParam<VecData> {};
@ -197,9 +207,9 @@ TEST_P(VecBadType, Handles_Unknown_Type) {
} }
INSTANTIATE_TEST_SUITE_P(ParserImplTest, INSTANTIATE_TEST_SUITE_P(ParserImplTest,
VecBadType, VecBadType,
testing::Values(VecData{"vec2<unknown", 2}, testing::Values(VecData{"vec2<unknown", 2, {}},
VecData{"vec3<unknown", 3}, VecData{"vec3<unknown", 3, {}},
VecData{"vec4<unknown", 4})); VecData{"vec4<unknown", 4, {}}));
class VecMissingType : public ParserImplTestWithParam<VecData> {}; class VecMissingType : public ParserImplTestWithParam<VecData> {};
@ -215,9 +225,9 @@ TEST_P(VecMissingType, Handles_Missing_Type) {
} }
INSTANTIATE_TEST_SUITE_P(ParserImplTest, INSTANTIATE_TEST_SUITE_P(ParserImplTest,
VecMissingType, VecMissingType,
testing::Values(VecData{"vec2<>", 2}, testing::Values(VecData{"vec2<>", 2, {}},
VecData{"vec3<>", 3}, VecData{"vec3<>", 3, {}},
VecData{"vec4<>", 4})); VecData{"vec4<>", 4, {}}));
TEST_F(ParserImplTest, TypeDecl_Ptr) { TEST_F(ParserImplTest, TypeDecl_Ptr) {
auto p = parser("ptr<function, f32>"); auto p = parser("ptr<function, f32>");
@ -231,6 +241,7 @@ TEST_F(ParserImplTest, TypeDecl_Ptr) {
auto* ptr = t->As<sem::Pointer>(); auto* ptr = t->As<sem::Pointer>();
ASSERT_TRUE(ptr->type()->Is<sem::F32>()); ASSERT_TRUE(ptr->type()->Is<sem::F32>());
ASSERT_EQ(ptr->storage_class(), ast::StorageClass::kFunction); 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) { TEST_F(ParserImplTest, TypeDecl_Ptr_ToVec) {
@ -249,6 +260,7 @@ TEST_F(ParserImplTest, TypeDecl_Ptr_ToVec) {
auto* vec = ptr->type()->As<sem::Vector>(); auto* vec = ptr->type()->As<sem::Vector>();
ASSERT_EQ(vec->size(), 2u); ASSERT_EQ(vec->size(), 2u);
ASSERT_TRUE(vec->type()->Is<sem::F32>()); 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) { TEST_F(ParserImplTest, TypeDecl_Ptr_MissingLessThan) {
@ -345,6 +357,7 @@ TEST_F(ParserImplTest, TypeDecl_Array) {
ASSERT_EQ(a->size(), 5u); ASSERT_EQ(a->size(), 5u);
ASSERT_TRUE(a->type()->Is<sem::F32>()); ASSERT_TRUE(a->type()->Is<sem::F32>());
EXPECT_EQ(a->decorations().size(), 0u); 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) { TEST_F(ParserImplTest, TypeDecl_Array_Stride) {
@ -365,6 +378,7 @@ TEST_F(ParserImplTest, TypeDecl_Array_Stride) {
auto* stride = a->decorations()[0]; auto* stride = a->decorations()[0];
ASSERT_TRUE(stride->Is<ast::StrideDecoration>()); ASSERT_TRUE(stride->Is<ast::StrideDecoration>());
ASSERT_EQ(stride->As<ast::StrideDecoration>()->stride(), 16u); 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) { TEST_F(ParserImplTest, TypeDecl_Array_Runtime_Stride) {
@ -384,6 +398,7 @@ TEST_F(ParserImplTest, TypeDecl_Array_Runtime_Stride) {
auto* stride = a->decorations()[0]; auto* stride = a->decorations()[0];
ASSERT_TRUE(stride->Is<ast::StrideDecoration>()); ASSERT_TRUE(stride->Is<ast::StrideDecoration>());
ASSERT_EQ(stride->As<ast::StrideDecoration>()->stride(), 16u); 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) { TEST_F(ParserImplTest, TypeDecl_Array_MultipleDecorations_OneBlock) {
@ -405,6 +420,7 @@ TEST_F(ParserImplTest, TypeDecl_Array_MultipleDecorations_OneBlock) {
EXPECT_EQ(decos[0]->As<ast::StrideDecoration>()->stride(), 16u); EXPECT_EQ(decos[0]->As<ast::StrideDecoration>()->stride(), 16u);
EXPECT_TRUE(decos[1]->Is<ast::StrideDecoration>()); EXPECT_TRUE(decos[1]->Is<ast::StrideDecoration>());
EXPECT_EQ(decos[1]->As<ast::StrideDecoration>()->stride(), 32u); 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) { TEST_F(ParserImplTest, TypeDecl_Array_MultipleDecorations_MultipleBlocks) {
@ -426,6 +442,7 @@ TEST_F(ParserImplTest, TypeDecl_Array_MultipleDecorations_MultipleBlocks) {
EXPECT_EQ(decos[0]->As<ast::StrideDecoration>()->stride(), 16u); EXPECT_EQ(decos[0]->As<ast::StrideDecoration>()->stride(), 16u);
EXPECT_TRUE(decos[1]->Is<ast::StrideDecoration>()); EXPECT_TRUE(decos[1]->Is<ast::StrideDecoration>());
EXPECT_EQ(decos[1]->As<ast::StrideDecoration>()->stride(), 32u); 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) { TEST_F(ParserImplTest, TypeDecl_Array_Decoration_MissingArray) {
@ -522,6 +539,7 @@ TEST_F(ParserImplTest, TypeDecl_Array_Runtime) {
auto* a = t->As<sem::ArrayType>(); auto* a = t->As<sem::ArrayType>();
ASSERT_TRUE(a->IsRuntimeArray()); ASSERT_TRUE(a->IsRuntimeArray());
ASSERT_TRUE(a->type()->Is<sem::U32>()); 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) { TEST_F(ParserImplTest, TypeDecl_Array_Runtime_Vec) {
@ -536,6 +554,7 @@ TEST_F(ParserImplTest, TypeDecl_Array_Runtime_Vec) {
auto* a = t->As<sem::ArrayType>(); auto* a = t->As<sem::ArrayType>();
ASSERT_TRUE(a->IsRuntimeArray()); ASSERT_TRUE(a->IsRuntimeArray());
ASSERT_TRUE(a->type()->is_unsigned_integer_vector()); 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) { TEST_F(ParserImplTest, TypeDecl_Array_BadType) {
@ -612,6 +631,7 @@ struct MatrixData {
const char* input; const char* input;
size_t columns; size_t columns;
size_t rows; size_t rows;
Source::Range range;
}; };
inline std::ostream& operator<<(std::ostream& out, MatrixData data) { inline std::ostream& operator<<(std::ostream& out, MatrixData data) {
out << std::string(data.input); out << std::string(data.input);
@ -632,18 +652,21 @@ TEST_P(MatrixTest, Parse) {
auto* mat = t->As<sem::Matrix>(); auto* mat = t->As<sem::Matrix>();
EXPECT_EQ(mat->rows(), params.rows); EXPECT_EQ(mat->rows(), params.rows);
EXPECT_EQ(mat->columns(), params.columns); EXPECT_EQ(mat->columns(), params.columns);
EXPECT_EQ(t.value.ast->source().range, params.range);
} }
INSTANTIATE_TEST_SUITE_P(ParserImplTest, INSTANTIATE_TEST_SUITE_P(
ParserImplTest,
MatrixTest, MatrixTest,
testing::Values(MatrixData{"mat2x2<f32>", 2, 2}, testing::Values(
MatrixData{"mat2x3<f32>", 2, 3}, MatrixData{"mat2x2<f32>", 2, 2, Source::Range{{1u, 1u}, {1u, 12u}}},
MatrixData{"mat2x4<f32>", 2, 4}, MatrixData{"mat2x3<f32>", 2, 3, Source::Range{{1u, 1u}, {1u, 12u}}},
MatrixData{"mat3x2<f32>", 3, 2}, MatrixData{"mat2x4<f32>", 2, 4, Source::Range{{1u, 1u}, {1u, 12u}}},
MatrixData{"mat3x3<f32>", 3, 3}, MatrixData{"mat3x2<f32>", 3, 2, Source::Range{{1u, 1u}, {1u, 12u}}},
MatrixData{"mat3x4<f32>", 3, 4}, MatrixData{"mat3x3<f32>", 3, 3, Source::Range{{1u, 1u}, {1u, 12u}}},
MatrixData{"mat4x2<f32>", 4, 2}, MatrixData{"mat3x4<f32>", 3, 4, Source::Range{{1u, 1u}, {1u, 12u}}},
MatrixData{"mat4x3<f32>", 4, 3}, MatrixData{"mat4x2<f32>", 4, 2, Source::Range{{1u, 1u}, {1u, 12u}}},
MatrixData{"mat4x4<f32>", 4, 4})); MatrixData{"mat4x3<f32>", 4, 3, Source::Range{{1u, 1u}, {1u, 12u}}},
MatrixData{"mat4x4<f32>", 4, 4, Source::Range{{1u, 1u}, {1u, 12u}}}));
class MatrixMissingGreaterThanTest class MatrixMissingGreaterThanTest
: public ParserImplTestWithParam<MatrixData> {}; : public ParserImplTestWithParam<MatrixData> {};
@ -660,15 +683,15 @@ TEST_P(MatrixMissingGreaterThanTest, Handles_Missing_GreaterThan) {
} }
INSTANTIATE_TEST_SUITE_P(ParserImplTest, INSTANTIATE_TEST_SUITE_P(ParserImplTest,
MatrixMissingGreaterThanTest, MatrixMissingGreaterThanTest,
testing::Values(MatrixData{"mat2x2<f32", 2, 2}, testing::Values(MatrixData{"mat2x2<f32", 2, 2, {}},
MatrixData{"mat2x3<f32", 2, 3}, MatrixData{"mat2x3<f32", 2, 3, {}},
MatrixData{"mat2x4<f32", 2, 4}, MatrixData{"mat2x4<f32", 2, 4, {}},
MatrixData{"mat3x2<f32", 3, 2}, MatrixData{"mat3x2<f32", 3, 2, {}},
MatrixData{"mat3x3<f32", 3, 3}, MatrixData{"mat3x3<f32", 3, 3, {}},
MatrixData{"mat3x4<f32", 3, 4}, MatrixData{"mat3x4<f32", 3, 4, {}},
MatrixData{"mat4x2<f32", 4, 2}, MatrixData{"mat4x2<f32", 4, 2, {}},
MatrixData{"mat4x3<f32", 4, 3}, MatrixData{"mat4x3<f32", 4, 3, {}},
MatrixData{"mat4x4<f32", 4, 4})); MatrixData{"mat4x4<f32", 4, 4, {}}));
class MatrixMissingLessThanTest : public ParserImplTestWithParam<MatrixData> {}; class MatrixMissingLessThanTest : public ParserImplTestWithParam<MatrixData> {};
@ -684,15 +707,15 @@ TEST_P(MatrixMissingLessThanTest, Handles_Missing_GreaterThan) {
} }
INSTANTIATE_TEST_SUITE_P(ParserImplTest, INSTANTIATE_TEST_SUITE_P(ParserImplTest,
MatrixMissingLessThanTest, MatrixMissingLessThanTest,
testing::Values(MatrixData{"mat2x2 f32>", 2, 2}, testing::Values(MatrixData{"mat2x2 f32>", 2, 2, {}},
MatrixData{"mat2x3 f32>", 2, 3}, MatrixData{"mat2x3 f32>", 2, 3, {}},
MatrixData{"mat2x4 f32>", 2, 4}, MatrixData{"mat2x4 f32>", 2, 4, {}},
MatrixData{"mat3x2 f32>", 3, 2}, MatrixData{"mat3x2 f32>", 3, 2, {}},
MatrixData{"mat3x3 f32>", 3, 3}, MatrixData{"mat3x3 f32>", 3, 3, {}},
MatrixData{"mat3x4 f32>", 3, 4}, MatrixData{"mat3x4 f32>", 3, 4, {}},
MatrixData{"mat4x2 f32>", 4, 2}, MatrixData{"mat4x2 f32>", 4, 2, {}},
MatrixData{"mat4x3 f32>", 4, 3}, MatrixData{"mat4x3 f32>", 4, 3, {}},
MatrixData{"mat4x4 f32>", 4, 4})); MatrixData{"mat4x4 f32>", 4, 4, {}}));
class MatrixBadType : public ParserImplTestWithParam<MatrixData> {}; class MatrixBadType : public ParserImplTestWithParam<MatrixData> {};
@ -706,17 +729,18 @@ TEST_P(MatrixBadType, Handles_Unknown_Type) {
ASSERT_TRUE(p->has_error()); ASSERT_TRUE(p->has_error());
ASSERT_EQ(p->error(), "1:8: unknown constructed type 'unknown'"); ASSERT_EQ(p->error(), "1:8: unknown constructed type 'unknown'");
} }
INSTANTIATE_TEST_SUITE_P(ParserImplTest, INSTANTIATE_TEST_SUITE_P(
ParserImplTest,
MatrixBadType, MatrixBadType,
testing::Values(MatrixData{"mat2x2<unknown>", 2, 2}, testing::Values(MatrixData{"mat2x2<unknown>", 2, 2, {}},
MatrixData{"mat2x3<unknown>", 2, 3}, MatrixData{"mat2x3<unknown>", 2, 3, {}},
MatrixData{"mat2x4<unknown>", 2, 4}, MatrixData{"mat2x4<unknown>", 2, 4, {}},
MatrixData{"mat3x2<unknown>", 3, 2}, MatrixData{"mat3x2<unknown>", 3, 2, {}},
MatrixData{"mat3x3<unknown>", 3, 3}, MatrixData{"mat3x3<unknown>", 3, 3, {}},
MatrixData{"mat3x4<unknown>", 3, 4}, MatrixData{"mat3x4<unknown>", 3, 4, {}},
MatrixData{"mat4x2<unknown>", 4, 2}, MatrixData{"mat4x2<unknown>", 4, 2, {}},
MatrixData{"mat4x3<unknown>", 4, 3}, MatrixData{"mat4x3<unknown>", 4, 3, {}},
MatrixData{"mat4x4<unknown>", 4, 4})); MatrixData{"mat4x4<unknown>", 4, 4, {}}));
class MatrixMissingType : public ParserImplTestWithParam<MatrixData> {}; class MatrixMissingType : public ParserImplTestWithParam<MatrixData> {};
@ -732,15 +756,15 @@ TEST_P(MatrixMissingType, Handles_Missing_Type) {
} }
INSTANTIATE_TEST_SUITE_P(ParserImplTest, INSTANTIATE_TEST_SUITE_P(ParserImplTest,
MatrixMissingType, MatrixMissingType,
testing::Values(MatrixData{"mat2x2<>", 2, 2}, testing::Values(MatrixData{"mat2x2<>", 2, 2, {}},
MatrixData{"mat2x3<>", 2, 3}, MatrixData{"mat2x3<>", 2, 3, {}},
MatrixData{"mat2x4<>", 2, 4}, MatrixData{"mat2x4<>", 2, 4, {}},
MatrixData{"mat3x2<>", 3, 2}, MatrixData{"mat3x2<>", 3, 2, {}},
MatrixData{"mat3x3<>", 3, 3}, MatrixData{"mat3x3<>", 3, 3, {}},
MatrixData{"mat3x4<>", 3, 4}, MatrixData{"mat3x4<>", 3, 4, {}},
MatrixData{"mat4x2<>", 4, 2}, MatrixData{"mat4x2<>", 4, 2, {}},
MatrixData{"mat4x3<>", 4, 3}, MatrixData{"mat4x3<>", 4, 3, {}},
MatrixData{"mat4x4<>", 4, 4})); MatrixData{"mat4x4<>", 4, 4, {}}));
TEST_F(ParserImplTest, TypeDecl_Sampler) { TEST_F(ParserImplTest, TypeDecl_Sampler) {
auto p = parser("sampler"); auto p = parser("sampler");
@ -755,6 +779,7 @@ TEST_F(ParserImplTest, TypeDecl_Sampler) {
EXPECT_EQ(t.value, type); EXPECT_EQ(t.value, type);
ASSERT_TRUE(t->Is<sem::Sampler>()); ASSERT_TRUE(t->Is<sem::Sampler>());
ASSERT_FALSE(t->As<sem::Sampler>()->IsComparison()); 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) { TEST_F(ParserImplTest, TypeDecl_Texture) {
@ -772,6 +797,7 @@ TEST_F(ParserImplTest, TypeDecl_Texture) {
ASSERT_TRUE(t->Is<sem::Texture>()); ASSERT_TRUE(t->Is<sem::Texture>());
ASSERT_TRUE(t->Is<sem::SampledTexture>()); ASSERT_TRUE(t->Is<sem::SampledTexture>());
ASSERT_TRUE(t->As<sem::SampledTexture>()->type()->Is<sem::F32>()); ASSERT_TRUE(t->As<sem::SampledTexture>()->type()->Is<sem::F32>());
EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 18u}}));
} }
} // namespace } // namespace

View File

@ -29,10 +29,8 @@ TEST_F(ParserImplTest, VariableDecl_Parses) {
EXPECT_NE(v->type, nullptr); EXPECT_NE(v->type, nullptr);
EXPECT_TRUE(v->type->Is<sem::F32>()); EXPECT_TRUE(v->type->Is<sem::F32>());
EXPECT_EQ(v->source.range.begin.line, 1u); EXPECT_EQ(v->source.range, (Source::Range{{1u, 5u}, {1u, 11u}}));
EXPECT_EQ(v->source.range.begin.column, 5u); EXPECT_EQ(v->type.ast->source().range, (Source::Range{{1u, 14u}, {1u, 17u}}));
EXPECT_EQ(v->source.range.end.line, 1u);
EXPECT_EQ(v->source.range.end.column, 11u);
} }
TEST_F(ParserImplTest, VariableDecl_MissingVar) { TEST_F(ParserImplTest, VariableDecl_MissingVar) {

View File

@ -30,10 +30,9 @@ TEST_F(ParserImplTest, VariableIdentDecl_Parses) {
ASSERT_NE(decl->type, nullptr); ASSERT_NE(decl->type, nullptr);
ASSERT_TRUE(decl->type->Is<sem::F32>()); ASSERT_TRUE(decl->type->Is<sem::F32>());
ASSERT_EQ(decl->source.range.begin.line, 1u); EXPECT_EQ(decl->source.range, (Source::Range{{1u, 1u}, {1u, 7u}}));
ASSERT_EQ(decl->source.range.begin.column, 1u); EXPECT_EQ(decl->type.ast->source().range,
ASSERT_EQ(decl->source.range.end.line, 1u); (Source::Range{{1u, 10u}, {1u, 13u}}));
ASSERT_EQ(decl->source.range.end.column, 7u);
} }
TEST_F(ParserImplTest, VariableIdentDecl_MissingIdent) { TEST_F(ParserImplTest, VariableIdentDecl_MissingIdent) {

View File

@ -84,13 +84,6 @@ class ScopedAssignment {
T old_value_; 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) { bool IsValidStorageTextureDimension(ast::TextureDimension dim) {
switch (dim) { switch (dim) {
case ast::TextureDimension::k1d: case ast::TextureDimension::k1d:
@ -1392,7 +1385,7 @@ bool Resolver::ValidateVectorConstructor(const sem::Vector* vec_type,
"attempted to construct '" + "attempted to construct '" +
vec_type->FriendlyName(builder_->Symbols()) + "' with " + vec_type->FriendlyName(builder_->Symbols()) + "' with " +
std::to_string(value_cardinality_sum) + " component(s)", std::to_string(value_cardinality_sum) + " component(s)",
CombineSourceRange(values_start, values_end)); Source::Combine(values_start, values_end));
return false; return false;
} }
return true; return true;
@ -1414,7 +1407,7 @@ bool Resolver::ValidateMatrixConstructor(const sem::Matrix* matrix_type,
VectorPretty(matrix_type->rows(), elem_type) + "' arguments in '" + VectorPretty(matrix_type->rows(), elem_type) + "' arguments in '" +
matrix_type->FriendlyName(builder_->Symbols()) + matrix_type->FriendlyName(builder_->Symbols()) +
"' constructor, found " + std::to_string(values.size()), "' constructor, found " + std::to_string(values.size()),
CombineSourceRange(values_start, values_end)); Source::Combine(values_start, values_end));
return false; return false;
} }

View File

@ -18,6 +18,7 @@
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <tuple>
#include <vector> #include <vector>
namespace tint { namespace tint {
@ -65,6 +66,27 @@ class Source {
size_t line = 0; size_t line = 0;
/// the 1-based column number. 0 represents no column information. /// the 1-based column number. 0 represents no column information.
size_t column = 0; 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). /// Range holds a Location interval described by [begin, end).
@ -75,12 +97,14 @@ class Source {
/// Constructs a zero-length Range starting at `loc` /// Constructs a zero-length Range starting at `loc`
/// @param loc the start and end location for the range /// @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` /// Constructs the Range beginning at `b` and ending at `e`
/// @param b the range start location /// @param b the range start location
/// @param e the range end 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 /// Return a column-shifted Range
/// @param n the number of characters to shift by /// @param n the number of characters to shift by
@ -89,6 +113,18 @@ class Source {
return Range{{begin.line, begin.column + n}, {end.line, end.column + n}}; 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. /// The location of the first character in the range.
Location begin; Location begin;
/// The location of one-past the last character in the range. /// The location of one-past the last character in the range.
@ -139,6 +175,29 @@ class Source {
return Source(range + n, file_path, file_content); 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 is the span of text this source refers to in #file_path
Range range; Range range;
/// file is the optional file path this source refers to /// file is the optional file path this source refers to
@ -147,6 +206,25 @@ class Source {
const FileContent* file_content = nullptr; 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. /// Writes the Source to the std::ostream.
/// @param out the std::ostream to write to /// @param out the std::ostream to write to
/// @param source the source to write /// @param source the source to write

View File

@ -33,6 +33,7 @@ class Alias;
class Array; class Array;
class Bool; class Bool;
class DepthTexture; class DepthTexture;
class ExternalTexture;
class F32; class F32;
class I32; class I32;
class Matrix; class Matrix;
@ -54,6 +55,7 @@ class Alias;
class ArrayType; class ArrayType;
class Bool; class Bool;
class DepthTexture; class DepthTexture;
class ExternalTexture;
class F32; class F32;
class I32; class I32;
class Matrix; class Matrix;
@ -145,6 +147,7 @@ using Alias = TypePair<ast::Alias, sem::Alias>;
using Array = TypePair<ast::Array, sem::ArrayType>; using Array = TypePair<ast::Array, sem::ArrayType>;
using Bool = TypePair<ast::Bool, sem::Bool>; using Bool = TypePair<ast::Bool, sem::Bool>;
using DepthTexture = TypePair<ast::DepthTexture, sem::DepthTexture>; using DepthTexture = TypePair<ast::DepthTexture, sem::DepthTexture>;
using ExternalTexture = TypePair<ast::ExternalTexture, sem::ExternalTexture>;
using F32 = TypePair<ast::F32, sem::F32>; using F32 = TypePair<ast::F32, sem::F32>;
using I32 = TypePair<ast::I32, sem::I32>; using I32 = TypePair<ast::I32, sem::I32>;
using Matrix = TypePair<ast::Matrix, sem::Matrix>; using Matrix = TypePair<ast::Matrix, sem::Matrix>;