text: // 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/bitcast_expression.h"
  #include "src/resolver/resolver.h"
  #include "src/resolver/resolver_test_helper.h"
  
  #include "gmock/gmock.h"
  
  namespace tint {
  namespace resolver {
  namespace {
  
  struct Type {
    template <typename T>
    static constexpr Type Create() {
      return Type{builder::DataType<T>::AST, builder::DataType<T>::Sem,
                  builder::DataType<T>::Expr};
    }
  
    builder::ast_type_func_ptr ast;
    builder::sem_type_func_ptr sem;
    builder::ast_expr_func_ptr expr;
  };
  
  static constexpr Type kNumericScalars[] = {
      Type::Create<builder::f32>(),
      Type::Create<builder::i32>(),
      Type::Create<builder::u32>(),
  };
  static constexpr Type kVec2NumericScalars[] = {
      Type::Create<builder::vec2<builder::f32>>(),
      Type::Create<builder::vec2<builder::i32>>(),
      Type::Create<builder::vec2<builder::u32>>(),
  };
  static constexpr Type kVec3NumericScalars[] = {
      Type::Create<builder::vec3<builder::f32>>(),
      Type::Create<builder::vec3<builder::i32>>(),
      Type::Create<builder::vec3<builder::u32>>(),
  };
  static constexpr Type kVec4NumericScalars[] = {
      Type::Create<builder::vec4<builder::f32>>(),
      Type::Create<builder::vec4<builder::i32>>(),
      Type::Create<builder::vec4<builder::u32>>(),
  };
  static constexpr Type kInvalid[] = {
      // A non-exhaustive selection of uncastable types
      Type::Create<bool>(),
      Type::Create<builder::vec2<bool>>(),
      Type::Create<builder::vec3<bool>>(),
      Type::Create<builder::vec4<bool>>(),
      Type::Create<builder::array<2, builder::i32>>(),
      Type::Create<builder::array<3, builder::u32>>(),
      Type::Create<builder::array<4, builder::f32>>(),
      Type::Create<builder::array<5, bool>>(),
      Type::Create<builder::mat2x2<builder::f32>>(),
      Type::Create<builder::mat3x3<builder::f32>>(),
      Type::Create<builder::mat4x4<builder::f32>>(),
      Type::Create<builder::ptr<builder::i32>>(),
      Type::Create<builder::ptr<builder::array<2, builder::i32>>>(),
      Type::Create<builder::ptr<builder::mat2x2<builder::f32>>>(),
  };
  
  using ResolverBitcastValidationTest =
      ResolverTestWithParam<std::tuple<Type, Type>>;
  
  ////////////////////////////////////////////////////////////////////////////////
  // Valid bitcasts
  ////////////////////////////////////////////////////////////////////////////////
  using ResolverBitcastValidationTestPass = ResolverBitcastValidationTest;
  TEST_P(ResolverBitcastValidationTestPass, Test) {
    auto src = std::get<0>(GetParam());
    auto dst = std::get<1>(GetParam());
  
    auto* cast = Bitcast(dst.ast(*this), src.expr(*this, 0));
    WrapInFunction(cast);
  
    ASSERT_TRUE(r()->Resolve()) << r()->error();
    EXPECT_EQ(TypeOf(cast), dst.sem(*this));
  }
  INSTANTIATE_TEST_SUITE_P(Scalars,
                           ResolverBitcastValidationTestPass,
                           testing::Combine(testing::ValuesIn(kNumericScalars),
                                            testing::ValuesIn(kNumericScalars)));
  INSTANTIATE_TEST_SUITE_P(
      Vec2,
      ResolverBitcastValidationTestPass,
      testing::Combine(testing::ValuesIn(kVec2NumericScalars),
                       testing::ValuesIn(kVec2NumericScalars)));
  INSTANTIATE_TEST_SUITE_P(
      Vec3,
      ResolverBitcastValidationTestPass,
      testing::Combine(testing::ValuesIn(kVec3NumericScalars),
                       testing::ValuesIn(kVec3NumericScalars)));
  INSTANTIATE_TEST_SUITE_P(
      Vec4,
      ResolverBitcastValidationTestPass,
      testing::Combine(testing::ValuesIn(kVec4NumericScalars),
                       testing::ValuesIn(kVec4NumericScalars)));
  
  ////////////////////////////////////////////////////////////////////////////////
  // Invalid source type for bitcasts
  ////////////////////////////////////////////////////////////////////////////////
  using ResolverBitcastValidationTestInvalidSrcTy = ResolverBitcastValidationTest;
  TEST_P(ResolverBitcastValidationTestInvalidSrcTy, Test) {
    auto src = std::get<0>(GetParam());
    auto dst = std::get<1>(GetParam());
  
    auto* cast = Bitcast(dst.ast(*this), Expr(Source{{12, 34}}, "src"));
    WrapInFunction(Const("src", nullptr, src.expr(*this, 0)), cast);
  
    auto expected = "12:34 error: '" + src.sem(*this)->FriendlyName(Symbols()) +
                    "' cannot be bitcast";
  
    EXPECT_FALSE(r()->Resolve());
    EXPECT_EQ(r()->error(), expected);
  }
  INSTANTIATE_TEST_SUITE_P(Scalars,
                           ResolverBitcastValidationTestInvalidSrcTy,
                           testing::Combine(testing::ValuesIn(kInvalid),
                                            testing::ValuesIn(kNumericScalars)));
  INSTANTIATE_TEST_SUITE_P(
      Vec2,
      ResolverBitcastValidationTestInvalidSrcTy,
      testing::Combine(testing::ValuesIn(kInvalid),
                       testing::ValuesIn(kVec2NumericScalars)));
  INSTANTIATE_TEST_SUITE_P(
      Vec3,
      ResolverBitcastValidationTestInvalidSrcTy,
      testing::Combine(testing::ValuesIn(kInvalid),
                       testing::ValuesIn(kVec3NumericScalars)));
  INSTANTIATE_TEST_SUITE_P(
      Vec4,
      ResolverBitcastValidationTestInvalidSrcTy,
      testing::Combine(testing::ValuesIn(kInvalid),
                       testing::ValuesIn(kVec4NumericScalars)));
  
  ////////////////////////////////////////////////////////////////////////////////
  // Invalid target type for bitcasts
  ////////////////////////////////////////////////////////////////////////////////
  using ResolverBitcastValidationTestInvalidDstTy = ResolverBitcastValidationTest;
  TEST_P(ResolverBitcastValidationTestInvalidDstTy, Test) {
    auto src = std::get<0>(GetParam());
    auto dst = std::get<1>(GetParam());
  
    // Use an alias so we can put a Source on the bitcast type
    Alias("T", dst.ast(*this));
    WrapInFunction(
        Bitcast(ty.type_name(Source{{12, 34}}, "T"), src.expr(*this, 0)));
  
    auto expected = "12:34 error: cannot bitcast to '" +
                    dst.sem(*this)->FriendlyName(Symbols()) + "'";
  
    EXPECT_FALSE(r()->Resolve());
    EXPECT_EQ(r()->error(), expected);
  }
  INSTANTIATE_TEST_SUITE_P(Scalars,
                           ResolverBitcastValidationTestInvalidDstTy,
                           testing::Combine(testing::ValuesIn(kNumericScalars),
                                            testing::ValuesIn(kInvalid)));
  INSTANTIATE_TEST_SUITE_P(
      Vec2,
      ResolverBitcastValidationTestInvalidDstTy,
      testing::Combine(testing::ValuesIn(kVec2NumericScalars),
                       testing::ValuesIn(kInvalid)));
  INSTANTIATE_TEST_SUITE_P(
      Vec3,
      ResolverBitcastValidationTestInvalidDstTy,
      testing::Combine(testing::ValuesIn(kVec3NumericScalars),
                       testing::ValuesIn(kInvalid)));
  INSTANTIATE_TEST_SUITE_P(
      Vec4,
      ResolverBitcastValidationTestInvalidDstTy,
      testing::Combine(testing::ValuesIn(kVec4NumericScalars),
                       testing::ValuesIn(kInvalid)));
  
  ////////////////////////////////////////////////////////////////////////////////
  // Incompatible bitcast, but both src and dst types are valid
  ////////////////////////////////////////////////////////////////////////////////
  using ResolverBitcastValidationTestIncompatible = ResolverBitcastValidationTest;
  TEST_P(ResolverBitcastValidationTestIncompatible, Test) {
    auto src = std::get<0>(GetParam());
    auto dst = std::get<1>(GetParam());
  
    WrapInFunction(Bitcast(Source{{12, 34}}, dst.ast(*this), src.expr(*this, 0)));
  
    auto expected = "12:34 error: cannot bitcast from '" +
                    src.sem(*this)->FriendlyName(Symbols()) + "' to '" +
                    dst.sem(*this)->FriendlyName(Symbols()) + "'";
  
    EXPECT_FALSE(r()->Resolve());
    EXPECT_EQ(r()->error(), expected);
  }
  INSTANTIATE_TEST_SUITE_P(
      ScalarToVec2,
      ResolverBitcastValidationTestIncompatible,
      testing::Combine(testing::ValuesIn(kNumericScalars),
                       testing::ValuesIn(kVec2NumericScalars)));
  INSTANTIATE_TEST_SUITE_P(
      Vec2ToVec3,
      ResolverBitcastValidationTestIncompatible,
      testing::Combine(testing::ValuesIn(kVec2NumericScalars),
                       testing::ValuesIn(kVec3NumericScalars)));
  INSTANTIATE_TEST_SUITE_P(
      Vec3ToVec4,
      ResolverBitcastValidationTestIncompatible,
      testing::Combine(testing::ValuesIn(kVec3NumericScalars),
                       testing::ValuesIn(kVec4NumericScalars)));
  INSTANTIATE_TEST_SUITE_P(
      Vec4ToScalar,
      ResolverBitcastValidationTestIncompatible,
      testing::Combine(testing::ValuesIn(kVec4NumericScalars),
                       testing::ValuesIn(kNumericScalars)));
  
  }  // namespace
  }  // namespace resolver
  }  // namespace tint
  