Skip to content

Commit 2904750

Browse files
vrn-snaatxehgoldsteinTalha Pathanvegorov-rbx
authored
Sync to upstream/release/657 (#1619)
## General - Fix a parsing bug related to the starting position of function names. - Rename Luau's `Table` struct to `LuaTable`. ## New Solver - Add support for generics in user-defined type functions ([RFC](https://rfcs.luau.org/support-for-generic-function-types-in-user-defined-type-functions.html)). - Provide a definition of `math.lerp` to the typechecker. - Implement error suppression in `string.format`. - Fixes #1587. - Ensure function call discriminant types are always filled when resolving `FunctionCallConstraint`. --- Co-authored-by: Ariel Weiss <[email protected]> Co-authored-by: Hunter Goldstein <[email protected]> Co-authored-by: Talha Pathan <[email protected]> Co-authored-by: Vyacheslav Egorov <[email protected]>
1 parent 67e9d85 commit 2904750

Some content is hidden

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

64 files changed

+2209
-675
lines changed

Analysis/include/Luau/ConstraintSolver.h

+5
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,11 @@ struct ConstraintSolver
420420
void throwUserCancelError() const;
421421

422422
ToStringOptions opts;
423+
424+
void fillInDiscriminantTypes(
425+
NotNull<const Constraint> constraint,
426+
const std::vector<std::optional<TypeId>>& discriminantTypes
427+
);
423428
};
424429

425430
void dump(NotNull<Scope> rootScope, struct ToStringOptions& opts);

Analysis/include/Luau/TypeFunctionRuntime.h

+21-2
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,14 @@ struct TypeFunctionVariadicTypePack
119119
TypeFunctionTypeId type;
120120
};
121121

122-
using TypeFunctionTypePackVariant = Variant<TypeFunctionTypePack, TypeFunctionVariadicTypePack>;
122+
struct TypeFunctionGenericTypePack
123+
{
124+
bool isNamed = false;
125+
126+
std::string name;
127+
};
128+
129+
using TypeFunctionTypePackVariant = Variant<TypeFunctionTypePack, TypeFunctionVariadicTypePack, TypeFunctionGenericTypePack>;
123130

124131
struct TypeFunctionTypePackVar
125132
{
@@ -135,6 +142,9 @@ struct TypeFunctionTypePackVar
135142

136143
struct TypeFunctionFunctionType
137144
{
145+
std::vector<TypeFunctionTypeId> generics;
146+
std::vector<TypeFunctionTypePackId> genericPacks;
147+
138148
TypeFunctionTypePackId argTypes;
139149
TypeFunctionTypePackId retTypes;
140150
};
@@ -210,6 +220,14 @@ struct TypeFunctionClassType
210220
std::string name;
211221
};
212222

223+
struct TypeFunctionGenericType
224+
{
225+
bool isNamed = false;
226+
bool isPack = false;
227+
228+
std::string name;
229+
};
230+
213231
using TypeFunctionTypeVariant = Luau::Variant<
214232
TypeFunctionPrimitiveType,
215233
TypeFunctionAnyType,
@@ -221,7 +239,8 @@ using TypeFunctionTypeVariant = Luau::Variant<
221239
TypeFunctionNegationType,
222240
TypeFunctionFunctionType,
223241
TypeFunctionTableType,
224-
TypeFunctionClassType>;
242+
TypeFunctionClassType,
243+
TypeFunctionGenericType>;
225244

226245
struct TypeFunctionType
227246
{

Analysis/src/AstQuery.cpp

+6-30
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313

1414
LUAU_FASTFLAG(LuauSolverV2)
1515

16-
LUAU_FASTFLAGVARIABLE(LuauDocumentationAtPosition)
17-
1816
namespace Luau
1917
{
2018

@@ -518,7 +516,6 @@ static std::optional<DocumentationSymbol> getMetatableDocumentation(
518516
const AstName& index
519517
)
520518
{
521-
LUAU_ASSERT(FFlag::LuauDocumentationAtPosition);
522519
auto indexIt = mtable->props.find("__index");
523520
if (indexIt == mtable->props.end())
524521
return std::nullopt;
@@ -575,26 +572,7 @@ std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const Source
575572
}
576573
else if (const ClassType* ctv = get<ClassType>(parentTy))
577574
{
578-
if (FFlag::LuauDocumentationAtPosition)
579-
{
580-
while (ctv)
581-
{
582-
if (auto propIt = ctv->props.find(indexName->index.value); propIt != ctv->props.end())
583-
{
584-
if (FFlag::LuauSolverV2)
585-
{
586-
if (auto ty = propIt->second.readTy)
587-
return checkOverloadedDocumentationSymbol(module, *ty, parentExpr, propIt->second.documentationSymbol);
588-
}
589-
else
590-
return checkOverloadedDocumentationSymbol(
591-
module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol
592-
);
593-
}
594-
ctv = ctv->parent ? Luau::get<Luau::ClassType>(*ctv->parent) : nullptr;
595-
}
596-
}
597-
else
575+
while (ctv)
598576
{
599577
if (auto propIt = ctv->props.find(indexName->index.value); propIt != ctv->props.end())
600578
{
@@ -608,17 +586,15 @@ std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const Source
608586
module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol
609587
);
610588
}
589+
ctv = ctv->parent ? Luau::get<Luau::ClassType>(*ctv->parent) : nullptr;
611590
}
612591
}
613-
else if (FFlag::LuauDocumentationAtPosition)
592+
else if (const PrimitiveType* ptv = get<PrimitiveType>(parentTy); ptv && ptv->metatable)
614593
{
615-
if (const PrimitiveType* ptv = get<PrimitiveType>(parentTy); ptv && ptv->metatable)
594+
if (auto mtable = get<TableType>(*ptv->metatable))
616595
{
617-
if (auto mtable = get<TableType>(*ptv->metatable))
618-
{
619-
if (std::optional<std::string> docSymbol = getMetatableDocumentation(module, parentExpr, mtable, indexName->index))
620-
return docSymbol;
621-
}
596+
if (std::optional<std::string> docSymbol = getMetatableDocumentation(module, parentExpr, mtable, indexName->index))
597+
return docSymbol;
622598
}
623599
}
624600
}

