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/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 @@ class ProgramBuilder {
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 @@ class ProgramBuilder {
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 @@ class ProgramBuilder {
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 @@ class ProgramBuilder {
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 @@ 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
/// @param access the access control
/// @param type the inner type
@ -611,6 +694,19 @@ class ProgramBuilder {
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 @@ class ProgramBuilder {
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 @@ class ProgramBuilder {
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 @@ class ProgramBuilder {
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 @@ class ProgramBuilder {
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 @@ class ProgramBuilder {
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 @@ class ProgramBuilder {
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

View File

@ -177,9 +177,38 @@ struct BlockCounters {
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 @@ Token ParserImpl::next() {
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 @@ Token ParserImpl::peek() {
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 @@ Maybe<typ::Type> ParserImpl::texture_sampler_types() {
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 @@ Maybe<typ::Type> ParserImpl::texture_sampler_types() {
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 @@ Maybe<typ::Type> ParserImpl::texture_sampler_types() {
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 @@ Maybe<typ::Type> ParserImpl::texture_sampler_types() {
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 @@ Maybe<typ::Type> ParserImpl::texture_sampler_types() {
// : 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 @@ Maybe<ast::TextureDimension> ParserImpl::sampled_texture_type() {
// 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 @@ Maybe<ast::TextureDimension> ParserImpl::storage_texture_type() {
// | 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 @@ Expect<ParserImpl::TypedIdentifier> ParserImpl::expect_variable_ident_decl(
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 @@ Maybe<typ::Type> ParserImpl::type_alias() {
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() {
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 @@ Maybe<typ::Type> ParserImpl::type_decl(ast::DecorationList& decos) {
}
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 @@ Expect<typ::Type> ParserImpl::expect_type(const std::string& use) {
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 @@ Expect<typ::Type> ParserImpl::expect_type_decl_vector(Token t) {
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 @@ Expect<typ::Type> ParserImpl::expect_type_decl_array(
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 @@ Expect<typ::Type> ParserImpl::expect_type_decl_matrix(Token t) {
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 @@ Maybe<ast::Function*> ParserImpl::function_decl(ast::DecorationList& decos) {
// : 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 @@ T ParserImpl::without_error(F&& body) {
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

View File

@ -331,6 +331,8 @@ class ParserImpl {
/// @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 @@ class ParserImpl {
/// 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 @@ class ParserImpl {
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 @@ class ParserImpl {
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_;

View File

@ -38,6 +38,7 @@ TEST_F(ParserImplTest, DepthTextureType_2d) {
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 @@ TEST_F(ParserImplTest, DepthTextureType_2dArray) {
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 @@ TEST_F(ParserImplTest, DepthTextureType_Cube) {
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 @@ TEST_F(ParserImplTest, DepthTextureType_CubeArray) {
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

View File

@ -32,6 +32,7 @@ TEST_F(ParserImplTest, ExternalTextureType) {
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

View File

@ -29,6 +29,7 @@ TEST_F(ParserImplTest, FunctionTypeDecl_Void) {
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 @@ TEST_F(ParserImplTest, FunctionTypeDecl_Type) {
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) {

View File

@ -37,6 +37,7 @@ TEST_F(ParserImplTest, SamplerType_Sampler) {
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 @@ TEST_F(ParserImplTest, SamplerType_ComparisonSampler) {
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

View File

@ -39,10 +39,8 @@ TEST_F(ParserImplTest, StructMember_Parses) {
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 @@ TEST_F(ParserImplTest, StructMember_ParsesWithOffsetDecoration_DEPRECATED) {
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 @@ TEST_F(ParserImplTest, StructMember_ParsesWithAlignDecoration) {
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 @@ TEST_F(ParserImplTest, StructMember_ParsesWithSizeDecoration) {
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 @@ TEST_F(ParserImplTest, StructMember_ParsesWithDecoration) {
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 @@ TEST_F(ParserImplTest, StructMember_ParsesWithMultipleDecorations) {
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) {

View File

@ -40,6 +40,7 @@ TEST_F(ParserImplTest, TextureSamplerTypes_Sampler) {
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 @@ TEST_F(ParserImplTest, TextureSamplerTypes_SamplerComparison) {
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 @@ TEST_F(ParserImplTest, TextureSamplerTypes_DepthTexture) {
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 @@ TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_F32) {
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 @@ TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_I32) {
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 @@ TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_U32) {
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 @@ TEST_F(ParserImplTest, TextureSamplerTypes_MultisampledTexture_I32) {
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 @@ TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_Readonly1dR8Unorm) {
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 @@ TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_Writeonly2dR16Float) {
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) {

View File

@ -33,6 +33,8 @@ TEST_F(ParserImplTest, TypeDecl_ParsesType) {
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 @@ TEST_F(ParserImplTest, TypeDecl_ParsesStruct_Ident) {
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) {

View File

@ -50,6 +50,7 @@ TEST_F(ParserImplTest, TypeDecl_Identifier) {
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 @@ TEST_F(ParserImplTest, TypeDecl_Bool) {
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 @@ TEST_F(ParserImplTest, TypeDecl_F32) {
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 @@ TEST_F(ParserImplTest, TypeDecl_I32) {
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 @@ TEST_F(ParserImplTest, TypeDecl_U32) {
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 @@ TEST_P(VecTest, Parse) {
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 @@ TEST_P(VecMissingGreaterThanTest, Handles_Missing_GreaterThan) {
}
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 @@ TEST_P(VecMissingLessThanTest, Handles_Missing_GreaterThan) {
}
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 @@ TEST_P(VecBadType, Handles_Unknown_Type) {
}
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 @@ TEST_P(VecMissingType, Handles_Missing_Type) {
}
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 @@ TEST_F(ParserImplTest, TypeDecl_Ptr) {
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 @@ TEST_F(ParserImplTest, TypeDecl_Ptr_ToVec) {
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 @@ TEST_F(ParserImplTest, TypeDecl_Array) {
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 @@ TEST_F(ParserImplTest, TypeDecl_Array_Stride) {
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 @@ TEST_F(ParserImplTest, TypeDecl_Array_Runtime_Stride) {
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 @@ TEST_F(ParserImplTest, TypeDecl_Array_MultipleDecorations_OneBlock) {
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 @@ TEST_F(ParserImplTest, TypeDecl_Array_MultipleDecorations_MultipleBlocks) {
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 @@ TEST_F(ParserImplTest, TypeDecl_Array_Runtime) {
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 @@ TEST_F(ParserImplTest, TypeDecl_Array_Runtime_Vec) {
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 @@ struct MatrixData {
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 @@ TEST_P(MatrixTest, Parse) {
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 @@ TEST_P(MatrixMissingGreaterThanTest, Handles_Missing_GreaterThan) {
}
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 @@ TEST_P(MatrixMissingLessThanTest, Handles_Missing_GreaterThan) {
}
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 @@ TEST_P(MatrixBadType, Handles_Unknown_Type) {
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 @@ TEST_P(MatrixMissingType, Handles_Missing_Type) {
}
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 @@ TEST_F(ParserImplTest, TypeDecl_Sampler) {
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 @@ TEST_F(ParserImplTest, TypeDecl_Texture) {
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

View File

@ -29,10 +29,8 @@ TEST_F(ParserImplTest, VariableDecl_Parses) {
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) {

View File

@ -30,10 +30,9 @@ TEST_F(ParserImplTest, VariableIdentDecl_Parses) {
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) {

View File

@ -84,13 +84,6 @@ class ScopedAssignment {
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 @@ bool Resolver::ValidateVectorConstructor(const sem::Vector* vec_type,
"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 @@ bool Resolver::ValidateMatrixConstructor(const sem::Matrix* matrix_type,
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;
}

View File

@ -18,6 +18,7 @@
#include <iostream>
#include <string>
#include <tuple>
#include <vector>
namespace tint {
@ -65,6 +66,27 @@ class Source {
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 @@ class Source {
/// 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 @@ class Source {
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 @@ class Source {
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 @@ class Source {
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

View File

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