Sync to upstream/release/643 (#1408)

Pretty small release this week as well.

## New Solver
* We now unconditionally generalize functions with explicit generics
* Bugfixes for how we run builtin tests

## VM
* Fixed running Luau conformance tests in LUA_VECTOR_SIZE == 4
configuration

Internal Contributors:

Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: James McNellis <jmcnellis@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Junseo Yoo <jyoo@roblox.com>
This commit is contained in:
Vighnesh-V 2024-09-13 11:48:25 -07:00 committed by GitHub
parent f2488bdfa4
commit b765d7bafd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 132 additions and 29 deletions

View File

@ -887,9 +887,6 @@ std::optional<TypeId> generalize(
if (ty->owningArena != arena || ty->persistent)
return ty;
if (const FunctionType* ft = get<FunctionType>(ty); ft && (!ft->generics.empty() || !ft->genericPacks.empty()))
return ty;
FreeTypeSearcher fts{scope, cachedTypes};
fts.traverse(ty);
@ -912,8 +909,17 @@ std::optional<TypeId> generalize(
FunctionType* ftv = getMutable<FunctionType>(ty);
if (ftv)
{
ftv->generics = std::move(gen.generics);
ftv->genericPacks = std::move(gen.genericPacks);
// If we're generalizing a function type, add any of the newly inferred
// generics to the list of existing generic types.
for (const auto g : std::move(gen.generics))
{
ftv->generics.push_back(g);
}
// Ditto for generic packs.
for (const auto gp : std::move(gen.genericPacks))
{
ftv->genericPacks.push_back(gp);
}
}
return ty;

View File

@ -107,6 +107,7 @@ std::optional<ErrorVec> OverloadResolver::testIsSubtype(const Location& location
case ErrorSuppression::NormalizationFailed:
errors.emplace_back(location, NormalizationTooComplex{});
// intentionally fallthrough here since we couldn't prove this was error-suppressing
[[fallthrough]];
case ErrorSuppression::DoNotSuppress:
errors.emplace_back(location, TypeMismatch{superTy, subTy});
break;
@ -136,6 +137,7 @@ std::optional<ErrorVec> OverloadResolver::testIsSubtype(const Location& location
case ErrorSuppression::NormalizationFailed:
errors.emplace_back(location, NormalizationTooComplex{});
// intentionally fallthrough here since we couldn't prove this was error-suppressing
[[fallthrough]];
case ErrorSuppression::DoNotSuppress:
errors.emplace_back(location, TypePackMismatch{superTy, subTy});
break;
@ -301,6 +303,7 @@ std::pair<OverloadResolver::Analysis, ErrorVec> OverloadResolver::checkOverload_
case ErrorSuppression::NormalizationFailed:
errors.emplace_back(argLocation, NormalizationTooComplex{});
// intentionally fallthrough here since we couldn't prove this was error-suppressing
[[fallthrough]];
case ErrorSuppression::DoNotSuppress:
// TODO extract location from the SubtypingResult path and argExprs
switch (reason.variance)

View File

@ -141,6 +141,7 @@ Relation combine(Relation a, Relation b)
case Relation::Superset:
return Relation::Intersects;
}
break;
case Relation::Coincident:
switch (b)
{
@ -155,6 +156,7 @@ Relation combine(Relation a, Relation b)
case Relation::Superset:
return Relation::Intersects;
}
break;
case Relation::Superset:
switch (b)
{
@ -169,6 +171,7 @@ Relation combine(Relation a, Relation b)
case Relation::Superset:
return Relation::Superset;
}
break;
case Relation::Subset:
switch (b)
{
@ -183,6 +186,7 @@ Relation combine(Relation a, Relation b)
case Relation::Superset:
return Relation::Intersects;
}
break;
case Relation::Intersects:
switch (b)
{
@ -197,6 +201,7 @@ Relation combine(Relation a, Relation b)
case Relation::Superset:
return Relation::Intersects;
}
break;
}
LUAU_UNREACHABLE();

View File

@ -1378,7 +1378,7 @@ void TypeChecker2::visitCall(AstExprCall* call)
break;
case ErrorSuppression::NormalizationFailed:
reportError(NormalizationTooComplex{}, call->func->location);
// fallthrough intentional
[[fallthrough]];
case ErrorSuppression::DoNotSuppress:
reportError(OptionalValueAccess{fnTy}, call->func->location);
}
@ -1582,7 +1582,7 @@ TypeId TypeChecker2::stripFromNilAndReport(TypeId ty, const Location& location)
break;
case ErrorSuppression::NormalizationFailed:
reportError(NormalizationTooComplex{}, location);
// fallthrough intentional
[[fallthrough]];
case ErrorSuppression::DoNotSuppress:
reportError(OptionalValueAccess{ty}, location);
}
@ -1666,7 +1666,7 @@ void TypeChecker2::visit(AstExprIndexExpr* indexExpr, ValueContext context)
break;
case ErrorSuppression::NormalizationFailed:
reportError(NormalizationTooComplex{}, indexExpr->location);
// fallthrough intentional
[[fallthrough]];
case ErrorSuppression::DoNotSuppress:
reportError(OptionalValueAccess{exprType}, indexExpr->location);
}
@ -2723,6 +2723,7 @@ void TypeChecker2::explainError(TypeId subTy, TypeId superTy, Location location,
return;
case ErrorSuppression::NormalizationFailed:
reportError(NormalizationTooComplex{}, location);
break;
case ErrorSuppression::DoNotSuppress:
break;
}
@ -2741,6 +2742,7 @@ void TypeChecker2::explainError(TypePackId subTy, TypePackId superTy, Location l
return;
case ErrorSuppression::NormalizationFailed:
reportError(NormalizationTooComplex{}, location);
break;
case ErrorSuppression::DoNotSuppress:
break;
}