Analysis/src/BuiltinDefinitions.cpp

+22-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
LUAU_FASTFLAG(LuauSolverV2)
3232
LUAU_FASTFLAGVARIABLE(LuauTypestateBuiltins2)
3333
LUAU_FASTFLAGVARIABLE(LuauStringFormatArityFix)
34+
LUAU_FASTFLAGVARIABLE(LuauStringFormatErrorSuppression)
3435
LUAU_FASTFLAG(AutocompleteRequirePathSuggestions2)
3536
LUAU_FASTFLAG(LuauVectorDefinitionsExtra)
3637

@@ -631,10 +632,29 @@ static void dcrMagicFunctionTypeCheckFormat(MagicFunctionTypeCheckContext contex
631632
Location location = context.callSite->args.data[i + (calledWithSelf ? 0 : paramOffset)]->location;
632633
// use subtyping instead here
633634
SubtypingResult result = context.typechecker->subtyping->isSubtype(actualTy, expectedTy, context.checkScope);
635+
634636
if (!result.isSubtype)
635637
{
636-
Reasonings reasonings = context.typechecker->explainReasonings(actualTy, expectedTy, location, result);
637-
context.typechecker->reportError(TypeMismatch{expectedTy, actualTy, reasonings.toString()}, location);
638+
if (FFlag::LuauStringFormatErrorSuppression)
639+
{
640+
switch (shouldSuppressErrors(NotNull{&context.typechecker->normalizer}, actualTy))
641+
{
642+
case ErrorSuppression::Suppress:
643+
break;
644+
case ErrorSuppression::NormalizationFailed:
645+
break;
646+
case ErrorSuppression::DoNotSuppress:
647+
Reasonings reasonings = context.typechecker->explainReasonings(actualTy, expectedTy, location, result);
648+
649+
if (!reasonings.suppressed)
650+
context.typechecker->reportError(TypeMismatch{expectedTy, actualTy, reasonings.toString()}, location);
651+
}
652+
}
653+
else
654+
{
655+
Reasonings reasonings = context.typechecker->explainReasonings(actualTy, expectedTy, location, result);
656+
context.typechecker->reportError(TypeMismatch{expectedTy, actualTy, reasonings.toString()}, location);
657+
}
638658
}
639659
}
640660
}

Analysis/src/ConstraintSolver.cpp

+76-24
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ LUAU_FASTFLAG(LuauNewSolverPopulateTableLocations)
3737
LUAU_FASTFLAGVARIABLE(LuauAllowNilAssignmentToIndexer)
3838
LUAU_FASTFLAG(LuauUserTypeFunNoExtraConstraint)
3939
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
40+
LUAU_FASTFLAGVARIABLE(LuauAlwaysFillInFunctionCallDiscriminantTypes)
4041

4142
namespace Luau
4243
{
@@ -1153,6 +1154,42 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
11531154
return true;
11541155
}
11551156

