Skip to content

Commit

Permalink
Sync to upstream/release/647 (#1469)
Browse files Browse the repository at this point in the history
# General Updates
Fix an old solver crash that occurs in the presence of cyclic
`requires()`

## New Solver
- Improvements to Luau user-defined type function library
- Avoid asserting on unexpected metatable types
- Properties in user defined type functions should have a consistent
iteration order - in this case it is insertion ordering

# Runtime
- Track VM allocations for telemetry

---
Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: James McNellis <jmcnellis@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Junseo Yoo <jyoo@roblox.com>
  • Loading branch information
9 people authored Oct 12, 2024
1 parent 4559ef2 commit 77295c3
Show file tree
Hide file tree
Showing 26 changed files with 426 additions and 175 deletions.
7 changes: 4 additions & 3 deletions Analysis/include/Luau/TypeFunctionRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

#include <optional>
#include <string>
#include <unordered_map>
#include <map>
#include <vector>

using lua_State = struct lua_State;
Expand Down Expand Up @@ -182,7 +182,7 @@ struct TypeFunctionProperty
struct TypeFunctionTableType
{
using Name = std::string;
using Props = std::unordered_map<Name, TypeFunctionProperty>;
using Props = std::map<Name, TypeFunctionProperty>;

Props props;

Expand All @@ -195,7 +195,7 @@ struct TypeFunctionTableType
struct TypeFunctionClassType
{
using Name = std::string;
using Props = std::unordered_map<Name, TypeFunctionProperty>;
using Props = std::map<Name, TypeFunctionProperty>;

Props props;

Expand Down Expand Up @@ -260,6 +260,7 @@ bool isTypeUserData(lua_State* L, int idx);
TypeFunctionTypeId getTypeUserData(lua_State* L, int idx);
std::optional<TypeFunctionTypeId> optionalTypeUserData(lua_State* L, int idx);

void registerTypesLibrary(lua_State* L);
void registerTypeUserData(lua_State* L);

void setTypeFunctionEnvironment(lua_State* L);
Expand Down
4 changes: 1 addition & 3 deletions Analysis/src/BuiltinDefinitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
*/

LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTFLAGVARIABLE(LuauDCRMagicFunctionTypeChecker, false);

namespace Luau
{
Expand Down Expand Up @@ -931,8 +930,7 @@ TypeId makeStringMetatable(NotNull<BuiltinTypes> builtinTypes)
formatFTV.isCheckedFunction = true;
const TypeId formatFn = arena->addType(formatFTV);
attachDcrMagicFunction(formatFn, dcrMagicFunctionFormat);
if (FFlag::LuauDCRMagicFunctionTypeChecker)
attachDcrMagicFunctionTypeCheck(formatFn, dcrMagicFunctionTypeCheckFormat);
attachDcrMagicFunctionTypeCheck(formatFn, dcrMagicFunctionTypeCheckFormat);


const TypeId stringToStringType = makeFunction(*arena, std::nullopt, {}, {}, {stringType}, {}, {stringType}, /* checked */ true);
Expand Down
5 changes: 1 addition & 4 deletions Analysis/src/ConstraintSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,7 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogSolver, false)
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverIncludeDependencies, false)
LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings, false)
LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500)

// The default value here is 643 because the first release in which this was implemented is 644,
// and actively we want new changes to be off by default until they're enabled consciously.
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeSolverRelease, 643)
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)

namespace Luau
{
Expand Down
3 changes: 3 additions & 0 deletions Analysis/src/Differ.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace Luau
{

std::string DiffPathNode::toString() const
{
switch (kind)
Expand Down Expand Up @@ -944,12 +945,14 @@ std::vector<std::pair<TypeId, TypeId>>::const_reverse_iterator DifferEnvironment
return visitingStack.crend();
}


DifferResult diff(TypeId ty1, TypeId ty2)
{
DifferEnvironment differEnv{ty1, ty2, std::nullopt, std::nullopt};
return diffUsingEnv(differEnv, ty1, ty2);
}


DifferResult diffWithSymbols(TypeId ty1, TypeId ty2, std::optional<std::string> symbol1, std::optional<std::string> symbol2)
{
DifferEnvironment differEnv{ty1, ty2, symbol1, symbol2};
Expand Down
15 changes: 9 additions & 6 deletions Analysis/src/Frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile, false)
LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes, false)
LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode, false)
LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode, false)
LUAU_FASTFLAGVARIABLE(LuauSourceModuleUpdatedWithSelectedMode, false)
LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctionNoEvaluation, false)
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauRunCustomModuleChecks, false)
LUAU_FASTFLAGVARIABLE(LuauMoreThoroughCycleDetection, false)

LUAU_FASTFLAG(StudioReportLuauAny2)

Expand Down Expand Up @@ -883,14 +883,18 @@ void Frontend::addBuildQueueItems(
data.environmentScope = getModuleEnvironment(*sourceModule, data.config, frontendOptions.forAutocomplete);
data.recordJsonLog = FFlag::DebugLuauLogSolverToJson;

Mode mode = sourceModule->mode.value_or(data.config.mode);
const Mode mode = sourceModule->mode.value_or(data.config.mode);

// in NoCheck mode we only need to compute the value of .cyclic for typeck
// in the future we could replace toposort with an algorithm that can flag cyclic nodes by itself
// however, for now getRequireCycles isn't expensive in practice on the cases we care about, and long term
// all correct programs must be acyclic so this code triggers rarely
if (cycleDetected)
data.requireCycles = getRequireCycles(fileResolver, sourceNodes, sourceNode.get(), mode == Mode::NoCheck);
{
if (FFlag::LuauMoreThoroughCycleDetection)
data.requireCycles = getRequireCycles(fileResolver, sourceNodes, sourceNode.get(), false);
else
data.requireCycles = getRequireCycles(fileResolver, sourceNodes, sourceNode.get(), mode == Mode::NoCheck);
}

data.options = frontendOptions;

Expand Down Expand Up @@ -922,8 +926,7 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item)
else
mode = sourceModule.mode.value_or(config.mode);

