Skip to content

Commit

Permalink
Merge branch 'upstream' into merge
Browse files Browse the repository at this point in the history
  • Loading branch information
andyfriesen committed Aug 9, 2024
2 parents 057bdf3 + 8a99f25 commit c229a9e
Show file tree
Hide file tree
Showing 33 changed files with 695 additions and 332 deletions.
5 changes: 3 additions & 2 deletions Analysis/include/Luau/AnyTypeSummary.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ struct AnyTypeSummary
std::optional<TypePackId> lookupPackAnnotation(AstTypePack* annotation, const Module* module);
TypeId checkForTypeFunctionInhabitance(const TypeId instance, const Location location);

enum Pattern: uint64_t
enum Pattern : uint64_t
{
Casts,
FuncArg,
Expand All @@ -82,7 +82,8 @@ struct AnyTypeSummary
VarAny,
TableProp,
Alias,
Assign
Assign,
TypePk
};

struct TypeInfo
Expand Down
6 changes: 3 additions & 3 deletions Analysis/include/Luau/DataFlowGraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,9 @@ struct DataFlowGraphBuilder
DataFlowResult visitExpr(DfgScope* scope, AstExprInterpString* i);
DataFlowResult visitExpr(DfgScope* scope, AstExprError* error);

void visitLValue(DfgScope* scope, AstExpr* e, DefId incomingDef, bool isCompoundAssignment = false);
DefId visitLValue(DfgScope* scope, AstExprLocal* l, DefId incomingDef, bool isCompoundAssignment);
DefId visitLValue(DfgScope* scope, AstExprGlobal* g, DefId incomingDef, bool isCompoundAssignment);
void visitLValue(DfgScope* scope, AstExpr* e, DefId incomingDef);
DefId visitLValue(DfgScope* scope, AstExprLocal* l, DefId incomingDef);
DefId visitLValue(DfgScope* scope, AstExprGlobal* g, DefId incomingDef);
DefId visitLValue(DfgScope* scope, AstExprIndexName* i, DefId incomingDef);
DefId visitLValue(DfgScope* scope, AstExprIndexExpr* i, DefId incomingDef);
DefId visitLValue(DfgScope* scope, AstExprError* e, DefId incomingDef);
Expand Down
1 change: 1 addition & 0 deletions Analysis/include/Luau/Subtyping.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ struct Subtyping
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const ClassType* subClass, const ClassType* superClass);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const ClassType* subClass, TypeId superTy, const TableType* superTable);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const FunctionType* subFunction, const FunctionType* superFunction);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TableType* subTable, const PrimitiveType* superPrim);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const PrimitiveType* subPrim, const TableType* superTable);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const SingletonType* subSingleton, const TableType* superTable);

Expand Down
13 changes: 12 additions & 1 deletion Analysis/include/Luau/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -594,10 +594,21 @@ struct TypeFunctionInstanceType
std::vector<TypeId> typeArguments;
std::vector<TypePackId> packArguments;

TypeFunctionInstanceType(NotNull<const TypeFunction> function, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments)
std::optional<AstName> userFuncName; // Name of the user-defined type function; only available for UDTFs
std::optional<AstExprFunction*> userFuncBody; // Body of the user-defined type function; only available for UDTFs

TypeFunctionInstanceType(
NotNull<const TypeFunction> function,
std::vector<TypeId> typeArguments,
std::vector<TypePackId> packArguments,
std::optional<AstName> userFuncName = std::nullopt,
std::optional<AstExprFunction*> userFuncBody = std::nullopt
)
: function(function)
, typeArguments(typeArguments)
, packArguments(packArguments)
, userFuncName(userFuncName)
, userFuncBody(userFuncBody)
{
}

Expand Down
5 changes: 5 additions & 0 deletions Analysis/include/Luau/TypeFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ struct TypeFunctionContext
// The constraint being reduced in this run of the reduction
const Constraint* constraint;

std::optional<AstName> userFuncName; // Name of the user-defined type function; only available for UDTFs
std::optional<AstExprFunction*> userFuncBody; // Body of the user-defined type function; only available for UDTFs