1157+
void ConstraintSolver::fillInDiscriminantTypes(
1158+
NotNull<const Constraint> constraint,
1159+
const std::vector<std::optional<TypeId>>& discriminantTypes
1160+
)
1161+
{
1162+
for (std::optional<TypeId> ty : discriminantTypes)
1163+
{
1164+
if (!ty)
1165+
continue;
1166+
1167+
// If the discriminant type has been transmuted, we need to unblock them.
1168+
if (!isBlocked(*ty))
1169+
{
1170+
unblock(*ty, constraint->location);
1171+
continue;
1172+
}
1173+
1174+
if (FFlag::LuauRemoveNotAnyHack)
1175+
{
1176+
// We bind any unused discriminants to the `*no-refine*` type indicating that it can be safely ignored.
1177+
emplaceType<BoundType>(asMutable(follow(*ty)), builtinTypes->noRefineType);
1178+
}
1179+
else
1180+
{
1181+
// We use `any` here because the discriminant type may be pointed at by both branches,
1182+
// where the discriminant type is not negated, and the other where it is negated, i.e.
1183+
// `unknown ~ unknown` and `~unknown ~ never`, so `T & unknown ~ T` and `T & ~unknown ~ never`
1184+
// v.s.
1185+
// `any ~ any` and `~any ~ any`, so `T & any ~ T` and `T & ~any ~ T`
1186+
//
1187+
// In practice, users cannot negate `any`, so this is an implementation detail we can always change.
1188+
emplaceType<BoundType>(asMutable(follow(*ty)), builtinTypes->anyType);
1189+
}
1190+
}
1191+
}
1192+
11561193
bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<const Constraint> constraint)
11571194
{
11581195
TypeId fn = follow(c.fn);
@@ -1168,19 +1205,25 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
11681205
{
11691206
emplaceTypePack<BoundTypePack>(asMutable(c.result), builtinTypes->anyTypePack);
11701207
unblock(c.result, constraint->location);
1208+
if (FFlag::LuauAlwaysFillInFunctionCallDiscriminantTypes)
1209+
fillInDiscriminantTypes(constraint, c.discriminantTypes);
11711210
return true;
11721211
}
11731212

11741213
// if we're calling an error type, the result is an error type, and that's that.
11751214
if (get<ErrorType>(fn))
11761215
{
11771216
bind(constraint, c.result, builtinTypes->errorRecoveryTypePack());
1217+
if (FFlag::LuauAlwaysFillInFunctionCallDiscriminantTypes)
1218+
fillInDiscriminantTypes(constraint, c.discriminantTypes);
11781219
return true;
11791220
}
11801221

11811222
if (get<NeverType>(fn))
11821223
{
11831224
bind(constraint, c.result, builtinTypes->neverTypePack);
1225+
if (FFlag::LuauAlwaysFillInFunctionCallDiscriminantTypes)
1226+
fillInDiscriminantTypes(constraint, c.discriminantTypes);
11841227
return true;
11851228
}
11861229

@@ -1261,36 +1304,45 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
12611304
emplace<FreeTypePack>(constraint, c.result, constraint->scope);
12621305
}
12631306

1264-
for (std::optional<TypeId> ty : c.discriminantTypes)
1307+
if (FFlag::LuauAlwaysFillInFunctionCallDiscriminantTypes)
12651308
{
1266-
if (!ty)
1267-
continue;
1268-
1269-
// If the discriminant type has been transmuted, we need to unblock them.
1270-
if (!isBlocked(*ty))
1309+
fillInDiscriminantTypes(constraint, c.discriminantTypes);
1310+
}
1311+
else
1312+
{
1313+
// NOTE: This is the body of the `fillInDiscriminantTypes` helper.
1314+
for (std::optional<TypeId> ty : c.discriminantTypes)
12711315
{
1272-
unblock(*ty, constraint->location);
1273-
continue;
1274-
}
1316+
if (!ty)
1317+
continue;
12751318

1276-
if (FFlag::LuauRemoveNotAnyHack)
1277-
{
1278-
// We bind any unused discriminants to the `*no-refine*` type indicating that it can be safely ignored.
1279-
emplaceType<BoundType>(asMutable(follow(*ty)), builtinTypes->noRefineType);
1280-
}
1281-
else
1282-
{
1283-
// We use `any` here because the discriminant type may be pointed at by both branches,
1284-
// where the discriminant type is not negated, and the other where it is negated, i.e.
1285-
// `unknown ~ unknown` and `~unknown ~ never`, so `T & unknown ~ T` and `T & ~unknown ~ never`
1286-
// v.s.
1287-
// `any ~ any` and `~any ~ any`, so `T & any ~ T` and `T & ~any ~ T`
1288-
//
1289-
// In practice, users cannot negate `any`, so this is an implementation detail we can always change.
1290-
emplaceType<BoundType>(asMutable(follow(*ty)), builtinTypes->anyType);
1319+
// If the discriminant type has been transmuted, we need to unblock them.
1320+
if (!isBlocked(*ty))
1321+
{
1322+
unblock(*ty, constraint->location);
1323+
continue;
1324+
}
1325+
1326+
if (FFlag::LuauRemoveNotAnyHack)
1327+
{
1328+
// We bind any unused discriminants to the `*no-refine*` type indicating that it can be safely ignored.
1329+
emplaceType<BoundType>(asMutable(follow(*ty)), builtinTypes->noRefineType);
1330+
}
1331+
else
1332+
{
1333+
// We use `any` here because the discriminant type may be pointed at by both branches,
1334+
// where the discriminant type is not negated, and the other where it is negated, i.e.
1335+
// `unknown ~ unknown` and `~unknown ~ never`, so `T & unknown ~ T` and `T & ~unknown ~ never`
1336+
// v.s.
1337+
// `any ~ any` and `~any ~ any`, so `T & any ~ T` and `T & ~any ~ T`
1338+
//
1339+
// In practice, users cannot negate `any`, so this is an implementation detail we can always change.
1340+
emplaceType<BoundType>(asMutable(follow(*ty)), builtinTypes->anyType);
1341+
}
12911342
}
12921343
}
12931344

1345+
12941346
OverloadResolver resolver{
12951347
builtinTypes,
12961348
NotNull{arena},

0 commit comments

Comments
 (0)