Skip to content

Commit 4ca11db

Browse files
annieetangvegorov-rbxaatxeandyfriesenhgoldstein
authored
Sync to upstream/release/716 (luau-lang#2339)
Hi everyone! Another week, another release 🌺 🐰 This week we've made some major performance boosts for property accesses, feature fixes, as well as our regular bug fixes and improvements! ### Analysis * `Luau/OverloadResolution.h` has been renamed to `Luau/OverloadResolver.h` (to match the struct name) * Added syntax highlighting for the `const` keyword! * Fixed an internal compiler error that could occur when type checking erroneous uses of `const`, for example: ```luau -- This is not a valid `const` declaration as we require that `const` declarations -- have values, as it is almost certainly a mistake if they don't (as you cannot assign -- to them later). Previously this _also_ caused an ICE in the new solver, which is not -- desirable. const foobar return foobar ``` * Fixed a bug that could result in function calls failing to type check due to ungeneralized free types: ```luau function add(a, b) return a + b end local vec2 = {} function vec2.new(x, y) return setmetatable({ x = x or 0, y = y or 0 }, { __add = function(v1, v2) return { x = v1.x + v2.x, y = v1.y + v2.y } end, }) end -- Prior, this would fail to type check and we'd get warnings -- about ungeneralized types (`number <: 'a` isn't a subtype of blah) local a = add(vec2.new(0, 0), vec2.new(1, 1)) ``` * Fixed a bug where `type(x) == "vector"` always refined `x` to `never`: ```luau local x: unknown if type(x) == "vector" then local y = x -- Prior, x would be `never` end ``` ### Compiler & Runtime * 10-30% performance improvement for Luau userdata property accesses via new property descriptor bytecode caching * Increased precision for `math.noise()` * Fixed a bug where generic `for` loops were incorrectly optimized when the global environment was modified ```luau local env = getfenv(1) env.next = {1, 2, 3} -- This will now disable `LOP_FORGPREP_NEXT` optimization, and run successfully local ok, err = pcall(function() for k, v in next, {} do end end) ``` * Added 64-bit Integer output to the AST Json Encoder, and fixed some bugs from the initial implementation * NCG: fixed an issue where certain fast-call sequences with multiple return values could cause incorrect register tracking * NCG: improved compiler performance by caching register tags and computing them only when consumed by an instruction ### Miscellaneous * Improved `lldb` debugger support by adding visualization for `lua_State`, including a new `set_userdata_type_name` method to configure the debugger for custom `userdata` structures. -------------------------------------- Thank you to all our contributors this week! Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Ilya Rezvov <irezvov@roblox.com> Co-authored-by: Karl Rehm <krehm@roblox.com> Co-authored-by: Simone Guggiari <sguggiari@roblox.com> Co-authored-by: Thomas Schollenberger <tschollenberger@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: @PhoenixWhitefire --------- Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Ariel Weiss <arielweiss@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com> Co-authored-by: Sora Kanosue <skanosue@roblox.com>
1 parent 40d4815 commit 4ca11db

72 files changed

Lines changed: 2164 additions & 674 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
File renamed without changes.

Analysis/include/Luau/TypeUtils.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,4 +414,16 @@ bool containsGeneric(TypePackId ty, NotNull<DenseHashSet<const void*>> generics)
414414
*/
415415
bool isBlocked(TypeId ty);
416416

417+
418+
/**
419+
* **YOU SHOULD PROBABLY NOT USE THIS FUNCTION.**
420+
*
421+
* This function is a stop-gap while we rework function call inference and
422+
* eager generalization.
423+
*
424+
* @return An approximate return type of `ty`, assuming `ty` is a function or
425+
* union of functions.
426+
*/
427+
std::optional<TypePackId> getApproximateReturnTypeForFunctionCall(TypeId ty);
428+
417429
} // namespace Luau

Analysis/src/AstJsonEncoder.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,18 @@ struct AstJsonEncoder : public AstVisitor
314314
);
315315
}
316316