TypeFunctionContext(NotNull<ConstraintSolver> cs, NotNull<Scope> scope, NotNull<const Constraint> constraint)
: arena(cs->arena)
, builtins(cs->builtinTypes)
Expand Down Expand Up @@ -156,6 +159,8 @@ struct BuiltinTypeFunctions
{
BuiltinTypeFunctions();

TypeFunction userFunc;

TypeFunction notFunc;
TypeFunction lenFunc;
TypeFunction unmFunc;
Expand Down
19 changes: 18 additions & 1 deletion Analysis/src/AnyTypeSummary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ void AnyTypeSummary::visit(const Scope* scope, AstStatReturn* ret, const Module*
const Scope* retScope = findInnerMostScope(ret->location, module);

auto ctxNode = getNode(rootSrc, ret);
bool seenTP = false;

for (auto val : ret->list)
{
Expand All @@ -160,7 +161,23 @@ void AnyTypeSummary::visit(const Scope* scope, AstStatReturn* ret, const Module*
typeInfo.push_back(ti);
}
}

if (ret->list.size > 1 && !seenTP)
{
if (containsAny(retScope->returnType))
{
seenTP = true;

TelemetryTypePair types;

types.inferredType = toString(retScope->returnType);

TypeInfo ti{Pattern::TypePk, toString(ctxNode), types};
typeInfo.push_back(ti);
}
}
}

}

void AnyTypeSummary::visit(const Scope* scope, AstStatLocal* local, const Module* module, NotNull<BuiltinTypes> builtinTypes)
Expand Down Expand Up @@ -189,7 +206,7 @@ void AnyTypeSummary::visit(const Scope* scope, AstStatLocal* local, const Module
typeInfo.push_back(ti);
}
}

const AstExprTypeAssertion* maybeRequire = local->values.data[posn]->as<AstExprTypeAssertion>();
if (!maybeRequire)
continue;
Expand Down
60 changes: 60 additions & 0 deletions Analysis/src/ConstraintGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
TypeId domainTy = builtinTypes->neverType;
for (TypeId d : domain)
{
d = follow(d);
if (d == ty)
continue;
domainTy = simplifyUnion(builtinTypes, arena, domainTy, d).result;
Expand Down Expand Up @@ -663,6 +664,51 @@ ControlFlow ConstraintGenerator::visitBlockWithoutChildScope(const ScopePtr& sco
astTypeAliasDefiningScopes[alias] = defnScope;
aliasDefinitionLocations[alias->name.value] = alias->location;
}
else if (auto function = stat->as<AstStatTypeFunction>())
{
// If a type function w/ same name has already been defined, error for having duplicates
if (scope->exportedTypeBindings.count(function->name.value) || scope->privateTypeBindings.count(function->name.value))
{
auto it = aliasDefinitionLocations.find(function->name.value);
LUAU_ASSERT(it != aliasDefinitionLocations.end());
reportError(function->location, DuplicateTypeDefinition{function->name.value, it->second});
continue;
}

ScopePtr defnScope = childScope(function, scope);

// Create TypeFunctionInstanceType

std::vector<TypeId> typeParams;
typeParams.reserve(function->body->args.size);

std::vector<GenericTypeDefinition> quantifiedTypeParams;
quantifiedTypeParams.reserve(function->body->args.size);

for (size_t i = 0; i < function->body->args.size; i++)
{
std::string name = format("T%zu", i);
TypeId ty = arena->addType(GenericType{name});
typeParams.push_back(ty);

GenericTypeDefinition genericTy{ty};
quantifiedTypeParams.push_back(genericTy);
}

TypeId typeFunctionTy = arena->addType(TypeFunctionInstanceType{
NotNull{&builtinTypeFunctions().userFunc},
std::move(typeParams),
{},
function->name,
function->body,
});

TypeFun typeFunction{std::move(quantifiedTypeParams), typeFunctionTy};

// Set type bindings and definition locations for this user-defined type function
scope->privateTypeBindings[function->name.value] = std::move(typeFunction);
aliasDefinitionLocations[function->name.value] = function->location;
}
}

