mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-12-14 23:56:16 +00:00
Implement type inference
Bug: tint:672 Change-Id: I3f586ee867f75427c4e8c309f72fb468643c23c0 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/53182 Reviewed-by: Ben Clayton <bclayton@google.com> Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: Antonio Maiorano <amaiorano@google.com>
This commit is contained in:
committed by
Tint LUCI CQ
parent
7697c31e84
commit
9834fefa7f
@@ -580,6 +580,7 @@ if(${TINT_BUILD_TESTS})
|
||||
resolver/entry_point_validation_test.cc
|
||||
resolver/function_validation_test.cc
|
||||
resolver/host_shareable_validation_test.cc
|
||||
resolver/inferred_type_test.cc
|
||||
resolver/intrinsic_test.cc
|
||||
resolver/intrinsic_validation_test.cc
|
||||
resolver/is_host_shareable_test.cc
|
||||
|
||||
@@ -55,15 +55,6 @@ TEST_F(ModuleTest, Assert_Null_GlobalVariable) {
|
||||
"internal compiler error");
|
||||
}
|
||||
|
||||
TEST_F(ModuleTest, Assert_Invalid_GlobalVariable) {
|
||||
EXPECT_FATAL_FAILURE(
|
||||
{
|
||||
ProgramBuilder builder;
|
||||
builder.Global("var", nullptr, StorageClass::kInput);
|
||||
},
|
||||
"internal compiler error");
|
||||
}
|
||||
|
||||
TEST_F(ModuleTest, Assert_Null_ConstructedType) {
|
||||
EXPECT_FATAL_FAILURE(
|
||||
{
|
||||
|
||||
@@ -40,8 +40,6 @@ Variable::Variable(ProgramID program_id,
|
||||
declared_storage_class_(declared_storage_class) {
|
||||
TINT_ASSERT(symbol_.IsValid());
|
||||
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(symbol_, program_id);
|
||||
// no type means we must have a constructor to infer it
|
||||
TINT_ASSERT(type_ || constructor);
|
||||
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(constructor, program_id);
|
||||
}
|
||||
|
||||
|
||||
@@ -71,15 +71,6 @@ TEST_F(VariableTest, Assert_MissingSymbol) {
|
||||
"internal compiler error");
|
||||
}
|
||||
|
||||
TEST_F(VariableTest, Assert_Null_Type) {
|
||||
EXPECT_FATAL_FAILURE(
|
||||
{
|
||||
ProgramBuilder b;
|
||||
b.Var("x", nullptr, StorageClass::kNone);
|
||||
},
|
||||
"internal compiler error");
|
||||
}
|
||||
|
||||
TEST_F(VariableTest, Assert_DifferentProgramID_Symbol) {
|
||||
EXPECT_FATAL_FAILURE(
|
||||
{
|
||||
|
||||
@@ -496,7 +496,7 @@ Maybe<ast::Variable*> ParserImpl::global_constant_decl(
|
||||
|
||||
const char* use = "let declaration";
|
||||
|
||||
auto decl = expect_variable_ident_decl(use);
|
||||
auto decl = expect_variable_ident_decl(use, /* allow_inferred = */ true);
|
||||
if (decl.errored)
|
||||
return Failure::kErrored;
|
||||
|
||||
@@ -520,7 +520,7 @@ Maybe<ast::Variable*> ParserImpl::global_constant_decl(
|
||||
|
||||
// variable_decl
|
||||
// : VAR variable_storage_decoration? variable_ident_decl
|
||||
Maybe<ParserImpl::VarDeclInfo> ParserImpl::variable_decl() {
|
||||
Maybe<ParserImpl::VarDeclInfo> ParserImpl::variable_decl(bool allow_inferred) {
|
||||
if (!match(Token::Type::kVar))
|
||||
return Failure::kNoMatch;
|
||||
|
||||
@@ -544,7 +544,8 @@ Maybe<ParserImpl::VarDeclInfo> ParserImpl::variable_decl() {
|
||||
}
|
||||
}
|
||||
|
||||
auto decl = expect_variable_ident_decl("variable declaration");
|
||||
auto decl =
|
||||
expect_variable_ident_decl("variable declaration", allow_inferred);
|
||||
if (decl.errored)
|
||||
return Failure::kErrored;
|
||||
|
||||
@@ -898,11 +899,16 @@ Expect<ast::ImageFormat> ParserImpl::expect_image_storage_type(
|
||||
// variable_ident_decl
|
||||
// : IDENT COLON variable_decoration_list* type_decl
|
||||
Expect<ParserImpl::TypedIdentifier> ParserImpl::expect_variable_ident_decl(
|
||||
const std::string& use) {
|
||||
const std::string& use,
|
||||
bool allow_inferred) {
|
||||
auto ident = expect_ident(use);
|
||||
if (ident.errored)
|
||||
return Failure::kErrored;
|
||||
|
||||
if (allow_inferred && !peek().Is(Token::Type::kColon)) {
|
||||
return TypedIdentifier{nullptr, ident.value, ident.source};
|
||||
}
|
||||
|
||||
if (!expect(use, Token::Type::kColon))
|
||||
return Failure::kErrored;
|
||||
|
||||
@@ -1689,7 +1695,8 @@ Maybe<ast::ReturnStatement*> ParserImpl::return_stmt() {
|
||||
// | CONST variable_ident_decl EQUAL logical_or_expression
|
||||
Maybe<ast::VariableDeclStatement*> ParserImpl::variable_stmt() {
|
||||
if (match(Token::Type::kLet)) {
|
||||
auto decl = expect_variable_ident_decl("let declaration");
|
||||
auto decl = expect_variable_ident_decl("let declaration",
|
||||
/*allow_inferred = */ true);
|
||||
if (decl.errored)
|
||||
return Failure::kErrored;
|
||||
|
||||
@@ -1714,7 +1721,7 @@ Maybe<ast::VariableDeclStatement*> ParserImpl::variable_stmt() {
|
||||
return create<ast::VariableDeclStatement>(decl->source, var);
|
||||
}
|
||||
|
||||
auto decl = variable_decl();
|
||||
auto decl = variable_decl(/*allow_inferred = */ true);
|
||||
if (decl.errored)
|
||||
return Failure::kErrored;
|
||||
if (!decl.matched)
|
||||
|
||||
@@ -214,8 +214,8 @@ class ParserImpl {
|
||||
/// Destructor
|
||||
~TypedIdentifier();
|
||||
|
||||
/// Parsed type.
|
||||
ast::Type* type;
|
||||
/// Parsed type. May be nullptr for inferred types.
|
||||
ast::Type* type = nullptr;
|
||||
/// Parsed identifier.
|
||||
std::string name;
|
||||
/// Source to the identifier.
|
||||
@@ -387,13 +387,19 @@ class ParserImpl {
|
||||
/// @param decos the list of decorations for the constant declaration.
|
||||
Maybe<ast::Variable*> global_constant_decl(ast::DecorationList& decos);
|
||||
/// Parses a `variable_decl` grammar element
|
||||
/// @param allow_inferred if true, do not fail if variable decl does not
|
||||
/// specify type
|
||||
/// @returns the parsed variable declaration info
|
||||
Maybe<VarDeclInfo> variable_decl();
|
||||
Maybe<VarDeclInfo> variable_decl(bool allow_inferred = false);
|
||||
/// Parses a `variable_ident_decl` grammar element, erroring on parse
|
||||
/// failure.
|
||||
/// @param use a description of what was being parsed if an error was raised.
|
||||
/// @param allow_inferred if true, do not fail if variable decl does not
|
||||
/// specify type
|
||||
/// @returns the identifier and type parsed or empty otherwise
|
||||
Expect<TypedIdentifier> expect_variable_ident_decl(const std::string& use);
|
||||
Expect<TypedIdentifier> expect_variable_ident_decl(
|
||||
const std::string& use,
|
||||
bool allow_inferred = false);
|
||||
/// Parses a `variable_storage_decoration` grammar element
|
||||
/// @returns the storage class or StorageClass::kNone if none matched
|
||||
Maybe<ast::StorageClass> variable_storage_decoration();
|
||||
|
||||
@@ -48,6 +48,33 @@ TEST_F(ParserImplTest, GlobalConstantDecl) {
|
||||
ast::HasDecoration<ast::OverrideDecoration>(e.value->decorations()));
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, GlobalConstantDecl_Inferred) {
|
||||
auto p = parser("let a = 1.");
|
||||
auto decos = p->decoration_list();
|
||||
EXPECT_FALSE(decos.errored);
|
||||
EXPECT_FALSE(decos.matched);
|
||||
auto e = p->global_constant_decl(decos.value);
|
||||
EXPECT_FALSE(p->has_error()) << p->error();
|
||||
EXPECT_TRUE(e.matched);
|
||||
EXPECT_FALSE(e.errored);
|
||||
ASSERT_NE(e.value, nullptr);
|
||||
|
||||
EXPECT_TRUE(e->is_const());
|
||||
EXPECT_EQ(e->symbol(), p->builder().Symbols().Get("a"));
|
||||
EXPECT_EQ(e->type(), nullptr);
|
||||
|
||||
EXPECT_EQ(e->source().range.begin.line, 1u);
|
||||
EXPECT_EQ(e->source().range.begin.column, 5u);
|
||||
EXPECT_EQ(e->source().range.end.line, 1u);
|
||||
EXPECT_EQ(e->source().range.end.column, 6u);
|
||||
|
||||
ASSERT_NE(e->constructor(), nullptr);
|
||||
EXPECT_TRUE(e->constructor()->Is<ast::ConstructorExpression>());
|
||||
|
||||
EXPECT_FALSE(
|
||||
ast::HasDecoration<ast::OverrideDecoration>(e.value->decorations()));
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, GlobalConstantDecl_InvalidVariable) {
|
||||
auto p = parser("let a : invalid = 1.");
|
||||
auto decos = p->decoration_list();
|
||||
|
||||
@@ -37,6 +37,13 @@ TEST_F(ParserImplTest, GlobalDecl_GlobalVariable) {
|
||||
EXPECT_EQ(v->symbol(), program.Symbols().Get("a"));
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, GlobalDecl_GlobalVariable_Inferred_Invalid) {
|
||||
auto p = parser("var<private> a = vec2<i32>(1, 2);");
|
||||
p->expect_global_decl();
|
||||
ASSERT_TRUE(p->has_error());
|
||||
EXPECT_EQ(p->error(), "1:16: expected ':' for variable declaration");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, GlobalDecl_GlobalVariable_Invalid) {
|
||||
auto p = parser("var<private> a : vec2<invalid>;");
|
||||
p->expect_global_decl();
|
||||
|
||||
@@ -33,6 +33,18 @@ TEST_F(ParserImplTest, VariableDecl_Parses) {
|
||||
EXPECT_EQ(v->type->source().range, (Source::Range{{1u, 14u}, {1u, 17u}}));
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, VariableDecl_Inferred_Parses) {
|
||||
auto p = parser("var my_var = 1.0");
|
||||
auto v = p->variable_decl(/*allow_inferred = */ true);
|
||||
EXPECT_FALSE(p->has_error());
|
||||
EXPECT_TRUE(v.matched);
|
||||
EXPECT_FALSE(v.errored);
|
||||
EXPECT_EQ(v->name, "my_var");
|
||||
EXPECT_EQ(v->type, nullptr);
|
||||
|
||||
EXPECT_EQ(v->source.range, (Source::Range{{1u, 5u}, {1u, 11u}}));
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, VariableDecl_MissingVar) {
|
||||
auto p = parser("my_var : f32");
|
||||
auto v = p->variable_decl();
|
||||
|
||||
@@ -33,6 +33,17 @@ TEST_F(ParserImplTest, VariableIdentDecl_Parses) {
|
||||
EXPECT_EQ(decl->type->source().range, (Source::Range{{1u, 10u}, {1u, 13u}}));
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, VariableIdentDecl_Inferred_Parses) {
|
||||
auto p = parser("my_var = 1.0");
|
||||
auto decl = p->expect_variable_ident_decl("test", /*allow_inferred = */ true);
|
||||
ASSERT_FALSE(p->has_error()) << p->error();
|
||||
ASSERT_FALSE(decl.errored);
|
||||
ASSERT_EQ(decl->name, "my_var");
|
||||
ASSERT_EQ(decl->type, nullptr);
|
||||
|
||||
EXPECT_EQ(decl->source.range, (Source::Range{{1u, 1u}, {1u, 7u}}));
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, VariableIdentDecl_MissingIdent) {
|
||||
auto p = parser(": f32");
|
||||
auto decl = p->expect_variable_ident_decl("test");
|
||||
|
||||
165
src/resolver/inferred_type_test.cc
Normal file
165
src/resolver/inferred_type_test.cc
Normal file
@@ -0,0 +1,165 @@
|
||||
// Copyright 2021 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/ast/struct_block_decoration.h"
|
||||
#include "src/resolver/resolver.h"
|
||||
#include "src/resolver/resolver_test_helper.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
namespace tint {
|
||||
namespace resolver {
|
||||
namespace {
|
||||
|
||||
// Helpers and typedefs
|
||||
using i32 = ProgramBuilder::i32;
|
||||
using u32 = ProgramBuilder::u32;
|
||||
using f32 = ProgramBuilder::f32;
|
||||
|
||||
struct ResolverInferredTypeTest : public resolver::TestHelper,
|
||||
public testing::Test {};
|
||||
|
||||
struct Params {
|
||||
create_ast_type_func_ptr create_type;
|
||||
create_sem_type_func_ptr create_expected_type;
|
||||
};
|
||||
|
||||
Params all_cases[] = {
|
||||
{ast_bool, sem_bool},
|
||||
{ast_u32, sem_u32},
|
||||
{ast_i32, sem_i32},
|
||||
{ast_f32, sem_f32},
|
||||
{ast_vec3<bool>, sem_vec3<sem_bool>},
|
||||
{ast_vec3<i32>, sem_vec3<sem_i32>},
|
||||
{ast_vec3<u32>, sem_vec3<sem_u32>},
|
||||
{ast_vec3<f32>, sem_vec3<sem_f32>},
|
||||
{ast_mat3x3<i32>, sem_mat3x3<sem_i32>},
|
||||
{ast_mat3x3<u32>, sem_mat3x3<sem_u32>},
|
||||
{ast_mat3x3<f32>, sem_mat3x3<sem_f32>},
|
||||
|
||||
{ast_alias<ast_bool>, sem_bool},
|
||||
{ast_alias<ast_u32>, sem_u32},
|
||||
{ast_alias<ast_i32>, sem_i32},
|
||||
{ast_alias<ast_f32>, sem_f32},
|
||||
{ast_alias<ast_vec3<bool>>, sem_vec3<sem_bool>},
|
||||
{ast_alias<ast_vec3<i32>>, sem_vec3<sem_i32>},
|
||||
{ast_alias<ast_vec3<u32>>, sem_vec3<sem_u32>},
|
||||
{ast_alias<ast_vec3<f32>>, sem_vec3<sem_f32>},
|
||||
{ast_alias<ast_mat3x3<i32>>, sem_mat3x3<sem_i32>},
|
||||
{ast_alias<ast_mat3x3<u32>>, sem_mat3x3<sem_u32>},
|
||||
{ast_alias<ast_mat3x3<f32>>, sem_mat3x3<sem_f32>},
|
||||
};
|
||||
|
||||
using ResolverInferredTypeParamTest = ResolverTestWithParam<Params>;
|
||||
|
||||
TEST_P(ResolverInferredTypeParamTest, GlobalLet_Pass) {
|
||||
auto& params = GetParam();
|
||||
|
||||
auto* type = params.create_type(ty);
|
||||
auto* expected_type = params.create_expected_type(ty);
|
||||
|
||||
// let a = <type constructor>;
|
||||
auto* ctor_expr = ConstructValueFilledWith(type);
|
||||
auto* var = GlobalConst("a", nullptr, ctor_expr);
|
||||
WrapInFunction();
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
EXPECT_EQ(TypeOf(var), expected_type);
|
||||
}
|
||||
|
||||
TEST_P(ResolverInferredTypeParamTest, GlobalVar_Fail) {
|
||||
auto& params = GetParam();
|
||||
|
||||
auto* type = params.create_type(ty);
|
||||
|
||||
// var a = <type constructor>;
|
||||
auto* ctor_expr = ConstructValueFilledWith(type);
|
||||
Global(Source{{12, 34}}, "a", nullptr, ast::StorageClass::kPrivate,
|
||||
ctor_expr);
|
||||
WrapInFunction();
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: global var declaration must specify a type");
|
||||
}
|
||||
|
||||
TEST_P(ResolverInferredTypeParamTest, LocalLet_Pass) {
|
||||
auto& params = GetParam();
|
||||
|
||||
auto* type = params.create_type(ty);
|
||||
auto* expected_type = params.create_expected_type(ty);
|
||||
|
||||
// let a = <type constructor>;
|
||||
auto* ctor_expr = ConstructValueFilledWith(type);
|
||||
auto* var = Const("a", nullptr, ctor_expr);
|
||||
WrapInFunction(var);
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
EXPECT_EQ(TypeOf(var), expected_type);
|
||||
}
|
||||
|
||||
TEST_P(ResolverInferredTypeParamTest, LocalVar_Pass) {
|
||||
auto& params = GetParam();
|
||||
|
||||
auto* type = params.create_type(ty);
|
||||
auto* expected_type = params.create_expected_type(ty);
|
||||
|
||||
// var a = <type constructor>;
|
||||
auto* ctor_expr = ConstructValueFilledWith(type);
|
||||
auto* var = Var("a", nullptr, ast::StorageClass::kFunction, ctor_expr);
|
||||
WrapInFunction(var);
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
EXPECT_EQ(TypeOf(var)->UnwrapRef(), expected_type);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(ResolverTest,
|
||||
ResolverInferredTypeParamTest,
|
||||
testing::ValuesIn(all_cases));
|
||||
|
||||
TEST_F(ResolverInferredTypeTest, InferArray_Pass) {
|
||||
auto* type = ty.array(ty.u32(), 10);
|
||||
auto* expected_type =
|
||||
create<sem::Array>(create<sem::U32>(), 10, 4, 4 * 10, 4, true);
|
||||
|
||||
auto* ctor_expr = Construct(type);
|
||||
auto* var = Var("a", nullptr, ast::StorageClass::kFunction, ctor_expr);
|
||||
WrapInFunction(var);
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
EXPECT_EQ(TypeOf(var)->UnwrapRef(), expected_type);
|
||||
}
|
||||
|
||||
TEST_F(ResolverInferredTypeTest, InferStruct_Pass) {
|
||||
auto* member = Member("x", ty.i32());
|
||||
auto* type = Structure("S", {member}, {create<ast::StructBlockDecoration>()});
|
||||
|
||||
auto* expected_type =
|
||||
create<sem::Struct>(type,
|
||||
sem::StructMemberList{create<sem::StructMember>(
|
||||
member, create<sem::I32>(), 0, 0, 0, 4)},
|
||||
0, 4, 4);
|
||||
|
||||
auto* ctor_expr = Construct(type);
|
||||
|
||||
auto* var = Var("a", nullptr, ast::StorageClass::kFunction, ctor_expr);
|
||||
WrapInFunction(var);
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
EXPECT_EQ(TypeOf(var)->UnwrapRef(), expected_type);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace resolver
|
||||
} // namespace tint
|
||||
@@ -417,6 +417,12 @@ Resolver::VariableInfo* Resolver::Variable(ast::Variable* var,
|
||||
|
||||
// If the variable has no declared type, infer it from the RHS
|
||||
if (!storage_type) {
|
||||
if (!var->is_const() && kind == VariableKind::kGlobal) {
|
||||
diagnostics_.add_error("global var declaration must specify a type",
|
||||
var->source());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
type_name = rhs_type_name;
|
||||
storage_type = rhs_type->UnwrapRef(); // Implicit load of RHS
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user