This commit is contained in:
Alexander McCord 2025-03-25 16:24:13 -07:00 committed by GitHub
commit 67713d7f8c
Signed by: DevComp
GPG key ID: B5690EEEBB952194
2 changed files with 60 additions and 7 deletions

View file

@ -46,6 +46,7 @@ LUAU_FASTFLAGVARIABLE(LuauInferLocalTypesInMultipleAssignments)
LUAU_FASTFLAGVARIABLE(LuauDoNotLeakNilInRefinement)
LUAU_FASTFLAGVARIABLE(LuauExtraFollows)
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
LUAU_FASTFLAGVARIABLE(LuauRefineJustTheReadProperty)
namespace Luau
{
@ -530,11 +531,18 @@ void ConstraintGenerator::computeRefinement(
TypeId nextDiscriminantTy = arena->addType(TableType{});
NotNull<TableType> table{getMutable<TableType>(nextDiscriminantTy)};
// When we fully support read-write properties (i.e. when we allow properties with
// completely disparate read and write types), then the following property can be
// set to read-only since refinements only tell us about what we read. This cannot
// be allowed yet though because it causes read and write types to diverge.
table->props[*key->propName] = Property::rw(discriminantTy);
if (FFlag::LuauRefineJustTheReadProperty)
{
table->props[*key->propName] = Property::readonly(discriminantTy);
}
else
{
// When we fully support read-write properties (i.e. when we allow properties with
// completely disparate read and write types), then the following property can be
// set to read-only since refinements only tell us about what we read. This cannot
// be allowed yet though because it causes read and write types to diverge.
table->props[*key->propName] = Property::rw(discriminantTy);
}
table->scope = scope.get();
table->state = TableState::Sealed;
@ -548,7 +556,7 @@ void ConstraintGenerator::computeRefinement(
refis->get(proposition->key->def)->shouldAppendNilType =
(sense || !eq) && containsSubscriptedDefinition(proposition->key->def) && !proposition->implicitFromCall;
}
else
else
{
refis->get(proposition->key->def)->shouldAppendNilType = (sense || !eq) && containsSubscriptedDefinition(proposition->key->def);
}

View file

@ -14,6 +14,7 @@ LUAU_FASTFLAG(LuauIntersectNotNil)
LUAU_FASTFLAG(LuauSkipNoRefineDuringRefinement)
LUAU_FASTFLAG(LuauFunctionCallsAreNotNilable)
LUAU_FASTFLAG(LuauDoNotLeakNilInRefinement)
LUAU_FASTFLAG(LuauRefineJustTheReadProperty)
using namespace Luau;
@ -2538,7 +2539,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "function_calls_are_not_nillable")
return nil
end
)"));
}
TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1528_method_calls_are_not_nillable")
@ -2562,4 +2562,49 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1528_method_calls_are_not_nillable")
)"));
}
TEST_CASE_FIXTURE(Fixture, "refine_just_the_read_property")
{
ScopedFastFlag sff[]{
{FFlag::LuauSolverV2, true},
{FFlag::LuauRefineJustTheReadProperty, true},
};
CheckResult result = check(R"(
type Foo = { p: boolean }
function f(x: Foo)
if x.p == true then return end
x.p = true
x.p = false
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
// The first check is correct because it reflects the state of the program by that point.
// The second check is not. It fails to account for transitive typestate change from the
// previous statement.
CHECK_EQ("Foo & { read p: ~true }", toString(requireTypeAtPosition({6, 12})));
CHECK_EQ("Foo & { read p: ~true }", toString(requireTypeAtPosition({7, 12})));
}
TEST_CASE_FIXTURE(Fixture, "refine_just_the_read_property_typestate")
{
ScopedFastFlag sff[]{
{FFlag::LuauSolverV2, true},
{FFlag::LuauRefineJustTheReadProperty, true},
};
CheckResult result = check(R"(
type Foo = { p: {}? }
function f(x: Foo)
if not x.p then x.p = {} end
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_SUITE_END();