Skip to content

Commit 416fc1a

Browse files
andyfriesenhgoldsteinvrn-snayoungbloodrbxmenarulalam
authored
Sync to upstream/release/706 (#2208)
Hey everyone! We've been hard at work fixing bugs and improving native codegen. Enjoy! # Analysis * Rename the class tag to extern in type functions * Fix #2204 * Rework type unification in terms of subtyping. This uses the new solver's understanding of subtyping to drive unification for some internal callsites. This is a broad change that should result in more consistent inference. For example: ```luau local function sum<T>(x: T, y: T, z: (T, T) -> T) return z(x, y) end local function sumrec(f: typeof(sum)) return sum(2, 3, function<X>(g: X, h: X): add<X, X> return g + h end) end -- Prior we would claim that `b` is of type `add<X, X> | number`: not good! -- We now correctly claim that it is of type `number` thanks to bug fixes -- made to subtyping that did not make their way to the unifier. local b = sumrec(sum) ``` # Native Codegen * Improve vector handling in binary operations * Fix missed optimization causing an assertion failure * Propagate loads of STORE_VECTOR components produced by UINT_TO_FLOAT * Constant-fold new operand of CHECK_BUFFER_LEN * For register load-store propagation, use the second operand only for LOAD_FLOAT * TValue store cannot be removed when it's partially over-written * Rework equality and ordering comparisons to fix issues and be more type aware * Migrate Luau NCG IrInst operands representation to SmallVector # Runtime * Fix 64-bit countrz to build on 32 bit Windows. * Remap Luau local debug and type information after performing jump expansion * Add support for building Luau as a shared library * Luau should be able to make fastcalls even if library name is obscured by a local polyfill # OSS Contributions * #2163 * #2167 # Internal Contributors Co-authored-by: Ariel Weiss <arielweiss@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Ilya Rezvov <irezvov@roblox.com> Co-authored-by: Varun Saini <vsaini@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@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: Alexander Youngblood <ayoungblood@roblox.com> Co-authored-by: Menarul Alam <malam@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Ariel Weiss <aaronweiss@roblox.com> Co-authored-by: Ilya Rezvov <irezvov@roblox.com> Co-authored-by: Ariel Weiss <arielweiss@roblox.com>
1 parent 93c83a4 commit 416fc1a

File tree

88 files changed

+5089
-3401
lines changed

Some content is hidden

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

88 files changed

+5089
-3401
lines changed

Analysis/include/Luau/NativeStackGuard.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ struct NativeStackGuard
1818
uintptr_t low;
1919
};
2020

21-
}
21+
} // namespace Luau
2222

2323
namespace Luau
2424
{

Analysis/include/Luau/OverloadResolution.h

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ using IncompatibilityReason = Variant<SubtypingReasonings, ErrorVec>;
4242
*/
4343
struct SelectedOverload
4444
{
45-
/**
45+
/**
4646
* An unambiguous overload, if one can be selected. This is _not_ necessarily
4747
* an overload that is valid for the argument pack provided. For example:
4848
*
@@ -108,7 +108,6 @@ struct OverloadResolution
108108
* documentation.
109109
*/
110110
SelectedOverload getUnambiguousOverload() const;
111-
112111
};
113112

114113
struct OverloadResolver
@@ -177,13 +176,7 @@ struct OverloadResolver
177176
NotNull<DenseHashSet<TypeId>> uniqueTypes
178177
);
179178

180-
void testFunction(
181-
OverloadResolution& result,
182-
TypeId fnTy,
183-
TypePackId argsPack,
184-
Location fnLocation,
185-
NotNull<DenseHashSet<TypeId>> uniqueTypes
186-
);
179+
void testFunction(OverloadResolution& result, TypeId fnTy, TypePackId argsPack, Location fnLocation, NotNull<DenseHashSet<TypeId>> uniqueTypes);
187180

188181
void testFunctionOrCallMetamethod(
189182
OverloadResolution& result,

Analysis/include/Luau/RecursionCounter.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ struct NonExceptionalRecursionLimiter : RecursionCounter
4444
bool isOk(int limit) const;
4545

4646
NonExceptionalRecursionLimiter(int* count);
47-
4847
};
4948