317+
void write(class AstExprConstantInteger* node)
318+
{
319+
writeNode(
320+
node,
321+
"AstExprConstantInteger",
322+
[&]()
323+
{
324+
write("value", node->value);
325+
}
326+
);
327+
}
328+
317329
void write(class AstExprConstantString* node)
318330
{
319331
writeNode(

Analysis/src/BuiltinTypeFunctions.cpp

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#include "Luau/Common.h"
66
#include "Luau/ConstraintSolver.h"
77
#include "Luau/Instantiation.h"
8-
#include "Luau/OverloadResolution.h"
8+
#include "Luau/OverloadResolver.h"
99
#include "Luau/Scope.h"
1010
#include "Luau/Simplify.h"
1111
#include "Luau/Subtyping.h"
@@ -20,7 +20,7 @@
2020
LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit)
2121
LUAU_DYNAMIC_FASTINTVARIABLE(LuauStepRefineRecursionLimit, 64)
2222

23-
LUAU_FASTFLAG(LuauOverloadGetsInstantiated)
23+
LUAU_FASTFLAG(LuauOverloadGetsInstantiated2)
2424
LUAU_FASTFLAGVARIABLE(LuauTypeFunctionsCaptureNestedInstances)
2525
LUAU_FASTFLAGVARIABLE(LuauTypeFunctionsAddFreeTypePackWithPositivePolarity)
2626
LUAU_FASTFLAGVARIABLE(LuauThreadUniferStateThroughTypeFunctionReduction)
@@ -171,20 +171,29 @@ static std::optional<TypePackId> solveFunctionCall(NotNull<TypeFunctionContext>
171171
return std::nullopt;
172172
}
173173

174-
if (!unifier.genericSubstitutions.empty() || !unifier.genericPackSubstitutions.empty())
174+
if (FFlag::LuauOverloadGetsInstantiated2)
175175
{
176-
Subtyping subtyping{ctx->builtins, ctx->arena, ctx->normalizer, ctx->typeFunctionRuntime, ctx->ice};
177-
std::optional<TypePackId> subst = instantiate2(
178-
ctx->arena, std::move(unifier.genericSubstitutions), std::move(unifier.genericPackSubstitutions), NotNull{&subtyping}, ctx->scope, retPack
179-
);
180-
if (!subst)
181-
return std::nullopt;
182-
else
176+
177+
if (!unifier.genericSubstitutions.empty() || !unifier.genericPackSubstitutions.empty())
178+
{
179+
Subtyping subtyping{ctx->builtins, ctx->arena, ctx->normalizer, ctx->typeFunctionRuntime, ctx->ice};
180+
auto newRetTp = getApproximateReturnTypeForFunctionCall(*selected.overload).value_or(ctx->builtins->errorTypePack);
181+
182+
std::optional<TypePackId> subst = instantiate2(
183+
ctx->arena,
184+
std::move(unifier.genericSubstitutions),
185+
std::move(unifier.genericPackSubstitutions),
186+
NotNull{&subtyping},
187+
ctx->scope,
188+
newRetTp
189+
);
190+
191+
if (!subst)
192+
return std::nullopt;
193+
183194
retPack = *subst;
184-
}
195+
}
185196

186-
if (FFlag::LuauOverloadGetsInstantiated)
187-
{
188197
// After we solve for the instantiated function type of this metamethod,
189198
// we may have new free types if the metamethod was generic. We capture
190199
// these so that they can be generalized later and we don't end up with
@@ -195,6 +204,26 @@ static std::optional<TypePackId> solveFunctionCall(NotNull<TypeFunctionContext>
195204
for (const auto& tp : unifier.newFreshTypePacks)
196205
trackInteriorFreeTypePack(ctx->scope, tp);
197206
}
207+
else
208+
{
209+
210+
if (!unifier.genericSubstitutions.empty() || !unifier.genericPackSubstitutions.empty())
211+
{
212+
Subtyping subtyping{ctx->builtins, ctx->arena, ctx->normalizer, ctx->typeFunctionRuntime, ctx->ice};
213+
std::optional<TypePackId> subst = instantiate2(
214+
ctx->arena,
215+
std::move(unifier.genericSubstitutions),
216+
std::move(unifier.genericPackSubstitutions),
217+
NotNull{&subtyping},
218+
ctx->scope,
219+
retPack
220+
);
221+
if (!subst)
222+
return std::nullopt;
223+
else
224+
retPack = *subst;
225+
}
226+
}
198227

199228
return retPack;
200229
}

Analysis/src/ConstraintSolver.cpp

Lines changed: 86 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
#include "Luau/IterativeTypeVisitor.h"
1616
#include "Luau/Location.h"
1717
#include "Luau/ModuleResolver.h"
18-
#include "Luau/OverloadResolution.h"
18+
#include "Luau/OverloadResolver.h"
1919
#include "Luau/RecursionCounter.h"
2020
#include "Luau/ScopedSeenSet.h"
2121
#include "Luau/Simplify.h"
@@ -47,7 +47,7 @@ LUAU_FASTFLAGVARIABLE(LuauUnifyWithSubtyping2)
4747
LUAU_FASTFLAG(LuauRelateHandlesCoincidentTables)
4848
LUAU_FASTFLAG(LuauUnpackRespectsAnnotations)
4949
LUAU_FASTFLAG(LuauReplacerRespectsReboundGenerics)
50-
LUAU_FASTFLAGVARIABLE(LuauOverloadGetsInstantiated)
50+
LUAU_FASTFLAGVARIABLE(LuauOverloadGetsInstantiated2)
5151
LUAU_FASTFLAGVARIABLE(LuauFollowInExplicitInstantiation)
5252
LUAU_FASTFLAGVARIABLE(LuauUseConstraintSetsToTrackFreeTypes)
5353

@@ -1668,26 +1668,24 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
16681668
argsPack = arena->addTypePack(TypePack{{fn}, argsPack});
16691669
}
16701670

