Skip to content
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
4 changes: 2 additions & 2 deletions .github/workflows/new-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ jobs:
steps:
- name: validate version format
run: |
if [[ ! "${{ github.event.inputs.version }}" == *"."* ]]; then
echo "Error: Version must contain a '.'"
if [[ ! "${{ github.event.inputs.version }}" =~ ^0\.[0-9]{3,}$ ]]; then
echo "Error: Version must be in the format 0.XYZ"
exit 1
fi
- name: create release
Expand Down
4 changes: 2 additions & 2 deletions Analysis/include/Luau/AstUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
namespace Luau
{

// Search through the expression 'expr' for types that are known to represent
// uniquely held references. Append these types to 'uniqueTypes'.
// Search through the expression 'expr' for typeArguments that are known to represent
// uniquely held references. Append these typeArguments to 'uniqueTypes'.
void findUniqueTypes(NotNull<DenseHashSet<TypeId>> uniqueTypes, AstExpr* expr, NotNull<const DenseHashMap<const AstExpr*, TypeId>> astTypes);

void findUniqueTypes(
Expand Down
16 changes: 15 additions & 1 deletion Analysis/include/Luau/Constraint.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ struct FunctionCallConstraint
class AstExprCall* callSite = nullptr;
std::vector<std::optional<TypeId>> discriminantTypes;

std::vector<TypeId> typeArguments;
std::vector<TypePackId> typePackArguments;

// When we dispatch this constraint, we update the key at this map to record
// the overload that we selected.
DenseHashMap<const AstNode*, TypeId>* astOverloadResolvedTypes = nullptr;
Expand Down Expand Up @@ -292,6 +295,16 @@ struct PushFunctionTypeConstraint
bool isSelf;
};

// Binds the function to a set of explicitly specified types,
// for f<<T>>.
struct TypeInstantiationConstraint
{
TypeId functionType;
TypeId placeholderType;
std::vector<TypeId> typeArguments;
std::vector<TypePackId> typePackArguments;
};

struct PushTypeConstraint
{
TypeId expectedType;
Expand Down Expand Up @@ -321,7 +334,8 @@ using ConstraintV = Variant<
EqualityConstraint,
SimplifyConstraint,
PushFunctionTypeConstraint,
PushTypeConstraint>;
PushTypeConstraint,
TypeInstantiationConstraint>;

struct Constraint
{
Expand Down
3 changes: 3 additions & 0 deletions Analysis/include/Luau/ConstraintGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ struct ConstraintGenerator
Inference check(const ScopePtr& scope, AstExprIfElse* ifElse, std::optional<TypeId> expectedType);
Inference check(const ScopePtr& scope, AstExprTypeAssertion* typeAssert);
Inference check(const ScopePtr& scope, AstExprInterpString* interpString);
Inference check(const ScopePtr& scope, AstExprInstantiate* explicitTypeInstantiation);
Inference check(const ScopePtr& scope, AstExprTable* expr, std::optional<TypeId> expectedType);
std::tuple<TypeId, TypeId, RefinementId> checkBinary(
const ScopePtr& scope,
Expand Down Expand Up @@ -482,6 +483,8 @@ struct ConstraintGenerator

void fillInInferredBindings(const ScopePtr& globalScope, AstStatBlock* block);

std::pair<std::vector<TypeId>, std::vector<TypePackId>> resolveTypeArguments(const ScopePtr& scope, const AstArray<AstTypeOrPack>& typeArguments);

/** Given a function type annotation, return a vector describing the expected types of the calls to the function
* For example, calling a function with annotation ((number) -> string & ((string) -> number))
* yields a vector of size 1, with value: [number | string]
Expand Down
10 changes: 9 additions & 1 deletion Analysis/include/Luau/ConstraintSolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ struct ConstraintSolver
bool tryDispatch(const FunctionCheckConstraint& c, NotNull<const Constraint> constraint, bool force);
bool tryDispatch(const PrimitiveTypeConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const HasPropConstraint& c, NotNull<const Constraint> constraint);

bool tryDispatch(const TypeInstantiationConstraint& c, NotNull<const Constraint> constraint);

bool tryDispatchHasIndexer(
int& recursionDepth,
Expand Down Expand Up @@ -470,6 +470,14 @@ struct ConstraintSolver

TypeId simplifyUnion(NotNull<Scope> scope, Location location, TypeId left, TypeId right);

TypeId instantiateFunctionType(
TypeId functionTypeId,
const std::vector<TypeId>& typeArguments,
const std::vector<TypePackId>& typePackArguments,
NotNull<Scope> scope,
const Location& location
);

TypePackId anyifyModuleReturnTypePackGenerics(TypePackId tp);

void throwTimeLimitError() const;
Expand Down
1 change: 1 addition & 0 deletions Analysis/include/Luau/DataFlowGraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ struct DataFlowGraphBuilder
DataFlowResult visitExpr(AstExprTypeAssertion* t);
DataFlowResult visitExpr(AstExprIfElse* i);
DataFlowResult visitExpr(AstExprInterpString* i);
DataFlowResult visitExpr(AstExprInstantiate* i);
DataFlowResult visitExpr(AstExprError* error);

void visitLValue(AstExpr* e, DefId incomingDef);
Expand Down
35 changes: 33 additions & 2 deletions Analysis/include/Luau/Error.h
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,36 @@ struct GenericBoundsMismatch
bool operator==(const GenericBoundsMismatch& rhs) const;
};

// Used `f<<T>>` where f is not a function
struct InstantiateGenericsOnNonFunction
{
enum class InterestingEdgeCase
{
None,
MetatableCall,
Intersection,
};

InterestingEdgeCase interestingEdgeCase;

bool operator==(const InstantiateGenericsOnNonFunction&) const;
};

// Provided too many generics inside `f<<T>>`
struct TypeInstantiationCountMismatch
{
std::optional<std::string> functionName;
TypeId functionType;

size_t providedTypes = 0;
size_t maximumTypes = 0;

size_t providedTypePacks = 0;
size_t maximumTypePacks = 0;

bool operator==(const TypeInstantiationCountMismatch&) const;
};

// Error when referencing a type function without providing explicit generics.
//
// type function create_table_with_key()
Expand Down Expand Up @@ -609,8 +639,9 @@ using TypeErrorData = Variant<
MultipleNonviableOverloads,
RecursiveRestraintViolation,
GenericBoundsMismatch,
UnappliedTypeFunction>;

UnappliedTypeFunction,
InstantiateGenericsOnNonFunction,
TypeInstantiationCountMismatch>;

struct TypeErrorSummary
{
Expand Down
2 changes: 2 additions & 0 deletions Analysis/include/Luau/ToString.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ std::string dump(const std::optional<TypeId>& ty);
std::string dump(TypePackId ty);
std::string dump(const std::optional<TypePackId>& ty);
std::string dump(const std::vector<TypeId>& types);
std::string dump(const std::vector<TypePackId>& types);
std::string dump(DenseHashMap<TypeId, TypeId>& types);
std::string dump(DenseHashMap<TypePackId, TypePackId>& types);

Expand All @@ -163,4 +164,5 @@ inline std::string toString(const TypeOrPack& tyOrTp)
std::string dump(const TypeOrPack& tyOrTp);

std::string toStringVector(const std::vector<TypeId>& types, ToStringOptions& opts);
std::string toStringVector(const std::vector<TypePackId>& typePacks, ToStringOptions& opts);
} // namespace Luau
3 changes: 3 additions & 0 deletions Analysis/include/Luau/TypeChecker2.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ struct TypeChecker2
void visit(AstExprTypeAssertion* expr);
void visit(AstExprIfElse* expr);
void visit(AstExprInterpString* interpString);
void visit(AstExprInstantiate* explicitTypeInstantiation);
void visit(AstExprError* expr);
TypeId flattenPack(TypePackId pack);
void visitGenerics(AstArray<AstGenericType*> generics, AstArray<AstGenericTypePack*> genericPacks);
Expand Down Expand Up @@ -233,6 +234,8 @@ struct TypeChecker2

void suggestAnnotations(AstExprFunction* expr, TypeId ty);

void checkTypeInstantiation(AstExpr* baseFunctionExpr, TypeId fnType, const Location& location, const AstArray<AstTypeOrPack>& typeArguments);

void diagnoseMissingTableKey(UnknownProperty* utk, TypeErrorData& data) const;
bool isErrorSuppressing(Location loc, TypeId ty);
bool isErrorSuppressing(Location loc1, TypeId ty1, Location loc2, TypeId ty2);
Expand Down
3 changes: 3 additions & 0 deletions Analysis/include/Luau/TypeFunctionRuntimeBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ struct TypeFunctionRuntimeBuilderState
};

TypeFunctionTypeId serialize(TypeId ty, TypeFunctionRuntimeBuilderState* state);
TypeFunctionTypePackId serialize(TypePackId tp, TypeFunctionRuntimeBuilderState* state);

TypeId deserialize(TypeFunctionTypeId ty, TypeFunctionRuntimeBuilderState* state);
TypePackId deserialize(TypeFunctionTypePackId tp, TypeFunctionRuntimeBuilderState* state);

} // namespace Luau
9 changes: 9 additions & 0 deletions Analysis/include/Luau/TypeInfer.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ struct TypeChecker
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprError& expr);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprIfElse& expr, std::optional<TypeId> expectedType = std::nullopt);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprInterpString& expr);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprInstantiate& expr);

TypeId checkExprTable(
const ScopePtr& scope,
Expand Down Expand Up @@ -227,6 +228,14 @@ struct TypeChecker
const std::vector<std::optional<TypeId>>& expectedTypes = {}
);

TypeId instantiateTypeParameters(
const ScopePtr& scope,
TypeId baseType,
const AstArray<AstTypeOrPack>& explicitTypes,
const AstExpr* functionExpr,
const Location& location
);

static std::optional<AstExpr*> matchRequire(const AstExprCall& call);
TypeId checkRequire(const ScopePtr& scope, const ModuleInfo& moduleInfo, const Location& location);

Expand Down
36 changes: 23 additions & 13 deletions Analysis/src/BuiltinTypeFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit)
LUAU_DYNAMIC_FASTINTVARIABLE(LuauStepRefineRecursionLimit, 64)
LUAU_FASTFLAG(LuauReduceSetTypeStackPressure)

LUAU_FASTFLAGVARIABLE(LuauRefineNoRefineAlways)
LUAU_FASTFLAGVARIABLE(LuauRefineNoRefineAlways2)
LUAU_FASTFLAGVARIABLE(LuauRefineDistributesOverUnions)
LUAU_FASTFLAG(LuauEGFixGenericsList)
LUAU_FASTFLAG(LuauNoMoreComparisonTypeFunctions)
Expand Down Expand Up @@ -1304,28 +1304,38 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
}

std::vector<TypeId> discriminantTypes;
for (size_t i = 1; i < typeParams.size(); i++)
discriminantTypes.push_back(follow(typeParams.at(i)));

if (FFlag::LuauRefineNoRefineAlways)
if (FFlag::LuauRefineNoRefineAlways2)
{
bool hasAnyRealRefinements = false;
for (auto discriminant : discriminantTypes)
for (size_t i = 1; i < typeParams.size(); i++)
{
auto discriminant = follow(typeParams[i]);

// Filter out any top level types that are meaningless to refine
// against.
if (is<UnknownType, NoRefineType>(discriminant))
continue;

// If the discriminant type is only:
// - The `*no-refine*` type or,
// - tables, metatables, unions, intersections, functions, or negations _containing_ `*no-refine*`.
// - The `*no-refine*` type (covered above) or;
// - tables, metatables, unions, intersections, functions, or
// negations containing `*no-refine*` (covered below).
// There's no point in refining against it.
ContainsRefinableType crt;
crt.traverse(discriminant);

hasAnyRealRefinements = hasAnyRealRefinements || crt.found;
if (crt.found)
discriminantTypes.push_back(discriminant);
}

// if we don't have any real refinements, i.e. they're all `*no-refine*`, then we can reduce immediately.
if (!hasAnyRealRefinements)
if (discriminantTypes.empty())
return {targetTy, {}};
}
else
{
for (size_t i = 1; i < typeParams.size(); i++)
discriminantTypes.push_back(follow(typeParams.at(i)));
}

const bool targetIsPending = isBlockedOrUnsolvedType(targetTy);

Expand Down Expand Up @@ -1385,8 +1395,8 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
}
else
{
// FFlag::LuauRefineNoRefineAlways moves this check upwards so that it runs even if the thing being refined is pending.
if (!FFlag::LuauRefineNoRefineAlways)
// FFlag::LuauRefineNoRefineAlways2 moves this check upwards so that it runs even if the thing being refined is pending.
if (!FFlag::LuauRefineNoRefineAlways2)
{
// If the discriminant type is only:
// - The `*no-refine*` type or,
Expand Down
Loading