5049
} // namespace Luau

Analysis/include/Luau/StructuralTypeEquality.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ bool areEqual(SeenSet& seen, const Type& lhs, const Type& rhs);
1515
bool areEqual(SeenSet& seen, const TypePackVar& lhs, const TypePackVar& rhs);
1616
bool areEqual(SeenSet& seen, TypeId lhs, TypeId rhs);
1717

18-
}
18+
} // namespace Luau

Analysis/include/Luau/Subtyping.h

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -195,12 +195,13 @@ struct SubtypingEnvironment
195195
*/
196196
DenseHashMap<TypeId, TypeId> substitutions{nullptr};
197197

198-
199198
// We use this cache to track pairs of subtypes that we tried to subtype, and found them to be in the seen set at the time.
200199
// In those situations, we return True, but mark the result as not cacheable, because we don't want to cache broader results which
201200
// led to the seen pair. However, those results were previously being cache in the ephemeralCache, and we still want to cache them somewhere
202201
// for performance reasons.
203202
DenseHashMap<std::pair<TypeId, TypeId>, SubtypingResult, TypePairHash> seenSetCache{{}};
203+
204+
int iterationCount = 0;
204205
};
205206

206207
struct Subtyping
@@ -248,12 +249,7 @@ struct Subtyping
248249
// TODO recursion limits
249250

250251
SubtypingResult isSubtype(TypeId subTy, TypeId superTy, NotNull<Scope> scope);
251-
SubtypingResult isSubtype(
252-
TypePackId subTp,
253-
TypePackId superTp,
254-
NotNull<Scope> scope,
255-
const std::vector<TypeId>& bindableGenerics
256-
);
252+
SubtypingResult isSubtype(TypePackId subTp, TypePackId superTp, NotNull<Scope> scope, const std::vector<TypeId>& bindableGenerics);
257253
SubtypingResult isSubtype(
258254
TypePackId subTp,
259255
TypePackId superTp,
@@ -334,12 +330,7 @@ struct Subtyping
334330
const FunctionType* superFunction,
335331
NotNull<Scope> scope
336332
);
337-
SubtypingResult isCovariantWith(
338-
SubtypingEnvironment& env,
339-
const MetatableType* subMt,
340-
const PrimitiveType* superPrim,
341-
NotNull<Scope> scope
342-
);
333+
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const PrimitiveType* superPrim, NotNull<Scope> scope);
343334
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TableType* subTable, const PrimitiveType* superPrim, NotNull<Scope> scope);
344335
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const PrimitiveType* subPrim, const TableType* superTable, NotNull<Scope> scope);
345336
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const SingletonType* subSingleton, const TableType* superTable, NotNull<Scope> scope);
@@ -442,15 +433,53 @@ struct Subtyping
442433
);
443434

444435
// Markers to help overload selection.
445-
struct Anything{};
446-
struct Nothing{};
436+
struct Anything
437+
{
438+
};
439+
struct Nothing
440+
{
441+
};
447442

