Skip to content

Commit 147f24f

Browse files
committed
HUGE find, missing dependency fix for Type Functions, which also fixes an issue reported in the devforums
1 parent e8a7acb commit 147f24f

File tree

3 files changed

+301
-2
lines changed

3 files changed

+301
-2
lines changed

Analysis/include/Luau/ConstraintSolver.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ struct ConstraintSolver
9898
DenseHashSet<TypeId> generalizedTypes_{nullptr};
9999
const NotNull<DenseHashSet<TypeId>> generalizedTypes{&generalizedTypes_};
100100

101+
// The current Constraint that is being processed, can be nullptr.
102+
const Constraint* currentConstraintRef;
103+
101104
// Recorded errors that take place within the solver.
102105
ErrorVec errors;
103106

Analysis/src/ConstraintSolver.cpp

Lines changed: 268 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,248 @@ void dump(ConstraintSolver* cs, ToStringOptions& opts)
284284
}
285285
}
286286

287+
struct TypeFinder : TypeOnceVisitor
288+
{
289+
TypeId tyToFind;
290+
bool hasFoundType = false;
291+
292+
// SOLID Principle for telling that we found the type.
293+
void makeFound(TypeId ty)
294+
{
295+
hasFoundType = true;
296+
}
297+
298+
// Reset the boolean
299+
// so we don't tell that we found a type we didn't actually find.
300+
void reset()
301+
{
302+
hasFoundType = false;
303+
}
304+
305+
bool visit(TypeId ty) override
306+
{
307+
ty = follow(ty);
308+
309+
if (ty == tyToFind)
310+
makeFound(ty); // If we found the type
311+
312+
return true;
313+
}
314+
315+
// Override for IntersectionTypes
316+
bool visit(TypeId ty, const IntersectionType& iTy) override
317+
{
318+
for (TypeId part : iTy.parts)
319+
{
320+
part = follow(part);
321+
322+
if (part == tyToFind)
323+
makeFound(part); // If we found the type
324+
}
325+
326+
return visit(ty);
327+
}
328+
329+
bool isMatch(TypeId ty)
330+
{
331+
reset();
332+
333+
if (ty == tyToFind)
334+
makeFound(ty);
335+
else
336+
traverse(ty);
337+
338+
if (hasFoundType == true)
339+
return true;
340+
else
341+
return false;
342+
}
343+
};
344+
345+
// A way to find a type, such as a dependency through Constraints
346+
struct ConstraintTypeFinder
347+
{
348+
TypeId tyToFind;
349+
TypeFinder typeFinder{};
350+
351+
explicit ConstraintTypeFinder(TypeId tyToFind)
352+
: tyToFind(tyToFind)
353+
{
354+
typeFinder.tyToFind = tyToFind;
355+
}
356+
357+
bool visit(AssignIndexConstraint c)
358+
{
359+
if (typeFinder.isMatch(c.indexType))
360+
return true;
361+
if (typeFinder.isMatch(c.lhsType))
362+
return true;
363+
if (typeFinder.isMatch(c.propType))
364+
return true;
365+
if (typeFinder.isMatch(c.rhsType))
366+
return true;
367+
368+
return false;
369+
}
370+
bool visit(AssignPropConstraint c)
371+
{
372+
if (typeFinder.isMatch(c.lhsType))
373+
return true;
374+
if (typeFinder.isMatch(c.propType))
375+
return true;
376+
if (typeFinder.isMatch(c.rhsType))
377+
return true;
378+
379+
return false;
380+
}
381+
bool visit(EqualityConstraint c)
382+
{
383+
if (typeFinder.isMatch(c.assignmentType))
384+
return true;
385+
if (typeFinder.isMatch(c.resultType))
386+
return true;
387+
388+
return false;
389+
}
390+
bool visit(FunctionCallConstraint c)
391+
{
392+
if (typeFinder.isMatch(c.fn))
393+
return true;
394+
// argPacks not included
395+
396+
return false;
397+
}
398+
bool visit(FunctionCheckConstraint c)
399+
{
400+
if (typeFinder.isMatch(c.fn))
401+
return true;
402+
// argsPacks not included
403+
404+
return false;
405+
}
406+
bool visit(GeneralizationConstraint c)
407+
{
408+
if (typeFinder.isMatch(c.generalizedType))
409+
return true;
410+
if (typeFinder.isMatch(c.sourceType))
411+
return true;
412+
for (auto ty : c.interiorTypes)
413+
{
414+
if (typeFinder.isMatch(ty))
415+
return true;
416+
}
417+
418+
return false;
419+
}
420+
bool visit(IterableConstraint c)
421+
{
422+
for (auto ty : c.variables)
423+
{
424+
if (typeFinder.isMatch(ty))
425+
return true;
426+
}
427+
428+
return false;
429+
}
430+
bool visit(NameConstraint c)
431+
{
432+
if (typeFinder.isMatch(c.namedType))
433+
return true;
434+
435+
for (auto ty : c.typeParameters)
436+
{
437+
if (typeFinder.isMatch(ty))
438+
return true;
439+
}
440+
441+
return false;
442+
}
443+
bool visit(PackSubtypeConstraint c)
444+
{
445+
// ?
446+
return false;
447+
}
448+
bool visit(PrimitiveTypeConstraint c)
449+
{
450+
if (typeFinder.isMatch(c.freeType))
451+
return true;
452+
if (typeFinder.isMatch(c.primitiveType))
453+
return true;
454+
// expectedType needed?
455+
456+
return false;
457+
}
458+
bool visit(ReduceConstraint c)
459+
{
460+
if (typeFinder.isMatch(c.ty))
461+
return true;
462+
463+
return false;
464+
}
465+
bool visit(ReducePackConstraint c)
466+
{
467+
// ?
468+
return false;
469+
}
470+
bool visit(SubtypeConstraint c)
471+
{
472+
if (typeFinder.isMatch(c.subType))
473+
return true;
474+
if (typeFinder.isMatch(c.superType))
475+
return true;
476+
477+
return false;
478+
}
479+
bool visit(TypeAliasExpansionConstraint c)
480+
{
481+
if (typeFinder.isMatch(c.target))
482+
return true;
483+
484+
return false;
485+
}
486+
bool visit(UnpackConstraint c)
487+
{
488+
// ?
489+
return false;
490+
}
491+
492+
493+
// Returns true if it found the type.
494+
bool traverseAndFind(ConstraintV cV)
495+
{
496+
if (auto c = get_if<AssignIndexConstraint>(&cV))
497+
return visit(*c);
498+
else if (auto c = get_if<AssignPropConstraint>(&cV))
499+
return visit(*c);
500+
else if (auto c = get_if<EqualityConstraint>(&cV))
501+
return visit(*c);
502+
else if (auto c = get_if<FunctionCallConstraint>(&cV))
503+
return visit(*c);
504+
else if (auto c = get_if<FunctionCheckConstraint>(&cV))
505+
return visit(*c);
506+
else if (auto c = get_if<GeneralizationConstraint>(&cV))
507+
return visit(*c);
508+
else if (auto c = get_if<IterableConstraint>(&cV))
509+
return visit(*c);
510+
else if (auto c = get_if<NameConstraint>(&cV))
511+
return visit(*c);
512+
else if (auto c = get_if<PackSubtypeConstraint>(&cV))
513+
return visit(*c);
514+
else if (auto c = get_if<PrimitiveTypeConstraint>(&cV))
515+
return visit(*c);
516+
else if (auto c = get_if<ReduceConstraint>(&cV))
517+
return visit(*c);
518+
else if (auto c = get_if<ReducePackConstraint>(&cV))
519+
return visit(*c);
520+
else if (auto c = get_if<SubtypeConstraint>(&cV))
521+
return visit(*c);
522+
else if (auto c = get_if<TypeAliasExpansionConstraint>(&cV))
523+
return visit(*c);
524+
else if (auto c = get_if<UnpackConstraint>(&cV))
525+
return visit(*c);
526+
}
527+
};
528+
287529
struct InstantiationQueuer : TypeOnceVisitor
288530
{
289531
ConstraintSolver* solver;
@@ -299,13 +541,34 @@ struct InstantiationQueuer : TypeOnceVisitor
299541

300542
bool visit(TypeId ty, const PendingExpansionType& petv) override
301543
{
302-
solver->pushConstraint(scope, location, TypeAliasExpansionConstraint{ty});
544+
auto test = solver->pushConstraint(scope, location, TypeAliasExpansionConstraint{ty});
545+
303546
return false;
304547
}
305548

306549
bool visit(TypeId ty, const TypeFunctionInstanceType&) override
307550
{
308-
solver->pushConstraint(scope, location, ReduceConstraint{ty});
551+
auto newConstraint = solver->pushConstraint(scope, location, ReduceConstraint{ty});
552+
553+
// CUSTOM-4
554+
if (solver->currentConstraintRef)
555+
{
556+
if (auto currentC = get_if<TypeAliasExpansionConstraint>(&solver->currentConstraintRef->c))
557+
{
558+
559+
auto cFinder = ConstraintTypeFinder(currentC->target);
560+
561+
for (auto constraint : solver->constraints)
562+
{
563+
if (solver->currentConstraintRef == constraint || newConstraint == constraint)
564+
continue;
565+
566+
if (cFinder.traverseAndFind(constraint.get()->c))
567+
solver->block(static_cast<NotNull<const Constraint>>(newConstraint), constraint);
568+
}
569+
}
570+
}
571+
309572
return true;
310573
}
311574

@@ -426,6 +689,9 @@ void ConstraintSolver::run()
426689
snapshot = logger->prepareStepSnapshot(rootScope, c, force, unsolvedConstraints);
427690
}
428691

692+
// Set current Constraint
693+
currentConstraintRef = c.get();
694+
429695
bool success = tryDispatch(c, force);
430696

431697
progress |= success;

tests/TypeFunction.test.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -953,6 +953,36 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_wait_for_pending_no_crash")
953953
// Should not crash!
954954
}
955955

956+
TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_andGeneralTypeFunction_dependency_issue1")
957+
{
958+
if (!FFlag::LuauSolverV2)
959+
return;
960+
961+
// Missing Dependency Fix check
962+
963+
CheckResult result = check(R"(
964+
--!strict
965+
966+
local PlayerData = {
967+
Coins = 0,
968+
Level = 1,
969+
Exp = 0,
970+
MaxExp = 100
971+
}
972+
973+
type Keys = keyof<typeof(PlayerData)>
974+
975+
-- This function makes it think that there's going to be a pending expansion
976+
local function UpdateData(key: Keys, value)
977+
PlayerData[key] = value
978+
end
979+
980+
UpdateData("Coins", 2)
981+
)");
982+
983+
LUAU_REQUIRE_NO_ERRORS(result);
984+
}
985+
956986
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works_w_array")
957987
{
958988
if (!FFlag::LuauSolverV2)

0 commit comments

Comments
 (0)