mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 14:25:44 +08:00
Missing dependency fix for Type Functions
This commit is contained in:
parent
e8a7acb802
commit
0f3102c254
@ -98,6 +98,9 @@ struct ConstraintSolver
|
|||||||
DenseHashSet<TypeId> generalizedTypes_{nullptr};
|
DenseHashSet<TypeId> generalizedTypes_{nullptr};
|
||||||
const NotNull<DenseHashSet<TypeId>> generalizedTypes{&generalizedTypes_};
|
const NotNull<DenseHashSet<TypeId>> generalizedTypes{&generalizedTypes_};
|
||||||
|
|
||||||
|
// The current Constraint that is being processed, can be nullptr.
|
||||||
|
const Constraint* currentConstraintRef;
|
||||||
|
|
||||||
// Recorded errors that take place within the solver.
|
// Recorded errors that take place within the solver.
|
||||||
ErrorVec errors;
|
ErrorVec errors;
|
||||||
|
|
||||||
|
@ -284,6 +284,252 @@ void dump(ConstraintSolver* cs, ToStringOptions& opts)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct TypeFinder : TypeOnceVisitor
|
||||||
|
{
|
||||||
|
TypeId tyToFind;
|
||||||
|
bool hasFoundType = false;
|
||||||
|
|
||||||
|
// SOLID Principle for telling that we found the type.
|
||||||
|
void makeFound(TypeId ty)
|
||||||
|
{
|
||||||
|
hasFoundType = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the boolean
|
||||||
|
// so we don't tell that we found a type we didn't actually find.
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
hasFoundType = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(TypeId ty) override
|
||||||
|
{
|
||||||
|
ty = follow(ty);
|
||||||
|
|
||||||
|
if (ty == tyToFind)
|
||||||
|
makeFound(ty); // If we found the type
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override for IntersectionTypes
|
||||||
|
bool visit(TypeId ty, const IntersectionType& iTy) override
|
||||||
|
{
|
||||||
|
for (TypeId part : iTy.parts)
|
||||||
|
{
|
||||||
|
part = follow(part);
|
||||||
|
|
||||||
|
if (part == tyToFind)
|
||||||
|
makeFound(part); // If we found the type
|
||||||
|
}
|
||||||
|
|
||||||
|
return visit(ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isMatch(TypeId ty)
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
|
||||||
|
if (ty == tyToFind)
|
||||||
|
makeFound(ty);
|
||||||
|
else
|
||||||
|
traverse(ty);
|
||||||
|
|
||||||
|
if (hasFoundType == true)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// A way to find a type, such as a dependency through Constraints
|
||||||
|
struct ConstraintTypeFinder
|
||||||
|
{
|
||||||
|
// TODO: Standardize Constraint Visitors?
|
||||||
|
|
||||||
|
TypeId tyToFind;
|
||||||
|
TypeFinder typeFinder{};
|
||||||
|
|
||||||
|
explicit ConstraintTypeFinder(TypeId tyToFind)
|
||||||
|
: tyToFind(tyToFind)
|
||||||
|
{
|
||||||
|
typeFinder.tyToFind = tyToFind;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(AssignIndexConstraint c)
|
||||||
|
{
|
||||||
|
if (typeFinder.isMatch(c.indexType))
|
||||||
|
return true;
|
||||||
|
if (typeFinder.isMatch(c.lhsType))
|
||||||
|
return true;
|
||||||
|
if (typeFinder.isMatch(c.propType))
|
||||||
|
return true;
|
||||||
|
if (typeFinder.isMatch(c.rhsType))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool visit(AssignPropConstraint c)
|
||||||
|
{
|
||||||
|
if (typeFinder.isMatch(c.lhsType))
|
||||||
|
return true;
|
||||||
|
if (typeFinder.isMatch(c.propType))
|
||||||
|
return true;
|
||||||
|
if (typeFinder.isMatch(c.rhsType))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool visit(EqualityConstraint c)
|
||||||
|
{
|
||||||
|
if (typeFinder.isMatch(c.assignmentType))
|
||||||
|
return true;
|
||||||
|
if (typeFinder.isMatch(c.resultType))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool visit(FunctionCallConstraint c)
|
||||||
|
{
|
||||||
|
if (typeFinder.isMatch(c.fn))
|
||||||
|
return true;
|
||||||
|
// argPacks not included
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool visit(FunctionCheckConstraint c)
|
||||||
|
{
|
||||||
|
if (typeFinder.isMatch(c.fn))
|
||||||
|
return true;
|
||||||
|
// argsPacks not included
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool visit(GeneralizationConstraint c)
|
||||||
|
{
|
||||||
|
if (typeFinder.isMatch(c.generalizedType))
|
||||||
|
return true;
|
||||||
|
if (typeFinder.isMatch(c.sourceType))
|
||||||
|
return true;
|
||||||
|
for (auto ty : c.interiorTypes)
|
||||||
|
{
|
||||||
|
if (typeFinder.isMatch(ty))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool visit(IterableConstraint c)
|
||||||
|
{
|
||||||
|
for (auto ty : c.variables)
|
||||||
|
{
|
||||||
|
if (typeFinder.isMatch(ty))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool visit(NameConstraint c)
|
||||||
|
{
|
||||||
|
if (typeFinder.isMatch(c.namedType))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
for (auto ty : c.typeParameters)
|
||||||
|
{
|
||||||
|
if (typeFinder.isMatch(ty))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool visit(PackSubtypeConstraint c)
|
||||||
|
{
|
||||||
|
// ?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool visit(PrimitiveTypeConstraint c)
|
||||||
|
{
|
||||||
|
if (typeFinder.isMatch(c.freeType))
|
||||||
|
return true;
|
||||||
|
if (typeFinder.isMatch(c.primitiveType))
|
||||||
|
return true;
|
||||||
|
// expectedType needed?
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool visit(ReduceConstraint c)
|
||||||
|
{
|
||||||
|
if (typeFinder.isMatch(c.ty))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool visit(ReducePackConstraint c)
|
||||||
|
{
|
||||||
|
// ?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool visit(SubtypeConstraint c)
|
||||||
|
{
|
||||||
|
if (typeFinder.isMatch(c.subType))
|
||||||
|
return true;
|
||||||
|
if (typeFinder.isMatch(c.superType))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool visit(TypeAliasExpansionConstraint c)
|
||||||
|
{
|
||||||
|
if (typeFinder.isMatch(c.target))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool visit(UnpackConstraint c)
|
||||||
|
{
|
||||||
|
// ?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Returns true if it found the type.
|
||||||
|
bool traverseAndFind(ConstraintV cV)
|
||||||
|
{
|
||||||
|
if (auto c = get_if<AssignIndexConstraint>(&cV))
|
||||||
|
return visit(*c);
|
||||||
|
else if (auto c = get_if<AssignPropConstraint>(&cV))
|
||||||
|
return visit(*c);
|
||||||
|
else if (auto c = get_if<EqualityConstraint>(&cV))
|
||||||
|
return visit(*c);
|
||||||
|
else if (auto c = get_if<FunctionCallConstraint>(&cV))
|
||||||
|
return visit(*c);
|
||||||
|
else if (auto c = get_if<FunctionCheckConstraint>(&cV))
|
||||||
|
return visit(*c);
|
||||||
|
else if (auto c = get_if<GeneralizationConstraint>(&cV))
|
||||||
|
return visit(*c);
|
||||||
|
else if (auto c = get_if<IterableConstraint>(&cV))
|
||||||
|
return visit(*c);
|
||||||
|
else if (auto c = get_if<NameConstraint>(&cV))
|
||||||
|
return visit(*c);
|
||||||
|
else if (auto c = get_if<PackSubtypeConstraint>(&cV))
|
||||||
|
return visit(*c);
|
||||||
|
else if (auto c = get_if<PrimitiveTypeConstraint>(&cV))
|
||||||
|
return visit(*c);
|
||||||
|
else if (auto c = get_if<ReduceConstraint>(&cV))
|
||||||
|
return visit(*c);
|
||||||
|
else if (auto c = get_if<ReducePackConstraint>(&cV))
|
||||||
|
return visit(*c);
|
||||||
|
else if (auto c = get_if<SubtypeConstraint>(&cV))
|
||||||
|
return visit(*c);
|
||||||
|
else if (auto c = get_if<TypeAliasExpansionConstraint>(&cV))
|
||||||
|
return visit(*c);
|
||||||
|
else if (auto c = get_if<UnpackConstraint>(&cV))
|
||||||
|
return visit(*c);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct InstantiationQueuer : TypeOnceVisitor
|
struct InstantiationQueuer : TypeOnceVisitor
|
||||||
{
|
{
|
||||||
ConstraintSolver* solver;
|
ConstraintSolver* solver;
|
||||||
@ -305,7 +551,27 @@ struct InstantiationQueuer : TypeOnceVisitor
|
|||||||
|
|
||||||
bool visit(TypeId ty, const TypeFunctionInstanceType&) override
|
bool visit(TypeId ty, const TypeFunctionInstanceType&) override
|
||||||
{
|
{
|
||||||
solver->pushConstraint(scope, location, ReduceConstraint{ty});
|
auto newConstraint = solver->pushConstraint(scope, location, ReduceConstraint{ty});
|
||||||
|
|
||||||
|
if (solver->currentConstraintRef)
|
||||||
|
{
|
||||||
|
// Block the things that depend on the Pending Expansion that is being expanded
|
||||||
|
// On the resolution of the new Reduction Constraint
|
||||||
|
if (auto currentC = get_if<TypeAliasExpansionConstraint>(&solver->currentConstraintRef->c))
|
||||||
|
{
|
||||||
|
auto cFinder = ConstraintTypeFinder(currentC->target);
|
||||||
|
|
||||||
|
for (auto constraint : solver->constraints)
|
||||||
|
{
|
||||||
|
if (solver->currentConstraintRef == constraint || newConstraint == constraint)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (cFinder.traverseAndFind(constraint.get()->c))
|
||||||
|
solver->block(static_cast<NotNull<const Constraint>>(newConstraint), constraint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,6 +692,9 @@ void ConstraintSolver::run()
|
|||||||
snapshot = logger->prepareStepSnapshot(rootScope, c, force, unsolvedConstraints);
|
snapshot = logger->prepareStepSnapshot(rootScope, c, force, unsolvedConstraints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set current Constraint
|
||||||
|
currentConstraintRef = c.get();
|
||||||
|
|
||||||
bool success = tryDispatch(c, force);
|
bool success = tryDispatch(c, force);
|
||||||
|
|
||||||
progress |= success;
|
progress |= success;
|
||||||
|
@ -953,6 +953,36 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_wait_for_pending_no_crash")
|
|||||||
// Should not crash!
|
// Should not crash!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_andGeneralTypeFunction_dependency_issue1")
|
||||||
|
{
|
||||||
|
if (!FFlag::LuauSolverV2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Missing Dependency Fix check
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
--!strict
|
||||||
|
|
||||||
|
local PlayerData = {
|
||||||
|
Coins = 0,
|
||||||
|
Level = 1,
|
||||||
|
Exp = 0,
|
||||||
|
MaxExp = 100
|
||||||
|
}
|
||||||
|
|
||||||
|
type Keys = keyof<typeof(PlayerData)>
|
||||||
|
|
||||||
|
-- This function makes it think that there's going to be a pending expansion
|
||||||
|
local function UpdateData(key: Keys, value)
|
||||||
|
PlayerData[key] = value
|
||||||
|
end
|
||||||
|
|
||||||
|
UpdateData("Coins", 2)
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works_w_array")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works_w_array")
|
||||||
{
|
{
|
||||||
if (!FFlag::LuauSolverV2)
|
if (!FFlag::LuauSolverV2)
|
||||||
|
Loading…
Reference in New Issue
Block a user