| // Copyright 2022 The Dawn & Tint Authors |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // 1. Redistributions of source code must retain the above copyright notice, this |
| // list of conditions and the following disclaimer. |
| // |
| // 2. Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // |
| // 3. Neither the name of the copyright holder nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "src/tint/lang/wgsl/resolver/sem_helper.h" |
| |
| #include "src/tint/lang/wgsl/resolver/incomplete_type.h" |
| #include "src/tint/lang/wgsl/resolver/unresolved_identifier.h" |
| #include "src/tint/lang/wgsl/sem/builtin_enum_expression.h" |
| #include "src/tint/lang/wgsl/sem/function.h" |
| #include "src/tint/lang/wgsl/sem/function_expression.h" |
| #include "src/tint/lang/wgsl/sem/type_expression.h" |
| #include "src/tint/lang/wgsl/sem/value_expression.h" |
| #include "src/tint/utils/rtti/switch.h" |
| #include "src/tint/utils/text/styled_text.h" |
| #include "src/tint/utils/text/text_style.h" |
| |
| namespace tint::resolver { |
| |
| SemHelper::SemHelper(ProgramBuilder* builder) : builder_(builder) {} |
| |
| SemHelper::~SemHelper() = default; |
| |
| std::string SemHelper::TypeNameOf(const core::type::Type* ty) const { |
| return RawTypeNameOf(ty->UnwrapRef()); |
| } |
| |
| std::string SemHelper::RawTypeNameOf(const core::type::Type* ty) const { |
| return ty->FriendlyName(); |
| } |
| |
| core::type::Type* SemHelper::TypeOf(const ast::Expression* expr) const { |
| auto* sem = GetVal(expr); |
| return sem ? const_cast<core::type::Type*>(sem->Type()) : nullptr; |
| } |
| |
| sem::TypeExpression* SemHelper::AsTypeExpression(sem::Expression* expr) const { |
| if (TINT_UNLIKELY(!expr)) { |
| return nullptr; |
| } |
| |
| auto* ty_expr = expr->As<sem::TypeExpression>(); |
| if (TINT_UNLIKELY(!ty_expr)) { |
| ErrorUnexpectedExprKind(expr, "type"); |
| return nullptr; |
| } |
| |
| auto* type = ty_expr->Type(); |
| if (auto* incomplete = type->As<IncompleteType>(); TINT_UNLIKELY(incomplete)) { |
| AddError(expr->Declaration()->source.End()) |
| << "expected " << style::Code("<") << " for " << style::Type(incomplete->builtin); |
| return nullptr; |
| } |
| |
| return ty_expr; |
| } |
| |
| StyledText SemHelper::Describe(const sem::Expression* expr) const { |
| StyledText text; |
| |
| Switch( |
| expr, // |
| [&](const sem::VariableUser* var_expr) { |
| auto* variable = var_expr->Variable()->Declaration(); |
| auto name = variable->name->symbol.NameView(); |
| Switch( |
| variable, // |
| [&](const ast::Var*) { text << style::Keyword("var") << style::Code(" "); }, // |
| [&](const ast::Let*) { text << style::Keyword("let") << style::Code(" "); }, // |
| [&](const ast::Const*) { text << style::Keyword("const") << style::Code(" "); }, // |
| [&](const ast::Parameter*) { text << "parameter "; }, // |
| [&](const ast::Override*) { |
| text << style::Keyword("override") << style::Code(" "); |
| }, // |
| [&](Default) { text << "variable "; }); |
| text << style::Variable(name); |
| }, |
| [&](const sem::ValueExpression* val_expr) { |
| text << "value of type " << style::Type(val_expr->Type()->FriendlyName()); |
| }, |
| [&](const sem::TypeExpression* ty_expr) { |
| text << "type " << style::Type(ty_expr->Type()->FriendlyName()); |
| }, |
| [&](const sem::FunctionExpression* fn_expr) { |
| auto* fn = fn_expr->Function()->Declaration(); |
| text << "function " << style::Function(fn->name->symbol.NameView()); |
| }, |
| [&](const sem::BuiltinEnumExpression<wgsl::BuiltinFn>* fn) { |
| text << "builtin function " << style::Function(fn->Value()); |
| }, |
| [&](const sem::BuiltinEnumExpression<core::Access>* access) { |
| text << "access " << style::Enum(access->Value()); |
| }, |
| [&](const sem::BuiltinEnumExpression<core::AddressSpace>* addr) { |
| text << "address space " << style::Enum(addr->Value()); |
| }, |
| [&](const sem::BuiltinEnumExpression<core::BuiltinValue>* builtin) { |
| text << "builtin value " << style::Enum(builtin->Value()); |
| }, |
| [&](const sem::BuiltinEnumExpression<core::InterpolationSampling>* fmt) { |
| text << "interpolation sampling " << style::Enum(fmt->Value()); |
| }, |
| [&](const sem::BuiltinEnumExpression<core::InterpolationType>* fmt) { |
| text << "interpolation type " << style::Enum(fmt->Value()); |
| }, |
| [&](const sem::BuiltinEnumExpression<core::TexelFormat>* fmt) { |
| text << "texel format " << style::Enum(fmt->Value()); |
| }, |
| [&](const UnresolvedIdentifier* ui) { |
| auto name = ui->Identifier()->identifier->symbol.NameView(); |
| text << "unresolved identifier " << style::Code(name); |
| }, // |
| TINT_ICE_ON_NO_MATCH); |
| |
| return text; |
| } |
| |
| void SemHelper::ErrorUnexpectedExprKind( |
| const sem::Expression* expr, |
| std::string_view wanted, |
| tint::Slice<const std::string_view> suggestions /* = Empty */) const { |
| if (auto* ui = expr->As<UnresolvedIdentifier>()) { |
| auto* ident = ui->Identifier(); |
| auto name = ident->identifier->symbol.NameView(); |
| AddError(ident->source) << "unresolved " << wanted << " " << style::Code(name); |
| if (!suggestions.IsEmpty()) { |
| // Filter out suggestions that have a leading underscore. |
| Vector<std::string_view, 8> filtered; |
| for (auto str : suggestions) { |
| if (str[0] != '_') { |
| filtered.Push(str); |
| } |
| } |
| auto& note = AddNote(ident->source); |
| SuggestAlternativeOptions opts; |
| opts.alternatives_style = style::Enum; |
| SuggestAlternatives(name, filtered.Slice(), note.message, opts); |
| } |
| return; |
| } |
| |
| AddError(expr->Declaration()->source) << "cannot use " << Describe(expr) << " as " << wanted; |
| NoteDeclarationSource(expr->Declaration()); |
| } |
| |
| void SemHelper::ErrorExpectedValueExpr(const sem::Expression* expr) const { |
| ErrorUnexpectedExprKind(expr, "value"); |
| if (auto* ident = expr->Declaration()->As<ast::IdentifierExpression>()) { |
| if (expr->IsAnyOf<sem::FunctionExpression, sem::TypeExpression, |
| sem::BuiltinEnumExpression<wgsl::BuiltinFn>>()) { |
| AddNote(ident->source.End()) |
| << "are you missing " << style::Code("()") << style::Plain("?"); |
| } |
| } |
| } |
| |
| 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(n->source) << style::Keyword("struct ") |
| << style::Type(n->name->symbol.NameView()) << " declared here"; |
| }, |
| [&](const ast::Alias* n) { |
| AddNote(n->source) << style::Keyword("alias ") |
| << style::Type(n->name->symbol.NameView()) << " declared here"; |
| }, |
| [&](const ast::Var* n) { |
| AddNote(n->source) << style::Keyword("var ") |
| << style::Variable(n->name->symbol.NameView()) << " declared here"; |
| }, |
| [&](const ast::Let* n) { |
| AddNote(n->source) << style::Keyword("let ") |
| << style::Variable(n->name->symbol.NameView()) << " declared here"; |
| }, |
| [&](const ast::Override* n) { |
| AddNote(n->source) << style::Keyword("override ") |
| << style::Variable(n->name->symbol.NameView()) << " declared here"; |
| }, |
| [&](const ast::Const* n) { |
| AddNote(n->source) << style::Keyword("const ") |
| << style::Variable(n->name->symbol.NameView()) << " declared here"; |
| }, |
| [&](const ast::Parameter* n) { |
| AddNote(n->source) << "parameter " << style::Variable(n->name->symbol.NameView()) |
| << " declared here"; |
| }, |
| [&](const ast::Function* n) { |
| AddNote(n->source) << "function " << style::Function(n->name->symbol.NameView()) |
| << " declared here"; |
| }); |
| } |
| |
| diag::Diagnostic& SemHelper::AddError(const Source& source) const { |
| return builder_->Diagnostics().AddError(source); |
| } |
| |
| diag::Diagnostic& SemHelper::AddWarning(const Source& source) const { |
| return builder_->Diagnostics().AddWarning(source); |
| } |
| |
| diag::Diagnostic& SemHelper::AddNote(const Source& source) const { |
| return builder_->Diagnostics().AddNote(source); |
| } |
| } // namespace tint::resolver |