std::optional<ControlFlow> firstControlFlow;
Expand Down Expand Up @@ -1368,6 +1414,20 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeAlias*

ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunction* function)
{
// If a type function with the same name was already defined, we skip over
auto bindingIt = scope->privateTypeBindings.find(function->name.value);
if (bindingIt == scope->privateTypeBindings.end())
return ControlFlow::None;

TypeFun typeFunction = bindingIt->second;

// Adding typeAliasExpansionConstraint on user-defined type function for the constraint solver
if (auto typeFunctionTy = get<TypeFunctionInstanceType>(typeFunction.type))
{
TypeId expansionTy = arena->addType(PendingExpansionType{{}, function->name, typeFunctionTy->typeArguments, typeFunctionTy->packArguments});
addConstraint(scope, function->location, TypeAliasExpansionConstraint{/* target */ expansionTy});
}

return ControlFlow::None;
}

Expand Down
6 changes: 4 additions & 2 deletions Analysis/src/ConstraintSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,10 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
return true;
}

// Adding ReduceConstraint on type function for the constraint solver
if (auto typeFn = get<TypeFunctionInstanceType>(follow(tf->type)))
pushConstraint(NotNull(constraint->scope.get()), constraint->location, ReduceConstraint{tf->type});

// If there are no parameters to the type function we can just use the type
// directly.
if (tf->typeParams.empty() && tf->typePackParams.empty())
Expand Down Expand Up @@ -1051,7 +1055,6 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
// there are e.g. generic saturatedTypeArguments that go unused.
const TableType* tfTable = getTableType(tf->type);

//clang-format off
bool needsClone = follow(tf->type) == target || (tfTable != nullptr && tfTable == getTableType(target)) ||
std::any_of(
typeArguments.begin(),
Expand All @@ -1061,7 +1064,6 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
return other == target;
}
);
//clang-format on

// Only tables have the properties we're trying to set.
TableType* ttv = getMutableTableType(target);
Expand Down
35 changes: 7 additions & 28 deletions Analysis/src/DataFlowGraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -570,15 +570,8 @@ ControlFlow DataFlowGraphBuilder::visit(DfgScope* scope, AstStatAssign* a)

ControlFlow DataFlowGraphBuilder::visit(DfgScope* scope, AstStatCompoundAssign* c)
{
// TODO: This needs revisiting because this is incorrect. The `c->var` part is both being read and written to,
// but the `c->var` only has one pointer address, so we need to come up with a way to store both.
// For now, it's not important because we don't have type states, but it is going to be important, e.g.
//
// local a = 5 -- a-1
// a += 5 -- a-2 = a-1 + 5
// We can't just visit `c->var` as a rvalue and then separately traverse `c->var` as an lvalue, since that's O(n^2).
DefId def = visitExpr(scope, c->value).def;
visitLValue(scope, c->var, def, /* isCompoundAssignment */ true);
(void) visitExpr(scope, c->value);
(void) visitExpr(scope, c->var);

return ControlFlow::None;
}
Expand Down Expand Up @@ -920,14 +913,14 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(DfgScope* scope, AstExprError* er
return {defArena->freshCell(), nullptr};
}

