tint: Add ast::TemplatedIdentifier Will be used to replace all type identifiers that take templated arguments. Bug: tint:1810 Change-Id: I31ad8dc4826375a783143cc33f336d8a4860613c Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/117893 Reviewed-by: Dan Sinclair <dsinclair@chromium.org> Commit-Queue: Ben Clayton <bclayton@chromium.org> Reviewed-by: James Price <jrprice@google.com> Kokoro: Ben Clayton <bclayton@chromium.org> Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn index f7e4854..481c267 100644 --- a/src/tint/BUILD.gn +++ b/src/tint/BUILD.gn
@@ -315,6 +315,7 @@ "ast/struct_member_offset_attribute.h", "ast/struct_member_size_attribute.h", "ast/switch_statement.h", + "ast/templated_identifier.h", "ast/texture.h", "ast/traverse_expressions.h", "ast/type.h", @@ -697,6 +698,8 @@ "ast/struct_member_size_attribute.h", "ast/switch_statement.cc", "ast/switch_statement.h", + "ast/templated_identifier.cc", + "ast/templated_identifier.h", "ast/texture.cc", "ast/texture.h", "ast/traverse_expressions.h", @@ -1358,6 +1361,7 @@ "ast/struct_member_test.cc", "ast/struct_test.cc", "ast/switch_statement_test.cc", + "ast/templated_identifier_test.cc", "ast/test_helper.h", "ast/texture_test.cc", "ast/traverse_expressions_test.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt index 2223d7d..bdb1bbf 100644 --- a/src/tint/CMakeLists.txt +++ b/src/tint/CMakeLists.txt
@@ -227,6 +227,8 @@ ast/struct.h ast/switch_statement.cc ast/switch_statement.h + ast/templated_identifier.cc + ast/templated_identifier.h ast/texture.cc ast/texture.h ast/traverse_expressions.h @@ -880,6 +882,7 @@ ast/struct_test.cc ast/switch_statement_test.cc ast/test_helper.h + ast/templated_identifier_test.cc ast/texture_test.cc ast/traverse_expressions_test.cc ast/type_name_test.cc
diff --git a/src/tint/ast/diagnostic_control.cc b/src/tint/ast/diagnostic_control.cc index a814d58..6d6ed09 100644 --- a/src/tint/ast/diagnostic_control.cc +++ b/src/tint/ast/diagnostic_control.cc
@@ -30,6 +30,19 @@ namespace tint::ast { +DiagnosticControl::DiagnosticControl(ProgramID pid, + NodeID nid, + const Source& src, + DiagnosticSeverity sev, + const Identifier* rule) + : Base(pid, nid, src), severity(sev), rule_name(rule) { + TINT_ASSERT(AST, rule != nullptr); + if (rule) { + // It is invalid for a diagnostic rule name to be templated + TINT_ASSERT(AST, !rule->Is<TemplatedIdentifier>()); + } +} + DiagnosticControl::~DiagnosticControl() = default; const DiagnosticControl* DiagnosticControl::Clone(CloneContext* ctx) const {
diff --git a/src/tint/ast/diagnostic_control.cc.tmpl b/src/tint/ast/diagnostic_control.cc.tmpl index c6dc463..c7ca2b8 100644 --- a/src/tint/ast/diagnostic_control.cc.tmpl +++ b/src/tint/ast/diagnostic_control.cc.tmpl
@@ -20,6 +20,19 @@ namespace tint::ast { +DiagnosticControl::DiagnosticControl(ProgramID pid, + NodeID nid, + const Source& src, + DiagnosticSeverity sev, + const Identifier* rule) + : Base(pid, nid, src), severity(sev), rule_name(rule) { + TINT_ASSERT(AST, rule != nullptr); + if (rule) { + // It is invalid for a diagnostic rule name to be templated + TINT_ASSERT(AST, !rule->Is<TemplatedIdentifier>()); + } +} + DiagnosticControl::~DiagnosticControl() = default; const DiagnosticControl* DiagnosticControl::Clone(CloneContext* ctx) const {
diff --git a/src/tint/ast/diagnostic_control.h b/src/tint/ast/diagnostic_control.h index d1df329..6cf27b9 100644 --- a/src/tint/ast/diagnostic_control.h +++ b/src/tint/ast/diagnostic_control.h
@@ -103,8 +103,7 @@ NodeID nid, const Source& src, DiagnosticSeverity sev, - const Identifier* rule) - : Base(pid, nid, src), severity(sev), rule_name(rule) {} + const Identifier* rule); ~DiagnosticControl() override;
diff --git a/src/tint/ast/diagnostic_control.h.tmpl b/src/tint/ast/diagnostic_control.h.tmpl index c106a3e..9b8f1fe 100644 --- a/src/tint/ast/diagnostic_control.h.tmpl +++ b/src/tint/ast/diagnostic_control.h.tmpl
@@ -51,8 +51,7 @@ NodeID nid, const Source& src, DiagnosticSeverity sev, - const Identifier* rule) - : Base(pid, nid, src), severity(sev), rule_name(rule) {} + const Identifier* rule); ~DiagnosticControl() override;
diff --git a/src/tint/ast/diagnostic_control_test.cc b/src/tint/ast/diagnostic_control_test.cc index 798d187..742bee3 100644 --- a/src/tint/ast/diagnostic_control_test.cc +++ b/src/tint/ast/diagnostic_control_test.cc
@@ -22,6 +22,7 @@ #include <string> +#include "gtest/gtest-spi.h" #include "src/tint/ast/diagnostic_control.h" #include "src/tint/ast/test_helper.h" @@ -44,6 +45,16 @@ EXPECT_EQ(control->rule_name, name); } +TEST_F(DiagnosticControlTest, Assert_RuleNotTemplated) { + EXPECT_FATAL_FAILURE( + { + ProgramBuilder b; + b.create<ast::DiagnosticControl>(DiagnosticSeverity::kWarning, + b.Ident("name", "a", "b", "c")); + }, + "internal compiler error"); +} + namespace diagnostic_severity_tests { namespace parse_print_tests {
diff --git a/src/tint/ast/diagnostic_control_test.cc.tmpl b/src/tint/ast/diagnostic_control_test.cc.tmpl index 74ff73e..2eebba3 100644 --- a/src/tint/ast/diagnostic_control_test.cc.tmpl +++ b/src/tint/ast/diagnostic_control_test.cc.tmpl
@@ -12,6 +12,7 @@ #include <string> +#include "gtest/gtest-spi.h" #include "src/tint/ast/diagnostic_control.h" #include "src/tint/ast/test_helper.h" @@ -25,8 +26,7 @@ Source source; source.range.begin = Source::Location{20, 2}; source.range.end = Source::Location{20, 5}; - auto* control = create<ast::DiagnosticControl>(source, - DiagnosticSeverity::kWarning, name); + auto* control = create<ast::DiagnosticControl>(source, DiagnosticSeverity::kWarning, name); EXPECT_EQ(control->source.range.begin.line, 20u); EXPECT_EQ(control->source.range.begin.column, 2u); EXPECT_EQ(control->source.range.end.line, 20u); @@ -35,6 +35,16 @@ EXPECT_EQ(control->rule_name, name); } +TEST_F(DiagnosticControlTest, Assert_RuleNotTemplated) { + EXPECT_FATAL_FAILURE( + { + ProgramBuilder b; + b.create<ast::DiagnosticControl>(DiagnosticSeverity::kWarning, + b.Ident("name", "a", "b", "c")); + }, + "internal compiler error"); +} + namespace diagnostic_severity_tests { {{ Eval "TestParsePrintEnum" (Sem.Enum "diagnostic_severity")}}
diff --git a/src/tint/ast/identifier.h b/src/tint/ast/identifier.h index 9176403..0d99b00 100644 --- a/src/tint/ast/identifier.h +++ b/src/tint/ast/identifier.h
@@ -20,7 +20,7 @@ namespace tint::ast { /// An identifier -class Identifier final : public Castable<Identifier, ast::Node> { +class Identifier : public Castable<Identifier, ast::Node> { public: /// Constructor /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/identifier_expression.cc b/src/tint/ast/identifier_expression.cc index ca4e0cb..1f86d9f 100644 --- a/src/tint/ast/identifier_expression.cc +++ b/src/tint/ast/identifier_expression.cc
@@ -27,6 +27,10 @@ : Base(pid, nid, src), identifier(ident) { TINT_ASSERT(AST, identifier != nullptr); TINT_ASSERT_PROGRAM_IDS_EQUAL(AST, identifier, program_id); + + // It is currently invalid for a templated identifier expression to be used as an identifier + // expression, as this should parse as a ast::TypeName. + TINT_ASSERT(AST, !ident->Is<TemplatedIdentifier>()); } IdentifierExpression::IdentifierExpression(IdentifierExpression&&) = default;
diff --git a/src/tint/ast/identifier_expression_test.cc b/src/tint/ast/identifier_expression_test.cc index 3bef4ca..80742a2 100644 --- a/src/tint/ast/identifier_expression_test.cc +++ b/src/tint/ast/identifier_expression_test.cc
@@ -57,5 +57,14 @@ "internal compiler error"); } +TEST_F(IdentifierExpressionTest, Assert_IdentifierNotTemplated) { + EXPECT_FATAL_FAILURE( + { + ProgramBuilder b; + b.create<IdentifierExpression>(b.Ident("ident", "a", "b", "c")); + }, + "internal compiler error"); +} + } // namespace } // namespace tint::ast
diff --git a/src/tint/ast/member_accessor_expression.cc b/src/tint/ast/member_accessor_expression.cc index f414f0d..81ef085 100644 --- a/src/tint/ast/member_accessor_expression.cc +++ b/src/tint/ast/member_accessor_expression.cc
@@ -30,6 +30,11 @@ TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, structure, program_id); TINT_ASSERT(AST, member); TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, member, program_id); + + // It is currently invalid for a structure to hold a templated member + if (member) { + TINT_ASSERT(AST, !member->Is<TemplatedIdentifier>()); + } } MemberAccessorExpression::MemberAccessorExpression(MemberAccessorExpression&&) = default;
diff --git a/src/tint/ast/member_accessor_expression_test.cc b/src/tint/ast/member_accessor_expression_test.cc index bfc7218..1b05eeb 100644 --- a/src/tint/ast/member_accessor_expression_test.cc +++ b/src/tint/ast/member_accessor_expression_test.cc
@@ -80,5 +80,15 @@ "internal compiler error"); } +TEST_F(MemberAccessorExpressionTest, Assert_MemberNotTemplated) { + EXPECT_FATAL_FAILURE( + { + ProgramBuilder b; + b.create<MemberAccessorExpression>(b.Expr("structure"), + b.Ident("member", "a", "b", "c")); + }, + "internal compiler error"); +} + } // namespace } // namespace tint::ast
diff --git a/src/tint/ast/templated_identifier.cc b/src/tint/ast/templated_identifier.cc new file mode 100644 index 0000000..756b183 --- /dev/null +++ b/src/tint/ast/templated_identifier.cc
@@ -0,0 +1,48 @@ +// Copyright 2023 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/ast/templated_identifier.h" + +#include <utility> + +#include "src/tint/program_builder.h" + +TINT_INSTANTIATE_TYPEINFO(tint::ast::TemplatedIdentifier); + +namespace tint::ast { + +TemplatedIdentifier::TemplatedIdentifier(ProgramID pid, + NodeID nid, + const Source& src, + const Symbol& sym, + utils::VectorRef<const ast::Expression*> args) + : Base(pid, nid, src, sym), arguments(std::move(args)) { + for (auto* arg : arguments) { + TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, arg, program_id); + } +} + +TemplatedIdentifier::TemplatedIdentifier(TemplatedIdentifier&&) = default; + +TemplatedIdentifier::~TemplatedIdentifier() = default; + +const TemplatedIdentifier* TemplatedIdentifier::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 args = ctx->Clone(arguments); + return ctx->dst->create<TemplatedIdentifier>(src, sym, args); +} + +} // namespace tint::ast
diff --git a/src/tint/ast/templated_identifier.h b/src/tint/ast/templated_identifier.h new file mode 100644 index 0000000..7a49c05 --- /dev/null +++ b/src/tint/ast/templated_identifier.h
@@ -0,0 +1,56 @@ +// Copyright 2023 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. + +#ifndef SRC_TINT_AST_TEMPLATED_IDENTIFIER_H_ +#define SRC_TINT_AST_TEMPLATED_IDENTIFIER_H_ + +#include "src/tint/ast/identifier.h" + +// Forward declarations +namespace tint::ast { +class Expression; +} // namespace tint::ast + +namespace tint::ast { + +/// A templated identifier expression +class TemplatedIdentifier final : public Castable<TemplatedIdentifier, Identifier> { + public: + /// Constructor + /// @param pid the identifier of the program that owns this node + /// @param nid the unique node identifier + /// @param src the source of this node + /// @param sym the symbol for the identifier + /// @param args the template arguments + TemplatedIdentifier(ProgramID pid, + NodeID nid, + const Source& src, + const Symbol& sym, + utils::VectorRef<const Expression*> args); + /// Move constructor + TemplatedIdentifier(TemplatedIdentifier&&); + ~TemplatedIdentifier() override; + + /// Clones this node and all transitive child nodes using the `CloneContext` `ctx`. + /// @param ctx the clone context + /// @return the newly cloned node + const TemplatedIdentifier* Clone(CloneContext* ctx) const override; + + /// The templated arguments + const utils::Vector<const Expression*, 3> arguments; +}; + +} // namespace tint::ast + +#endif // SRC_TINT_AST_TEMPLATED_IDENTIFIER_H_
diff --git a/src/tint/ast/templated_identifier_test.cc b/src/tint/ast/templated_identifier_test.cc new file mode 100644 index 0000000..648c334 --- /dev/null +++ b/src/tint/ast/templated_identifier_test.cc
@@ -0,0 +1,80 @@ +// Copyright 2023 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 "gtest/gtest-spi.h" + +#include "src/tint/ast/test_helper.h" + +namespace tint::ast { +namespace { + +using namespace tint::number_suffixes; // NOLINT + +using TemplatedIdentifierTest = TestHelper; + +TEST_F(TemplatedIdentifierTest, Creation) { + auto* i = Ident("ident", 1_a, Add("x", "y"), false, "x"); + EXPECT_EQ(i->symbol, Symbols().Get("ident")); + ASSERT_EQ(i->arguments.Length(), 4u); + EXPECT_TRUE(i->arguments[0]->Is<ast::IntLiteralExpression>()); + EXPECT_TRUE(i->arguments[1]->Is<ast::BinaryExpression>()); + EXPECT_TRUE(i->arguments[2]->Is<ast::BoolLiteralExpression>()); + EXPECT_TRUE(i->arguments[3]->Is<ast::IdentifierExpression>()); +} + +TEST_F(TemplatedIdentifierTest, Creation_WithSource) { + auto* i = Ident(Source{{20, 2}}, "ident", 1_a, Add("x", "y"), false, "x"); + EXPECT_EQ(i->symbol, Symbols().Get("ident")); + ASSERT_EQ(i->arguments.Length(), 4u); + EXPECT_TRUE(i->arguments[0]->Is<ast::IntLiteralExpression>()); + EXPECT_TRUE(i->arguments[1]->Is<ast::BinaryExpression>()); + EXPECT_TRUE(i->arguments[2]->Is<ast::BoolLiteralExpression>()); + EXPECT_TRUE(i->arguments[3]->Is<ast::IdentifierExpression>()); + + auto src = i->source; + EXPECT_EQ(src.range.begin.line, 20u); + EXPECT_EQ(src.range.begin.column, 2u); +} + +TEST_F(TemplatedIdentifierTest, Assert_InvalidSymbol) { + EXPECT_FATAL_FAILURE( + { + ProgramBuilder b; + b.Expr(""); + }, + "internal compiler error"); +} + +TEST_F(TemplatedIdentifierTest, Assert_DifferentProgramID_Symbol) { + EXPECT_FATAL_FAILURE( + { + ProgramBuilder b1; + ProgramBuilder b2; + b1.Ident(b2.Sym("b2"), b1.Expr(1_i)); + }, + "internal compiler error"); +} + +TEST_F(TemplatedIdentifierTest, Assert_DifferentProgramID_TemplateArg) { + EXPECT_FATAL_FAILURE( + { + ProgramBuilder b1; + ProgramBuilder b2; + b1.Ident("b1", b2.Expr(1_i)); + }, + "internal compiler error"); +} + +} // namespace +} // namespace tint::ast
diff --git a/src/tint/ast/type_name_test.cc b/src/tint/ast/type_name_test.cc index 803dad3..9e01f0c 100644 --- a/src/tint/ast/type_name_test.cc +++ b/src/tint/ast/type_name_test.cc
@@ -28,6 +28,17 @@ EXPECT_EQ(t->name->symbol, Symbols().Get("ty")); } +TEST_F(TypeNameTest, Creation_Templated) { + auto* t = ty.type_name("ty", 1_a, 2._a, false); + auto* name = As<ast::TemplatedIdentifier>(t->name); + ASSERT_NE(name, nullptr); + EXPECT_EQ(name->symbol, Symbols().Get("ty")); + ASSERT_EQ(name->arguments.Length(), 3u); + EXPECT_TRUE(name->arguments[0]->Is<ast::IntLiteralExpression>()); + EXPECT_TRUE(name->arguments[1]->Is<ast::FloatLiteralExpression>()); + EXPECT_TRUE(name->arguments[2]->Is<ast::BoolLiteralExpression>()); +} + TEST_F(TypeNameTest, Creation_WithSource) { auto* t = ty.type_name(Source{{20, 2}}, "ty"); ASSERT_NE(t->name, nullptr);
diff --git a/src/tint/program_builder.h b/src/tint/program_builder.h index 7954a41..dc3560a 100644 --- a/src/tint/program_builder.h +++ b/src/tint/program_builder.h
@@ -81,6 +81,7 @@ #include "src/tint/ast/struct_member_offset_attribute.h" #include "src/tint/ast/struct_member_size_attribute.h" #include "src/tint/ast/switch_statement.h" +#include "src/tint/ast/templated_identifier.h" #include "src/tint/ast/type_name.h" #include "src/tint/ast/u32.h" #include "src/tint/ast/unary_op_expression.h" @@ -204,9 +205,7 @@ template <typename... ARGS> explicit LetOptions(ARGS&&... args) { static constexpr bool has_init = - (traits::IsTypeOrDerived<std::remove_pointer_t<std::remove_reference_t<ARGS>>, - ast::Expression> || - ...); + (traits::IsTypeOrDerived<traits::PtrElTy<ARGS>, ast::Expression> || ...); static_assert(has_init, "Let() must be constructed with an initializer expression"); (Set(std::forward<ARGS>(args)), ...); } @@ -229,9 +228,7 @@ template <typename... ARGS> explicit ConstOptions(ARGS&&... args) { static constexpr bool has_init = - (traits::IsTypeOrDerived<std::remove_pointer_t<std::remove_reference_t<ARGS>>, - ast::Expression> || - ...); + (traits::IsTypeOrDerived<traits::PtrElTy<ARGS>, ast::Expression> || ...); static_assert(has_init, "Const() must be constructed with an initializer expression"); (Set(std::forward<ARGS>(args)), ...); } @@ -904,19 +901,23 @@ /// Creates a type name /// @param name the name + /// @param args the optional template arguments /// @returns the type name - template <typename NAME> - const ast::TypeName* type_name(NAME&& name) const { - return builder->create<ast::TypeName>(builder->Ident(std::forward<NAME>(name))); + template <typename NAME, typename... ARGS, typename _ = DisableIfSource<NAME>> + const ast::TypeName* type_name(NAME&& name, ARGS&&... args) const { + return builder->create<ast::TypeName>( + builder->Ident(std::forward<NAME>(name), std::forward<ARGS>(args)...)); } /// Creates a type name /// @param source the Source of the node /// @param name the name + /// @param args the optional template arguments /// @returns the type name - template <typename NAME> - const ast::TypeName* type_name(const Source& source, NAME&& name) const { - return builder->create<ast::TypeName>(source, builder->Ident(std::forward<NAME>(name))); + template <typename NAME, typename... ARGS> + const ast::TypeName* type_name(const Source& source, NAME&& name, ARGS&&... args) const { + return builder->create<ast::TypeName>( + source, builder->Ident(std::forward<NAME>(name), std::forward<ARGS>(args)...)); } /// Creates an alias type @@ -1150,22 +1151,30 @@ /// @param source the source information /// @param identifier the identifier symbol + /// @param args optional templated identifier arguments /// @return an ast::Identifier with the given symbol - template <typename IDENTIFIER> - const ast::Identifier* Ident(const Source& source, IDENTIFIER&& identifier) { - return create<ast::Identifier>(source, Sym(std::forward<IDENTIFIER>(identifier))); + template <typename IDENTIFIER, typename... ARGS> + const auto* Ident(const Source& source, IDENTIFIER&& identifier, ARGS&&... args) { + Symbol sym = Sym(std::forward<IDENTIFIER>(identifier)); + if constexpr (sizeof...(args) > 0) { + return create<ast::TemplatedIdentifier>(source, sym, + ExprList(std::forward<ARGS>(args)...)); + } else { + return create<ast::Identifier>(source, sym); + } } /// @param identifier the identifier symbol + /// @param args optional templated identifier arguments /// @return an ast::Identifier with the given symbol - template <typename IDENTIFIER> - const ast::Identifier* Ident(IDENTIFIER&& identifier) { - if constexpr (traits::IsTypeOrDerived< - std::decay_t<std::remove_pointer_t<std::decay_t<IDENTIFIER>>>, - ast::Identifier>) { + template <typename IDENTIFIER, typename... ARGS, typename = DisableIfSource<IDENTIFIER>> + const auto* Ident(IDENTIFIER&& identifier, ARGS&&... args) { + if constexpr (traits::IsTypeOrDerived<traits::PtrElTy<IDENTIFIER>, ast::Identifier>) { + static_assert(sizeof...(args) == 0); return identifier; // Pass-through } else { - return create<ast::Identifier>(Sym(std::forward<IDENTIFIER>(identifier))); + return Ident(source_, std::forward<IDENTIFIER>(identifier), + std::forward<ARGS>(args)...); } } @@ -1232,6 +1241,16 @@ return create<ast::IdentifierExpression>(Ident(variable->symbol)); } + /// @param ident the identifier + /// @return an ast::IdentifierExpression with the given identifier + template <typename IDENTIFIER, typename = traits::EnableIfIsType<IDENTIFIER, ast::Identifier>> + const ast::IdentifierExpression* Expr(const IDENTIFIER* ident) { + static_assert(!traits::IsType<IDENTIFIER, ast::TemplatedIdentifier>, + "it is currently invalid for a templated identifier expression to be used as " + "an identifier expression, as this should parse as an ast::TypeName"); + return create<ast::IdentifierExpression>(ident); + } + /// @param source the source information /// @param value the boolean value /// @return a Scalar constructor for the given value @@ -2347,43 +2366,46 @@ } /// @param source the source information - /// @param obj the object for the index accessor expression - /// @param idx the index argument for the index accessor expression - /// @returns a `ast::IndexAccessorExpression` that indexes `arr` with `idx` - template <typename OBJ, typename IDX> - const ast::IndexAccessorExpression* IndexAccessor(const Source& source, OBJ&& obj, IDX&& idx) { - return create<ast::IndexAccessorExpression>(source, Expr(std::forward<OBJ>(obj)), - Expr(std::forward<IDX>(idx))); + /// @param object the object for the index accessor expression + /// @param index the index argument for the index accessor expression + /// @returns a `ast::IndexAccessorExpression` that indexes @p object with @p index + template <typename OBJECT, typename INDEX> + const ast::IndexAccessorExpression* IndexAccessor(const Source& source, + OBJECT&& object, + INDEX&& index) { + return create<ast::IndexAccessorExpression>(source, Expr(std::forward<OBJECT>(object)), + Expr(std::forward<INDEX>(index))); } - /// @param obj the object for the index accessor expression - /// @param idx the index argument for the index accessor expression - /// @returns a `ast::IndexAccessorExpression` that indexes `arr` with `idx` - template <typename OBJ, typename IDX> - const ast::IndexAccessorExpression* IndexAccessor(OBJ&& obj, IDX&& idx) { - return create<ast::IndexAccessorExpression>(Expr(std::forward<OBJ>(obj)), - Expr(std::forward<IDX>(idx))); + /// @param object the object for the index accessor expression + /// @param index the index argument for the index accessor expression + /// @returns a `ast::IndexAccessorExpression` that indexes @p object with @p index + template <typename OBJECT, typename INDEX> + const ast::IndexAccessorExpression* IndexAccessor(OBJECT&& object, INDEX&& index) { + return create<ast::IndexAccessorExpression>(Expr(std::forward<OBJECT>(object)), + Expr(std::forward<INDEX>(index))); } /// @param source the source information - /// @param obj the object for the member accessor expression - /// @param idx the index argument for the member accessor expression - /// @returns a `ast::MemberAccessorExpression` that indexes `obj` with `idx` - template <typename OBJ, typename IDX> + /// @param object the object for the member accessor expression + /// @param member the member argument for the member accessor expression + /// @returns a `ast::MemberAccessorExpression` that indexes @p object with @p member + template <typename OBJECT, typename MEMBER> const ast::MemberAccessorExpression* MemberAccessor(const Source& source, - OBJ&& obj, - IDX&& idx) { - return create<ast::MemberAccessorExpression>(source, Expr(std::forward<OBJ>(obj)), - Ident(std::forward<IDX>(idx))); + OBJECT&& object, + MEMBER&& member) { + static_assert(!traits::IsType<traits::PtrElTy<MEMBER>, ast::TemplatedIdentifier>, + "it is currently invalid for a structure to hold a templated member"); + return create<ast::MemberAccessorExpression>(source, Expr(std::forward<OBJECT>(object)), + Ident(std::forward<MEMBER>(member))); } - /// @param obj the object for the member accessor expression - /// @param idx the index argument for the member accessor expression - /// @returns a `ast::MemberAccessorExpression` that indexes `obj` with `idx` - template <typename OBJ, typename IDX> - const ast::MemberAccessorExpression* MemberAccessor(OBJ&& obj, IDX&& idx) { - return create<ast::MemberAccessorExpression>(Expr(std::forward<OBJ>(obj)), - Ident(std::forward<IDX>(idx))); + /// @param object the object for the member accessor expression + /// @param member the member argument for the member accessor expression + /// @returns a `ast::MemberAccessorExpression` that indexes @p object with @p member + template <typename OBJECT, typename MEMBER> + const ast::MemberAccessorExpression* MemberAccessor(OBJECT&& object, MEMBER&& member) { + return MemberAccessor(source_, std::forward<OBJECT>(object), std::forward<MEMBER>(member)); } /// Creates a ast::StructMemberOffsetAttribute @@ -3284,6 +3306,8 @@ const ast::DiagnosticAttribute* DiagnosticAttribute(const Source& source, ast::DiagnosticSeverity severity, NAME&& rule_name) { + static_assert(!traits::IsType<traits::PtrElTy<NAME>, ast::TemplatedIdentifier>, + "it is invalid for a diagnostic rule name to be templated"); return create<ast::DiagnosticAttribute>( source, DiagnosticControl(source, severity, std::forward<NAME>(rule_name))); }
diff --git a/src/tint/resolver/dependency_graph.cc b/src/tint/resolver/dependency_graph.cc index 6dc111e..d09a7e3 100644 --- a/src/tint/resolver/dependency_graph.cc +++ b/src/tint/resolver/dependency_graph.cc
@@ -61,6 +61,7 @@ #include "src/tint/ast/struct_member_offset_attribute.h" #include "src/tint/ast/struct_member_size_attribute.h" #include "src/tint/ast/switch_statement.h" +#include "src/tint/ast/templated_identifier.h" #include "src/tint/ast/traverse_expressions.h" #include "src/tint/ast/type_name.h" #include "src/tint/ast/u32.h"
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc index 36f9da8..1630642 100644 --- a/src/tint/resolver/resolver.cc +++ b/src/tint/resolver/resolver.cc
@@ -321,6 +321,10 @@ [&](const ast::TypeName* t) -> type::Type* { Mark(t->name); + if (t->name->Is<ast::TemplatedIdentifier>()) { + TINT_UNREACHABLE(Resolver, builder_->Diagnostics()) << "TODO(crbug.com/tint/1810)"; + } + auto* resolved = sem_.ResolvedSymbol(t); if (resolved == nullptr) { if (IsBuiltin(t->name->symbol)) {
diff --git a/src/tint/traits.h b/src/tint/traits.h index f6dac27..1d0c2f6 100644 --- a/src/tint/traits.h +++ b/src/tint/traits.h
@@ -15,6 +15,7 @@ #ifndef SRC_TINT_TRAITS_H_ #define SRC_TINT_TRAITS_H_ +#include <string> #include <tuple> #include <type_traits> #include <utility> @@ -177,6 +178,16 @@ template <typename T, typename TypeContainer> static constexpr bool IsTypeIn = detail::IsTypeIn<T, TypeContainer>::value; +/// Evaluates to the decayed pointer element type, or the decayed type T if T is not a pointer. +template <typename T> +using PtrElTy = Decay<std::remove_pointer_t<Decay<T>>>; + +/// Evaluates to true if `T` decayed is a `std::string`, `std::string_view` or `const char*` +template <typename T> +static constexpr bool IsStringLike = + std::is_same_v<Decay<T>, std::string> || std::is_same_v<Decay<T>, std::string_view> || + std::is_same_v<Decay<T>, const char*>; + } // namespace tint::traits #endif // SRC_TINT_TRAITS_H_
diff --git a/src/tint/traits_test.cc b/src/tint/traits_test.cc index c100107..e86e389 100644 --- a/src/tint/traits_test.cc +++ b/src/tint/traits_test.cc
@@ -19,6 +19,25 @@ namespace tint::traits { namespace { + +static_assert(std::is_same_v<PtrElTy<int*>, int>); +static_assert(std::is_same_v<PtrElTy<int const*>, int>); +static_assert(std::is_same_v<PtrElTy<int const* const>, int>); +static_assert(std::is_same_v<PtrElTy<int const* const volatile>, int>); +static_assert(std::is_same_v<PtrElTy<int>, int>); +static_assert(std::is_same_v<PtrElTy<int const>, int>); +static_assert(std::is_same_v<PtrElTy<int const volatile>, int>); + +static_assert(IsStringLike<std::string>); +static_assert(IsStringLike<std::string_view>); +static_assert(IsStringLike<const char*>); +static_assert(IsStringLike<const std::string&>); +static_assert(IsStringLike<const std::string_view&>); +static_assert(IsStringLike<const char*>); +static_assert(!IsStringLike<bool>); +static_assert(!IsStringLike<int>); +static_assert(!IsStringLike<const char**>); + struct S {}; void F1(S) {} void F3(int, S, float) {}