ast: Migrate to using ast::Type

Remove all sem::Type references from the AST.
ConstructedTypes are now all AST types.

The parsers will still create semantic types, but these are now disjoint
and ignored.
The parsers will be updated with future changes to stop creating these
semantic types.

Resolver creates semantic types from the AST types. Most downstream
logic continues to use the semantic types, however transforms will now
need to rebuild AST type information instead of reassigning semantic
information, as semantic nodes are fully rebuilt by the Resolver.

Bug: tint:724
Change-Id: I4ce03a075f13c77648cda5c3691bae202752ecc5
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/49747
Commit-Queue: Ben Clayton <bclayton@chromium.org>
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Reviewed-by: James Price <jrprice@google.com>
This commit is contained in:
Ben Clayton
2021-05-05 09:09:41 +00:00
committed by Commit Bot service account
parent 781de097eb
commit 02ebf0dcae
72 changed files with 1267 additions and 1091 deletions

View File

@@ -23,10 +23,10 @@ namespace ast {
BitcastExpression::BitcastExpression(ProgramID program_id,
const Source& source,
typ::Type type,
ast::Type* type,
Expression* expr)
: Base(program_id, source), type_(type), expr_(expr) {
TINT_ASSERT(type_.ast || type_.sem);
TINT_ASSERT(type_);
TINT_ASSERT(expr_);
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(expr, program_id);
}
@@ -37,7 +37,7 @@ BitcastExpression::~BitcastExpression() = default;
BitcastExpression* BitcastExpression::Clone(CloneContext* ctx) const {
// Clone arguments outside of create() call to have deterministic ordering
auto src = ctx->Clone(source());
auto ty = ctx->Clone(type());
auto* ty = ctx->Clone(type());
auto* e = ctx->Clone(expr_);
return ctx->dst->create<BitcastExpression>(src, ty, e);
}
@@ -46,9 +46,8 @@ void BitcastExpression::to_str(const sem::Info& sem,
std::ostream& out,
size_t indent) const {
make_indent(out, indent);
out << "Bitcast[" << result_type_str(sem) << "]<"
<< (type_.ast ? type_.ast->type_name() : type_.sem->type_name()) << ">{"
<< std::endl;
out << "Bitcast[" << result_type_str(sem) << "]<" << type_->type_name()
<< ">{" << std::endl;
expr_->to_str(sem, out, indent + 2);
make_indent(out, indent);
out << "}" << std::endl;

View File

@@ -30,14 +30,14 @@ class BitcastExpression : public Castable<BitcastExpression, Expression> {
/// @param expr the expr
BitcastExpression(ProgramID program_id,
const Source& source,
typ::Type type,
ast::Type* type,
Expression* expr);
/// Move constructor
BitcastExpression(BitcastExpression&&);
~BitcastExpression() override;
/// @returns the left side expression
typ::Type type() const { return type_; }
ast::Type* type() const { return type_; }
/// @returns the expression
Expression* expr() const { return expr_; }
@@ -58,7 +58,7 @@ class BitcastExpression : public Castable<BitcastExpression, Expression> {
private:
BitcastExpression(const BitcastExpression&) = delete;
typ::Type const type_;
ast::Type* const type_;
Expression* const expr_;
};

View File

@@ -27,7 +27,7 @@ TEST_F(BitcastExpressionTest, Create) {
auto* expr = Expr("expr");
auto* exp = create<BitcastExpression>(ty.f32(), expr);
ASSERT_EQ(exp->type(), ty.f32());
EXPECT_TRUE(exp->type()->Is<ast::F32>());
ASSERT_EQ(exp->expr(), expr);
}

View File

@@ -27,7 +27,7 @@ Function::Function(ProgramID program_id,
const Source& source,
Symbol symbol,
VariableList params,
typ::Type return_type,
ast::Type* return_type,
BlockStatement* body,
DecorationList decorations,
DecorationList return_type_decorations)
@@ -45,7 +45,7 @@ Function::Function(ProgramID program_id,
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(param, program_id);
}
TINT_ASSERT(symbol_.IsValid());
TINT_ASSERT(return_type_.ast || return_type_.sem);
TINT_ASSERT(return_type_);
for (auto* deco : decorations_) {
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(deco, program_id);
}
@@ -81,7 +81,7 @@ Function* Function::Clone(CloneContext* ctx) const {
auto src = ctx->Clone(source());
auto sym = ctx->Clone(symbol());
auto p = ctx->Clone(params_);
auto ret = ctx->Clone(return_type_);
auto* ret = ctx->Clone(return_type_);
auto* b = ctx->Clone(body_);
auto decos = ctx->Clone(decorations_);
auto ret_decos = ctx->Clone(return_type_decorations_);
@@ -92,9 +92,7 @@ void Function::to_str(const sem::Info& sem,
std::ostream& out,
size_t indent) const {
make_indent(out, indent);
out << "Function " << symbol_.to_str() << " -> "
<< (return_type_.ast ? return_type_.ast->type_name()
: return_type_.sem->type_name())
out << "Function " << symbol_.to_str() << " -> " << return_type_->type_name()
<< std::endl;
for (auto* deco : decorations()) {
@@ -134,7 +132,7 @@ std::string Function::type_name() const {
for (auto* param : params_) {
// No need for the sem::Variable here, functions params must have a
// type
out << param->declared_type()->type_name();
out << param->type()->type_name();
}
return out.str();

View File

@@ -49,7 +49,7 @@ class Function : public Castable<Function, Node> {
const Source& source,
Symbol symbol,
VariableList params,
typ::Type return_type,
ast::Type* return_type,
BlockStatement* body,
DecorationList decorations,
DecorationList return_type_decorations);
@@ -77,7 +77,7 @@ class Function : public Castable<Function, Node> {
bool IsEntryPoint() const { return pipeline_stage() != PipelineStage::kNone; }
/// @returns the function return type.
typ::Type return_type() const { return return_type_; }
ast::Type* return_type() const { return return_type_; }
/// @returns the decorations attached to the function return type.
const DecorationList& return_type_decorations() const {
@@ -115,7 +115,7 @@ class Function : public Castable<Function, Node> {
Symbol const symbol_;
VariableList const params_;
typ::Type const return_type_;
ast::Type* const return_type_;
BlockStatement* const body_;
DecorationList const decorations_;
DecorationList const return_type_decorations_;

View File

@@ -32,7 +32,7 @@ TEST_F(FunctionTest, Creation) {
auto* f = Func("func", params, ty.void_(), StatementList{}, DecorationList{});
EXPECT_EQ(f->symbol(), Symbols().Get("func"));
ASSERT_EQ(f->params().size(), 1u);
EXPECT_EQ(f->return_type(), ty.void_());
EXPECT_TRUE(f->return_type()->Is<ast::Void>());
EXPECT_EQ(f->params()[0], var);
}

View File

@@ -132,7 +132,7 @@ std::ostream& operator<<(std::ostream& out, const TextureOverloadCase& data) {
return out;
}
typ::Type TextureOverloadCase::resultVectorComponentType(
ast::Type* TextureOverloadCase::resultVectorComponentType(
ProgramBuilder* b) const {
switch (texture_data_type) {
case ast::intrinsic::test::TextureDataType::kF32:
@@ -149,7 +149,7 @@ typ::Type TextureOverloadCase::resultVectorComponentType(
ast::Variable* TextureOverloadCase::buildTextureVariable(
ProgramBuilder* b) const {
auto datatype = resultVectorComponentType(b);
auto* datatype = resultVectorComponentType(b);
DecorationList decos = {
b->create<ast::GroupDecoration>(0),
@@ -166,10 +166,9 @@ ast::Variable* TextureOverloadCase::buildTextureVariable(
ast::StorageClass::kUniformConstant, nullptr, decos);
case ast::intrinsic::test::TextureKind::kMultisampled:
return b->Global(
"texture",
b->ty.multisampled_texture(texture_dimension, datatype),
ast::StorageClass::kUniformConstant, nullptr, decos);
return b->Global("texture",
b->ty.multisampled_texture(texture_dimension, datatype),
ast::StorageClass::kUniformConstant, nullptr, decos);
case ast::intrinsic::test::TextureKind::kStorage: {
auto st = b->ty.storage_texture(texture_dimension, image_format);

View File

@@ -209,7 +209,7 @@ struct TextureOverloadCase {
/// @param builder the AST builder used for the test
/// @returns the vector component type of the texture function return value
typ::Type resultVectorComponentType(ProgramBuilder* builder) const;
ast::Type* resultVectorComponentType(ProgramBuilder* builder) const;
/// @param builder the AST builder used for the test
/// @returns a variable holding the test texture, automatically registered as
/// a global variable.

View File

@@ -29,14 +29,14 @@ Module::Module(ProgramID program_id, const Source& source)
Module::Module(ProgramID program_id,
const Source& source,
std::vector<Cloneable*> global_decls)
std::vector<ast::Node*> global_decls)
: Base(program_id, source), global_declarations_(std::move(global_decls)) {
for (auto* decl : global_declarations_) {
if (decl == nullptr) {
continue;
}
if (auto* ty = decl->As<sem::Type>()) {
if (auto* ty = decl->As<ast::NamedType>()) {
constructed_types_.push_back(ty);
} else if (auto* func = decl->As<Function>()) {
functions_.push_back(func);
@@ -52,16 +52,34 @@ Module::Module(ProgramID program_id,
Module::~Module() = default;
const ast::NamedType* Module::LookupType(Symbol name) const {
for (auto ct : ConstructedTypes()) {
if (auto* ty = ct.ast->As<ast::NamedType>()) {
if (ty->name() == name) {
return ty;
}
for (auto* ty : ConstructedTypes()) {
if (ty->name() == name) {
return ty;
}
}
return nullptr;
}
void Module::AddGlobalVariable(ast::Variable* var) {
TINT_ASSERT(var);
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(var, program_id());
global_variables_.push_back(var);
global_declarations_.push_back(var);
}
void Module::AddConstructedType(ast::NamedType* type) {
TINT_ASSERT(type);
constructed_types_.push_back(type);
global_declarations_.push_back(type);
}
void Module::AddFunction(ast::Function* func) {
TINT_ASSERT(func);
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(func, program_id());
functions_.push_back(func);
global_declarations_.push_back(func);
}
Module* Module::Clone(CloneContext* ctx) const {
auto* out = ctx->dst->create<Module>();
out->Copy(ctx, this);
@@ -74,7 +92,7 @@ void Module::Copy(CloneContext* ctx, const Module* src) {
TINT_ICE(ctx->dst->Diagnostics()) << "src global declaration was nullptr";
continue;
}
if (auto* ty = decl->As<sem::Type>()) {
if (auto* ty = decl->As<ast::NamedType>()) {
AddConstructedType(ty);
} else if (auto* func = decl->As<Function>()) {
AddFunction(func);
@@ -92,16 +110,16 @@ void Module::to_str(const sem::Info& sem,
make_indent(out, indent);
out << "Module{" << std::endl;
indent += 2;
for (auto const ty : constructed_types_) {
for (auto* ty : constructed_types_) {
make_indent(out, indent);
if (auto* alias = ty->As<sem::Alias>()) {
if (auto* alias = ty->As<ast::Alias>()) {
out << alias->symbol().to_str() << " -> " << alias->type()->type_name()
<< std::endl;
if (auto* str = alias->type()->As<sem::StructType>()) {
str->impl()->to_str(sem, out, indent);
if (auto* str = alias->type()->As<ast::Struct>()) {
str->to_str(sem, out, indent);
}
} else if (auto* str = ty->As<sem::StructType>()) {
str->impl()->to_str(sem, out, indent);
} else if (auto* str = ty->As<ast::Struct>()) {
str->to_str(sem, out, indent);
}
}
for (auto* var : global_variables_) {

View File

@@ -42,28 +42,23 @@ class Module : public Castable<Module, Node> {
/// the order they were declared in the source program
Module(ProgramID program_id,
const Source& source,
std::vector<Cloneable*> global_decls);
std::vector<ast::Node*> global_decls);
/// Destructor
~Module() override;
/// @returns the ordered global declarations for the translation unit
const std::vector<Cloneable*>& GlobalDeclarations() const {
const std::vector<ast::Node*>& GlobalDeclarations() const {
return global_declarations_;
}
/// Add a global variable to the Builder
/// @param var the variable to add
void AddGlobalVariable(ast::Variable* var) {
TINT_ASSERT(var);
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(var, program_id());
global_variables_.push_back(var);
global_declarations_.push_back(var);
}
void AddGlobalVariable(ast::Variable* var);
/// @returns true if the module has the global declaration `decl`
/// @param decl the declaration to check
bool HasGlobalDeclaration(const Cloneable* decl) const {
bool HasGlobalDeclaration(ast::Node* decl) const {
for (auto* d : global_declarations_) {
if (d == decl) {
return true;
@@ -79,31 +74,21 @@ class Module : public Castable<Module, Node> {
VariableList& GlobalVariables() { return global_variables_; }
/// Adds a constructed type to the Builder.
/// The type must be an alias or a struct.
/// @param type the constructed type to add
void AddConstructedType(typ::Type type) {
TINT_ASSERT(type);
constructed_types_.push_back(type);
global_declarations_.push_back(const_cast<sem::Type*>(type.sem));
}
void AddConstructedType(ast::NamedType* type);
/// @returns the NamedType registered as a ConstructedType()
/// @param name the name of the type to search for
const ast::NamedType* LookupType(Symbol name) const;
/// @returns the constructed types in the translation unit
const std::vector<typ::Type>& ConstructedTypes() const {
const std::vector<ast::NamedType*>& ConstructedTypes() const {
return constructed_types_;
}
/// Add a function to the Builder
/// @param func the function to add
void AddFunction(ast::Function* func) {
TINT_ASSERT(func);
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(func, program_id());
functions_.push_back(func);
global_declarations_.push_back(func);
}
void AddFunction(ast::Function* func);
/// @returns the functions declared in the translation unit
const FunctionList& Functions() const { return functions_; }
@@ -132,8 +117,8 @@ class Module : public Castable<Module, Node> {
std::string to_str(const sem::Info& sem) const;
private:
std::vector<Cloneable*> global_declarations_;
std::vector<typ::Type> constructed_types_;
std::vector<ast::Node*> global_declarations_;
std::vector<ast::NamedType*> constructed_types_;
FunctionList functions_;
VariableList global_variables_;
};

View File

@@ -24,13 +24,13 @@ namespace ast {
StructMember::StructMember(ProgramID program_id,
const Source& source,
const Symbol& sym,
typ::Type type,
ast::Type* type,
DecorationList decorations)
: Base(program_id, source),
symbol_(sym),
type_(type),
decorations_(std::move(decorations)) {
TINT_ASSERT(type.ast || type.sem);
TINT_ASSERT(type);
TINT_ASSERT(symbol_.IsValid());
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(symbol_, program_id);
for (auto* deco : decorations_) {
@@ -59,7 +59,7 @@ StructMember* StructMember::Clone(CloneContext* ctx) const {
// Clone arguments outside of create() call to have deterministic ordering
auto src = ctx->Clone(source());
auto sym = ctx->Clone(symbol_);
auto ty = ctx->Clone(type_);
auto* ty = ctx->Clone(type_);
auto decos = ctx->Clone(decorations_);
return ctx->dst->create<StructMember>(src, sym, ty, decos);
}
@@ -76,9 +76,7 @@ void StructMember::to_str(const sem::Info& sem,
out << "]] ";
}
out << symbol_.to_str() << ": "
<< (type_.ast ? type_.ast->type_name() : type_.sem->type_name()) << "}"
<< std::endl;
out << symbol_.to_str() << ": " << type_->type_name() << "}" << std::endl;
}
} // namespace ast

View File

@@ -36,7 +36,7 @@ class StructMember : public Castable<StructMember, Node> {
StructMember(ProgramID program_id,
const Source& source,
const Symbol& sym,
typ::Type type,
ast::Type* type,
DecorationList decorations);
/// Move constructor
StructMember(StructMember&&);
@@ -47,7 +47,7 @@ class StructMember : public Castable<StructMember, Node> {
const Symbol& symbol() const { return symbol_; }
/// @returns the type
typ::Type type() const { return type_; }
ast::Type* type() const { return type_; }
/// @returns the decorations
const DecorationList& decorations() const { return decorations_; }
@@ -75,7 +75,7 @@ class StructMember : public Castable<StructMember, Node> {
StructMember(const StructMember&) = delete;
Symbol const symbol_;
typ::Type const type_;
ast::Type* const type_;
DecorationList const decorations_;
};

View File

@@ -24,7 +24,7 @@ using StructMemberTest = TestHelper;
TEST_F(StructMemberTest, Creation) {
auto* st = Member("a", ty.i32(), {MemberSize(4)});
EXPECT_EQ(st->symbol(), Symbol(1, ID()));
EXPECT_EQ(st->type(), ty.i32());
EXPECT_TRUE(st->type()->Is<ast::I32>());
EXPECT_EQ(st->decorations().size(), 1u);
EXPECT_TRUE(st->decorations()[0]->Is<StructMemberSizeDecoration>());
EXPECT_EQ(st->source().range.begin.line, 0u);
@@ -38,7 +38,7 @@ TEST_F(StructMemberTest, CreationWithSource) {
Source{Source::Range{Source::Location{27, 4}, Source::Location{27, 8}}},
"a", ty.i32());
EXPECT_EQ(st->symbol(), Symbol(1, ID()));
EXPECT_EQ(st->type(), ty.i32());
EXPECT_TRUE(st->type()->Is<ast::I32>());
EXPECT_EQ(st->decorations().size(), 0u);
EXPECT_EQ(st->source().range.begin.line, 27u);
EXPECT_EQ(st->source().range.begin.column, 4u);

View File

@@ -23,10 +23,10 @@ namespace ast {
TypeConstructorExpression::TypeConstructorExpression(ProgramID program_id,
const Source& source,
typ::Type type,
ast::Type* type,
ExpressionList values)
: Base(program_id, source), type_(type), values_(std::move(values)) {
TINT_ASSERT(type_.ast || type_.sem);
TINT_ASSERT(type_);
for (auto* val : values_) {
TINT_ASSERT(val);
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(val, program_id);
@@ -42,7 +42,7 @@ TypeConstructorExpression* TypeConstructorExpression::Clone(
CloneContext* ctx) const {
// Clone arguments outside of create() call to have deterministic ordering
auto src = ctx->Clone(source());
auto ty = ctx->Clone(type());
auto* ty = ctx->Clone(type());
auto vals = ctx->Clone(values());
return ctx->dst->create<TypeConstructorExpression>(src, ty, vals);
}
@@ -53,8 +53,7 @@ void TypeConstructorExpression::to_str(const sem::Info& sem,
make_indent(out, indent);
out << "TypeConstructor[" << result_type_str(sem) << "]{" << std::endl;
make_indent(out, indent + 2);
out << (type_.ast ? type_.ast->type_name() : type_.sem->type_name())
<< std::endl;
out << type_->type_name() << std::endl;
for (auto* val : values_) {
val->to_str(sem, out, indent + 2);

View File

@@ -33,14 +33,14 @@ class TypeConstructorExpression
/// @param values the constructor values
TypeConstructorExpression(ProgramID program_id,
const Source& source,
typ::Type type,
ast::Type* type,
ExpressionList values);
/// Move constructor
TypeConstructorExpression(TypeConstructorExpression&&);
~TypeConstructorExpression() override;
/// @returns the type
typ::Type type() const { return type_; }
ast::Type* type() const { return type_; }
/// @returns the values
const ExpressionList& values() const { return values_; }
@@ -62,7 +62,7 @@ class TypeConstructorExpression
private:
TypeConstructorExpression(const TypeConstructorExpression&) = delete;
typ::Type const type_;
ast::Type* const type_;
ExpressionList const values_;
};

View File

@@ -26,7 +26,7 @@ TEST_F(TypeConstructorExpressionTest, Creation) {
expr.push_back(Expr("expr"));
auto* t = create<TypeConstructorExpression>(ty.f32(), expr);
EXPECT_EQ(t->type(), ty.f32());
EXPECT_TRUE(t->type()->Is<ast::F32>());
ASSERT_EQ(t->values().size(), 1u);
EXPECT_EQ(t->values()[0], expr[0]);
}

View File

@@ -27,7 +27,7 @@ Variable::Variable(ProgramID program_id,
const Source& source,
const Symbol& sym,
StorageClass declared_storage_class,
const typ::Type type,
ast::Type* type,
bool is_const,
Expression* constructor,
DecorationList decorations)
@@ -41,7 +41,7 @@ Variable::Variable(ProgramID program_id,
TINT_ASSERT(symbol_.IsValid());
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(symbol_, program_id);
// no type means we must have a constructor to infer it
TINT_ASSERT(type_.ast || type_.sem || constructor);
TINT_ASSERT(type_ || constructor);
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(constructor, program_id);
}
@@ -73,7 +73,7 @@ uint32_t Variable::constant_id() const {
Variable* Variable::Clone(CloneContext* ctx) const {
auto src = ctx->Clone(source());
auto sym = ctx->Clone(symbol());
auto ty = ctx->Clone(type());
auto* ty = ctx->Clone(type());
auto* ctor = ctx->Clone(constructor());
auto decos = ctx->Clone(decorations());
return ctx->dst->create<Variable>(src, sym, declared_storage_class(), ty,
@@ -90,8 +90,7 @@ void Variable::info_to_str(const sem::Info& sem,
out << (var_sem ? var_sem->StorageClass() : declared_storage_class())
<< std::endl;
make_indent(out, indent);
out << (type_.sem ? type_.sem->type_name() : type_.ast->type_name())
<< std::endl;
out << type_->type_name() << std::endl;
}
void Variable::constructor_to_str(const sem::Info& sem,

View File

@@ -109,7 +109,7 @@ class Variable : public Castable<Variable, Node> {
const Source& source,
const Symbol& sym,
StorageClass declared_storage_class,
typ::Type type,
ast::Type* type,
bool is_const,
Expression* constructor,
DecorationList decorations);
@@ -121,12 +121,8 @@ class Variable : public Castable<Variable, Node> {
/// @returns the variable symbol
const Symbol& symbol() const { return symbol_; }
/// @returns the declared type
// TODO(crbug.com/tint/697): Remove and use type() instead
sem::Type* declared_type() const { return const_cast<sem::Type*>(type_.sem); }
/// @returns the variable type
typ::Type type() const { return type_; }
ast::Type* type() const { return type_; }
/// @returns the declared storage class
StorageClass declared_storage_class() const {
@@ -185,7 +181,7 @@ class Variable : public Castable<Variable, Node> {
Symbol const symbol_;
// The value type if a const or formal paramter, and the store type if a var
typ::Type const type_;
ast::Type* const type_;
bool const is_const_;
Expression* const constructor_;
DecorationList const decorations_;

View File

@@ -27,7 +27,7 @@ TEST_F(VariableTest, Creation) {
EXPECT_EQ(v->symbol(), Symbol(1, ID()));
EXPECT_EQ(v->declared_storage_class(), StorageClass::kFunction);
EXPECT_EQ(v->declared_type(), ty.i32());
EXPECT_TRUE(v->type()->Is<ast::I32>());
EXPECT_EQ(v->source().range.begin.line, 0u);
EXPECT_EQ(v->source().range.begin.column, 0u);
EXPECT_EQ(v->source().range.end.line, 0u);
@@ -41,7 +41,7 @@ TEST_F(VariableTest, CreationWithSource) {
EXPECT_EQ(v->symbol(), Symbol(1, ID()));
EXPECT_EQ(v->declared_storage_class(), StorageClass::kPrivate);
EXPECT_EQ(v->declared_type(), ty.f32());
EXPECT_TRUE(v->type()->Is<ast::F32>());
EXPECT_EQ(v->source().range.begin.line, 27u);
EXPECT_EQ(v->source().range.begin.column, 4u);
EXPECT_EQ(v->source().range.end.line, 27u);
@@ -55,7 +55,7 @@ TEST_F(VariableTest, CreationEmpty) {
EXPECT_EQ(v->symbol(), Symbol(1, ID()));
EXPECT_EQ(v->declared_storage_class(), StorageClass::kWorkgroup);
EXPECT_EQ(v->declared_type(), ty.i32());
EXPECT_TRUE(v->type()->Is<ast::I32>());
EXPECT_EQ(v->source().range.begin.line, 27u);
EXPECT_EQ(v->source().range.begin.column, 4u);
EXPECT_EQ(v->source().range.end.line, 27u);