void DataFlowGraphBuilder::visitLValue(DfgScope* scope, AstExpr* e, DefId incomingDef, bool isCompoundAssignment)
void DataFlowGraphBuilder::visitLValue(DfgScope* scope, AstExpr* e, DefId incomingDef)
{
auto go = [&]()
{
if (auto l = e->as<AstExprLocal>())
return visitLValue(scope, l, incomingDef, isCompoundAssignment);
return visitLValue(scope, l, incomingDef);
else if (auto g = e->as<AstExprGlobal>())
return visitLValue(scope, g, incomingDef, isCompoundAssignment);
return visitLValue(scope, g, incomingDef);
else if (auto i = e->as<AstExprIndexName>())
return visitLValue(scope, i, incomingDef);
else if (auto i = e->as<AstExprIndexExpr>())
Expand All @@ -941,15 +934,8 @@ void DataFlowGraphBuilder::visitLValue(DfgScope* scope, AstExpr* e, DefId incomi
graph.astDefs[e] = go();
}

DefId DataFlowGraphBuilder::visitLValue(DfgScope* scope, AstExprLocal* l, DefId incomingDef, bool isCompoundAssignment)
DefId DataFlowGraphBuilder::visitLValue(DfgScope* scope, AstExprLocal* l, DefId incomingDef)
{
// We need to keep the previous def around for a compound assignment.
if (isCompoundAssignment)
{
DefId def = lookup(scope, l->local);
graph.compoundAssignDefs[l] = def;
}

// In order to avoid alias tracking, we need to clip the reference to the parent def.
if (scope->canUpdateDefinition(l->local))
{
Expand All @@ -962,15 +948,8 @@ DefId DataFlowGraphBuilder::visitLValue(DfgScope* scope, AstExprLocal* l, DefId
return visitExpr(scope, static_cast<AstExpr*>(l)).def;
}

DefId DataFlowGraphBuilder::visitLValue(DfgScope* scope, AstExprGlobal* g, DefId incomingDef, bool isCompoundAssignment)
DefId DataFlowGraphBuilder::visitLValue(DfgScope* scope, AstExprGlobal* g, DefId incomingDef)
{
// We need to keep the previous def around for a compound assignment.
if (isCompoundAssignment)
{
DefId def = lookup(scope, g->name);
graph.compoundAssignDefs[g] = def;
}

// In order to avoid alias tracking, we need to clip the reference to the parent def.
if (scope->canUpdateDefinition(g->name))
{
Expand Down
40 changes: 34 additions & 6 deletions Analysis/src/Normalize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2186,6 +2186,11 @@ void Normalizer::intersectClasses(NormalizedClassType& heres, const NormalizedCl

if (isSubclass(thereTy, hereTy))
{
// If thereTy is a subtype of hereTy, we need to replace hereTy
// by thereTy and combine their negation lists.
//
// If any types in the negation list are not subtypes of
// thereTy, they need to be removed from the negation list.
TypeIds negations = std::move(hereNegations);

for (auto nIt = negations.begin(); nIt != negations.end();)
Expand All @@ -2209,22 +2214,45 @@ void Normalizer::intersectClasses(NormalizedClassType& heres, const NormalizedCl
}
else if (isSubclass(hereTy, thereTy))
{
// If thereTy is a supertype of hereTy, we need to extend the
// negation list of hereTy by that of thereTy.
//
// If any of the types of thereTy's negations are not subtypes
// of hereTy, they must not be added to hereTy's negation list.
//
// If any of the types of thereTy's negations are supertypes of
// hereTy, then hereTy must be removed entirely.
//
// If any of the types of thereTy's negations are supertypes of
// the negations of herety, the former must supplant the latter.
TypeIds negations = thereNegations;

bool erasedHere = false;

for (auto nIt = negations.begin(); nIt != negations.end();)
{
if (!isSubclass(*nIt, hereTy))
if (isSubclass(hereTy, *nIt))
{
nIt = negations.erase(nIt);
// eg SomeClass & (class & ~SomeClass)
// or SomeClass & (class & ~ParentClass)
heres.classes.erase(hereTy);
it = heres.ordering.erase(it);
erasedHere = true;
break;
}

// eg SomeClass & (class & ~Unrelated)
if (!isSubclass(*nIt, hereTy))
nIt = negations.erase(nIt);
else
{
++nIt;
}
}

unionClasses(hereNegations, negations);
break;
if (!erasedHere)
{
unionClasses(hereNegations, negations);
++it;
}
}
else if (hereTy == thereTy)
{
Expand Down
2 changes: 1 addition & 1 deletion Analysis/src/OverloadResolution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ std::pair<OverloadResolver::Analysis, ErrorVec> OverloadResolver::checkOverload_
// function arguments are options, then this function call
// is ok.

const size_t firstUnsatisfiedArgument = argExprs->size();
const size_t firstUnsatisfiedArgument = args->head.size();
const auto [requiredHead, _requiredTail] = flatten(fn->argTypes);

// If too many arguments were supplied, this overload
Expand Down
Loading

0 comments on commit c229a9e

Please sign in to comment.