View File

@ -2751,7 +2751,7 @@ TypeId TypeChecker::checkRelationalOperation(
if (lhsIsAny || rhsIsAny)
return booleanType;
// Fallthrough here is intentional
[[fallthrough]];
}
case AstExprBinary::CompareLt:
case AstExprBinary::CompareGt:

View File

@ -784,6 +784,7 @@ AstStat* Parser::parseAttributeStat()
AstExpr* expr = parsePrimaryExpr(/* asStatement= */ true);
return parseDeclaration(expr->location, attributes);
}
[[fallthrough]];
default:
return reportStatError(
lexer.current().location,

View File

@ -204,7 +204,18 @@ void RequireResolver::substituteAliasIfPresent(std::string& path)
{
if (path.size() < 1 || path[0] != '@')
return;
std::string potentialAlias = path.substr(1, path.find_first_of("\\/"));
// To ignore the '@' alias prefix when processing the alias
const size_t aliasStartPos = 1;
// If a directory separator was found, the length of the alias is the
// distance between the start of the alias and the separator. Otherwise,
// the whole string after the alias symbol is the alias.
size_t aliasLen = path.find_first_of("\\/");
if (aliasLen != std::string::npos)
aliasLen -= aliasStartPos;
const std::string potentialAlias = path.substr(aliasStartPos, aliasLen);
// Not worth searching when potentialAlias cannot be an alias
if (!Luau::isValidAlias(potentialAlias))

View File

@ -111,6 +111,7 @@ if(MSVC)
list(APPEND LUAU_OPTIONS "/we4388") # Also signed/unsigned mismatch
else()
list(APPEND LUAU_OPTIONS -Wall) # All warnings
list(APPEND LUAU_OPTIONS -Wimplicit-fallthrough)
list(APPEND LUAU_OPTIONS -Wsign-compare) # This looks to be included in -Wall for GCC but not clang
endif()

View File

@ -20,6 +20,19 @@
#define LUAU_DEBUGBREAK() __builtin_trap()
#endif
// LUAU_FALLTHROUGH is a C++11-compatible alternative to [[fallthrough]] for use in the VM library
#if defined(__clang__) && defined(__has_warning)
#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough")
#define LUAU_FALLTHROUGH [[clang::fallthrough]]
#else
#define LUAU_FALLTHROUGH
#endif
#elif defined(__GNUC__) && __GNUC__ >= 7
#define LUAU_FALLTHROUGH [[gnu::fallthrough]]
#else
#define LUAU_FALLTHROUGH
#endif
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define LUAU_BIG_ENDIAN
#endif

View File

@ -552,9 +552,9 @@ init: // using goto's to optimize tail recursion
}
break;
}
case '+': // 1 or more repetitions
s++; // 1 match already done
// go through
case '+': // 1 or more repetitions
s++; // 1 match already done
LUAU_FALLTHROUGH; // go through
case '*': // 0 or more repetitions
s = max_expand(ms, s, p, ep);
break;
@ -1480,7 +1480,8 @@ static int str_pack(lua_State* L)
break;
}
case Kpadding:
luaL_addchar(&b, LUAL_PACKPADBYTE); // FALLTHROUGH
luaL_addchar(&b, LUAL_PACKPADBYTE);
LUAU_FALLTHROUGH;
case Kpaddalign:
case Knop:
arg--; // undo increment