1671-
if (!usedMagic)
1671+
1672+
if (FFlag::LuauOverloadGetsInstantiated2)
16721673
{
1673-
emplace<FreeTypePack>(constraint, c.result, constraint->scope, Polarity::Positive);
1674-
trackInteriorFreeTypePack(constraint->scope, c.result);
1675-
}
1674+
TypePackId retTp = arena->freshTypePack(constraint->scope, Polarity::Positive);
1675+
trackInteriorFreeTypePack(constraint->scope, retTp);
16761676

1677-
TypeId inferredTy = arena->addType(FunctionType{TypeLevel{}, argsPack, c.result});
1677+
TypeId inferredTy = arena->addType(FunctionType{TypeLevel{}, argsPack, retTp});
16781678

1679-
Unifier2 u2{NotNull{arena}, builtinTypes, constraint->scope, NotNull{&iceReporter}};
1679+
Unifier2 u2{NotNull{arena}, builtinTypes, constraint->scope, NotNull{&iceReporter}};
16801680

1681-
// TODO: This should probably use ConstraintSolver::unify
1682-
const UnifyResult unifyResult = u2.unify(overloadToUse, inferredTy);
1681+
// TODO: This should probably use ConstraintSolver::unify
1682+
const UnifyResult unifyResult = u2.unify(overloadToUse, inferredTy);
16831683

1684-
for (TypeId freeTy : u2.newFreshTypes)
1685-
trackInteriorFreeType(constraint->scope, freeTy);
1686-
for (TypePackId freeTp : u2.newFreshTypePacks)
1687-
trackInteriorFreeTypePack(constraint->scope, freeTp);
1684+
for (TypeId freeTy : u2.newFreshTypes)
1685+
trackInteriorFreeType(constraint->scope, freeTy);
1686+
for (TypePackId freeTp : u2.newFreshTypePacks)
1687+
trackInteriorFreeTypePack(constraint->scope, freeTp);
16881688

