resolver: Be const-correct with sem::Types

Make all the sem::Type pointers const.
The later stages still have not been fixed up, so there's liberal usage of const_cast where we create semantic nodes.

Bug: tint:745
Change-Id: I160b791f2b7944f8966bc961e061d1e5996c1973
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/49343
Auto-Submit: Ben Clayton <bclayton@google.com>
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
This commit is contained in:
Ben Clayton 2021-04-28 12:38:13 +00:00 committed by Commit Bot service account
parent 761e6b139c
commit 12ed13d0d4
4 changed files with 110 additions and 98 deletions

View File

@ -78,7 +78,7 @@ class Matcher {
/// The map of open types. A new entry is assigned the first time an /// The map of open types. A new entry is assigned the first time an
/// OpenType is encountered. If the OpenType is encountered again, a /// OpenType is encountered. If the OpenType is encountered again, a
/// comparison is made to see if the type is consistent. /// comparison is made to see if the type is consistent.
std::unordered_map<OpenType, sem::Type*> open_types; std::unordered_map<OpenType, const sem::Type*> open_types;
/// The map of open numbers. A new entry is assigned the first time an /// The map of open numbers. A new entry is assigned the first time an
/// OpenNumber is encountered. If the OpenNumber is encountered again, a /// OpenNumber is encountered. If the OpenNumber is encountered again, a
/// comparison is made to see if the number is consistent. /// comparison is made to see if the number is consistent.
@ -92,7 +92,7 @@ class Matcher {
/// Aliases are automatically unwrapped before matching. /// Aliases are automatically unwrapped before matching.
/// Match may add to, or compare against the open types and numbers in state. /// Match may add to, or compare against the open types and numbers in state.
/// @returns true if the argument type is as expected. /// @returns true if the argument type is as expected.
bool Match(MatchState& state, sem::Type* argument_type) const { bool Match(MatchState& state, const sem::Type* argument_type) const {
auto* unwrapped = argument_type->UnwrapAliasIfNeeded(); auto* unwrapped = argument_type->UnwrapAliasIfNeeded();
return MatchUnwrapped(state, unwrapped); return MatchUnwrapped(state, unwrapped);
} }
@ -111,12 +111,12 @@ class Matcher {
/// Match may add to, or compare against the open types and numbers in state. /// Match may add to, or compare against the open types and numbers in state.
/// @returns true if the argument type is as expected. /// @returns true if the argument type is as expected.
virtual bool MatchUnwrapped(MatchState& state, virtual bool MatchUnwrapped(MatchState& state,
sem::Type* argument_type) const = 0; const sem::Type* argument_type) const = 0;
/// Checks `state.open_type` to see if the OpenType `t` is equal to the type /// Checks `state.open_type` to see if the OpenType `t` is equal to the type
/// `ty`. If `state.open_type` does not contain an entry for `t`, then `ty` /// `ty`. If `state.open_type` does not contain an entry for `t`, then `ty`
/// is added and returns true. /// is added and returns true.
bool MatchOpenType(MatchState& state, OpenType t, sem::Type* ty) const { bool MatchOpenType(MatchState& state, OpenType t, const sem::Type* ty) const {
auto it = state.open_types.find(t); auto it = state.open_types.find(t);
if (it != state.open_types.end()) { if (it != state.open_types.end()) {
return it->second == ty; return it->second == ty;
@ -148,7 +148,7 @@ class Builder : public Matcher {
/// The type manager used to construct new types /// The type manager used to construct new types
sem::Manager& ty_mgr; sem::Manager& ty_mgr;
/// The final resolved list of open types /// The final resolved list of open types
std::unordered_map<OpenType, sem::Type*> const open_types; std::unordered_map<OpenType, const sem::Type*> const open_types;
/// The final resolved list of open numbers /// The final resolved list of open numbers
std::unordered_map<OpenNumber, uint32_t> const open_numbers; std::unordered_map<OpenNumber, uint32_t> const open_numbers;
}; };
@ -157,7 +157,7 @@ class Builder : public Matcher {
~Builder() override = default; ~Builder() override = default;
/// Constructs and returns the expected type /// Constructs and returns the expected type
virtual sem::Type* Build(BuildState& state) const = 0; virtual const sem::Type* Build(BuildState& state) const = 0;
}; };
/// OpenTypeBuilder is a Matcher / Builder for an open type (T etc). /// OpenTypeBuilder is a Matcher / Builder for an open type (T etc).
@ -167,11 +167,11 @@ class OpenTypeBuilder : public Builder {
public: public:
explicit OpenTypeBuilder(OpenType open_type) : open_type_(open_type) {} explicit OpenTypeBuilder(OpenType open_type) : open_type_(open_type) {}
bool MatchUnwrapped(MatchState& state, sem::Type* ty) const override { bool MatchUnwrapped(MatchState& state, const sem::Type* ty) const override {
return MatchOpenType(state, open_type_, ty); return MatchOpenType(state, open_type_, ty);
} }
sem::Type* Build(BuildState& state) const override { const sem::Type* Build(BuildState& state) const override {
return state.open_types.at(open_type_); return state.open_types.at(open_type_);
} }
@ -184,7 +184,7 @@ class OpenTypeBuilder : public Builder {
/// VoidBuilder is a Matcher / Builder for void types. /// VoidBuilder is a Matcher / Builder for void types.
class VoidBuilder : public Builder { class VoidBuilder : public Builder {
public: public:
bool MatchUnwrapped(MatchState&, sem::Type* ty) const override { bool MatchUnwrapped(MatchState&, const sem::Type* ty) const override {
return ty->Is<sem::Void>(); return ty->Is<sem::Void>();
} }
sem::Type* Build(BuildState& state) const override { sem::Type* Build(BuildState& state) const override {
@ -196,7 +196,7 @@ class VoidBuilder : public Builder {
/// BoolBuilder is a Matcher / Builder for boolean types. /// BoolBuilder is a Matcher / Builder for boolean types.
class BoolBuilder : public Builder { class BoolBuilder : public Builder {
public: public:
bool MatchUnwrapped(MatchState&, sem::Type* ty) const override { bool MatchUnwrapped(MatchState&, const sem::Type* ty) const override {
return ty->Is<sem::Bool>(); return ty->Is<sem::Bool>();
} }
sem::Type* Build(BuildState& state) const override { sem::Type* Build(BuildState& state) const override {
@ -208,7 +208,7 @@ class BoolBuilder : public Builder {
/// F32Builder is a Matcher / Builder for f32 types. /// F32Builder is a Matcher / Builder for f32 types.
class F32Builder : public Builder { class F32Builder : public Builder {
public: public:
bool MatchUnwrapped(MatchState&, sem::Type* ty) const override { bool MatchUnwrapped(MatchState&, const sem::Type* ty) const override {
return ty->Is<sem::F32>(); return ty->Is<sem::F32>();
} }
sem::Type* Build(BuildState& state) const override { sem::Type* Build(BuildState& state) const override {
@ -220,7 +220,7 @@ class F32Builder : public Builder {
/// U32Builder is a Matcher / Builder for u32 types. /// U32Builder is a Matcher / Builder for u32 types.
class U32Builder : public Builder { class U32Builder : public Builder {
public: public:
bool MatchUnwrapped(MatchState&, sem::Type* ty) const override { bool MatchUnwrapped(MatchState&, const sem::Type* ty) const override {
return ty->Is<sem::U32>(); return ty->Is<sem::U32>();
} }
sem::Type* Build(BuildState& state) const override { sem::Type* Build(BuildState& state) const override {
@ -232,7 +232,7 @@ class U32Builder : public Builder {
/// I32Builder is a Matcher / Builder for i32 types. /// I32Builder is a Matcher / Builder for i32 types.
class I32Builder : public Builder { class I32Builder : public Builder {
public: public:
bool MatchUnwrapped(MatchState&, sem::Type* ty) const override { bool MatchUnwrapped(MatchState&, const sem::Type* ty) const override {
return ty->Is<sem::I32>(); return ty->Is<sem::I32>();
} }
sem::Type* Build(BuildState& state) const override { sem::Type* Build(BuildState& state) const override {
@ -244,7 +244,7 @@ class I32Builder : public Builder {
/// IU32Matcher is a Matcher for i32 or u32 types. /// IU32Matcher is a Matcher for i32 or u32 types.
class IU32Matcher : public Matcher { class IU32Matcher : public Matcher {
public: public:
bool MatchUnwrapped(MatchState&, sem::Type* ty) const override { bool MatchUnwrapped(MatchState&, const sem::Type* ty) const override {
return ty->Is<sem::I32>() || ty->Is<sem::U32>(); return ty->Is<sem::I32>() || ty->Is<sem::U32>();
} }
std::string str() const override { return "i32 or u32"; } std::string str() const override { return "i32 or u32"; }
@ -253,7 +253,7 @@ class IU32Matcher : public Matcher {
/// FIU32Matcher is a Matcher for f32, i32 or u32 types. /// FIU32Matcher is a Matcher for f32, i32 or u32 types.
class FIU32Matcher : public Matcher { class FIU32Matcher : public Matcher {
public: public:
bool MatchUnwrapped(MatchState&, sem::Type* ty) const override { bool MatchUnwrapped(MatchState&, const sem::Type* ty) const override {
return ty->Is<sem::F32>() || ty->Is<sem::I32>() || ty->Is<sem::U32>(); return ty->Is<sem::F32>() || ty->Is<sem::I32>() || ty->Is<sem::U32>();
} }
std::string str() const override { return "f32, i32 or u32"; } std::string str() const override { return "f32, i32 or u32"; }
@ -262,7 +262,7 @@ class FIU32Matcher : public Matcher {
/// ScalarMatcher is a Matcher for f32, i32, u32 or boolean types. /// ScalarMatcher is a Matcher for f32, i32, u32 or boolean types.
class ScalarMatcher : public Matcher { class ScalarMatcher : public Matcher {
public: public:
bool MatchUnwrapped(MatchState&, sem::Type* ty) const override { bool MatchUnwrapped(MatchState&, const sem::Type* ty) const override {
return ty->is_scalar(); return ty->is_scalar();
} }
std::string str() const override { return "scalar"; } std::string str() const override { return "scalar"; }
@ -275,7 +275,7 @@ class OpenSizeVecBuilder : public Builder {
OpenSizeVecBuilder(OpenNumber size, Builder* element_builder) OpenSizeVecBuilder(OpenNumber size, Builder* element_builder)
: size_(size), element_builder_(element_builder) {} : size_(size), element_builder_(element_builder) {}
bool MatchUnwrapped(MatchState& state, sem::Type* ty) const override { bool MatchUnwrapped(MatchState& state, const sem::Type* ty) const override {
if (auto* vec = ty->As<sem::Vector>()) { if (auto* vec = ty->As<sem::Vector>()) {
if (!MatchOpenNumber(state, size_, vec->size())) { if (!MatchOpenNumber(state, size_, vec->size())) {
return false; return false;
@ -307,7 +307,7 @@ class VecBuilder : public Builder {
VecBuilder(uint32_t size, Builder* element_builder) VecBuilder(uint32_t size, Builder* element_builder)
: size_(size), element_builder_(element_builder) {} : size_(size), element_builder_(element_builder) {}
bool MatchUnwrapped(MatchState& state, sem::Type* ty) const override { bool MatchUnwrapped(MatchState& state, const sem::Type* ty) const override {
if (auto* vec = ty->As<sem::Vector>()) { if (auto* vec = ty->As<sem::Vector>()) {
if (vec->size() == size_) { if (vec->size() == size_) {
return element_builder_->Match(state, vec->type()); return element_builder_->Match(state, vec->type());
@ -339,7 +339,7 @@ class OpenSizeMatBuilder : public Builder {
Builder* element_builder) Builder* element_builder)
: columns_(columns), rows_(rows), element_builder_(element_builder) {} : columns_(columns), rows_(rows), element_builder_(element_builder) {}
bool MatchUnwrapped(MatchState& state, sem::Type* ty) const override { bool MatchUnwrapped(MatchState& state, const sem::Type* ty) const override {
if (auto* mat = ty->As<sem::Matrix>()) { if (auto* mat = ty->As<sem::Matrix>()) {
if (!MatchOpenNumber(state, columns_, mat->columns())) { if (!MatchOpenNumber(state, columns_, mat->columns())) {
return false; return false;
@ -356,7 +356,8 @@ class OpenSizeMatBuilder : public Builder {
auto* el = element_builder_->Build(state); auto* el = element_builder_->Build(state);
auto columns = state.open_numbers.at(columns_); auto columns = state.open_numbers.at(columns_);
auto rows = state.open_numbers.at(rows_); auto rows = state.open_numbers.at(rows_);
return state.ty_mgr.Get<sem::Matrix>(el, rows, columns); return state.ty_mgr.Get<sem::Matrix>(const_cast<sem::Type*>(el), rows,
columns);
} }
std::string str() const override { std::string str() const override {
@ -376,7 +377,7 @@ class PtrBuilder : public Builder {
explicit PtrBuilder(Builder* element_builder) explicit PtrBuilder(Builder* element_builder)
: element_builder_(element_builder) {} : element_builder_(element_builder) {}
bool MatchUnwrapped(MatchState& state, sem::Type* ty) const override { bool MatchUnwrapped(MatchState& state, const sem::Type* ty) const override {
if (auto* ptr = ty->As<sem::Pointer>()) { if (auto* ptr = ty->As<sem::Pointer>()) {
return element_builder_->Match(state, ptr->type()); return element_builder_->Match(state, ptr->type());
} }
@ -385,7 +386,8 @@ class PtrBuilder : public Builder {
sem::Type* Build(BuildState& state) const override { sem::Type* Build(BuildState& state) const override {
auto* el = element_builder_->Build(state); auto* el = element_builder_->Build(state);
return state.ty_mgr.Get<sem::Pointer>(el, ast::StorageClass::kNone); return state.ty_mgr.Get<sem::Pointer>(const_cast<sem::Type*>(el),
ast::StorageClass::kNone);
} }
bool ExpectsPointer() const override { return true; } bool ExpectsPointer() const override { return true; }
@ -404,7 +406,7 @@ class ArrayBuilder : public Builder {
explicit ArrayBuilder(Builder* element_builder) explicit ArrayBuilder(Builder* element_builder)
: element_builder_(element_builder) {} : element_builder_(element_builder) {}
bool MatchUnwrapped(MatchState& state, sem::Type* ty) const override { bool MatchUnwrapped(MatchState& state, const sem::Type* ty) const override {
if (auto* arr = ty->As<sem::ArrayType>()) { if (auto* arr = ty->As<sem::ArrayType>()) {
if (arr->size() == 0) { if (arr->size() == 0) {
return element_builder_->Match(state, arr->type()); return element_builder_->Match(state, arr->type());
@ -415,7 +417,8 @@ class ArrayBuilder : public Builder {
sem::Type* Build(BuildState& state) const override { sem::Type* Build(BuildState& state) const override {
auto* el = element_builder_->Build(state); auto* el = element_builder_->Build(state);
return state.ty_mgr.Get<sem::ArrayType>(el, 0, ast::DecorationList{}); return state.ty_mgr.Get<sem::ArrayType>(const_cast<sem::Type*>(el), 0,
ast::DecorationList{});
} }
std::string str() const override { std::string str() const override {
@ -433,7 +436,7 @@ class SampledTextureBuilder : public Builder {
Builder* type_builder) Builder* type_builder)
: dimensions_(dimensions), type_builder_(type_builder) {} : dimensions_(dimensions), type_builder_(type_builder) {}
bool MatchUnwrapped(MatchState& state, sem::Type* ty) const override { bool MatchUnwrapped(MatchState& state, const sem::Type* ty) const override {
if (auto* tex = ty->As<sem::SampledTexture>()) { if (auto* tex = ty->As<sem::SampledTexture>()) {
if (tex->dim() == dimensions_) { if (tex->dim() == dimensions_) {
return type_builder_->Match(state, tex->type()); return type_builder_->Match(state, tex->type());
@ -466,7 +469,7 @@ class MultisampledTextureBuilder : public Builder {
Builder* type_builder) Builder* type_builder)
: dimensions_(dimensions), type_builder_(type_builder) {} : dimensions_(dimensions), type_builder_(type_builder) {}
bool MatchUnwrapped(MatchState& state, sem::Type* ty) const override { bool MatchUnwrapped(MatchState& state, const sem::Type* ty) const override {
if (auto* tex = ty->As<sem::MultisampledTexture>()) { if (auto* tex = ty->As<sem::MultisampledTexture>()) {
if (tex->dim() == dimensions_) { if (tex->dim() == dimensions_) {
return type_builder_->Match(state, tex->type()); return type_builder_->Match(state, tex->type());
@ -498,7 +501,7 @@ class DepthTextureBuilder : public Builder {
explicit DepthTextureBuilder(ast::TextureDimension dimensions) explicit DepthTextureBuilder(ast::TextureDimension dimensions)
: dimensions_(dimensions) {} : dimensions_(dimensions) {}
bool MatchUnwrapped(MatchState&, sem::Type* ty) const override { bool MatchUnwrapped(MatchState&, const sem::Type* ty) const override {
if (auto* tex = ty->As<sem::DepthTexture>()) { if (auto* tex = ty->As<sem::DepthTexture>()) {
return tex->dim() == dimensions_; return tex->dim() == dimensions_;
} }
@ -531,7 +534,7 @@ class StorageTextureBuilder : public Builder {
texel_format_(texel_format), texel_format_(texel_format),
channel_format_(channel_format) {} channel_format_(channel_format) {}
bool MatchUnwrapped(MatchState& state, sem::Type* ty) const override { bool MatchUnwrapped(MatchState& state, const sem::Type* ty) const override {
if (auto* ac = ty->As<sem::AccessControl>()) { if (auto* ac = ty->As<sem::AccessControl>()) {
// If we have an storage texture argument that's got an access control // If we have an storage texture argument that's got an access control
// type wrapped around it, accept it. Signatures that don't include an // type wrapped around it, accept it. Signatures that don't include an
@ -555,8 +558,8 @@ class StorageTextureBuilder : public Builder {
auto texel_format = auto texel_format =
static_cast<ast::ImageFormat>(state.open_numbers.at(texel_format_)); static_cast<ast::ImageFormat>(state.open_numbers.at(texel_format_));
auto* channel_format = state.open_types.at(channel_format_); auto* channel_format = state.open_types.at(channel_format_);
return state.ty_mgr.Get<sem::StorageTexture>(dimensions_, texel_format, return state.ty_mgr.Get<sem::StorageTexture>(
channel_format); dimensions_, texel_format, const_cast<sem::Type*>(channel_format));
} }
std::string str() const override { std::string str() const override {
@ -576,7 +579,7 @@ class ExternalTextureBuilder : public Builder {
public: public:
ExternalTextureBuilder() {} ExternalTextureBuilder() {}
bool MatchUnwrapped(MatchState&, sem::Type* ty) const override { bool MatchUnwrapped(MatchState&, const sem::Type* ty) const override {
return ty->Is<sem::ExternalTexture>(); return ty->Is<sem::ExternalTexture>();
} }
@ -592,7 +595,7 @@ class SamplerBuilder : public Builder {
public: public:
explicit SamplerBuilder(ast::SamplerKind kind) : kind_(kind) {} explicit SamplerBuilder(ast::SamplerKind kind) : kind_(kind) {}
bool MatchUnwrapped(MatchState&, sem::Type* ty) const override { bool MatchUnwrapped(MatchState&, const sem::Type* ty) const override {
if (auto* sampler = ty->As<sem::Sampler>()) { if (auto* sampler = ty->As<sem::Sampler>()) {
return sampler->kind() == kind_; return sampler->kind() == kind_;
} }
@ -624,7 +627,7 @@ class AccessControlBuilder : public Builder {
Builder* type) Builder* type)
: access_control_(access_control), type_(type) {} : access_control_(access_control), type_(type) {}
bool MatchUnwrapped(MatchState& state, sem::Type* ty) const override { bool MatchUnwrapped(MatchState& state, const sem::Type* ty) const override {
if (auto* ac = ty->As<sem::AccessControl>()) { if (auto* ac = ty->As<sem::AccessControl>()) {
if (ac->access_control() == access_control_) { if (ac->access_control() == access_control_) {
return type_->Match(state, ty); return type_->Match(state, ty);
@ -656,7 +659,7 @@ class Impl : public IntrinsicTable {
IntrinsicTable::Result Lookup(ProgramBuilder& builder, IntrinsicTable::Result Lookup(ProgramBuilder& builder,
sem::IntrinsicType type, sem::IntrinsicType type,
const std::vector<sem::Type*>& args, const std::vector<const sem::Type*>& args,
const Source& source) const override; const Source& source) const override;
/// Holds the information about a single overload parameter used for matching /// Holds the information about a single overload parameter used for matching
@ -678,7 +681,7 @@ class Impl : public IntrinsicTable {
/// (positive representing a greater match), and nullptr is returned. /// (positive representing a greater match), and nullptr is returned.
sem::Intrinsic* Match(ProgramBuilder& builder, sem::Intrinsic* Match(ProgramBuilder& builder,
sem::IntrinsicType type, sem::IntrinsicType type,
const std::vector<sem::Type*>& arg_types, const std::vector<const sem::Type*>& arg_types,
diag::List& diagnostics, diag::List& diagnostics,
int& match_score) const; int& match_score) const;
@ -1328,7 +1331,7 @@ std::string str(const Impl::Overload& overload) {
/// types. /// types.
std::string CallSignature(ProgramBuilder& builder, std::string CallSignature(ProgramBuilder& builder,
sem::IntrinsicType type, sem::IntrinsicType type,
const std::vector<sem::Type*>& args) { const std::vector<const sem::Type*>& args) {
std::stringstream ss; std::stringstream ss;
ss << sem::str(type) << "("; ss << sem::str(type) << "(";
{ {
@ -1348,7 +1351,7 @@ std::string CallSignature(ProgramBuilder& builder,
IntrinsicTable::Result Impl::Lookup(ProgramBuilder& builder, IntrinsicTable::Result Impl::Lookup(ProgramBuilder& builder,
sem::IntrinsicType type, sem::IntrinsicType type,
const std::vector<sem::Type*>& args, const std::vector<const sem::Type*>& args,
const Source& source) const { const Source& source) const {
diag::List diagnostics; diag::List diagnostics;
// Candidate holds information about a mismatched overload that could be what // Candidate holds information about a mismatched overload that could be what
@ -1398,7 +1401,7 @@ IntrinsicTable::Result Impl::Lookup(ProgramBuilder& builder,
sem::Intrinsic* Impl::Overload::Match(ProgramBuilder& builder, sem::Intrinsic* Impl::Overload::Match(ProgramBuilder& builder,
sem::IntrinsicType intrinsic, sem::IntrinsicType intrinsic,
const std::vector<sem::Type*>& args, const std::vector<const sem::Type*>& args,
diag::List& diagnostics, diag::List& diagnostics,
int& match_score) const { int& match_score) const {
if (type != intrinsic) { if (type != intrinsic) {
@ -1495,10 +1498,12 @@ sem::Intrinsic* Impl::Overload::Match(ProgramBuilder& builder,
for (size_t i = 0; i < args.size(); i++) { for (size_t i = 0; i < args.size(); i++) {
auto& parameter = parameters[i]; auto& parameter = parameters[i];
auto* ty = parameter.matcher->Build(builder_state); auto* ty = parameter.matcher->Build(builder_state);
params.emplace_back(sem::Parameter{ty, parameter.usage}); params.emplace_back(
sem::Parameter{const_cast<sem::Type*>(ty), parameter.usage});
} }
return builder.create<sem::Intrinsic>(intrinsic, ret, params); return builder.create<sem::Intrinsic>(intrinsic, const_cast<sem::Type*>(ret),
params);
} }
} // namespace } // namespace

View File

@ -51,7 +51,7 @@ class IntrinsicTable {
/// @return the semantic intrinsic if found, otherwise nullptr /// @return the semantic intrinsic if found, otherwise nullptr
virtual Result Lookup(ProgramBuilder& builder, virtual Result Lookup(ProgramBuilder& builder,
sem::IntrinsicType type, sem::IntrinsicType type,
const std::vector<sem::Type*>& args, const std::vector<const sem::Type*>& args,
const Source& source) const = 0; const Source& source) const = 0;
}; };

View File

@ -161,15 +161,15 @@ bool Resolver::Resolve() {
} }
// https://gpuweb.github.io/gpuweb/wgsl.html#storable-types // https://gpuweb.github.io/gpuweb/wgsl.html#storable-types
bool Resolver::IsStorable(sem::Type* type) { bool Resolver::IsStorable(const sem::Type* type) {
type = type->UnwrapIfNeeded(); type = type->UnwrapIfNeeded();
if (type->is_scalar() || type->Is<sem::Vector>() || type->Is<sem::Matrix>()) { if (type->is_scalar() || type->Is<sem::Vector>() || type->Is<sem::Matrix>()) {
return true; return true;
} }
if (sem::ArrayType* arr = type->As<sem::ArrayType>()) { if (auto* arr = type->As<sem::ArrayType>()) {
return IsStorable(arr->type()); return IsStorable(arr->type());
} }
if (sem::StructType* str = type->As<sem::StructType>()) { if (auto* str = type->As<sem::StructType>()) {
for (const auto* member : str->impl()->members()) { for (const auto* member : str->impl()->members()) {
if (!IsStorable(member->type())) { if (!IsStorable(member->type())) {
return false; return false;
@ -181,7 +181,7 @@ bool Resolver::IsStorable(sem::Type* type) {
} }
// https://gpuweb.github.io/gpuweb/wgsl.html#host-shareable-types // https://gpuweb.github.io/gpuweb/wgsl.html#host-shareable-types
bool Resolver::IsHostShareable(sem::Type* type) { bool Resolver::IsHostShareable(const sem::Type* type) {
type = type->UnwrapIfNeeded(); type = type->UnwrapIfNeeded();
if (type->IsAnyOf<sem::I32, sem::U32, sem::F32>()) { if (type->IsAnyOf<sem::I32, sem::U32, sem::F32>()) {
return true; return true;
@ -206,7 +206,7 @@ bool Resolver::IsHostShareable(sem::Type* type) {
return false; return false;
} }
bool Resolver::IsValidAssignment(sem::Type* lhs, sem::Type* rhs) { bool Resolver::IsValidAssignment(const sem::Type* lhs, const sem::Type* rhs) {
// TODO(crbug.com/tint/659): This is a rough approximation, and is missing // TODO(crbug.com/tint/659): This is a rough approximation, and is missing
// checks for writability of pointer storage class, access control, etc. // checks for writability of pointer storage class, access control, etc.
// This will need to be fixed after WGSL agrees the behavior of pointers / // This will need to be fixed after WGSL agrees the behavior of pointers /
@ -273,7 +273,7 @@ bool Resolver::ResolveInternal() {
return true; return true;
} }
sem::Type* Resolver::Type(ast::Type* ty) { sem::Type* Resolver::Type(const ast::Type* ty) {
Mark(ty); Mark(ty);
sem::Type* s = nullptr; sem::Type* s = nullptr;
if (ty->Is<ast::Void>()) { if (ty->Is<ast::Void>()) {
@ -305,7 +305,7 @@ sem::Type* Resolver::Type(ast::Type* ty) {
auto* el = Type(ptr->type()); auto* el = Type(ptr->type());
s = builder_->create<sem::Pointer>(el, ptr->storage_class()); s = builder_->create<sem::Pointer>(el, ptr->storage_class());
} else if (auto* str = ty->As<ast::Struct>()) { } else if (auto* str = ty->As<ast::Struct>()) {
s = builder_->create<sem::StructType>(str); s = builder_->create<sem::StructType>(const_cast<ast::Struct*>(str));
} else if (auto* sampler = ty->As<ast::Sampler>()) { } else if (auto* sampler = ty->As<ast::Sampler>()) {
s = builder_->create<sem::Sampler>(sampler->kind()); s = builder_->create<sem::Sampler>(sampler->kind());
} else if (auto* sampled_tex = ty->As<ast::SampledTexture>()) { } else if (auto* sampled_tex = ty->As<ast::SampledTexture>()) {
@ -342,8 +342,9 @@ bool Resolver::Type(sem::Type* ty) {
return true; return true;
} }
Resolver::VariableInfo* Resolver::Variable(ast::Variable* var, Resolver::VariableInfo* Resolver::Variable(
sem::Type* type /*=nullptr*/) { ast::Variable* var,
const sem::Type* type /* = nullptr*/) {
auto it = variable_to_info_.find(var); auto it = variable_to_info_.find(var);
if (it != variable_to_info_.end()) { if (it != variable_to_info_.end()) {
return it->second; return it->second;
@ -658,7 +659,7 @@ bool Resolver::ValidateEntryPoint(const ast::Function* func,
}; };
// Inner lambda that is applied to a type and all of its members. // Inner lambda that is applied to a type and all of its members.
auto validate_entry_point_decorations_inner = auto validate_entry_point_decorations_inner =
[&](const ast::DecorationList& decos, sem::Type* ty, Source source, [&](const ast::DecorationList& decos, const sem::Type* ty, Source source,
ParamOrRetType param_or_ret, bool is_struct_member) { ParamOrRetType param_or_ret, bool is_struct_member) {
// Scan decorations for pipeline IO attributes. // Scan decorations for pipeline IO attributes.
// Check for overlap with attributes that have been seen previously. // Check for overlap with attributes that have been seen previously.
@ -739,7 +740,8 @@ bool Resolver::ValidateEntryPoint(const ast::Function* func,
// Outer lambda for validating the entry point decorations for a type. // Outer lambda for validating the entry point decorations for a type.
auto validate_entry_point_decorations = [&](const ast::DecorationList& decos, auto validate_entry_point_decorations = [&](const ast::DecorationList& decos,
sem::Type* ty, Source source, const sem::Type* ty,
Source source,
ParamOrRetType param_or_ret) { ParamOrRetType param_or_ret) {
// Validate the decorations for the type. // Validate the decorations for the type.
if (!validate_entry_point_decorations_inner(decos, ty, source, param_or_ret, if (!validate_entry_point_decorations_inner(decos, ty, source, param_or_ret,
@ -1274,7 +1276,7 @@ bool Resolver::Call(ast::CallExpression* call) {
bool Resolver::IntrinsicCall(ast::CallExpression* call, bool Resolver::IntrinsicCall(ast::CallExpression* call,
sem::IntrinsicType intrinsic_type) { sem::IntrinsicType intrinsic_type) {
std::vector<sem::Type*> arg_tys; std::vector<const sem::Type*> arg_tys;
arg_tys.reserve(call->params().size()); arg_tys.reserve(call->params().size());
for (auto* expr : call->params()) { for (auto* expr : call->params()) {
arg_tys.emplace_back(TypeOf(expr)); arg_tys.emplace_back(TypeOf(expr));
@ -1325,10 +1327,10 @@ bool Resolver::Constructor(ast::ConstructorExpression* expr) {
bool Resolver::ValidateVectorConstructor(const sem::Vector* vec_type, bool Resolver::ValidateVectorConstructor(const sem::Vector* vec_type,
const ast::ExpressionList& values) { const ast::ExpressionList& values) {
sem::Type* elem_type = vec_type->type()->UnwrapAll(); auto* elem_type = vec_type->type()->UnwrapAll();
size_t value_cardinality_sum = 0; size_t value_cardinality_sum = 0;
for (auto* value : values) { for (auto* value : values) {
sem::Type* value_type = TypeOf(value)->UnwrapAll(); auto* value_type = TypeOf(value)->UnwrapAll();
if (value_type->is_scalar()) { if (value_type->is_scalar()) {
if (elem_type != value_type) { if (elem_type != value_type) {
diagnostics_.add_error( diagnostics_.add_error(
@ -1398,7 +1400,7 @@ bool Resolver::ValidateMatrixConstructor(const sem::Matrix* matrix_type,
return true; return true;
} }
sem::Type* elem_type = matrix_type->type()->UnwrapAll(); auto* elem_type = matrix_type->type()->UnwrapAll();
if (matrix_type->columns() != values.size()) { if (matrix_type->columns() != values.size()) {
const Source& values_start = values[0]->source(); const Source& values_start = values[0]->source();
const Source& values_end = values[values.size() - 1]->source(); const Source& values_end = values[values.size() - 1]->source();
@ -1412,7 +1414,7 @@ bool Resolver::ValidateMatrixConstructor(const sem::Matrix* matrix_type,
} }
for (auto* value : values) { for (auto* value : values) {
sem::Type* value_type = TypeOf(value)->UnwrapAll(); auto* value_type = TypeOf(value)->UnwrapAll();
auto* value_vec = value_type->As<sem::Vector>(); auto* value_vec = value_type->As<sem::Vector>();
if (!value_vec || value_vec->size() != matrix_type->rows() || if (!value_vec || value_vec->size() != matrix_type->rows() ||
@ -1442,8 +1444,8 @@ bool Resolver::Identifier(ast::IdentifierExpression* expr) {
} else if (var->type->Is<sem::Pointer>()) { } else if (var->type->Is<sem::Pointer>()) {
SetType(expr, var->type); SetType(expr, var->type);
} else { } else {
SetType(expr, SetType(expr, builder_->create<sem::Pointer>(
builder_->create<sem::Pointer>(var->type, var->storage_class)); const_cast<sem::Type*>(var->type), var->storage_class));
} }
var->users.push_back(expr); var->users.push_back(expr);
@ -1830,7 +1832,7 @@ bool Resolver::Binary(ast::BinaryExpression* expr) {
auto* rhs_mat = rhs_type->As<sem::Matrix>(); auto* rhs_mat = rhs_type->As<sem::Matrix>();
auto* lhs_vec = lhs_type->As<sem::Vector>(); auto* lhs_vec = lhs_type->As<sem::Vector>();
auto* rhs_vec = rhs_type->As<sem::Vector>(); auto* rhs_vec = rhs_type->As<sem::Vector>();
sem::Type* result_type; const sem::Type* result_type = nullptr;
if (lhs_mat && rhs_mat) { if (lhs_mat && rhs_mat) {
result_type = builder_->create<sem::Matrix>( result_type = builder_->create<sem::Matrix>(
lhs_mat->type(), lhs_mat->rows(), rhs_mat->columns()); lhs_mat->type(), lhs_mat->rows(), rhs_mat->columns());
@ -1884,7 +1886,7 @@ bool Resolver::VariableDeclStatement(const ast::VariableDeclStatement* stmt) {
ast::Variable* var = stmt->variable(); ast::Variable* var = stmt->variable();
Mark(var); Mark(var);
sem::Type* type = var->declared_type(); const sem::Type* type = var->declared_type();
bool is_global = false; bool is_global = false;
if (variable_stack_.get(var->symbol(), nullptr, &is_global)) { if (variable_stack_.get(var->symbol(), nullptr, &is_global)) {
@ -1960,7 +1962,7 @@ bool Resolver::VariableDeclStatement(const ast::VariableDeclStatement* stmt) {
return true; return true;
} }
sem::Type* Resolver::TypeOf(ast::Expression* expr) { const sem::Type* Resolver::TypeOf(ast::Expression* expr) {
auto it = expr_info_.find(expr); auto it = expr_info_.find(expr);
if (it != expr_info_.end()) { if (it != expr_info_.end()) {
return it->second.type; return it->second.type;
@ -1968,7 +1970,7 @@ sem::Type* Resolver::TypeOf(ast::Expression* expr) {
return nullptr; return nullptr;
} }
void Resolver::SetType(ast::Expression* expr, sem::Type* type) { void Resolver::SetType(ast::Expression* expr, const sem::Type* type) {
if (expr_info_.count(expr)) { if (expr_info_.count(expr)) {
TINT_ICE(builder_->Diagnostics()) TINT_ICE(builder_->Diagnostics())
<< "SetType() called twice for the same expression"; << "SetType() called twice for the same expression";
@ -2078,9 +2080,9 @@ void Resolver::CreateSemanticNodes() const {
auto* info = it.second; auto* info = it.second;
builder_->Sem().Add( builder_->Sem().Add(
str, builder_->create<sem::Struct>( str, builder_->create<sem::Struct>(
str, std::move(info->members), info->align, info->size, const_cast<sem::StructType*>(str), std::move(info->members),
info->size_no_padding, info->storage_class_usage, info->align, info->size, info->size_no_padding,
info->pipeline_stage_uses)); info->storage_class_usage, info->pipeline_stage_uses));
} }
} }
@ -2148,7 +2150,8 @@ bool Resolver::DefaultAlignAndSize(sem::Type* ty,
return false; return false;
} }
const sem::Array* Resolver::Array(sem::ArrayType* arr, const Source& source) { const sem::Array* Resolver::Array(const sem::ArrayType* arr,
const Source& source) {
if (auto* sem = builder_->Sem().Get(arr)) { if (auto* sem = builder_->Sem().Get(arr)) {
// Semantic info already constructed for this array type // Semantic info already constructed for this array type
return sem; return sem;
@ -2171,7 +2174,8 @@ const sem::Array* Resolver::Array(sem::ArrayType* arr, const Source& source) {
// WebGPU requires runtime arrays have at least one element, but the AST // WebGPU requires runtime arrays have at least one element, but the AST
// records an element count of 0 for it. // records an element count of 0 for it.
auto size = std::max<uint32_t>(arr->size(), 1) * stride; auto size = std::max<uint32_t>(arr->size(), 1) * stride;
auto* sem = builder_->create<sem::Array>(arr, align, size, stride); auto* sem = builder_->create<sem::Array>(const_cast<sem::ArrayType*>(arr),
align, size, stride);
builder_->Sem().Add(arr, sem); builder_->Sem().Add(arr, sem);
return sem; return sem;
}; };
@ -2305,7 +2309,7 @@ bool Resolver::ValidateStructure(const sem::StructType* st) {
return true; return true;
} }
Resolver::StructInfo* Resolver::Structure(sem::StructType* str) { Resolver::StructInfo* Resolver::Structure(const sem::StructType* str) {
auto info_it = struct_info_.find(str); auto info_it = struct_info_.find(str);
if (info_it != struct_info_.end()) { if (info_it != struct_info_.end()) {
// StructInfo already resolved for this structure type // StructInfo already resolved for this structure type
@ -2617,7 +2621,7 @@ bool Resolver::Assignment(ast::AssignmentStatement* a) {
} }
bool Resolver::ApplyStorageClassUsageToType(ast::StorageClass sc, bool Resolver::ApplyStorageClassUsageToType(ast::StorageClass sc,
sem::Type* ty, const sem::Type* ty,
const Source& usage) { const Source& usage) {
ty = ty->UnwrapIfNeeded(); ty = ty->UnwrapIfNeeded();
@ -2676,27 +2680,29 @@ std::string Resolver::VectorPretty(uint32_t size, sem::Type* element_type) {
return vec_type.FriendlyName(builder_->Symbols()); return vec_type.FriendlyName(builder_->Symbols());
} }
sem::Type* Resolver::Canonical(sem::Type* type) { const sem::Type* Resolver::Canonical(const sem::Type* type) {
using AccessControl = sem::AccessControl; using AccessControl = sem::AccessControl;
using Alias = sem::Alias; using Alias = sem::Alias;
using Matrix = sem::Matrix; using Matrix = sem::Matrix;
using Type = sem::Type; using Type = sem::Type;
using Vector = sem::Vector; using Vector = sem::Vector;
std::function<Type*(Type*)> make_canonical; std::function<const Type*(const Type*)> make_canonical;
make_canonical = [&](Type* t) -> sem::Type* { make_canonical = [&](const Type* t) -> const sem::Type* {
// Unwrap alias sequence // Unwrap alias sequence
Type* ct = t; const Type* ct = t;
while (auto* p = ct->As<Alias>()) { while (auto* p = ct->As<Alias>()) {
ct = p->type(); ct = p->type();
} }
if (auto* v = ct->As<Vector>()) { if (auto* v = ct->As<Vector>()) {
return builder_->create<Vector>(make_canonical(v->type()), v->size()); return builder_->create<Vector>(
const_cast<sem::Type*>(make_canonical(v->type())), v->size());
} }
if (auto* m = ct->As<Matrix>()) { if (auto* m = ct->As<Matrix>()) {
return builder_->create<Matrix>(make_canonical(m->type()), m->rows(), return builder_->create<Matrix>(
m->columns()); const_cast<sem::Type*>(make_canonical(m->type())), m->rows(),
m->columns());
} }
if (auto* ac = ct->As<AccessControl>()) { if (auto* ac = ct->As<AccessControl>()) {
return builder_->create<AccessControl>(ac->access_control(), return builder_->create<AccessControl>(ac->access_control(),
@ -2709,7 +2715,7 @@ sem::Type* Resolver::Canonical(sem::Type* type) {
[&] { return make_canonical(type); }); [&] { return make_canonical(type); });
} }
void Resolver::Mark(ast::Node* node) { void Resolver::Mark(const ast::Node* node) {
if (node == nullptr) { if (node == nullptr) {
TINT_ICE(diagnostics_) << "Resolver::Mark() called with nullptr"; TINT_ICE(diagnostics_) << "Resolver::Mark() called with nullptr";
} }
@ -2722,7 +2728,8 @@ void Resolver::Mark(ast::Node* node) {
<< "At: " << node->source(); << "At: " << node->source();
} }
Resolver::VariableInfo::VariableInfo(ast::Variable* decl, sem::Type* ctype) Resolver::VariableInfo::VariableInfo(const ast::Variable* decl,
const sem::Type* ctype)
: declaration(decl), : declaration(decl),
type(ctype), type(ctype),
storage_class(decl->declared_storage_class()) {} storage_class(decl->declared_storage_class()) {}

View File

@ -73,34 +73,34 @@ class Resolver {
/// @param type the given type /// @param type the given type
/// @returns true if the given type is storable /// @returns true if the given type is storable
static bool IsStorable(sem::Type* type); static bool IsStorable(const sem::Type* type);
/// @param type the given type /// @param type the given type
/// @returns true if the given type is host-shareable /// @returns true if the given type is host-shareable
static bool IsHostShareable(sem::Type* type); static bool IsHostShareable(const sem::Type* type);
/// @param lhs the assignment store type (non-pointer) /// @param lhs the assignment store type (non-pointer)
/// @param rhs the assignment source type (non-pointer or pointer with /// @param rhs the assignment source type (non-pointer or pointer with
/// auto-deref) /// auto-deref)
/// @returns true an expression of type `rhs` can be assigned to a variable, /// @returns true an expression of type `rhs` can be assigned to a variable,
/// structure member or array element of type `lhs` /// structure member or array element of type `lhs`
static bool IsValidAssignment(sem::Type* lhs, sem::Type* rhs); static bool IsValidAssignment(const sem::Type* lhs, const sem::Type* rhs);
/// @param type the input type /// @param type the input type
/// @returns the canonical type for `type`; that is, a type with all aliases /// @returns the canonical type for `type`; that is, a type with all aliases
/// removed. For example, `Canonical(alias<alias<vec3<alias<f32>>>>)` is /// removed. For example, `Canonical(alias<alias<vec3<alias<f32>>>>)` is
/// `vec3<f32>`. /// `vec3<f32>`.
sem::Type* Canonical(sem::Type* type); const sem::Type* Canonical(const sem::Type* type);
private: private:
/// Structure holding semantic information about a variable. /// Structure holding semantic information about a variable.
/// Used to build the sem::Variable nodes at the end of resolving. /// Used to build the sem::Variable nodes at the end of resolving.
struct VariableInfo { struct VariableInfo {
VariableInfo(ast::Variable* decl, sem::Type* type); VariableInfo(const ast::Variable* decl, const sem::Type* type);
~VariableInfo(); ~VariableInfo();
ast::Variable* const declaration; ast::Variable const* const declaration;
sem::Type* type; sem::Type const* type;
ast::StorageClass storage_class; ast::StorageClass storage_class;
std::vector<ast::IdentifierExpression*> users; std::vector<ast::IdentifierExpression*> users;
}; };
@ -124,7 +124,7 @@ class Resolver {
/// Structure holding semantic information about an expression. /// Structure holding semantic information about an expression.
/// Used to build the sem::Expression nodes at the end of resolving. /// Used to build the sem::Expression nodes at the end of resolving.
struct ExpressionInfo { struct ExpressionInfo {
sem::Type* type; sem::Type const* type;
sem::Statement* statement; sem::Statement* statement;
}; };
@ -260,25 +260,25 @@ class Resolver {
/// hasn't been constructed already. If an error is raised, nullptr is /// hasn't been constructed already. If an error is raised, nullptr is
/// returned. /// returned.
/// @param ty the ast::Type /// @param ty the ast::Type
sem::Type* Type(ast::Type* ty); sem::Type* Type(const ast::Type* ty);
/// @returns the semantic information for the array `arr`, building it if it /// @returns the semantic information for the array `arr`, building it if it
/// hasn't been constructed already. If an error is raised, nullptr is /// hasn't been constructed already. If an error is raised, nullptr is
/// returned. /// returned.
/// @param arr the Array to get semantic information for /// @param arr the Array to get semantic information for
/// @param source the Source of the ast node with this array as its type /// @param source the Source of the ast node with this array as its type
const sem::Array* Array(sem::ArrayType* arr, const Source& source); const sem::Array* Array(const sem::ArrayType* arr, const Source& source);
/// @returns the StructInfo for the structure `str`, building it if it hasn't /// @returns the StructInfo for the structure `str`, building it if it hasn't
/// been constructed already. If an error is raised, nullptr is returned. /// been constructed already. If an error is raised, nullptr is returned.
StructInfo* Structure(sem::StructType* str); StructInfo* Structure(const sem::StructType* str);
/// @returns the VariableInfo for the variable `var`, building it if it hasn't /// @returns the VariableInfo for the variable `var`, building it if it hasn't
/// been constructed already. If an error is raised, nullptr is returned. /// been constructed already. If an error is raised, nullptr is returned.
/// @param var the variable to create or return the `VariableInfo` for /// @param var the variable to create or return the `VariableInfo` for
/// @param type optional type of `var` to use instead of /// @param type optional type of `var` to use instead of
/// `var->declared_type()`. For type inference. /// `var->declared_type()`. For type inference.
VariableInfo* Variable(ast::Variable* var, sem::Type* type = nullptr); VariableInfo* Variable(ast::Variable* var, const sem::Type* type = nullptr);
/// Records the storage class usage for the given type, and any transient /// Records the storage class usage for the given type, and any transient
/// dependencies of the type. Validates that the type can be used for the /// dependencies of the type. Validates that the type can be used for the
@ -289,7 +289,7 @@ class Resolver {
/// given type and storage class. Used for generating sensible error messages. /// given type and storage class. Used for generating sensible error messages.
/// @returns true on success, false on error /// @returns true on success, false on error
bool ApplyStorageClassUsageToType(ast::StorageClass sc, bool ApplyStorageClassUsageToType(ast::StorageClass sc,
sem::Type* ty, const sem::Type* ty,
const Source& usage); const Source& usage);
/// @param align the output default alignment in bytes for the type `ty` /// @param align the output default alignment in bytes for the type `ty`
@ -303,13 +303,13 @@ class Resolver {
/// @returns the resolved type of the ast::Expression `expr` /// @returns the resolved type of the ast::Expression `expr`
/// @param expr the expression /// @param expr the expression
sem::Type* TypeOf(ast::Expression* expr); const sem::Type* TypeOf(ast::Expression* expr);
/// Creates a sem::Expression node with the resolved type `type`, and /// Creates a sem::Expression node with the resolved type `type`, and
/// assigns this semantic node to the expression `expr`. /// assigns this semantic node to the expression `expr`.
/// @param expr the expression /// @param expr the expression
/// @param type the resolved type /// @param type the resolved type
void SetType(ast::Expression* expr, sem::Type* type); void SetType(ast::Expression* expr, const sem::Type* type);
/// Constructs a new BlockInfo with the given type and with #current_block_ as /// Constructs a new BlockInfo with the given type and with #current_block_ as
/// its parent, assigns this to #current_block_, and then calls `callback`. /// its parent, assigns this to #current_block_, and then calls `callback`.
@ -329,7 +329,7 @@ class Resolver {
/// Mark records that the given AST node has been visited, and asserts that /// Mark records that the given AST node has been visited, and asserts that
/// the given node has not already been seen. Diamonds in the AST are illegal. /// the given node has not already been seen. Diamonds in the AST are illegal.
/// @param node the AST node. /// @param node the AST node.
void Mark(ast::Node* node); void Mark(const ast::Node* node);
ProgramBuilder* const builder_; ProgramBuilder* const builder_;
std::unique_ptr<IntrinsicTable> const intrinsic_table_; std::unique_ptr<IntrinsicTable> const intrinsic_table_;
@ -341,9 +341,9 @@ class Resolver {
std::unordered_map<const ast::Variable*, VariableInfo*> variable_to_info_; std::unordered_map<const ast::Variable*, VariableInfo*> variable_to_info_;
std::unordered_map<ast::CallExpression*, FunctionCallInfo> function_calls_; std::unordered_map<ast::CallExpression*, FunctionCallInfo> function_calls_;
std::unordered_map<ast::Expression*, ExpressionInfo> expr_info_; std::unordered_map<ast::Expression*, ExpressionInfo> expr_info_;
std::unordered_map<sem::StructType*, StructInfo*> struct_info_; std::unordered_map<const sem::StructType*, StructInfo*> struct_info_;
std::unordered_map<sem::Type*, sem::Type*> type_to_canonical_; std::unordered_map<const sem::Type*, const sem::Type*> type_to_canonical_;
std::unordered_set<ast::Node*> marked_; std::unordered_set<const ast::Node*> marked_;
FunctionInfo* current_function_ = nullptr; FunctionInfo* current_function_ = nullptr;
sem::Statement* current_statement_ = nullptr; sem::Statement* current_statement_ = nullptr;
BlockAllocator<VariableInfo> variable_infos_; BlockAllocator<VariableInfo> variable_infos_;