if (FFlag::LuauSourceModuleUpdatedWithSelectedMode)
item.sourceModule->mode = {mode};
item.sourceModule->mode = {mode};
ScopePtr environmentScope = item.environmentScope;
double timestamp = getTimestamp();
const std::vector<RequireCycle>& requireCycles = item.requireCycles;
Expand Down
60 changes: 17 additions & 43 deletions Analysis/src/Normalize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,13 @@
#include "Luau/Unifier.h"

LUAU_FASTFLAGVARIABLE(DebugLuauCheckNormalizeInvariant, false)
LUAU_FASTFLAGVARIABLE(LuauFixReduceStackPressure, false);
LUAU_FASTFLAGVARIABLE(LuauFixCyclicTablesBlowingStack, false);

LUAU_FASTINTVARIABLE(LuauNormalizeCacheLimit, 100000);
LUAU_FASTFLAG(LuauSolverV2);

LUAU_FASTFLAGVARIABLE(LuauUseNormalizeIntersectionLimit, false)
LUAU_FASTINTVARIABLE(LuauNormalizeIntersectionLimit, 200)

static bool fixReduceStackPressure()
{
return FFlag::LuauFixReduceStackPressure || FFlag::LuauSolverV2;
}

static bool fixCyclicTablesBlowingStack()
{
return FFlag::LuauFixCyclicTablesBlowingStack || FFlag::LuauSolverV2;
}

namespace Luau
{

Expand Down Expand Up @@ -2583,43 +2571,29 @@ std::optional<TypeId> Normalizer::intersectionOfTables(TypeId here, TypeId there
if (tprop.readTy.has_value())
{
// if the intersection of the read types of a property is uninhabited, the whole table is `never`.
if (fixReduceStackPressure())
// We've seen these table prop elements before and we're about to ask if their intersection
// is inhabited
if (seenSet.contains(*hprop.readTy) && seenSet.contains(*tprop.readTy))
{
// We've seen these table prop elements before and we're about to ask if their intersection
// is inhabited
if (fixCyclicTablesBlowingStack())
{
if (seenSet.contains(*hprop.readTy) && seenSet.contains(*tprop.readTy))
{
seenSet.erase(*hprop.readTy);
seenSet.erase(*tprop.readTy);
return {builtinTypes->neverType};
}
else
{
seenSet.insert(*hprop.readTy);
seenSet.insert(*tprop.readTy);
}
}

NormalizationResult res = isIntersectionInhabited(*hprop.readTy, *tprop.readTy);

// Cleanup
if (fixCyclicTablesBlowingStack())
{
seenSet.erase(*hprop.readTy);
seenSet.erase(*tprop.readTy);
}

if (NormalizationResult::True != res)
return {builtinTypes->neverType};
seenSet.erase(*hprop.readTy);
seenSet.erase(*tprop.readTy);
return {builtinTypes->neverType};
}
else
{
if (NormalizationResult::False == isIntersectionInhabited(*hprop.readTy, *tprop.readTy))
return {builtinTypes->neverType};
seenSet.insert(*hprop.readTy);
seenSet.insert(*tprop.readTy);
}

NormalizationResult res = isIntersectionInhabited(*hprop.readTy, *tprop.readTy);

// Cleanup
seenSet.erase(*hprop.readTy);
seenSet.erase(*tprop.readTy);

if (NormalizationResult::True != res)
return {builtinTypes->neverType};

TypeId ty = simplifyIntersection(builtinTypes, NotNull{arena}, *hprop.readTy, *tprop.readTy).result;
prop.readTy = ty;
hereSubThere &= (ty == hprop.readTy);
Expand Down
7 changes: 5 additions & 2 deletions Analysis/src/TypeChecker2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1625,8 +1625,11 @@ void TypeChecker2::indexExprMetatableHelper(AstExprIndexExpr* indexExpr, const M
indexExprMetatableHelper(indexExpr, mtmt, exprType, indexType);
else
{
LUAU_ASSERT(tt || get<PrimitiveType>(follow(metaTable->table)));

if (!(DFInt::LuauTypeSolverRelease >= 647))
{
LUAU_ASSERT(tt || get<PrimitiveType>(follow(metaTable->table)));
}
// CLI-122161: We're not handling unions correctly (probably).
reportError(CannotExtendTable{exprType, CannotExtendTable::Indexer, "indexer??"}, indexExpr->location);
}
}
Expand Down
5 changes: 4 additions & 1 deletion Analysis/src/TypeFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1);
LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies, false)
LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctions2, false)
LUAU_FASTFLAG(LuauUserDefinedTypeFunctionNoEvaluation)
LUAU_FASTFLAG(LuauUserTypeFunFixRegister)

LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)

Expand Down Expand Up @@ -1018,9 +1019,11 @@ void TypeFunctionRuntime::prepareState()

setTypeFunctionEnvironment(L);

// Register type userdata
registerTypeUserData(L);

if (FFlag::LuauUserTypeFunFixRegister)
registerTypesLibrary(L);

luaL_sandbox(L);
luaL_sandboxthread(L);
}
Expand Down
Loading

0 comments on commit 77295c3

Please sign in to comment.