mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-07-31 09:25:41 +00:00
This CL removes the following AST nodes: * ast::Array * ast::Atomic * ast::Matrix * ast::MultisampledTexture * ast::Pointer * ast::SampledTexture * ast::Texture * ast::TypeName * ast::Vector ast::Type, which used to be the base class for all AST types, is now a thin wrapper around ast::IdentifierExpression. All types are now referred to using their type name. The resolver now handles type resolution and validation of the types listed above based on the TemplateIdentifier arguments. Other changes: * ProgramBuilder has undergone substantial refactoring. * ProgramBuilder helpers for type inferencing is now more explicit. Instead of passing 'nullptr', a new 'Infer' template argument is passed. * ast::CheckIdentifier() is used for more tests that check identifiers, including types. Bug: tint:1810 Change-Id: I8e739ef49435dc1c20a462f3ec5ba265661a7edb Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/118723 Reviewed-by: Dan Sinclair <dsinclair@chromium.org> Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: Ben Clayton <bclayton@google.com> Reviewed-by: James Price <jrprice@google.com>
371 lines
13 KiB
C++
371 lines
13 KiB
C++
// 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/sem/load.h"
|
|
#include "gmock/gmock.h"
|
|
#include "src/tint/resolver/resolver.h"
|
|
#include "src/tint/resolver/resolver_test_helper.h"
|
|
#include "src/tint/sem/test_helper.h"
|
|
#include "src/tint/type/reference.h"
|
|
#include "src/tint/type/texture_dimension.h"
|
|
|
|
using namespace tint::number_suffixes; // NOLINT
|
|
|
|
namespace tint::resolver {
|
|
namespace {
|
|
|
|
using ResolverLoadTest = ResolverTest;
|
|
|
|
TEST_F(ResolverLoadTest, VarInitializer) {
|
|
// var ref = 1i;
|
|
// var v = ref;
|
|
auto* ident = Expr("ref");
|
|
WrapInFunction(Var("ref", Expr(1_i)), //
|
|
Var("v", ident));
|
|
|
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
|
auto* load = Sem().Get<sem::Load>(ident);
|
|
ASSERT_NE(load, nullptr);
|
|
EXPECT_TRUE(load->Type()->Is<type::I32>());
|
|
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
|
|
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::I32>());
|
|
}
|
|
|
|
TEST_F(ResolverLoadTest, LetInitializer) {
|
|
// var ref = 1i;
|
|
// let l = ref;
|
|
auto* ident = Expr("ref");
|
|
WrapInFunction(Var("ref", Expr(1_i)), //
|
|
Let("l", ident));
|
|
|
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
|
auto* load = Sem().Get<sem::Load>(ident);
|
|
ASSERT_NE(load, nullptr);
|
|
EXPECT_TRUE(load->Type()->Is<type::I32>());
|
|
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
|
|
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::I32>());
|
|
}
|
|
|
|
TEST_F(ResolverLoadTest, Assignment) {
|
|
// var ref = 1i;
|
|
// var v : i32;
|
|
// v = ref;
|
|
auto* ident = Expr("ref");
|
|
WrapInFunction(Var("ref", Expr(1_i)), //
|
|
Var("v", ty.i32()), //
|
|
Assign("v", ident));
|
|
|
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
|
auto* load = Sem().Get<sem::Load>(ident);
|
|
ASSERT_NE(load, nullptr);
|
|
EXPECT_TRUE(load->Type()->Is<type::I32>());
|
|
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
|
|
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::I32>());
|
|
}
|
|
|
|
TEST_F(ResolverLoadTest, CompoundAssignment) {
|
|
// var ref = 1i;
|
|
// var v : i32;
|
|
// v += ref;
|
|
auto* ident = Expr("ref");
|
|
WrapInFunction(Var("ref", Expr(1_i)), //
|
|
Var("v", ty.i32()), //
|
|
CompoundAssign("v", ident, ast::BinaryOp::kAdd));
|
|
|
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
|
auto* load = Sem().Get<sem::Load>(ident);
|
|
ASSERT_NE(load, nullptr);
|
|
EXPECT_TRUE(load->Type()->Is<type::I32>());
|
|
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
|
|
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::I32>());
|
|
}
|
|
|
|
TEST_F(ResolverLoadTest, UnaryOp) {
|
|
// var ref = 1i;
|
|
// var v = -ref;
|
|
auto* ident = Expr("ref");
|
|
WrapInFunction(Var("ref", Expr(1_i)), //
|
|
Var("v", Negation(ident)));
|
|
|
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
|
auto* load = Sem().Get<sem::Load>(ident);
|
|
ASSERT_NE(load, nullptr);
|
|
EXPECT_TRUE(load->Type()->Is<type::I32>());
|
|
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
|
|
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::I32>());
|
|
}
|
|
|
|
TEST_F(ResolverLoadTest, UnaryOp_NoLoad) {
|
|
// var ref = 1i;
|
|
// let v = &ref;
|
|
auto* ident = Expr("ref");
|
|
WrapInFunction(Var("ref", Expr(1_i)), //
|
|
Let("v", AddressOf(ident)));
|
|
|
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
|
auto* var_user = Sem().Get<sem::VariableUser>(ident);
|
|
ASSERT_NE(var_user, nullptr);
|
|
EXPECT_TRUE(var_user->Type()->Is<type::Reference>());
|
|
EXPECT_TRUE(var_user->Type()->UnwrapRef()->Is<type::I32>());
|
|
}
|
|
|
|
TEST_F(ResolverLoadTest, BinaryOp) {
|
|
// var ref = 1i;
|
|
// var v = ref * 1i;
|
|
auto* ident = Expr("ref");
|
|
WrapInFunction(Var("ref", Expr(1_i)), //
|
|
Var("v", Mul(ident, 1_i)));
|
|
|
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
|
auto* load = Sem().Get<sem::Load>(ident);
|
|
ASSERT_NE(load, nullptr);
|
|
EXPECT_TRUE(load->Type()->Is<type::I32>());
|
|
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
|
|
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::I32>());
|
|
}
|
|
|
|
TEST_F(ResolverLoadTest, Index) {
|
|
// var ref = 1i;
|
|
// var v = array<i32, 3>(1i, 2i, 3i)[ref];
|
|
auto* ident = Expr("ref");
|
|
WrapInFunction(Var("ref", Expr(1_i)), //
|
|
IndexAccessor(array<i32, 3>(1_i, 2_i, 3_i), ident));
|
|
|
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
|
auto* load = Sem().Get<sem::Load>(ident);
|
|
ASSERT_NE(load, nullptr);
|
|
EXPECT_TRUE(load->Type()->Is<type::I32>());
|
|
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
|
|
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::I32>());
|
|
}
|
|
|
|
TEST_F(ResolverLoadTest, MultiComponentSwizzle) {
|
|
// var ref = vec4(1);
|
|
// var v = ref.xyz;
|
|
auto* ident = Expr("ref");
|
|
WrapInFunction(Var("ref", vec4<i32>(1_i)), //
|
|
Var("v", MemberAccessor(ident, "xyz")));
|
|
|
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
|
auto* load = Sem().Get<sem::Load>(ident);
|
|
ASSERT_NE(load, nullptr);
|
|
EXPECT_TRUE(load->Type()->Is<type::Vector>());
|
|
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
|
|
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::Vector>());
|
|
}
|
|
|
|
TEST_F(ResolverLoadTest, Bitcast) {
|
|
// var ref = 1f;
|
|
// var v = bitcast<i32>(ref);
|
|
auto* ident = Expr("ref");
|
|
WrapInFunction(Var("ref", Expr(1_f)), //
|
|
Bitcast<i32>(ident));
|
|
|
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
|
auto* load = Sem().Get<sem::Load>(ident);
|
|
ASSERT_NE(load, nullptr);
|
|
EXPECT_TRUE(load->Type()->Is<type::F32>());
|
|
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
|
|
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::F32>());
|
|
}
|
|
|
|
TEST_F(ResolverLoadTest, BuiltinArg) {
|
|
// var ref = 1f;
|
|
// var v = abs(ref);
|
|
auto* ident = Expr("ref");
|
|
WrapInFunction(Var("ref", Expr(1_f)), //
|
|
Call("abs", ident));
|
|
|
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
|
auto* load = Sem().Get<sem::Load>(ident);
|
|
ASSERT_NE(load, nullptr);
|
|
EXPECT_TRUE(load->Type()->Is<type::F32>());
|
|
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
|
|
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::F32>());
|
|
}
|
|
|
|
TEST_F(ResolverLoadTest, FunctionArg) {
|
|
// fn f(x : f32) {}
|
|
// var ref = 1f;
|
|
// f(ref);
|
|
Func("f", utils::Vector{Param("x", ty.f32())}, ty.void_(), utils::Empty);
|
|
auto* ident = Expr("ref");
|
|
WrapInFunction(Var("ref", Expr(1_f)), //
|
|
CallStmt(Call("f", ident)));
|
|
|
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
|
auto* load = Sem().Get<sem::Load>(ident);
|
|
ASSERT_NE(load, nullptr);
|
|
EXPECT_TRUE(load->Type()->Is<type::F32>());
|
|
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
|
|
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::F32>());
|
|
}
|
|
|
|
TEST_F(ResolverLoadTest, FunctionArg_Handles) {
|
|
// @group(0) @binding(0) var t : texture_2d<f32>;
|
|
// @group(0) @binding(1) var s : sampler;
|
|
// fn f(tp : texture_2d<f32>, sp : sampler) -> vec4<f32> {
|
|
// return textureSampleLevel(tp, sp, vec2(), 0);
|
|
// }
|
|
// f(t, s);
|
|
GlobalVar("t", ty.sampled_texture(type::TextureDimension::k2d, ty.f32()),
|
|
utils::Vector{Group(0_a), Binding(0_a)});
|
|
GlobalVar("s", ty.sampler(type::SamplerKind::kSampler),
|
|
utils::Vector{Group(0_a), Binding(1_a)});
|
|
Func("f",
|
|
utils::Vector{
|
|
Param("tp", ty.sampled_texture(type::TextureDimension::k2d, ty.f32())),
|
|
Param("sp", ty.sampler(type::SamplerKind::kSampler)),
|
|
},
|
|
ty.vec4<f32>(),
|
|
utils::Vector{
|
|
Return(Call("textureSampleLevel", "tp", "sp", vec2<f32>(), 0_a)),
|
|
});
|
|
auto* t_ident = Expr("t");
|
|
auto* s_ident = Expr("s");
|
|
WrapInFunction(CallStmt(Call("f", t_ident, s_ident)));
|
|
|
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
|
|
|
{
|
|
auto* load = Sem().Get<sem::Load>(t_ident);
|
|
ASSERT_NE(load, nullptr);
|
|
EXPECT_TRUE(load->Type()->Is<type::SampledTexture>());
|
|
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
|
|
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::SampledTexture>());
|
|
}
|
|
{
|
|
auto* load = Sem().Get<sem::Load>(s_ident);
|
|
ASSERT_NE(load, nullptr);
|
|
EXPECT_TRUE(load->Type()->Is<type::Sampler>());
|
|
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
|
|
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::Sampler>());
|
|
}
|
|
}
|
|
|
|
TEST_F(ResolverLoadTest, FunctionReturn) {
|
|
// var ref = 1f;
|
|
// return ref;
|
|
auto* ident = Expr("ref");
|
|
Func("f", utils::Empty, ty.f32(),
|
|
utils::Vector{
|
|
Decl(Var("ref", Expr(1_f))),
|
|
Return(ident),
|
|
});
|
|
|
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
|
auto* load = Sem().Get<sem::Load>(ident);
|
|
ASSERT_NE(load, nullptr);
|
|
EXPECT_TRUE(load->Type()->Is<type::F32>());
|
|
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
|
|
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::F32>());
|
|
}
|
|
|
|
TEST_F(ResolverLoadTest, IfCond) {
|
|
// var ref = false;
|
|
// if (ref) {}
|
|
auto* ident = Expr("ref");
|
|
WrapInFunction(Var("ref", Expr(false)), //
|
|
If(ident, Block()));
|
|
|
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
|
auto* load = Sem().Get<sem::Load>(ident);
|
|
ASSERT_NE(load, nullptr);
|
|
EXPECT_TRUE(load->Type()->Is<type::Bool>());
|
|
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
|
|
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::Bool>());
|
|
}
|
|
|
|
TEST_F(ResolverLoadTest, Switch) {
|
|
// var ref = 1i;
|
|
// switch (ref) {
|
|
// default:
|
|
// }
|
|
auto* ident = Expr("ref");
|
|
WrapInFunction(Var("ref", Expr(1_i)), //
|
|
Switch(ident, DefaultCase()));
|
|
|
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
|
auto* load = Sem().Get<sem::Load>(ident);
|
|
ASSERT_NE(load, nullptr);
|
|
EXPECT_TRUE(load->Type()->Is<type::I32>());
|
|
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
|
|
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::I32>());
|
|
}
|
|
|
|
TEST_F(ResolverLoadTest, BreakIfCond) {
|
|
// var ref = false;
|
|
// loop {
|
|
// continuing {
|
|
// break if (ref);
|
|
// }
|
|
// }
|
|
auto* ident = Expr("ref");
|
|
WrapInFunction(Var("ref", Expr(false)), //
|
|
Loop(Block(), Block(BreakIf(ident))));
|
|
|
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
|
auto* load = Sem().Get<sem::Load>(ident);
|
|
ASSERT_NE(load, nullptr);
|
|
EXPECT_TRUE(load->Type()->Is<type::Bool>());
|
|
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
|
|
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::Bool>());
|
|
}
|
|
|
|
TEST_F(ResolverLoadTest, ForCond) {
|
|
// var ref = false;
|
|
// for (; ref; ) {}
|
|
auto* ident = Expr("ref");
|
|
WrapInFunction(Var("ref", Expr(false)), //
|
|
For(nullptr, ident, nullptr, Block()));
|
|
|
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
|
auto* load = Sem().Get<sem::Load>(ident);
|
|
ASSERT_NE(load, nullptr);
|
|
EXPECT_TRUE(load->Type()->Is<type::Bool>());
|
|
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
|
|
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::Bool>());
|
|
}
|
|
|
|
TEST_F(ResolverLoadTest, WhileCond) {
|
|
// var ref = false;
|
|
// while (ref) {}
|
|
auto* ident = Expr("ref");
|
|
WrapInFunction(Var("ref", Expr(false)), //
|
|
While(ident, Block()));
|
|
|
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
|
auto* load = Sem().Get<sem::Load>(ident);
|
|
ASSERT_NE(load, nullptr);
|
|
EXPECT_TRUE(load->Type()->Is<type::Bool>());
|
|
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
|
|
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::Bool>());
|
|
}
|
|
|
|
TEST_F(ResolverLoadTest, AddressOf) {
|
|
// var ref = 1i;
|
|
// let l = &ref;
|
|
auto* ident = Expr("ref");
|
|
WrapInFunction(Var("ref", Expr(1_i)), //
|
|
Let("l", AddressOf(ident)));
|
|
|
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
|
auto* no_load = Sem().GetVal(ident);
|
|
ASSERT_NE(no_load, nullptr);
|
|
EXPECT_TRUE(no_load->Type()->Is<type::Reference>()); // No load
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace tint::resolver
|