1689-
if (FFlag::LuauOverloadGetsInstantiated)
1690-
{
16911689
if (!u2.genericSubstitutions.empty() || !u2.genericPackSubstitutions.empty())
16921690
{
16931691
Subtyping subtyping{builtinTypes, arena, normalizer, typeFunctionRuntime, NotNull{&iceReporter}};
@@ -1714,77 +1712,62 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
17141712
if (auto ft = get<FreeType>(ty))
17151713
hasBound |= !is<NeverType>(follow(ft->lowerBound)) || !is<UnknownType>(follow(ft->upperBound));
17161714

1717-
if (auto overloadAsFn = get<FunctionType>(overloadToUse))
1715+
// If we have generics we can bind *and*
1716+
if (auto overloadAsFn = get<FunctionType>(overloadToUse); overloadAsFn && hasBound)
17181717
{
1719-
if (hasBound)
1720-
{
1721-
CloneState cs{builtinTypes};
1722-
// We want to clone persistent types here, for example if we try to instantiate
1723-
// `table.insert`
1724-
auto clonedTy = shallowClone(overloadToUse, *arena, cs, true);
1725-
auto clonedFn = getMutable<FunctionType>(clonedTy);
1726-
LUAU_ASSERT(clonedFn);
1727-
clonedFn->generics.clear();
1728-
clonedFn->genericPacks.clear();
1729-
// NOTE: This can be one call!
1730-
if (auto inst = instantiate2(
1731-
arena,
1732-
// Intentional copy, could be by reference.
1733-
std::move(u2.genericSubstitutions),
1734-
// Intentional copy, could be by reference.
1735-
std::move(u2.genericPackSubstitutions),
1736-
NotNull{&subtyping},
1737-
constraint->scope,
1738-
clonedTy
1739-
))
1740-
{
1741-
auto instantiatedFn = get<FunctionType>(inst);
1742-
LUAU_ASSERT(instantiatedFn);
1743-
overloadToUse = *inst;
1744-
result = follow(instantiatedFn->retTypes);
1745-
}
1746-
else
1747-
{
1748-
reportError(CodeTooComplex{}, constraint->location);
1749-
result = builtinTypes->errorTypePack;
1750-
}
1751-
}
1752-
else
1753-
{
1754-
auto tp = instantiate2(
1718+
CloneState cs{builtinTypes};
1719+
// We want to clone persistent types here, for example if we try to instantiate
1720+
// `table.insert`
1721+
auto clonedTy = shallowClone(overloadToUse, *arena, cs, true);
1722+
auto clonedFn = getMutable<FunctionType>(clonedTy);
1723+
LUAU_ASSERT(clonedFn);
1724+
clonedFn->generics.clear();
1725+
clonedFn->genericPacks.clear();
1726+
if (auto inst = instantiate2(
17551727
arena,
1728+
// Intentional copy, could be by reference.
17561729
std::move(u2.genericSubstitutions),
1730+
// Intentional copy, could be by reference.
17571731
std::move(u2.genericPackSubstitutions),
17581732
NotNull{&subtyping},
17591733
constraint->scope,
1760-
overloadAsFn->retTypes
1761-
);
1762-
if (tp)
1763-
result = *tp;
1764-
else
1765-
{
1766-
reportError(CodeTooComplex{}, constraint->location);
1767-
result = builtinTypes->errorTypePack;
1768-
}
1734+
clonedTy
1735+
))
1736+
{
1737+
auto instantiatedFn = get<FunctionType>(inst);
1738+
LUAU_ASSERT(instantiatedFn);
1739+
overloadToUse = *inst;
1740+
retTp = follow(instantiatedFn->retTypes);
1741+
}
1742+
else
1743+
{
1744+
reportError(CodeTooComplex{}, constraint->location);
1745+
result = builtinTypes->errorTypePack;
17691746
}
17701747
}
17711748
else
17721749
{
1750+
auto newRetTp = getApproximateReturnTypeForFunctionCall(overloadToUse)
1751+
.value_or(builtinTypes->errorTypePack);
1752+
17731753
std::optional<TypePackId> subst = instantiate2(
1774-
arena, std::move(u2.genericSubstitutions), std::move(u2.genericPackSubstitutions), NotNull{&subtyping}, constraint->scope, result
1754+
arena,
1755+
std::move(u2.genericSubstitutions),
1756+
std::move(u2.genericPackSubstitutions),
1757+
NotNull{&subtyping},
1758+
constraint->scope,
1759+
newRetTp
17751760
);
1776-
if (!subst)
1777-
{
1778-
reportError(CodeTooComplex{}, constraint->location);
1779-
result = builtinTypes->errorTypePack;
1780-
}
1761+
1762+
if (subst)
1763+
retTp = *subst;
17811764
else
1782-
result = *subst;
1765+
reportError(CodeTooComplex{}, constraint->location);
17831766
}
17841767
}
17851768

1786-
if (c.result != result && !usedMagic)
1787-
emplaceTypePack<BoundTypePack>(asMutable(c.result), result);
1769+
if (!usedMagic)
1770+
bind(constraint, c.result, retTp);
17881771

17891772
for (const auto& [expanded, additions] : u2.expandedFreeTypes)
17901773
{
@@ -1811,9 +1794,32 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
18111794
reportError(OccursCheckFailed{}, constraint->location);
18121795
break;
18131796
}
1797+
1798+
InstantiationQueuer queuer{constraint->scope, constraint->location, this};
1799+
queuer.traverse(overloadToUse);
1800+
queuer.traverse(result);
1801+
18141802
}
18151803
else
18161804
{
1805+
if (!usedMagic)
1806+
{
1807+
emplace<FreeTypePack>(constraint, c.result, constraint->scope, Polarity::Positive);
1808+
trackInteriorFreeTypePack(constraint->scope, c.result);
1809+
}
1810+
1811+
TypeId inferredTy = arena->addType(FunctionType{TypeLevel{}, argsPack, c.result});
1812+
1813+
Unifier2 u2{NotNull{arena}, builtinTypes, constraint->scope, NotNull{&iceReporter}};
1814+
1815+
// TODO: This should probably use ConstraintSolver::unify
1816+
const UnifyResult unifyResult = u2.unify(overloadToUse, inferredTy);
1817+
1818+
for (TypeId freeTy : u2.newFreshTypes)
1819+
trackInteriorFreeType(constraint->scope, freeTy);
1820+
for (TypePackId freeTp : u2.newFreshTypePacks)
1821+
trackInteriorFreeTypePack(constraint->scope, freeTp);
1822+
18171823
if (!u2.genericSubstitutions.empty() || !u2.genericPackSubstitutions.empty())
18181824
{
18191825
Subtyping subtyping{builtinTypes, arena, normalizer, typeFunctionRuntime, NotNull{&iceReporter}};
@@ -1854,15 +1860,16 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
18541860
break;
18551861
}
18561862
}
1857-
}
18581863