448-
SubtypingResult isTailCovariantWithTail(SubtypingEnvironment& env, NotNull<Scope> scope, TypePackId subTp, const VariadicTypePack* sub, TypePackId superTp, const VariadicTypePack* super);
449-
SubtypingResult isTailCovariantWithTail(SubtypingEnvironment& env, NotNull<Scope> scope, TypePackId subTp, const GenericTypePack* sub, TypePackId superTp, const GenericTypePack* super);
450-
SubtypingResult isTailCovariantWithTail(SubtypingEnvironment& env, NotNull<Scope> scope, TypePackId subTp, const VariadicTypePack* sub, TypePackId superTp, const GenericTypePack* super);
451-
SubtypingResult isTailCovariantWithTail(SubtypingEnvironment& env, NotNull<Scope> scope, TypePackId subTp, const GenericTypePack* sub, TypePackId superTp, const VariadicTypePack* super);
443+
SubtypingResult isTailCovariantWithTail(
444+
SubtypingEnvironment& env,
445+
NotNull<Scope> scope,
446+
TypePackId subTp,
447+
const VariadicTypePack* sub,
448+
TypePackId superTp,
449+
const VariadicTypePack* super
450+
);
451+
SubtypingResult isTailCovariantWithTail(
452+
SubtypingEnvironment& env,
453+
NotNull<Scope> scope,
454+
TypePackId subTp,
455+
const GenericTypePack* sub,
456+
TypePackId superTp,
457+
const GenericTypePack* super
458+
);
459+
SubtypingResult isTailCovariantWithTail(
460+
SubtypingEnvironment& env,
461+
NotNull<Scope> scope,
462+
TypePackId subTp,
463+
const VariadicTypePack* sub,
464+
TypePackId superTp,
465+
const GenericTypePack* super
466+
);
467+
SubtypingResult isTailCovariantWithTail(
468+
SubtypingEnvironment& env,
469+
NotNull<Scope> scope,
470+
TypePackId subTp,
471+
const GenericTypePack* sub,
472+
TypePackId superTp,
473+
const VariadicTypePack* super
474+
);
452475
SubtypingResult isTailCovariantWithTail(SubtypingEnvironment& env, NotNull<Scope> scope, TypePackId subTp, const GenericTypePack* sub, Nothing);
453-
SubtypingResult isTailCovariantWithTail(SubtypingEnvironment& env, NotNull<Scope> scope, Nothing, TypePackId superTp, const GenericTypePack* super);
476+
SubtypingResult isTailCovariantWithTail(
477+
SubtypingEnvironment& env,
478+
NotNull<Scope> scope,
479+
Nothing,
480+
TypePackId superTp,
481+
const GenericTypePack* super
482+
);
454483

455484
bool bindGeneric(SubtypingEnvironment& env, TypeId subTy, TypeId superTy) const;
456485

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2+
#pragma once
3+
4+
#include "Luau/Subtyping.h"
5+
#include "Luau/Constraint.h"
6+
#include "Luau/NotNull.h"
7+
#include "Luau/Type.h"
8+
#include "Luau/Unifier2.h"
9+
10+
namespace Luau
11+
{
12+
13+
/**
14+
* `SubtypingUnifier` performs unification of type variables by asking whether
15+
* the two are subtypes of one another, and then attempts to dispatch any
16+
* assumed constraints.
17+
*/
18+
struct SubtypingUnifier
19+
{
20+
21+
using UpperBounds = DenseHashMap<TypeId, std::vector<std::pair<Location, TypeId>>>;
22+
23+
/**
24+
* Represents attempting to dispatch some number of assumed constraints.
25+
*/
26+
struct Result
27+
{
28+
/**
29+
* Whether we were able to unify successfully. This can fail if,
30+
* for example, unifying two free type packs would create a cycle.
31+
*/
32+
UnifyResult unified;
33+
/**
34+
* A list of outstanding constraints, e.g. subtype constraints where
35+
* one of the arguments is a blocked type of some kind.
36+
*/
37+
std::vector<ConstraintV> outstandingConstraints;
38+
/**
39+
* Upper bound contributors to any free types that we mutated. This is
40+
* used if a function type's parameter is inferred to be `never`.
41+
*/
42+
UpperBounds upperBoundContributors;
43+
};
44+
45+
explicit SubtypingUnifier(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, NotNull<InternalErrorReporter> reporter);
46+
47+
/**
48+
* @param constraint The active constraint (used for location data).
49+
* @param assumedConstraints The set of assumed constraints for a given subtype check.
50+
*/
51+
Result dispatchConstraints(NotNull<const Constraint> constraint, std::vector<ConstraintV> assumedConstraints) const;
52+
53+
private:
54+
NotNull<TypeArena> arena;
55+
NotNull<BuiltinTypes> builtinTypes;
56+
NotNull<InternalErrorReporter> reporter;
57+
58+
/**
59+
* Attempt to dispatch a *single* constraint.
60+
* @return A pair containing whether we were able to unify and if we need to defer the constraint.
61+
*/
62+
std::pair<UnifyResult, bool> dispatchOneConstraint(
63+
NotNull<const Constraint> constraint,
64+
const ConstraintV& cv,
65+
UpperBounds& upperBoundContributors
66+
) const;
67+
68+
OccursCheckResult occursCheck(TypePackId needle, TypePackId haystack) const;
69+
70+
bool canBeUnified(TypeId ty) const;
71+
};
72+
73+
} // namespace Luau