View File

@ -659,7 +659,7 @@ const TValue* luaH_get(Table* t, const TValue* key)
luai_num2int(k, n);
if (luai_numeq(cast_num(k), nvalue(key))) // index is int?
return luaH_getnum(t, k); // use specialized version
// else go through
LUAU_FALLTHROUGH; // else go through
}
default:
{

View File

@ -133,7 +133,12 @@ static int lua_vector_cross(lua_State* L)
const float* a = luaL_checkvector(L, 1);
const float* b = luaL_checkvector(L, 2);
#if LUA_VECTOR_SIZE == 4
lua_pushvector(L, a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0], 0.0f);
#else
lua_pushvector(L, a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]);
#endif
return 1;
}
@ -144,15 +149,25 @@ static int lua_vector_index(lua_State* L)
if (strcmp(name, "Magnitude") == 0)
{
#if LUA_VECTOR_SIZE == 4
lua_pushnumber(L, sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3]));
#else
lua_pushnumber(L, sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]));
#endif
return 1;
}
if (strcmp(name, "Unit") == 0)
{
#if LUA_VECTOR_SIZE == 4
float invSqrt = 1.0f / sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3]);
lua_pushvector(L, v[0] * invSqrt, v[1] * invSqrt, v[2] * invSqrt, v[3] * invSqrt);
#else
float invSqrt = 1.0f / sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
lua_pushvector(L, v[0] * invSqrt, v[1] * invSqrt, v[2] * invSqrt);
#endif
return 1;
}

View File

