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};
|
||||
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.
|
||||
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
|
||||
{
|
||||
ConstraintSolver* solver;
|
||||
@ -305,7 +551,27 @@ struct InstantiationQueuer : TypeOnceVisitor
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -426,6 +692,9 @@ void ConstraintSolver::run()
|
||||
snapshot = logger->prepareStepSnapshot(rootScope, c, force, unsolvedConstraints);
|
||||
}
|
||||
|
||||
// Set current Constraint
|
||||
currentConstraintRef = c.get();
|
||||
|
||||
bool success = tryDispatch(c, force);
|
||||
|
||||
progress |= success;
|
||||
|
@ -953,6 +953,36 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_wait_for_pending_no_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")
|
||||
{
|
||||
if (!FFlag::LuauSolverV2)
|
||||
|
Loading…
Reference in New Issue
Block a user