| // Copyright 2022 The Tint Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "src/tint/resolver/sem_helper.h" |
| |
| #include "src/tint/sem/builtin_enum_expression.h" |
| #include "src/tint/sem/function.h" |
| #include "src/tint/sem/function_expression.h" |
| #include "src/tint/sem/type_expression.h" |
| #include "src/tint/sem/value_expression.h" |
| #include "src/tint/switch.h" |
| |
| namespace tint::resolver { |
| |
| SemHelper::SemHelper(ProgramBuilder* builder) : builder_(builder) {} |
| |
| SemHelper::~SemHelper() = default; |
| |
| std::string SemHelper::TypeNameOf(const type::Type* ty) const { |
| return RawTypeNameOf(ty->UnwrapRef()); |
| } |
| |
| std::string SemHelper::RawTypeNameOf(const type::Type* ty) const { |
| return ty->FriendlyName(); |
| } |
| |
| type::Type* SemHelper::TypeOf(const ast::Expression* expr) const { |
| auto* sem = GetVal(expr); |
| return sem ? const_cast<type::Type*>(sem->Type()) : nullptr; |
| } |
| |
| std::string SemHelper::Describe(const sem::Expression* expr) const { |
| return Switch( |
| expr, // |
| [&](const sem::VariableUser* var_expr) { |
| auto* variable = var_expr->Variable()->Declaration(); |
| auto name = variable->name->symbol.Name(); |
| auto* kind = Switch( |
| variable, // |
| [&](const ast::Var*) { return "var"; }, // |
| [&](const ast::Let*) { return "let"; }, // |
| [&](const ast::Const*) { return "const"; }, // |
| [&](const ast::Parameter*) { return "parameter"; }, // |
| [&](const ast::Override*) { return "override"; }, // |
| [&](Default) { return "variable"; }); |
| return std::string(kind) + " '" + name + "'"; |
| }, |
| [&](const sem::ValueExpression* val_expr) { |
| auto type = val_expr->Type()->FriendlyName(); |
| return "value expression of type '" + type + "'"; |
| }, |
| [&](const sem::TypeExpression* ty_expr) { |
| auto name = ty_expr->Type()->FriendlyName(); |
| return "type '" + name + "'"; |
| }, |
| [&](const sem::FunctionExpression* fn_expr) { |
| auto* fn = fn_expr->Function()->Declaration(); |
| auto name = fn->name->symbol.Name(); |
| return "function '" + name + "'"; |
| }, |
| [&](const sem::BuiltinEnumExpression<builtin::Access>* access) { |
| return "access '" + utils::ToString(access->Value()) + "'"; |
| }, |
| [&](const sem::BuiltinEnumExpression<builtin::AddressSpace>* addr) { |
| return "address space '" + utils::ToString(addr->Value()) + "'"; |
| }, |
| [&](const sem::BuiltinEnumExpression<builtin::BuiltinValue>* builtin) { |
| return "builtin value '" + utils::ToString(builtin->Value()) + "'"; |
| }, |
| [&](const sem::BuiltinEnumExpression<builtin::InterpolationSampling>* fmt) { |
| return "interpolation sampling '" + utils::ToString(fmt->Value()) + "'"; |
| }, |
| [&](const sem::BuiltinEnumExpression<builtin::InterpolationType>* fmt) { |
| return "interpolation type '" + utils::ToString(fmt->Value()) + "'"; |
| }, |
| [&](const sem::BuiltinEnumExpression<builtin::TexelFormat>* fmt) { |
| return "texel format '" + utils::ToString(fmt->Value()) + "'"; |
| }, |
| [&](Default) -> std::string { |
| TINT_ICE(Resolver, builder_->Diagnostics()) |
| << "unhandled sem::Expression type: " << (expr ? expr->TypeInfo().name : "<null>"); |
| return "<unknown>"; |
| }); |
| } |
| |
| void SemHelper::ErrorUnexpectedExprKind(const sem::Expression* expr, |
| std::string_view wanted) const { |
| AddError("cannot use " + Describe(expr) + " as " + std::string(wanted), |
| expr->Declaration()->source); |
| NoteDeclarationSource(expr->Declaration()); |
| } |
| |
| void SemHelper::ErrorExpectedValueExpr(const sem::Expression* expr) const { |
| ErrorUnexpectedExprKind(expr, "value"); |
| if (auto* ty_expr = expr->As<sem::TypeExpression>()) { |
| if (auto* ident = ty_expr->Declaration()->As<ast::IdentifierExpression>()) { |
| AddNote("are you missing '()' for value constructor?", |
| Source{{ident->source.range.end}}); |
| } |
| } |
| } |
| |
| void SemHelper::NoteDeclarationSource(const ast::Node* node) const { |
| if (!node) { |
| return; |
| } |
| |
| Switch( |
| Get(node), // |
| [&](const sem::VariableUser* var_expr) { node = var_expr->Variable()->Declaration(); }, |
| [&](const sem::TypeExpression* ty_expr) { |
| Switch(ty_expr->Type(), // |
| [&](const sem::Struct* s) { node = s->Declaration(); }); |
| }, |
| [&](const sem::FunctionExpression* fn_expr) { node = fn_expr->Function()->Declaration(); }); |
| |
| Switch( |
| node, |
| [&](const ast::Struct* n) { |
| AddNote("struct '" + n->name->symbol.Name() + "' declared here", n->source); |
| }, |
| [&](const ast::Alias* n) { |
| AddNote("alias '" + n->name->symbol.Name() + "' declared here", n->source); |
| }, |
| [&](const ast::Var* n) { |
| AddNote("var '" + n->name->symbol.Name() + "' declared here", n->source); |
| }, |
| [&](const ast::Let* n) { |
| AddNote("let '" + n->name->symbol.Name() + "' declared here", n->source); |
| }, |
| [&](const ast::Override* n) { |
| AddNote("override '" + n->name->symbol.Name() + "' declared here", n->source); |
| }, |
| [&](const ast::Const* n) { |
| AddNote("const '" + n->name->symbol.Name() + "' declared here", n->source); |
| }, |
| [&](const ast::Parameter* n) { |
| AddNote("parameter '" + n->name->symbol.Name() + "' declared here", n->source); |
| }, |
| [&](const ast::Function* n) { |
| AddNote("function '" + n->name->symbol.Name() + "' declared here", n->source); |
| }); |
| } |
| |
| void SemHelper::AddError(const std::string& msg, const Source& source) const { |
| builder_->Diagnostics().add_error(diag::System::Resolver, msg, source); |
| } |
| |
| void SemHelper::AddWarning(const std::string& msg, const Source& source) const { |
| builder_->Diagnostics().add_warning(diag::System::Resolver, msg, source); |
| } |
| |
| void SemHelper::AddNote(const std::string& msg, const Source& source) const { |
| builder_->Diagnostics().add_note(diag::System::Resolver, msg, source); |
| } |
| } // namespace tint::resolver |