2022-05-26 23:08:16 +01:00
|
|
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
|
|
#include "Luau/Instantiation.h"
|
2023-08-04 20:18:54 +01:00
|
|
|
|
|
|
|
#include "Luau/Common.h"
|
2024-02-23 20:08:34 +00:00
|
|
|
#include "Luau/Instantiation2.h" // including for `Replacer` which was stolen since it will be kept in the new solver
|
2023-08-04 20:18:54 +01:00
|
|
|
#include "Luau/ToString.h"
|
2022-05-26 23:08:16 +01:00
|
|
|
#include "Luau/TxnLog.h"
|
|
|
|
#include "Luau/TypeArena.h"
|
2023-08-04 20:18:54 +01:00
|
|
|
#include "Luau/TypeCheckLimits.h"
|
|
|
|
|
2023-11-16 19:51:16 +00:00
|
|
|
#include <algorithm>
|
|
|
|
|
2024-08-30 21:16:51 +01:00
|
|
|
LUAU_FASTFLAG(LuauSolverV2)
|
2022-05-26 23:08:16 +01:00
|
|
|
|
|
|
|
namespace Luau
|
|
|
|
{
|
|
|
|
|
2024-06-21 00:37:55 +01:00
|
|
|
void Instantiation::resetState(const TxnLog* log, TypeArena* arena, NotNull<BuiltinTypes> builtinTypes, TypeLevel level, Scope* scope)
|
|
|
|
{
|
|
|
|
Substitution::resetState(log, arena);
|
|
|
|
|
|
|
|
this->builtinTypes = builtinTypes;
|
|
|
|
|
|
|
|
this->level = level;
|
|
|
|
this->scope = scope;
|
|
|
|
}
|
|
|
|
|
2022-05-26 23:08:16 +01:00
|
|
|
bool Instantiation::isDirty(TypeId ty)
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
if (const FunctionType* ftv = log->getMutable<FunctionType>(ty))
|
2022-05-26 23:08:16 +01:00
|
|
|
{
|
2023-05-19 20:37:30 +01:00
|
|
|
if (ftv->hasNoFreeOrGenericTypes)
|
2022-05-26 23:08:16 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Instantiation::isDirty(TypePackId tp)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Instantiation::ignoreChildren(TypeId ty)
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
if (log->getMutable<FunctionType>(ty))
|
2022-05-26 23:08:16 +01:00
|
|
|
return true;
|
2023-06-24 07:19:39 +01:00
|
|
|
else if (get<ClassType>(ty))
|
2022-08-04 23:35:33 +01:00
|
|
|
return true;
|
2022-05-26 23:08:16 +01:00
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
TypeId Instantiation::clean(TypeId ty)
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
const FunctionType* ftv = log->getMutable<FunctionType>(ty);
|
2022-05-26 23:08:16 +01:00
|
|
|
LUAU_ASSERT(ftv);
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
FunctionType clone = FunctionType{level, scope, ftv->argTypes, ftv->retTypes, ftv->definition, ftv->hasSelf};
|
2022-05-26 23:08:16 +01:00
|
|
|
clone.magicFunction = ftv->magicFunction;
|
2022-09-02 00:14:03 +01:00
|
|
|
clone.dcrMagicFunction = ftv->dcrMagicFunction;
|
2023-02-10 19:40:38 +00:00
|
|
|
clone.dcrMagicRefinement = ftv->dcrMagicRefinement;
|
2022-05-26 23:08:16 +01:00
|
|
|
clone.tags = ftv->tags;
|
|
|
|
clone.argNames = ftv->argNames;
|
|
|
|
TypeId result = addType(std::move(clone));
|
|
|
|
|
2024-08-16 19:29:33 +01:00
|
|
|
// Annoyingly, we have to do this even if there are no generics,
|
|
|
|
// to replace any generic tables.
|
|
|
|
reusableReplaceGenerics.resetState(log, arena, builtinTypes, level, scope, ftv->generics, ftv->genericPacks);
|
2024-06-21 00:37:55 +01:00
|
|
|
|
2024-08-16 19:29:33 +01:00
|
|
|
// TODO: What to do if this returns nullopt?
|
|
|
|
// We don't have access to the error-reporting machinery
|
|
|
|
result = reusableReplaceGenerics.substitute(result).value_or(result);
|
2022-05-26 23:08:16 +01:00
|
|
|
|
|
|
|
asMutable(result)->documentationSymbol = ty->documentationSymbol;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
TypePackId Instantiation::clean(TypePackId tp)
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(false);
|
|
|
|
return tp;
|
|
|
|
}
|
|
|
|
|
2024-08-02 15:30:04 +01:00
|
|
|
void ReplaceGenerics::resetState(
|
|
|
|
const TxnLog* log,
|
|
|
|
TypeArena* arena,
|
|
|
|
NotNull<BuiltinTypes> builtinTypes,
|
|
|
|
TypeLevel level,
|
|
|
|
Scope* scope,
|
|
|
|
const std::vector<TypeId>& generics,
|
|
|
|
const std::vector<TypePackId>& genericPacks
|
|
|
|
)
|
2024-06-21 00:37:55 +01:00
|
|
|
{
|
|
|
|
Substitution::resetState(log, arena);
|
|
|
|
|
|
|
|
this->builtinTypes = builtinTypes;
|
|
|
|
|
|
|
|
this->level = level;
|
|
|
|
this->scope = scope;
|
|
|
|
|
|
|
|
this->generics = generics;
|
|
|
|
this->genericPacks = genericPacks;
|
|
|
|
}
|
|
|
|
|
2022-05-26 23:08:16 +01:00
|
|
|
bool ReplaceGenerics::ignoreChildren(TypeId ty)
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
if (const FunctionType* ftv = log->getMutable<FunctionType>(ty))
|
2022-05-26 23:08:16 +01:00
|
|
|
{
|
2023-05-19 20:37:30 +01:00
|
|
|
if (ftv->hasNoFreeOrGenericTypes)
|
2022-05-26 23:08:16 +01:00
|
|
|
return true;
|
|
|
|
|
|
|
|
// We aren't recursing in the case of a generic function which
|
|
|
|
// binds the same generics. This can happen if, for example, there's recursive types.
|
|
|
|
// If T = <a>(a,T)->T then instantiating T should produce T' = (X,T)->T not T' = (X,T')->T'.
|
|
|
|
// It's OK to use vector equality here, since we always generate fresh generics
|
|
|
|
// whenever we quantify, so the vectors overlap if and only if they are equal.
|
|
|
|
return (!generics.empty() || !genericPacks.empty()) && (ftv->generics == generics) && (ftv->genericPacks == genericPacks);
|
|
|
|
}
|
2023-06-24 07:19:39 +01:00
|
|
|
else if (get<ClassType>(ty))
|
2022-08-11 22:01:33 +01:00
|
|
|
return true;
|
2022-05-26 23:08:16 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ReplaceGenerics::isDirty(TypeId ty)
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
if (const TableType* ttv = log->getMutable<TableType>(ty))
|
2022-05-26 23:08:16 +01:00
|
|
|
return ttv->state == TableState::Generic;
|
2023-01-04 20:53:17 +00:00
|
|
|
else if (log->getMutable<GenericType>(ty))
|
2022-05-26 23:08:16 +01:00
|
|
|
return std::find(generics.begin(), generics.end(), ty) != generics.end();
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ReplaceGenerics::isDirty(TypePackId tp)
|
|
|
|
{
|
|
|
|
if (log->getMutable<GenericTypePack>(tp))
|
|
|
|
return std::find(genericPacks.begin(), genericPacks.end(), tp) != genericPacks.end();
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
TypeId ReplaceGenerics::clean(TypeId ty)
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(isDirty(ty));
|
2023-01-04 20:53:17 +00:00
|
|
|
if (const TableType* ttv = log->getMutable<TableType>(ty))
|
2022-05-26 23:08:16 +01:00
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
TableType clone = TableType{ttv->props, ttv->indexer, level, scope, TableState::Free};
|
2022-05-26 23:08:16 +01:00
|
|
|
clone.definitionModuleName = ttv->definitionModuleName;
|
2023-01-12 14:21:25 +00:00
|
|
|
clone.definitionLocation = ttv->definitionLocation;
|
2022-05-26 23:08:16 +01:00
|
|
|
return addType(std::move(clone));
|
|
|
|
}
|
2024-08-30 21:16:51 +01:00
|
|
|
else if (FFlag::LuauSolverV2)
|
2023-08-04 20:18:54 +01:00
|
|
|
{
|
|
|
|
TypeId res = freshType(NotNull{arena}, builtinTypes, scope);
|
|
|
|
getMutable<FreeType>(res)->level = level;
|
|
|
|
return res;
|
|
|
|
}
|
2022-05-26 23:08:16 +01:00
|
|
|
else
|
2023-08-04 20:18:54 +01:00
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
return addType(FreeType{scope, level});
|
2023-08-04 20:18:54 +01:00
|
|
|
}
|
2022-05-26 23:08:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
TypePackId ReplaceGenerics::clean(TypePackId tp)
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(isDirty(tp));
|
2023-03-24 18:03:04 +00:00
|
|
|
return addTypePack(TypePackVar(FreeTypePack{scope, level}));
|
2022-05-26 23:08:16 +01:00
|
|
|
}
|
|
|
|
|
2023-09-15 18:26:59 +01:00
|
|
|
std::optional<TypeId> instantiate(
|
2024-08-02 15:30:04 +01:00
|
|
|
NotNull<BuiltinTypes> builtinTypes,
|
|
|
|
NotNull<TypeArena> arena,
|
|
|
|
NotNull<TypeCheckLimits> limits,
|
|
|
|
NotNull<Scope> scope,
|
|
|
|
TypeId ty
|
|
|
|
)
|
2023-08-04 20:18:54 +01:00
|
|
|
{
|
|
|
|
ty = follow(ty);
|
|
|
|
|
|
|
|
const FunctionType* ft = get<FunctionType>(ty);
|
|
|
|
if (!ft)
|
|
|
|
return ty;
|
|
|
|
|
|
|
|
if (ft->generics.empty() && ft->genericPacks.empty())
|
|
|
|
return ty;
|
|
|
|
|
|
|
|
DenseHashMap<TypeId, TypeId> replacements{nullptr};
|
|
|
|
DenseHashMap<TypePackId, TypePackId> replacementPacks{nullptr};
|
|
|
|
|
|
|
|
for (TypeId g : ft->generics)
|
|
|
|
replacements[g] = freshType(arena, builtinTypes, scope);
|
|
|
|
|
|
|
|
for (TypePackId g : ft->genericPacks)
|
|
|
|
replacementPacks[g] = arena->freshTypePack(scope);
|
|
|
|
|
|
|
|
Replacer r{arena, std::move(replacements), std::move(replacementPacks)};
|
|
|
|
|
|
|
|
if (limits->instantiationChildLimit)
|
|
|
|
r.childLimit = *limits->instantiationChildLimit;
|
|
|
|
|
|
|
|
std::optional<TypeId> res = r.substitute(ty);
|
|
|
|
if (!res)
|
|
|
|
return res;
|
|
|
|
|
|
|
|
FunctionType* ft2 = getMutable<FunctionType>(*res);
|
|
|
|
LUAU_ASSERT(ft != ft2);
|
|
|
|
|
|
|
|
ft2->generics.clear();
|
|
|
|
ft2->genericPacks.clear();
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2022-05-26 23:08:16 +01:00
|
|
|
} // namespace Luau
|