Merge branch 'master' into merge

This commit is contained in:
Hunter Goldstein 2024-11-08 11:33:48 -08:00
commit 4399b17f95
8 changed files with 113 additions and 31 deletions

View File

@ -34,6 +34,8 @@ LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
LUAU_FASTFLAG(LuauTypestateBuiltins2)
LUAU_FASTFLAGVARIABLE(LuauNewSolverVisitErrorExprLvalues)
LUAU_FASTFLAGVARIABLE(LuauNewSolverPrePopulateClasses)
LUAU_FASTFLAGVARIABLE(LuauNewSolverPopulateTableLocations)
namespace Luau
{
@ -653,6 +655,7 @@ void ConstraintGenerator::applyRefinements(const ScopePtr& scope, Location locat
void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* block)
{
std::unordered_map<Name, Location> aliasDefinitionLocations;
std::unordered_map<Name, Location> classDefinitionLocations;
// In order to enable mutually-recursive type aliases, we need to
// populate the type bindings before we actually check any of the
@ -750,6 +753,32 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
scope->privateTypeBindings[function->name.value] = std::move(typeFunction);
aliasDefinitionLocations[function->name.value] = function->location;
}
else if (auto classDeclaration = stat->as<AstStatDeclareClass>())
{
if (!FFlag::LuauNewSolverPrePopulateClasses)
continue;
if (scope->exportedTypeBindings.count(classDeclaration->name.value))
{
auto it = classDefinitionLocations.find(classDeclaration->name.value);
LUAU_ASSERT(it != classDefinitionLocations.end());
reportError(classDeclaration->location, DuplicateTypeDefinition{classDeclaration->name.value, it->second});
continue;
}
// A class might have no name if the code is syntactically
// illegal. We mustn't prepopulate anything in this case.
if (classDeclaration->name == kParseNameError)
continue;
ScopePtr defnScope = childScope(classDeclaration, scope);
TypeId initialType = arena->addType(BlockedType{});
TypeFun initialFun{initialType};
scope->exportedTypeBindings[classDeclaration->name.value] = std::move(initialFun);
classDefinitionLocations[classDeclaration->name.value] = classDeclaration->location;
}
}
}
@ -1645,6 +1674,11 @@ static bool isMetamethod(const Name& name)
ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClass* declaredClass)
{
// If a class with the same name was already defined, we skip over
auto bindingIt = scope->exportedTypeBindings.find(declaredClass->name.value);
if (FFlag::LuauNewSolverPrePopulateClasses && bindingIt == scope->exportedTypeBindings.end())
return ControlFlow::None;
std::optional<TypeId> superTy = std::make_optional(builtinTypes->classType);
if (declaredClass->superName)
{
@ -1659,7 +1693,10 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas
// We don't have generic classes, so this assertion _should_ never be hit.
LUAU_ASSERT(lookupType->typeParams.size() == 0 && lookupType->typePackParams.size() == 0);
superTy = lookupType->type;
if (FFlag::LuauNewSolverPrePopulateClasses)
superTy = follow(lookupType->type);
else
superTy = lookupType->type;
if (!get<ClassType>(follow(*superTy)))
{
@ -1682,7 +1719,14 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas
ctv->metatable = metaTy;
scope->exportedTypeBindings[className] = TypeFun{{}, classTy};
if (FFlag::LuauNewSolverPrePopulateClasses)
{
TypeId classBindTy = bindingIt->second.type;
emplaceType<BoundType>(asMutable(classBindTy), classTy);
}
else
scope->exportedTypeBindings[className] = TypeFun{{}, classTy};
if (declaredClass->indexer)
{
@ -2844,6 +2888,10 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr,
ttv->state = TableState::Unsealed;
ttv->definitionModuleName = module->name;
if (FFlag::LuauNewSolverPopulateTableLocations)
{
ttv->definitionLocation = expr->location;
}
ttv->scope = scope.get();
interiorTypes.back().push_back(ty);
@ -3301,7 +3349,16 @@ TypeId ConstraintGenerator::resolveTableType(const ScopePtr& scope, AstType* ty,
ice->ice("Unexpected property access " + std::to_string(int(astIndexer->access)));
}
return arena->addType(TableType{props, indexer, scope->level, scope.get(), TableState::Sealed});
TypeId tableTy = arena->addType(TableType{props, indexer, scope->level, scope.get(), TableState::Sealed});
TableType* ttv = getMutable<TableType>(tableTy);
if (FFlag::LuauNewSolverPopulateTableLocations)
{
ttv->definitionModuleName = module->name;
ttv->definitionLocation = tab->location;
}
return tableTy;
}
TypeId ConstraintGenerator::resolveFunctionType(

View File

@ -33,6 +33,7 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings)
LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500)
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
LUAU_FASTFLAGVARIABLE(LuauRemoveNotAnyHack)
LUAU_FASTFLAG(LuauNewSolverPopulateTableLocations)
namespace Luau
{
@ -1109,9 +1110,15 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
target = follow(instantiated);
}
if (FFlag::LuauNewSolverPopulateTableLocations)
{
// This is a new type - redefine the location.
ttv->definitionLocation = constraint->location;
ttv->definitionModuleName = currentModuleName;
}
ttv->instantiatedTypeParams = typeArguments;
ttv->instantiatedTypePackParams = packArguments;
// TODO: Fill in definitionModuleName.
}
bindResult(target);
@ -1433,7 +1440,8 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<con
}
}
}
else if (expr->is<AstExprConstantBool>() || expr->is<AstExprConstantString>() || expr->is<AstExprConstantNumber>() || expr->is<AstExprConstantNil>())
else if (expr->is<AstExprConstantBool>() || expr->is<AstExprConstantString>() || expr->is<AstExprConstantNumber>() ||
expr->is<AstExprConstantNil>())
{
Unifier2 u2{arena, builtinTypes, constraint->scope, NotNull{&iceReporter}};
u2.unify(actualArgTy, expectedArgTy);
@ -2326,12 +2334,7 @@ bool ConstraintSolver::tryDispatchIterableTable(TypeId iteratorTy, const Iterabl
return true;
}
bool ConstraintSolver::tryDispatchIterableFunction(
TypeId nextTy,
TypeId tableTy,
const IterableConstraint& c,
NotNull<const Constraint> constraint
)
bool ConstraintSolver::tryDispatchIterableFunction(TypeId nextTy, TypeId tableTy, const IterableConstraint& c, NotNull<const Constraint> constraint)
{
const FunctionType* nextFn = get<FunctionType>(nextTy);
// If this does not hold, we should've never called `tryDispatchIterableFunction` in the first place.

View File

@ -8,7 +8,7 @@ Some questions help improve the language, implementation or documentation by ins
## Documentation
A [separate site repository](https://github.com/luau-lang/site) hosts the language documentation, which is accessible on https://luau-lang.org.
A [separate site repository](https://github.com/luau-lang/site) hosts the language documentation, which is accessible on https://luau.org.
Changes to this documentation that improve clarity, fix grammatical issues, explain aspects that haven't been explained before and the like are warmly welcomed.
Please feel free to [create a pull request](https://help.github.com/articles/about-pull-requests/) to improve our documentation. Note that at this point the documentation is English-only.

View File

@ -15,7 +15,7 @@ struct HotComment;
struct LintWarning
{
// Make sure any new lint codes are documented here: https://luau-lang.org/lint
// Make sure any new lint codes are documented here: https://luau.org/lint
// Note that in Studio, the active set of lint warnings is determined by FStringStudioLuauLints
enum Code
{

View File

@ -3,11 +3,11 @@ Luau ![CI](https://github.com/luau-lang/luau/actions/workflows/build.yml/badge.s
Luau (lowercase u, /ˈlu.aʊ/) is a fast, small, safe, gradually typed embeddable scripting language derived from [Lua](https://lua.org).
It is designed to be backwards compatible with Lua 5.1, as well as incorporating [some features](https://luau-lang.org/compatibility) from future Lua releases, but also expands the feature set (most notably with type annotations). Luau is largely implemented from scratch, with the language runtime being a very heavily modified version of Lua 5.1 runtime, with completely rewritten interpreter and other [performance innovations](https://luau-lang.org/performance). The runtime mostly preserves Lua 5.1 API, so existing bindings should be more or less compatible with a few caveats.
It is designed to be backwards compatible with Lua 5.1, as well as incorporating [some features](https://luau.org/compatibility) from future Lua releases, but also expands the feature set (most notably with type annotations). Luau is largely implemented from scratch, with the language runtime being a very heavily modified version of Lua 5.1 runtime, with completely rewritten interpreter and other [performance innovations](https://luau.org/performance). The runtime mostly preserves Lua 5.1 API, so existing bindings should be more or less compatible with a few caveats.
Luau is used by Roblox game developers to write game code, as well as by Roblox engineers to implement large parts of the user-facing application code as well as portions of the editor (Roblox Studio) as plugins. Roblox chose to open-source Luau to foster collaboration within the Roblox community as well as to allow other companies and communities to benefit from the ongoing language and runtime innovation. As a consequence, Luau is now also used by games like Alan Wake 2 and Warframe.
This repository hosts source code for the language implementation and associated tooling. Documentation for the language is available at https://luau-lang.org/ and accepts contributions via [site repository](https://github.com/luau-lang/site); the language is evolved through RFCs that are located in [rfcs repository](https://github.com/luau-lang/rfcs).
This repository hosts source code for the language implementation and associated tooling. Documentation for the language is available at https://luau.org/ and accepts contributions via [site repository](https://github.com/luau-lang/site); the language is evolved through RFCs that are located in [rfcs repository](https://github.com/luau-lang/rfcs).
# Usage
@ -15,7 +15,7 @@ Luau is an embeddable language, but it also comes with two command-line tools by
`luau` is a command-line REPL and can also run input files. Note that REPL runs in a sandboxed environment and as such doesn't have access to the underlying file system except for ability to `require` modules.
`luau-analyze` is a command-line type checker and linter; given a set of input files, it produces errors/warnings according to the file configuration, which can be customized by using `--!` comments in the files or [`.luaurc`](https://rfcs.luau-lang.org/config-luaurc) files. For details please refer to [type checking]( https://luau-lang.org/typecheck) and [linting](https://luau-lang.org/lint) documentation.
`luau-analyze` is a command-line type checker and linter; given a set of input files, it produces errors/warnings according to the file configuration, which can be customized by using `--!` comments in the files or [`.luaurc`](https://rfcs.luau.org/config-luaurc) files. For details please refer to [type checking]( https://luau.org/typecheck) and [linting](https://luau.org/lint) documentation.
# Installation
@ -28,7 +28,7 @@ Alternatively, you can use one of the packaged distributions (note that these ar
- Alpine Linux: [Enable community repositories](https://wiki.alpinelinux.org/w/index.php?title=Enable_Community_Repository) and run `apk add luau`
- Gentoo Linux: Luau is [officially packaged by Gentoo](https://packages.gentoo.org/packages/dev-lang/luau) and can be installed using `emerge dev-lang/luau`. You may have to unmask the package first before installing it (which can be done by including the `--autounmask=y` option in the `emerge` command).
After installing, you will want to validate the installation was successful by running the test case [here](https://luau-lang.org/getting-started).
After installing, you will want to validate the installation was successful by running the test case [here](https://luau.org/getting-started).
## Building

View File

@ -40,7 +40,7 @@ const char* lua_ident = "$Lua: Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Ri
"$URL: www.lua.org $\n";
const char* luau_ident = "$Luau: Copyright (C) 2019-2023 Roblox Corporation $\n"
"$URL: luau-lang.org $\n";
"$URL: luau.org $\n";
#define api_checknelems(L, n) api_check(L, (n) <= (L->top - L->base))

View File

@ -9,6 +9,8 @@
using namespace Luau;
LUAU_FASTFLAG(LuauNewSolverPrePopulateClasses)
TEST_SUITE_BEGIN("DefinitionTests");
TEST_CASE_FIXTURE(Fixture, "definition_file_simple")
@ -492,11 +494,8 @@ TEST_CASE_FIXTURE(Fixture, "class_definition_indexer")
TEST_CASE_FIXTURE(Fixture, "class_definitions_reference_other_classes")
{
unfreeze(frontend.globals.globalTypes);
LoadDefinitionFileResult result = frontend.loadDefinitionFile(
frontend.globals,
frontend.globals.globalScope,
R"(
ScopedFastFlag _{FFlag::LuauNewSolverPrePopulateClasses, true};
loadDefinition(R"(
declare class Channel
Messages: { Message }
OnMessage: (message: Message) -> ()
@ -506,13 +505,19 @@ TEST_CASE_FIXTURE(Fixture, "class_definitions_reference_other_classes")
Text: string
Channel: Channel
end
)",
"@test",
/* captureComments */ false
);
freeze(frontend.globals.globalTypes);
)");
REQUIRE(result.success);
CheckResult result = check(R"(
local a: Channel
local b = a.Messages[1]
local c = b.Channel
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ(toString(requireType("a")), "Channel");
CHECK_EQ(toString(requireType("b")), "Message");
CHECK_EQ(toString(requireType("c")), "Channel");
}
TEST_CASE_FIXTURE(Fixture, "definition_file_has_source_module_name_set")

View File

@ -14,6 +14,7 @@ LUAU_FASTFLAG(LuauInstantiateInSubtyping)
LUAU_FASTFLAG(LuauRequireCyclesDontAlwaysReturnAny)
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauTypestateBuiltins2)
LUAU_FASTFLAG(LuauNewSolverPopulateTableLocations)
using namespace Luau;
@ -466,7 +467,15 @@ local b: B.T = a
LUAU_REQUIRE_ERROR_COUNT(1, result);
if (FFlag::LuauSolverV2)
CHECK(toString(result.errors.at(0)) == "Type 'T' could not be converted into 'T'; at [read \"x\"], number is not exactly string");
{
if (FFlag::LuauNewSolverPopulateTableLocations)
CHECK(
toString(result.errors.at(0)) ==
"Type 'T' from 'game/A' could not be converted into 'T' from 'game/B'; at [read \"x\"], number is not exactly string"
);
else
CHECK(toString(result.errors.at(0)) == "Type 'T' could not be converted into 'T'; at [read \"x\"], number is not exactly string");
}
else
{
const std::string expected = R"(Type 'T' from 'game/A' could not be converted into 'T' from 'game/B'
@ -507,7 +516,15 @@ local b: B.T = a
LUAU_REQUIRE_ERROR_COUNT(1, result);
if (FFlag::LuauSolverV2)
CHECK(toString(result.errors.at(0)) == "Type 'T' could not be converted into 'T'; at [read \"x\"], number is not exactly string");
{
if (FFlag::LuauNewSolverPopulateTableLocations)
CHECK(
toString(result.errors.at(0)) ==
"Type 'T' from 'game/B' could not be converted into 'T' from 'game/C'; at [read \"x\"], number is not exactly string"
);
else
CHECK(toString(result.errors.at(0)) == "Type 'T' could not be converted into 'T'; at [read \"x\"], number is not exactly string");
}
else
{
const std::string expected = R"(Type 'T' from 'game/B' could not be converted into 'T' from 'game/C'