|
3 | 3 |
|
4 | 4 | #include "Luau/Ast.h"
|
5 | 5 | #include "Luau/Clone.h"
|
| 6 | +#include "Luau/DenseHash.h" |
6 | 7 | #include "Luau/Error.h"
|
7 | 8 | #include "Luau/Frontend.h"
|
8 | 9 | #include "Luau/Symbol.h"
|
|
29 | 30 |
|
30 | 31 | LUAU_FASTFLAG(LuauSolverV2)
|
31 | 32 | LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
|
32 |
| -LUAU_FASTFLAGVARIABLE(LuauTypestateBuiltins, false) |
33 |
| -LUAU_FASTFLAGVARIABLE(LuauStringFormatArityFix, false) |
| 33 | +LUAU_FASTFLAGVARIABLE(LuauTypestateBuiltins2) |
| 34 | +LUAU_FASTFLAGVARIABLE(LuauStringFormatArityFix) |
34 | 35 |
|
35 | 36 | LUAU_FASTFLAG(AutocompleteRequirePathSuggestions)
|
36 | 37 |
|
@@ -421,7 +422,7 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
421 | 422 |
|
422 | 423 | attachMagicFunction(ttv->props["pack"].type(), magicFunctionPack);
|
423 | 424 | attachDcrMagicFunction(ttv->props["pack"].type(), dcrMagicFunctionPack);
|
424 |
| - if (FFlag::LuauTypestateBuiltins) |
| 425 | + if (FFlag::LuauTypestateBuiltins2) |
425 | 426 | attachDcrMagicFunction(ttv->props["freeze"].type(), dcrMagicFunctionFreeze);
|
426 | 427 | }
|
427 | 428 |
|
@@ -1338,54 +1339,86 @@ static bool dcrMagicFunctionPack(MagicFunctionCallContext context)
|
1338 | 1339 | return true;
|
1339 | 1340 | }
|
1340 | 1341 |
|
| 1342 | +static std::optional<TypeId> freezeTable(TypeId inputType, MagicFunctionCallContext& context) |
| 1343 | +{ |
| 1344 | + TypeArena* arena = context.solver->arena; |
| 1345 | + |
| 1346 | + if (auto mt = get<MetatableType>(inputType)) |
| 1347 | + { |
| 1348 | + std::optional<TypeId> frozenTable = freezeTable(mt->table, context); |
| 1349 | + |
| 1350 | + if (!frozenTable) |
| 1351 | + return std::nullopt; |
| 1352 | + |
| 1353 | + TypeId resultType = arena->addType(MetatableType{*frozenTable, mt->metatable, mt->syntheticName}); |
| 1354 | + |
| 1355 | + return resultType; |
| 1356 | + } |
| 1357 | + |
| 1358 | + if (get<TableType>(inputType)) |
| 1359 | + { |
| 1360 | + // Clone the input type, this will become our final result type after we mutate it. |
| 1361 | + CloneState cloneState{context.solver->builtinTypes}; |
| 1362 | + TypeId resultType = shallowClone(inputType, *arena, cloneState); |
| 1363 | + auto tableTy = getMutable<TableType>(resultType); |
| 1364 | + // `clone` should not break this. |
| 1365 | + LUAU_ASSERT(tableTy); |
| 1366 | + tableTy->state = TableState::Sealed; |
| 1367 | + |
| 1368 | + // We'll mutate the table to make every property type read-only. |
| 1369 | + for (auto iter = tableTy->props.begin(); iter != tableTy->props.end();) |
| 1370 | + { |
| 1371 | + if (iter->second.isWriteOnly()) |
| 1372 | + iter = tableTy->props.erase(iter); |
| 1373 | + else |
| 1374 | + { |
| 1375 | + iter->second.writeTy = std::nullopt; |
| 1376 | + iter++; |
| 1377 | + } |
| 1378 | + } |
| 1379 | + |
| 1380 | + return resultType; |
| 1381 | + } |
| 1382 | + |
| 1383 | + context.solver->reportError(TypeMismatch{context.solver->builtinTypes->tableType, inputType}, context.callSite->argLocation); |
| 1384 | + return std::nullopt; |
| 1385 | +} |
| 1386 | + |
1341 | 1387 | static bool dcrMagicFunctionFreeze(MagicFunctionCallContext context)
|
1342 | 1388 | {
|
1343 |
| - LUAU_ASSERT(FFlag::LuauTypestateBuiltins); |
| 1389 | + LUAU_ASSERT(FFlag::LuauTypestateBuiltins2); |
1344 | 1390 |
|
1345 | 1391 | TypeArena* arena = context.solver->arena;
|
1346 | 1392 | const DataFlowGraph* dfg = context.solver->dfg.get();
|
1347 | 1393 | Scope* scope = context.constraint->scope.get();
|
1348 | 1394 |
|
1349 | 1395 | const auto& [paramTypes, paramTail] = extendTypePack(*arena, context.solver->builtinTypes, context.arguments, 1);
|
1350 |
| - LUAU_ASSERT(paramTypes.size() >= 1); |
1351 |
| - |
1352 |
| - TypeId inputType = follow(paramTypes.at(0)); |
1353 |
| - |
1354 |
| - // we'll check if it's a table first since this magic function also produces the error if it's not until we have bounded generics |
1355 |
| - if (!get<TableType>(inputType)) |
| 1396 | + if (paramTypes.empty() || context.callSite->args.size == 0) |
1356 | 1397 | {
|
1357 |
| - context.solver->reportError(TypeMismatch{context.solver->builtinTypes->tableType, inputType}, context.callSite->argLocation); |
| 1398 | + context.solver->reportError(CountMismatch{1, std::nullopt, 0}, context.callSite->argLocation); |
1358 | 1399 | return false;
|
1359 | 1400 | }
|
1360 | 1401 |
|
| 1402 | + TypeId inputType = follow(paramTypes[0]); |
| 1403 | + |
1361 | 1404 | AstExpr* targetExpr = context.callSite->args.data[0];
|
1362 | 1405 | std::optional<DefId> resultDef = dfg->getDefOptional(targetExpr);
|
1363 | 1406 | std::optional<TypeId> resultTy = resultDef ? scope->lookup(*resultDef) : std::nullopt;
|
1364 | 1407 |
|
1365 |
| - // Clone the input type, this will become our final result type after we mutate it. |
1366 |
| - CloneState cloneState{context.solver->builtinTypes}; |
1367 |
| - TypeId clonedType = shallowClone(inputType, *arena, cloneState); |
1368 |
| - auto tableTy = getMutable<TableType>(clonedType); |
1369 |
| - // `clone` should not break this. |
1370 |
| - LUAU_ASSERT(tableTy); |
1371 |
| - tableTy->state = TableState::Sealed; |
1372 |
| - tableTy->syntheticName = std::nullopt; |
1373 |
| - |
1374 |
| - // We'll mutate the table to make every property type read-only. |
1375 |
| - for (auto iter = tableTy->props.begin(); iter != tableTy->props.end();) |
| 1408 | + std::optional<TypeId> frozenType = freezeTable(inputType, context); |
| 1409 | + |
| 1410 | + if (!frozenType) |
1376 | 1411 | {
|
1377 |
| - if (iter->second.isWriteOnly()) |
1378 |
| - iter = tableTy->props.erase(iter); |
1379 |
| - else |
1380 |
| - { |
1381 |
| - iter->second.writeTy = std::nullopt; |
1382 |
| - iter++; |
1383 |
| - } |
| 1412 | + if (resultTy) |
| 1413 | + asMutable(*resultTy)->ty.emplace<BoundType>(context.solver->builtinTypes->errorType); |
| 1414 | + asMutable(context.result)->ty.emplace<BoundTypePack>(context.solver->builtinTypes->errorTypePack); |
| 1415 | + |
| 1416 | + return true; |
1384 | 1417 | }
|
1385 | 1418 |
|
1386 | 1419 | if (resultTy)
|
1387 |
| - asMutable(*resultTy)->ty.emplace<BoundType>(clonedType); |
1388 |
| - asMutable(context.result)->ty.emplace<BoundTypePack>(arena->addTypePack({clonedType})); |
| 1420 | + asMutable(*resultTy)->ty.emplace<BoundType>(*frozenType); |
| 1421 | + asMutable(context.result)->ty.emplace<BoundTypePack>(arena->addTypePack({*frozenType})); |
1389 | 1422 |
|
1390 | 1423 | return true;
|
1391 | 1424 | }
|
|
0 commit comments