@ -3,6 +3,7 @@
#include "Luau/AstQuery.h"
#include "Luau/BuiltinDefinitions.h"
#include "Luau/Common.h"
#include "Luau/Constraint.h"
#include "Luau/ModuleResolver.h"
#include "Luau/NotNull.h"
@ -25,6 +26,7 @@ static const char* mainModuleName = "MainModule";
LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTFLAG(DebugLuauFreezeArena);
LUAU_FASTFLAG(DebugLuauLogSolverToJsonFile)
LUAU_FASTFLAG(LuauDCRMagicFunctionTypeChecker);
extern std::optional<unsigned> randomSeed; // tests/main.cpp
@ -150,6 +152,8 @@ const Config& TestConfigResolver::getConfig(const ModuleName& name) const
Fixture::Fixture(bool freeze, bool prepareAutocomplete)
: sff_DebugLuauFreezeArena(FFlag::DebugLuauFreezeArena, freeze)
// In tests, we *always* want to register the extra magic functions for typechecking `string.format`.
, sff_LuauDCRMagicFunctionTypeChecker(FFlag::LuauDCRMagicFunctionTypeChecker, true)
, frontend(
&fileResolver,
&configResolver,

View File

@ -99,6 +99,7 @@ struct Fixture
TypeId requireExportedType(const ModuleName& moduleName, const std::string& name);
ScopedFastFlag sff_DebugLuauFreezeArena;
ScopedFastFlag sff_LuauDCRMagicFunctionTypeChecker;
TestFileResolver fileResolver;
TestConfigResolver configResolver;

View File

@ -937,6 +937,7 @@ bb_bytecode_0:
);
}
#if LUA_VECTOR_SIZE == 3
TEST_CASE("FastcallTypeInferThroughLocal")
{
CHECK_EQ(
@ -1045,6 +1046,7 @@ bb_bytecode_1:
)"
);
}
#endif
TEST_CASE("LoadAndMoveTypePropagation")
{
@ -1115,6 +1117,7 @@ bb_bytecode_4:
);
}
#if LUA_VECTOR_SIZE == 3
TEST_CASE("ArgumentTypeRefinement")
{
CHECK_EQ(
@ -1152,6 +1155,7 @@ bb_bytecode_0:
)"
);
}
#endif
TEST_CASE("InlineFunctionType")
{
@ -1422,6 +1426,7 @@ bb_2:
);
}
#if LUA_VECTOR_SIZE == 3
TEST_CASE("UnaryTypeResolve")
{
CHECK_EQ(
@ -1443,6 +1448,7 @@ end
)"
);
}
#endif
TEST_CASE("ForInManualAnnotation")
{

View File

@ -436,6 +436,12 @@ TEST_CASE_FIXTURE(ReplWithPathFixture, "RequirePathWithParentAlias")
assertOutputContainsAll({"true", "result from other_dependency"});
}
TEST_CASE_FIXTURE(ReplWithPathFixture, "RequirePathWithAliasPointingToDirectory")
{
std::string path = getLuauDirectory(PathType::Relative) + "/tests/require/with_config/src/directory_alias_requirer";
runProtectedRequire(path);
assertOutputContainsAll({"true", "result from subdirectory_dependency"});
}
TEST_CASE_FIXTURE(ReplWithPathFixture, "RequireAliasThatDoesNotExist")
{

View File

@ -843,7 +843,7 @@ TEST_CASE_FIXTURE(Fixture, "pick_distinct_names_for_mixed_explicit_and_implicit_
if (FFlag::LuauSolverV2)
{
CHECK("<a>(a, 'b) -> ()" == toString(requireType("foo")));
CHECK("<a>(a, unknown) -> ()" == toString(requireType("foo")));
}
else
CHECK("<a, b>(a, b) -> ()" == toString(requireType("foo")));

View File

@ -9,7 +9,6 @@
using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTFLAG(LuauDCRMagicFunctionTypeChecker);
TEST_SUITE_BEGIN("BuiltinTests");
@ -793,8 +792,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "select_with_variadic_typepack_tail_and_strin
TEST_CASE_FIXTURE(Fixture, "string_format_as_method")
{
ScopedFastFlag sff{FFlag::LuauDCRMagicFunctionTypeChecker, true};
CheckResult result = check("local _ = ('%s'):format(5)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
@ -807,7 +804,6 @@ TEST_CASE_FIXTURE(Fixture, "string_format_as_method")
TEST_CASE_FIXTURE(Fixture, "string_format_use_correct_argument")
{
ScopedFastFlag sff{FFlag::LuauDCRMagicFunctionTypeChecker, true};
CheckResult result = check(R"(
local _ = ("%s"):format("%d", "hello")
)");
@ -819,7 +815,6 @@ TEST_CASE_FIXTURE(Fixture, "string_format_use_correct_argument")
TEST_CASE_FIXTURE(Fixture, "string_format_use_correct_argument2")
{
ScopedFastFlag sff{FFlag::LuauDCRMagicFunctionTypeChecker, true};
CheckResult result = check(R"(
local _ = ("%s %d").format("%d %s", "A type error", 2)
)");
@ -878,7 +873,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "debug_info_is_crazy")
TEST_CASE_FIXTURE(BuiltinsFixture, "aliased_string_format")
{
ScopedFastFlag sff{FFlag::LuauDCRMagicFunctionTypeChecker, true};
CheckResult result = check(R"(
local fmt = string.format
local s = fmt("%d", "oops")
@ -938,7 +932,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "select_on_variadic")
TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_report_all_type_errors_at_correct_positions")
{
ScopedFastFlag sff{FFlag::LuauDCRMagicFunctionTypeChecker, true};
CheckResult result = check(R"(
("%s%d%s"):format(1, "hello", true)
string.format("%s%d%s", 1, "hello", true)

View File

@ -7,6 +7,7 @@
#include "Fixture.h"
#include "ScopedFlags.h"
#include "doctest.h"
LUAU_FASTFLAG(LuauInstantiateInSubtyping);
@ -1419,10 +1420,19 @@ TEST_CASE_FIXTURE(Fixture, "apply_type_function_nested_generics3")
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(Fixture, "quantify_functions_with_no_generics")
{
CheckResult result = check(R"(
function foo(f, x)
return f(x)
end
)");
CHECK("<a, b...>((a) -> (b...), a) -> (b...)" == toString(requireType("foo")));
}
TEST_CASE_FIXTURE(Fixture, "quantify_functions_even_if_they_have_an_explicit_generic")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
CheckResult result = check(R"(
function foo<X>(f, x: X)
return f(x)
@ -1432,6 +1442,17 @@ TEST_CASE_FIXTURE(Fixture, "quantify_functions_even_if_they_have_an_explicit_gen
CHECK("<X, a...>((X) -> (a...), X) -> (a...)" == toString(requireType("foo")));
}
TEST_CASE_FIXTURE(Fixture, "no_extra_quantification_for_generic_functions")
{
CheckResult result = check(R"(
function foo<X, Y>(f : (X) -> Y, x: X)
return f(x)
end
)");
CHECK("<X, Y>((X) -> Y, X) -> Y" == toString(requireType("foo")));
}
TEST_CASE_FIXTURE(Fixture, "do_not_always_instantiate_generic_intersection_types")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
@ -1535,6 +1556,19 @@ TEST_CASE_FIXTURE(Fixture, "missing_generic_type_parameter")
REQUIRE(get<UnknownSymbol>(result.errors[1]));
}
TEST_CASE_FIXTURE(Fixture, "generic_implicit_explicit_name_clash")
{
ScopedFastFlag _{FFlag::LuauSolverV2, true};
auto result = check(R"(
function apply<a>(func, argument: a)
return func(argument)
end
)");
CHECK("<a, b...>((a) -> (b...), a) -> (b...)" == toString(requireType("apply")));
}
TEST_CASE_FIXTURE(BuiltinsFixture, "generic_type_functions_work_in_subtyping")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};

View File

@ -1310,9 +1310,7 @@ TEST_CASE_FIXTURE(Fixture, "we_cannot_infer_functions_that_return_inconsistently
if (FFlag::LuauSolverV2)
{
LUAU_CHECK_ERROR_COUNT(2, result);
// The second argument should be unknown. CLI-111111
CHECK("<T>({T}, 'b) -> number" == toString(requireType("find_first")));
CHECK("<T>({T}, unknown) -> number" == toString(requireType("find_first")));
}
else
{

View File

@ -1,6 +1,7 @@
{
"paths": ["../ProjectLuauLibraries"],
"aliases": {
"dep": "dependency"
"dep": "dependency",
"subdir": "subdirectory"
}
}

View File

@ -0,0 +1 @@
return(require("@subdir/subdirectory_dependency"))

View File

@ -0,0 +1 @@
return {"result from subdirectory_dependency"}