Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync to upstream/release/657 #1619

Merged
merged 7 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Analysis/include/Luau/ConstraintSolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,11 @@ struct ConstraintSolver
void throwUserCancelError() const;

ToStringOptions opts;

void fillInDiscriminantTypes(
NotNull<const Constraint> constraint,
const std::vector<std::optional<TypeId>>& discriminantTypes
);
};

void dump(NotNull<Scope> rootScope, struct ToStringOptions& opts);
Expand Down
23 changes: 21 additions & 2 deletions Analysis/include/Luau/TypeFunctionRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,14 @@ struct TypeFunctionVariadicTypePack
TypeFunctionTypeId type;
};

using TypeFunctionTypePackVariant = Variant<TypeFunctionTypePack, TypeFunctionVariadicTypePack>;
struct TypeFunctionGenericTypePack
{
bool isNamed = false;

std::string name;
};

using TypeFunctionTypePackVariant = Variant<TypeFunctionTypePack, TypeFunctionVariadicTypePack, TypeFunctionGenericTypePack>;

struct TypeFunctionTypePackVar
{
Expand All @@ -135,6 +142,9 @@ struct TypeFunctionTypePackVar

struct TypeFunctionFunctionType
{
std::vector<TypeFunctionTypeId> generics;
std::vector<TypeFunctionTypePackId> genericPacks;

TypeFunctionTypePackId argTypes;
TypeFunctionTypePackId retTypes;
};
Expand Down Expand Up @@ -210,6 +220,14 @@ struct TypeFunctionClassType
std::string name;
};

struct TypeFunctionGenericType
{
bool isNamed = false;
bool isPack = false;

std::string name;
};

using TypeFunctionTypeVariant = Luau::Variant<
TypeFunctionPrimitiveType,
TypeFunctionAnyType,
Expand All @@ -221,7 +239,8 @@ using TypeFunctionTypeVariant = Luau::Variant<
TypeFunctionNegationType,
TypeFunctionFunctionType,
TypeFunctionTableType,
TypeFunctionClassType>;
TypeFunctionClassType,
TypeFunctionGenericType>;

struct TypeFunctionType
{
Expand Down
36 changes: 6 additions & 30 deletions Analysis/src/AstQuery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@

LUAU_FASTFLAG(LuauSolverV2)

LUAU_FASTFLAGVARIABLE(LuauDocumentationAtPosition)

namespace Luau
{

Expand Down Expand Up @@ -518,7 +516,6 @@ static std::optional<DocumentationSymbol> getMetatableDocumentation(
const AstName& index
)
{
LUAU_ASSERT(FFlag::LuauDocumentationAtPosition);
auto indexIt = mtable->props.find("__index");
if (indexIt == mtable->props.end())
return std::nullopt;
Expand Down Expand Up @@ -575,26 +572,7 @@ std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const Source
}
else if (const ClassType* ctv = get<ClassType>(parentTy))
{
if (FFlag::LuauDocumentationAtPosition)
{
while (ctv)
{
if (auto propIt = ctv->props.find(indexName->index.value); propIt != ctv->props.end())
{
if (FFlag::LuauSolverV2)
{
if (auto ty = propIt->second.readTy)
return checkOverloadedDocumentationSymbol(module, *ty, parentExpr, propIt->second.documentationSymbol);
}
else
return checkOverloadedDocumentationSymbol(
module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol
);
}
ctv = ctv->parent ? Luau::get<Luau::ClassType>(*ctv->parent) : nullptr;
}
}
else
while (ctv)
{
if (auto propIt = ctv->props.find(indexName->index.value); propIt != ctv->props.end())
{
Expand All @@ -608,17 +586,15 @@ std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const Source
module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol
);
}
ctv = ctv->parent ? Luau::get<Luau::ClassType>(*ctv->parent) : nullptr;
}
}
else if (FFlag::LuauDocumentationAtPosition)
else if (const PrimitiveType* ptv = get<PrimitiveType>(parentTy); ptv && ptv->metatable)
{
if (const PrimitiveType* ptv = get<PrimitiveType>(parentTy); ptv && ptv->metatable)
if (auto mtable = get<TableType>(*ptv->metatable))
{
if (auto mtable = get<TableType>(*ptv->metatable))
{
if (std::optional<std::string> docSymbol = getMetatableDocumentation(module, parentExpr, mtable, indexName->index))
return docSymbol;
}
if (std::optional<std::string> docSymbol = getMetatableDocumentation(module, parentExpr, mtable, indexName->index))
return docSymbol;
}
}
}
Expand Down
24 changes: 22 additions & 2 deletions Analysis/src/BuiltinDefinitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAGVARIABLE(LuauTypestateBuiltins2)
LUAU_FASTFLAGVARIABLE(LuauStringFormatArityFix)
LUAU_FASTFLAGVARIABLE(LuauStringFormatErrorSuppression)
LUAU_FASTFLAG(AutocompleteRequirePathSuggestions2)
LUAU_FASTFLAG(LuauVectorDefinitionsExtra)

