luau/tests/TypePack.test.cpp

215 lines
5.3 KiB
C++

// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/TypeInfer.h"
#include "Luau/Type.h"
#include "Fixture.h"
#include "doctest.h"
using namespace Luau;
namespace
{
struct TypePackFixture
{
TypePackFixture()
{
typeVars.emplace_back(new Type(PrimitiveType(PrimitiveType::NilType)));
typeVars.emplace_back(new Type(PrimitiveType(PrimitiveType::Boolean)));
typeVars.emplace_back(new Type(PrimitiveType(PrimitiveType::Number)));
typeVars.emplace_back(new Type(PrimitiveType(PrimitiveType::String)));
for (const auto& ptr : typeVars)
types.push_back(ptr.get());
}
TypePackId freshTypePack()
{
typePacks.emplace_back(new TypePackVar{FreeTypePack{TypeLevel{}}});
return typePacks.back().get();
}
TypePackId newTypePack(std::initializer_list<TypeId> types, std::optional<TypePackId> tail)
{
typePacks.emplace_back(new TypePackVar{TypePack{types, tail}});
return typePacks.back().get();
}
std::vector<std::unique_ptr<TypePackVar>> typePacks;
std::vector<std::unique_ptr<Type>> typeVars;
std::vector<TypeId> types;
};
} // namespace
TEST_SUITE_BEGIN("TypePackTests");
TEST_CASE_FIXTURE(TypePackFixture, "type_pack_hello")
{
auto tp = TypePackVar{TypePack{{types[0], types[1]}, std::nullopt}};
CHECK(tp == tp);
}
TEST_CASE_FIXTURE(TypePackFixture, "first_chases_Bound_TypePackVars")
{
Type nilType{PrimitiveType{PrimitiveType::NilType}};
auto tp1 = TypePackVar{TypePack{{&nilType}, std::nullopt}};
auto tp2 = TypePackVar{BoundTypePack{&tp1}};
auto tp3 = TypePackVar{TypePack{{}, &tp2}};
CHECK_EQ(first(&tp3), &nilType);
}
TEST_CASE_FIXTURE(TypePackFixture, "iterate_over_TypePack")
{
TypePackId typePack = newTypePack({types[0], types[1]}, std::nullopt);
std::vector<TypeId> res;
for (TypeId t : typePack)
res.push_back(t);
REQUIRE_EQ(2, res.size());
}
TEST_CASE_FIXTURE(TypePackFixture, "iterate_over_TypePack_with_2_links")
{
auto typePack1 = newTypePack({types[0], types[1]}, std::nullopt);
auto typePack2 = newTypePack({types[0], types[3]}, typePack1);
std::vector<TypeId> result;
for (TypeId ty : typePack2)
result.push_back(ty);
REQUIRE_EQ(4, result.size());
CHECK_EQ(types[0], result[0]);
CHECK_EQ(types[3], result[1]);
CHECK_EQ(types[0], result[2]);
CHECK_EQ(types[1], result[3]);
}
TEST_CASE_FIXTURE(TypePackFixture, "get_the_tail")
{
TypePackId freeTail = freshTypePack();
TypePackId typePack = newTypePack({types[0]}, freeTail);
auto it = begin(typePack);
auto endIt = end(typePack);
int count = 0;
while (it != endIt)
++count, ++it;
REQUIRE_EQ(1, count);
CHECK(it == end(typePack));
REQUIRE_EQ(it.tail(), freeTail);
}
TEST_CASE_FIXTURE(TypePackFixture, "tail_can_be_nullopt")
{
TypePackId typePack = newTypePack({types[0], types[0]}, std::nullopt);
auto it = end(typePack);
REQUIRE_EQ(std::nullopt, it.tail());
}
TEST_CASE_FIXTURE(TypePackFixture, "tail_is_end_for_free_TypePack")
{
TypePackId typePack = freshTypePack();
auto it = begin(typePack);
auto endIt = end(typePack);
while (it != endIt)
++it;
REQUIRE_EQ(typePack, it.tail());
}
TEST_CASE_FIXTURE(TypePackFixture, "skip_over_empty_head_typepack_with_tail")
{
TypePackId tailTP = newTypePack({types[2], types[3]}, std::nullopt);
TypePackId headTP = newTypePack({}, tailTP);
int count = 0;
for (TypeId ty : headTP)
{
(void)ty;
++count;
}
CHECK_EQ(2, count);
}
TEST_CASE_FIXTURE(TypePackFixture, "skip_over_empty_middle_link")
{
TypePackId tailTP = newTypePack({types[2], types[3]}, std::nullopt);
TypePackId middleTP = newTypePack({}, tailTP);
TypePackId headTP = newTypePack({types[0], types[1]}, middleTP);
int count = 0;
for (TypeId ty : headTP)
{
(void)ty;
++count;
}
CHECK_EQ(4, count);
}
TEST_CASE_FIXTURE(TypePackFixture, "follows_Bound_TypePacks")
{
TypePackId tailTP = newTypePack({types[2], types[3]}, std::nullopt);
TypePackId middleTP = freshTypePack();
*asMutable(middleTP) = Unifiable::Bound(tailTP);
TypePackId headTP = newTypePack({}, middleTP);
int count = 0;
for (TypeId ty : headTP)
{
(void)ty;
++count;
}
CHECK_EQ(2, count);
}
TEST_CASE_FIXTURE(TypePackFixture, "post_and_pre_increment")
{
TypePackId typePack = newTypePack({types[0], types[1], types[2], types[3]}, std::nullopt);
auto it1 = begin(typePack);
auto it2 = it1++;
auto it3 = ++it2;
CHECK_EQ(*it2, *it3);
}
TEST_CASE_FIXTURE(TypePackFixture, "std_distance")
{
TypePackId typePack = newTypePack({types[0], types[1], types[2], types[3]}, std::nullopt);
auto b = begin(typePack);
auto e = end(typePack);
CHECK_EQ(4, std::distance(b, e));
}
TEST_CASE("content_reassignment")
{
TypePackVar myError{Unifiable::Error{}, /*presistent*/ true};
TypeArena arena;
TypePackId futureError = arena.addTypePack(TypePackVar{FreeTypePack{TypeLevel{}}});
asMutable(futureError)->reassign(myError);
CHECK(get<ErrorType>(futureError) != nullptr);
CHECK(!futureError->persistent);
CHECK(futureError->owningArena == &arena);
}
TEST_SUITE_END();