1859-
InstantiationQueuer queuer{constraint->scope, constraint->location, this};
1860-
queuer.traverse(overloadToUse);
1861-
queuer.traverse(inferredTy);
1864+
InstantiationQueuer queuer{constraint->scope, constraint->location, this};
1865+
queuer.traverse(overloadToUse);
1866+
queuer.traverse(inferredTy);
1867+
1868+
// This can potentially contain free types if the return type of
1869+
// `inferredTy` is never unified elsewhere.
1870+
trackInteriorFreeType(constraint->scope, inferredTy);
1871+
}
18621872

1863-
// This can potentially contain free types if the return type of
1864-
// `inferredTy` is never unified elsewhere.
1865-
trackInteriorFreeType(constraint->scope, inferredTy);
18661873

18671874
unblock(c.result, constraint->location);
18681875

@@ -4049,7 +4056,7 @@ void ConstraintSolver::shiftReferences(TypeId source, TypeId target)
40494056
if (auto sourcerefs = typeToConstraintSet.find(source); sourcerefs != typeToConstraintSet.end())
40504057
{
40514058
auto [targetrefs, _] = typeToConstraintSet.try_emplace(target, Set<const Constraint*>{nullptr});
4052-
4059+
40534060
// This is a little sketchy as we are iterating over a hash set.
40544061
// It _should_ be fine as we aren't depending on the order here,
40554062
// this is all just moving values into different hash sets.

Analysis/src/ExpectedTypeVisitor.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include "Luau/TypeUtils.h"
99
#include "Luau/VisitType.h"
1010

11-
LUAU_FASTFLAG(LuauOverloadGetsInstantiated)
11+
LUAU_FASTFLAG(LuauOverloadGetsInstantiated2)
1212

1313
namespace Luau
1414
{
@@ -28,7 +28,7 @@ ExpectedTypeVisitor::ExpectedTypeVisitor(
2828
, builtinTypes(builtinTypes)
2929
, rootScope(rootScope)
3030
{
31-
LUAU_ASSERT(!FFlag::LuauOverloadGetsInstantiated);
31+
LUAU_ASSERT(!FFlag::LuauOverloadGetsInstantiated2);
3232
}
3333

3434
ExpectedTypeVisitor::ExpectedTypeVisitor(
@@ -48,7 +48,7 @@ ExpectedTypeVisitor::ExpectedTypeVisitor(
4848
, builtinTypes(builtinTypes)
4949
, rootScope(rootScope)
5050
{
51-
LUAU_ASSERT(FFlag::LuauOverloadGetsInstantiated);
51+
LUAU_ASSERT(FFlag::LuauOverloadGetsInstantiated2);
5252
}
5353

5454
bool ExpectedTypeVisitor::visit(AstStatAssign* stat)
@@ -191,7 +191,7 @@ bool ExpectedTypeVisitor::visit(AstExprIndexExpr* expr)
191191
bool ExpectedTypeVisitor::visit(AstExprCall* expr)
192192
{
193193
TypeId* ty = nullptr;
194-
if (FFlag::LuauOverloadGetsInstantiated)
194+
if (FFlag::LuauOverloadGetsInstantiated2)
195195
{
196196
ty = astOverloadResolvedTypes->find(expr);
197197
if (!ty)

0 commit comments

Comments
 (0)