Expand Down Expand Up @@ -631,10 +632,29 @@ static void dcrMagicFunctionTypeCheckFormat(MagicFunctionTypeCheckContext contex
Location location = context.callSite->args.data[i + (calledWithSelf ? 0 : paramOffset)]->location;
// use subtyping instead here
SubtypingResult result = context.typechecker->subtyping->isSubtype(actualTy, expectedTy, context.checkScope);

if (!result.isSubtype)
{
Reasonings reasonings = context.typechecker->explainReasonings(actualTy, expectedTy, location, result);
context.typechecker->reportError(TypeMismatch{expectedTy, actualTy, reasonings.toString()}, location);
if (FFlag::LuauStringFormatErrorSuppression)
{
switch (shouldSuppressErrors(NotNull{&context.typechecker->normalizer}, actualTy))
{
case ErrorSuppression::Suppress:
break;
case ErrorSuppression::NormalizationFailed:
break;
case ErrorSuppression::DoNotSuppress:
Reasonings reasonings = context.typechecker->explainReasonings(actualTy, expectedTy, location, result);

if (!reasonings.suppressed)
context.typechecker->reportError(TypeMismatch{expectedTy, actualTy, reasonings.toString()}, location);
}
}
else
{
Reasonings reasonings = context.typechecker->explainReasonings(actualTy, expectedTy, location, result);
context.typechecker->reportError(TypeMismatch{expectedTy, actualTy, reasonings.toString()}, location);
}
}
}
}
Expand Down
100 changes: 76 additions & 24 deletions Analysis/src/ConstraintSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ LUAU_FASTFLAG(LuauNewSolverPopulateTableLocations)
LUAU_FASTFLAGVARIABLE(LuauAllowNilAssignmentToIndexer)
LUAU_FASTFLAG(LuauUserTypeFunNoExtraConstraint)
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
LUAU_FASTFLAGVARIABLE(LuauAlwaysFillInFunctionCallDiscriminantTypes)

namespace Luau
{
Expand Down Expand Up @@ -1153,6 +1154,42 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
return true;
}

void ConstraintSolver::fillInDiscriminantTypes(
NotNull<const Constraint> constraint,
const std::vector<std::optional<TypeId>>& discriminantTypes
)
{
for (std::optional<TypeId> ty : discriminantTypes)
{
if (!ty)
continue;

// If the discriminant type has been transmuted, we need to unblock them.
if (!isBlocked(*ty))
{
unblock(*ty, constraint->location);
continue;
}

if (FFlag::LuauRemoveNotAnyHack)
{
// We bind any unused discriminants to the `*no-refine*` type indicating that it can be safely ignored.
emplaceType<BoundType>(asMutable(follow(*ty)), builtinTypes->noRefineType);
}
else
{
// We use `any` here because the discriminant type may be pointed at by both branches,
// where the discriminant type is not negated, and the other where it is negated, i.e.
// `unknown ~ unknown` and `~unknown ~ never`, so `T & unknown ~ T` and `T & ~unknown ~ never`
// v.s.
// `any ~ any` and `~any ~ any`, so `T & any ~ T` and `T & ~any ~ T`
//
// In practice, users cannot negate `any`, so this is an implementation detail we can always change.
emplaceType<BoundType>(asMutable(follow(*ty)), builtinTypes->anyType);
}
}
}

bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<const Constraint> constraint)
{
TypeId fn = follow(c.fn);
Expand All @@ -1168,19 +1205,25 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
{
emplaceTypePack<BoundTypePack>(asMutable(c.result), builtinTypes->anyTypePack);
unblock(c.result, constraint->location);
if (FFlag::LuauAlwaysFillInFunctionCallDiscriminantTypes)
fillInDiscriminantTypes(constraint, c.discriminantTypes);
return true;
}

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

if (get<NeverType>(fn))
{
bind(constraint, c.result, builtinTypes->neverTypePack);
if (FFlag::LuauAlwaysFillInFunctionCallDiscriminantTypes)
fillInDiscriminantTypes(constraint, c.discriminantTypes);
return true;
}

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

for (std::optional<TypeId> ty : c.discriminantTypes)
if (FFlag::LuauAlwaysFillInFunctionCallDiscriminantTypes)
{
if (!ty)
continue;

// If the discriminant type has been transmuted, we need to unblock them.
if (!isBlocked(*ty))
fillInDiscriminantTypes(constraint, c.discriminantTypes);
}
else
{
// NOTE: This is the body of the `fillInDiscriminantTypes` helper.
for (std::optional<TypeId> ty : c.discriminantTypes)
{
unblock(*ty, constraint->location);
continue;
}
if (!ty)
continue;

if (FFlag::LuauRemoveNotAnyHack)
{
// We bind any unused discriminants to the `*no-refine*` type indicating that it can be safely ignored.
emplaceType<BoundType>(asMutable(follow(*ty)), builtinTypes->noRefineType);
}
else
{
// We use `any` here because the discriminant type may be pointed at by both branches,
// where the discriminant type is not negated, and the other where it is negated, i.e.
// `unknown ~ unknown` and `~unknown ~ never`, so `T & unknown ~ T` and `T & ~unknown ~ never`
// v.s.
// `any ~ any` and `~any ~ any`, so `T & any ~ T` and `T & ~any ~ T`
//
// In practice, users cannot negate `any`, so this is an implementation detail we can always change.
emplaceType<BoundType>(asMutable(follow(*ty)), builtinTypes->anyType);
// If the discriminant type has been transmuted, we need to unblock them.
if (!isBlocked(*ty))
{
unblock(*ty, constraint->location);
continue;
}

if (FFlag::LuauRemoveNotAnyHack)
{
// We bind any unused discriminants to the `*no-refine*` type indicating that it can be safely ignored.
emplaceType<BoundType>(asMutable(follow(*ty)), builtinTypes->noRefineType);
}
else
{
// We use `any` here because the discriminant type may be pointed at by both branches,
// where the discriminant type is not negated, and the other where it is negated, i.e.
// `unknown ~ unknown` and `~unknown ~ never`, so `T & unknown ~ T` and `T & ~unknown ~ never`
// v.s.
// `any ~ any` and `~any ~ any`, so `T & any ~ T` and `T & ~any ~ T`
//
// In practice, users cannot negate `any`, so this is an implementation detail we can always change.
emplaceType<BoundType>(asMutable(follow(*ty)), builtinTypes->anyType);
}
}
}


OverloadResolver resolver{
builtinTypes,
NotNull{arena},
Expand Down
Loading
Loading