Analysis/include/Luau/TypeUtils.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,4 +413,11 @@ struct ContainsAnyGeneric final : public TypeOnceVisitor
413413
bool containsGeneric(TypeId ty, NotNull<DenseHashSet<const void*>> generics);
414414
bool containsGeneric(TypePackId ty, NotNull<DenseHashSet<const void*>> generics);
415415

416+
/**
417+
* @return Whether `ty` is a type that cannot be unified with another type,
418+
* such as a blocked type, pending expansion type, or an unsolved
419+
* type function.
420+
*/
421+
bool isBlocked(TypeId ty);
422+
416423
} // namespace Luau

Analysis/src/BuiltinDefinitions.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1747,7 +1747,8 @@ bool MagicFreeze::infer(const MagicFunctionCallContext& context)
17471747
return true;
17481748
}
17491749

1750-
// MagicFreeze is a magic function because table.freeze is a bounded version of the identity function with a custom output (accepts any subtype of `table` and returns a read-only version of that table).
1750+
// MagicFreeze is a magic function because table.freeze is a bounded version of the identity function with a custom output (accepts any subtype of
1751+
// `table` and returns a read-only version of that table).
17511752
bool MagicFreeze::typeCheck(const MagicFunctionTypeCheckContext& ctx)
17521753
{
17531754
if (!FFlag::LuauTableFreezeCheckIsSubtype)
@@ -1769,8 +1770,10 @@ bool MagicFreeze::typeCheck(const MagicFunctionTypeCheckContext& ctx)
17691770
}
17701771
else if (paramTail)
17711772
{
1772-
// TODO (CLI-185019): We ideally want to report a Count Mismatch error if there's no head but a variadic tail, but CountMismatch requires actual count size, which we don't have with variadic tails, so we can't report it properly yet.
1773-
// Instead, we continue to typecheck with the first argument in the variadic tail and report a type mismatch error based on that, which is more informative than reporting a count mismatch where the head (paramTypes.size()) is 0.
1773+
// TODO (CLI-185019): We ideally want to report a Count Mismatch error if there's no head but a variadic tail, but CountMismatch requires
1774+
// actual count size, which we don't have with variadic tails, so we can't report it properly yet. Instead, we continue to typecheck with the
1775+
// first argument in the variadic tail and report a type mismatch error based on that, which is more informative than reporting a count
1776+
// mismatch where the head (paramTypes.size()) is 0.
17741777
firstParamType = first(*paramTail);
17751778
}
17761779

@@ -1781,7 +1784,8 @@ bool MagicFreeze::typeCheck(const MagicFunctionTypeCheckContext& ctx)
17811784
}
17821785
else
17831786
{
1784-
// If we can't get a type from the type or type pack, we testIsSubtype against the entire context's argument type pack to report a Type Pack Mismatch error.
1787+
// If we can't get a type from the type or type pack, we testIsSubtype against the entire context's argument type pack to report a Type Pack
1788+
// Mismatch error.
17851789
TypePackId tableTyPack = ctx.typechecker->module->internalTypes.addTypePack({ctx.typechecker->builtinTypes->tableType});
17861790
ctx.typechecker->testIsSubtype(follow(ctx.arguments), tableTyPack, ctx.callSite->location);
17871791
return true;

Analysis/src/BuiltinTypeFunctions.cpp

Lines changed: 25 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -528,32 +528,14 @@ TypeFunctionReductionResult<TypeId> numericBinopTypeFunction(
528528

529529
if (!reversed)
530530
solveResult = solveFunctionCall_DEPRECATED(
531-
ctx->arena,
532-
ctx->builtins,
533-
ctx->normalizer,
534-
ctx->typeFunctionRuntime,
535-
ctx->ice,
536-
ctx->limits,
537-
ctx->scope,
538-
location,
539-
*mmType,
540-
argPack
531+
ctx->arena, ctx->builtins, ctx->normalizer, ctx->typeFunctionRuntime, ctx->ice, ctx->limits, ctx->scope, location, *mmType, argPack
541532
);
542533
else
543534
{
544535
TypePack* p = getMutable<TypePack>(argPack);
545536
std::swap(p->head.front(), p->head.back());
546537
solveResult = solveFunctionCall_DEPRECATED(
547-
ctx->arena,
548-
ctx->builtins,
549-
ctx->normalizer,
550-
ctx->typeFunctionRuntime,
551-
ctx->ice,
552-
ctx->limits,
553-
ctx->scope,
554-
location,
555-
*mmType,
556-
argPack
538+
ctx->arena, ctx->builtins, ctx->normalizer, ctx->typeFunctionRuntime, ctx->ice, ctx->limits, ctx->scope, location, *mmType, argPack
557539
);
558540
}
559541

@@ -755,7 +737,9 @@ TypeFunctionReductionResult<TypeId> concatTypeFunction(
755737
else
756738
inferredArgs = {rhsTy, lhsTy};
757739

758-
if (!solveFunctionCall(ctx, ctx->constraint ? ctx->constraint->location : Location{}, *mmType, ctx->arena->addTypePack(std::move(inferredArgs))))
740+
if (!solveFunctionCall(
741+
ctx, ctx->constraint ? ctx->constraint->location : Location{}, *mmType, ctx->arena->addTypePack(std::move(inferredArgs))
742+
))
759743
return {std::nullopt, Reduction::Erroneous, {}, {}};
760744
}
761745
else
@@ -1399,30 +1383,30 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
13991383
}
14001384

14011385
std::vector<TypeId> discriminantTypes;
1402-
for (size_t i = 1; i < typeParams.size(); i++)
1403-
{
1404-
auto discriminant = follow(typeParams[i]);
1386+
for (size_t i = 1; i < typeParams.size(); i++)
1387+
{
1388+
auto discriminant = follow(typeParams[i]);
14051389

1406-
// Filter out any top level types that are meaningless to refine
1407-
// against.
1408-
if (is<UnknownType, NoRefineType>(discriminant))
1409-
continue;
1390+
// Filter out any top level types that are meaningless to refine
1391+
// against.
1392+
if (is<UnknownType, NoRefineType>(discriminant))
1393+
continue;
14101394

1411-
// If the discriminant type is only:
1412-
// - The `*no-refine*` type (covered above) or;
1413-
// - tables, metatables, unions, intersections, functions, or
1414-
// negations containing `*no-refine*` (covered below).
1415-
// There's no point in refining against it.
1416-
ContainsRefinableType crt;
1417-
crt.traverse(discriminant);
1395+
// If the discriminant type is only:
1396+
// - The `*no-refine*` type (covered above) or;
1397+
// - tables, metatables, unions, intersections, functions, or
1398+
// negations containing `*no-refine*` (covered below).
1399+
// There's no point in refining against it.
1400+
ContainsRefinableType crt;
1401+
crt.traverse(discriminant);
14181402

1419-
if (crt.found)
1420-
discriminantTypes.push_back(discriminant);
1421-
}
1403+
if (crt.found)
1404+
discriminantTypes.push_back(discriminant);
1405+
}
14221406

1423-
// if we don't have any real refinements, i.e. they're all `*no-refine*`, then we can reduce immediately.
1424-
if (discriminantTypes.empty())
1425-
return {targetTy, {}};
1407+
// if we don't have any real refinements, i.e. they're all `*no-refine*`, then we can reduce immediately.
1408+
if (discriminantTypes.empty())
1409+
return {targetTy, {}};
14261410

14271411
const bool targetIsPending = isBlockedOrUnsolvedType(targetTy);
14281412

0 commit comments

Comments
 (0)