luau/Analysis/src/ToString.cpp

1871 lines
49 KiB
C++
Raw Normal View History

// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/ToString.h"
Sync to upstream/release/600 (#1076) ### What's Changed - Improve readability of unions and intersections by limiting the number of elements of those types that can be presented on a single line (gated under `FFlag::LuauToStringSimpleCompositeTypesSingleLine`) - Adds a new option to the compiler `--record-stats` to record and output compilation statistics - `if...then...else` expressions are now optimized into `AND/OR` form when possible. ### VM - Add a new `buffer` type to Luau based on the [buffer RFC](https://github.com/Roblox/luau/pull/739) and additional C API functions to work with it; this release does not include the library. - Internal C API to work with string buffers has been updated to align with Lua version more closely ### Native Codegen - Added support for new X64 instruction (rev) and new A64 instruction (bswap) in the assembler - Simplified the way numerical loop condition is translated to IR ### New Type Solver - Operator inference now handled by type families - Created a new system called `Type Paths` to explain why subtyping tests fail in order to improve the quality of error messages. - Systematic changes to implement Data Flow analysis in the new solver (`Breadcrumb` removed and replaced with `RefinementKey`) --- 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: Lily Brown <lbrown@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com>
2023-10-21 02:10:30 +01:00
#include "Luau/Common.h"
#include "Luau/Constraint.h"
#include "Luau/Location.h"
#include "Luau/Scope.h"
#include "Luau/TxnLog.h"
#include "Luau/TypeInfer.h"
#include "Luau/TypePack.h"
#include "Luau/Type.h"
Sync to upstream/release/576 (#928) * `ClassType` can now have an indexer defined on it. This allows custom types to be used in `t[x]` expressions. * Fixed search for closest executable breakpoint line. Previously, breakpoints might have been skipped in `else` blocks at the end of a function * Fixed how unification is performed for two optional types `a? <: b?`, previously it might have unified either 'a' or 'b' with 'nil'. Note that this fix is not enabled by default yet (see the list in `ExperimentalFlags.h`) In the new type solver, a concept of 'Type Families' has been introduced. Type families can be thought of as type aliases with custom type inference/reduction logic included with them. For example, we can have an `Add<T, U>` type family that will resolve the type that is the result of adding two values together. This will help type inference to figure out what 'T' and 'U' might be when explicit type annotations are not provided. In this update we don't define any type families, but they will be added in the near future. It is also possible for Luau embedders to define their own type families in the global/environment scope. Other changes include: * Fixed scope used to find out which generic types should be included in the function generic type list * Fixed a crash after cyclic bound types were created during unification And in native code generation (jit): * Use of arm64 target on M1 now requires macOS 13 * Entry into native code has been optimized. This is especially important for coroutine call/pcall performance as they involve going through a C call frame * LOP_LOADK(X) translation into IR has been improved to enable type tag/constant propagation * arm64 can use integer immediate values to synthesize floating-point values * x64 assembler removes duplicate 64bit numbers from the data section to save space * Linux `perf` can now be used to profile native Luau code (when running with --codegen-perf CLI argument)
2023-05-12 18:50:47 +01:00
#include "Luau/TypeFamily.h"
#include "Luau/VisitType.h"
Sync to upstream/release/600 (#1076) ### What's Changed - Improve readability of unions and intersections by limiting the number of elements of those types that can be presented on a single line (gated under `FFlag::LuauToStringSimpleCompositeTypesSingleLine`) - Adds a new option to the compiler `--record-stats` to record and output compilation statistics - `if...then...else` expressions are now optimized into `AND/OR` form when possible. ### VM - Add a new `buffer` type to Luau based on the [buffer RFC](https://github.com/Roblox/luau/pull/739) and additional C API functions to work with it; this release does not include the library. - Internal C API to work with string buffers has been updated to align with Lua version more closely ### Native Codegen - Added support for new X64 instruction (rev) and new A64 instruction (bswap) in the assembler - Simplified the way numerical loop condition is translated to IR ### New Type Solver - Operator inference now handled by type families - Created a new system called `Type Paths` to explain why subtyping tests fail in order to improve the quality of error messages. - Systematic changes to implement Data Flow analysis in the new solver (`Breadcrumb` removed and replaced with `RefinementKey`) --- 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: Lily Brown <lbrown@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com>
2023-10-21 02:10:30 +01:00
#include "Luau/TypeOrPack.h"
#include <algorithm>
#include <stdexcept>
#include <string>
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
LUAU_FASTFLAGVARIABLE(LuauToStringPrettifyLocation, false)
LUAU_FASTFLAGVARIABLE(LuauToStringSimpleCompositeTypesSingleLine, false)
2022-04-15 00:57:43 +01:00
/*
Sync to upstream/release/576 (#928) * `ClassType` can now have an indexer defined on it. This allows custom types to be used in `t[x]` expressions. * Fixed search for closest executable breakpoint line. Previously, breakpoints might have been skipped in `else` blocks at the end of a function * Fixed how unification is performed for two optional types `a? <: b?`, previously it might have unified either 'a' or 'b' with 'nil'. Note that this fix is not enabled by default yet (see the list in `ExperimentalFlags.h`) In the new type solver, a concept of 'Type Families' has been introduced. Type families can be thought of as type aliases with custom type inference/reduction logic included with them. For example, we can have an `Add<T, U>` type family that will resolve the type that is the result of adding two values together. This will help type inference to figure out what 'T' and 'U' might be when explicit type annotations are not provided. In this update we don't define any type families, but they will be added in the near future. It is also possible for Luau embedders to define their own type families in the global/environment scope. Other changes include: * Fixed scope used to find out which generic types should be included in the function generic type list * Fixed a crash after cyclic bound types were created during unification And in native code generation (jit): * Use of arm64 target on M1 now requires macOS 13 * Entry into native code has been optimized. This is especially important for coroutine call/pcall performance as they involve going through a C call frame * LOP_LOADK(X) translation into IR has been improved to enable type tag/constant propagation * arm64 can use integer immediate values to synthesize floating-point values * x64 assembler removes duplicate 64bit numbers from the data section to save space * Linux `perf` can now be used to profile native Luau code (when running with --codegen-perf CLI argument)
2023-05-12 18:50:47 +01:00
* Enables increasing levels of verbosity for Luau type names when stringifying.
* After level 2, test cases will break unpredictably because a pointer to their
* scope will be included in the stringification of generic and free types.
*
* Supported values:
*
* 0: Disabled, no changes.
*
* 1: Prefix free/generic types with free- and gen-, respectively. Also reveal
* hidden variadic tails.
*
* 2: Suffix free/generic types with their scope depth.
*
* 3: Suffix free/generic types with their scope pointer, if present.
*/
Sync to upstream/release/576 (#928) * `ClassType` can now have an indexer defined on it. This allows custom types to be used in `t[x]` expressions. * Fixed search for closest executable breakpoint line. Previously, breakpoints might have been skipped in `else` blocks at the end of a function * Fixed how unification is performed for two optional types `a? <: b?`, previously it might have unified either 'a' or 'b' with 'nil'. Note that this fix is not enabled by default yet (see the list in `ExperimentalFlags.h`) In the new type solver, a concept of 'Type Families' has been introduced. Type families can be thought of as type aliases with custom type inference/reduction logic included with them. For example, we can have an `Add<T, U>` type family that will resolve the type that is the result of adding two values together. This will help type inference to figure out what 'T' and 'U' might be when explicit type annotations are not provided. In this update we don't define any type families, but they will be added in the near future. It is also possible for Luau embedders to define their own type families in the global/environment scope. Other changes include: * Fixed scope used to find out which generic types should be included in the function generic type list * Fixed a crash after cyclic bound types were created during unification And in native code generation (jit): * Use of arm64 target on M1 now requires macOS 13 * Entry into native code has been optimized. This is especially important for coroutine call/pcall performance as they involve going through a C call frame * LOP_LOADK(X) translation into IR has been improved to enable type tag/constant propagation * arm64 can use integer immediate values to synthesize floating-point values * x64 assembler removes duplicate 64bit numbers from the data section to save space * Linux `perf` can now be used to profile native Luau code (when running with --codegen-perf CLI argument)
2023-05-12 18:50:47 +01:00
LUAU_FASTINTVARIABLE(DebugLuauVerboseTypeNames, 0)
LUAU_FASTFLAGVARIABLE(DebugLuauToStringNoLexicalSort, false)
namespace Luau
{
namespace
{
struct FindCyclicTypes final : TypeVisitor
{
FindCyclicTypes() = default;
FindCyclicTypes(const FindCyclicTypes&) = delete;
FindCyclicTypes& operator=(const FindCyclicTypes&) = delete;
bool exhaustive = false;
std::unordered_set<TypeId> visited;
std::unordered_set<TypePackId> visitedPacks;
2022-04-15 00:57:43 +01:00
std::set<TypeId> cycles;
std::set<TypePackId> cycleTPs;
2022-05-06 01:03:43 +01:00
void cycle(TypeId ty) override
{
cycles.insert(ty);
}
2022-05-06 01:03:43 +01:00
void cycle(TypePackId tp) override
{
cycleTPs.insert(tp);
}
2022-05-06 01:03:43 +01:00
bool visit(TypeId ty) override
{
return visited.insert(ty).second;
}
bool visit(TypePackId tp) override
{
return visitedPacks.insert(tp).second;
}
2022-05-06 01:03:43 +01:00
bool visit(TypeId ty, const FreeType& ft) override
{
if (!visited.insert(ty).second)
return false;
if (FFlag::DebugLuauDeferredConstraintResolution)
{
// TODO: Replace these if statements with assert()s when we
// delete FFlag::DebugLuauDeferredConstraintResolution.
//
// When the old solver is used, these pointers are always
// unused. When the new solver is used, they are never null.
if (ft.lowerBound)
traverse(ft.lowerBound);
if (ft.upperBound)
traverse(ft.upperBound);
}
return false;
}
bool visit(TypeId ty, const TableType& ttv) override
2022-05-06 01:03:43 +01:00
{
if (!visited.insert(ty).second)
return false;
if (ttv.name || ttv.syntheticName)
{
for (TypeId itp : ttv.instantiatedTypeParams)
traverse(itp);
for (TypePackId itp : ttv.instantiatedTypePackParams)
traverse(itp);
return exhaustive;
}
return true;
}
bool visit(TypeId ty, const ClassType&) override
2022-05-06 01:03:43 +01:00
{
return false;
}
bool visit(TypeId, const PendingExpansionType&) override
{
return false;
}
};
template<typename TID>
2022-04-15 00:57:43 +01:00
void findCyclicTypes(std::set<TypeId>& cycles, std::set<TypePackId>& cycleTPs, TID ty, bool exhaustive)
{
FindCyclicTypes fct;
fct.exhaustive = exhaustive;
fct.traverse(ty);
cycles = std::move(fct.cycles);
cycleTPs = std::move(fct.cycleTPs);
}
} // namespace
static std::pair<bool, std::optional<Luau::Name>> canUseTypeNameInScope(ScopePtr scope, const std::string& name)
{
for (ScopePtr curr = scope; curr; curr = curr->parent)
{
for (const auto& [importName, nameTable] : curr->importedTypeBindings)
{
if (nameTable.count(name))
return {true, importName};
}
if (curr->exportedTypeBindings.count(name))
return {true, std::nullopt};
}
return {false, std::nullopt};
}
struct StringifierState
{
ToStringOptions& opts;
ToStringResult& result;
std::unordered_map<TypeId, std::string> cycleNames;
std::unordered_map<TypePackId, std::string> cycleTpNames;
std::unordered_set<void*> seen;
std::unordered_set<std::string> usedNames;
2022-04-15 00:57:43 +01:00
size_t indentation = 0;
bool exhaustive;
StringifierState(ToStringOptions& opts, ToStringResult& result)
: opts(opts)
, result(result)
, exhaustive(opts.exhaustive)
{
for (const auto& [_, v] : opts.nameMap.types)
usedNames.insert(v);
for (const auto& [_, v] : opts.nameMap.typePacks)
usedNames.insert(v);
}
bool hasSeen(const void* tv)
{
void* ttv = const_cast<void*>(tv);
if (seen.find(ttv) != seen.end())
return true;
seen.insert(ttv);
return false;
}
void unsee(const void* tv)
{
void* ttv = const_cast<void*>(tv);
auto iter = seen.find(ttv);
if (iter != seen.end())
seen.erase(iter);
}
std::string getName(TypeId ty)
{
const size_t s = opts.nameMap.types.size();
std::string& n = opts.nameMap.types[ty];
if (!n.empty())
return n;
for (int count = 0; count < 256; ++count)
{
std::string candidate = generateName(usedNames.size() + count);
if (!usedNames.count(candidate))
{
usedNames.insert(candidate);
n = candidate;
return candidate;
}
}
return generateName(s);
}
2022-05-20 01:02:24 +01:00
int previousNameIndex = 0;
std::string getName(TypePackId ty)
{
const size_t s = opts.nameMap.typePacks.size();
std::string& n = opts.nameMap.typePacks[ty];
if (!n.empty())
return n;
for (int count = 0; count < 256; ++count)
{
2022-05-20 01:02:24 +01:00
std::string candidate = generateName(previousNameIndex + count);
if (!usedNames.count(candidate))
{
2022-05-20 01:02:24 +01:00
previousNameIndex += count;
usedNames.insert(candidate);
n = candidate;
return candidate;
}
}
return generateName(s);
}
void emit(const std::string& s)
{
if (opts.maxTypeLength > 0 && result.name.length() > opts.maxTypeLength)
return;
result.name += s;
}
2022-01-14 16:20:09 +00:00
void emitLevel(Scope* scope)
{
size_t count = 0;
for (Scope* s = scope; s; s = s->parent.get())
++count;
emit(count);
Sync to upstream/release/576 (#928) * `ClassType` can now have an indexer defined on it. This allows custom types to be used in `t[x]` expressions. * Fixed search for closest executable breakpoint line. Previously, breakpoints might have been skipped in `else` blocks at the end of a function * Fixed how unification is performed for two optional types `a? <: b?`, previously it might have unified either 'a' or 'b' with 'nil'. Note that this fix is not enabled by default yet (see the list in `ExperimentalFlags.h`) In the new type solver, a concept of 'Type Families' has been introduced. Type families can be thought of as type aliases with custom type inference/reduction logic included with them. For example, we can have an `Add<T, U>` type family that will resolve the type that is the result of adding two values together. This will help type inference to figure out what 'T' and 'U' might be when explicit type annotations are not provided. In this update we don't define any type families, but they will be added in the near future. It is also possible for Luau embedders to define their own type families in the global/environment scope. Other changes include: * Fixed scope used to find out which generic types should be included in the function generic type list * Fixed a crash after cyclic bound types were created during unification And in native code generation (jit): * Use of arm64 target on M1 now requires macOS 13 * Entry into native code has been optimized. This is especially important for coroutine call/pcall performance as they involve going through a C call frame * LOP_LOADK(X) translation into IR has been improved to enable type tag/constant propagation * arm64 can use integer immediate values to synthesize floating-point values * x64 assembler removes duplicate 64bit numbers from the data section to save space * Linux `perf` can now be used to profile native Luau code (when running with --codegen-perf CLI argument)
2023-05-12 18:50:47 +01:00
if (FInt::DebugLuauVerboseTypeNames >= 3)
{
emit("-");
char buffer[16];
uint32_t s = uint32_t(intptr_t(scope) & 0xFFFFFF);
snprintf(buffer, sizeof(buffer), "0x%x", s);
emit(buffer);
}
}
2022-05-13 20:36:37 +01:00
void emit(TypeLevel level)
{
emit(std::to_string(level.level));
emit("-");
emit(std::to_string(level.subLevel));
}
2022-01-14 16:20:09 +00:00
void emit(const char* s)
{
if (opts.maxTypeLength > 0 && result.name.length() > opts.maxTypeLength)
return;
result.name += s;
}
2022-04-15 00:57:43 +01:00
2022-06-17 02:05:14 +01:00
void emit(int i)
{
emit(std::to_string(i).c_str());
}
2022-08-04 23:35:33 +01:00
void emit(size_t i)
{
emit(std::to_string(i).c_str());
}
2022-04-15 00:57:43 +01:00
void indent()
{
indentation += 4;
}
void dedent()
{
indentation -= 4;
}
void newline()
{
if (!opts.useLineBreaks)
return emit(" ");
emit("\n");
emitIndentation();
}
private:
void emitIndentation()
{
if (!opts.useLineBreaks)
return;
emit(std::string(indentation, ' '));
2022-04-15 00:57:43 +01:00
}
};
struct TypeStringifier
{
StringifierState& state;
explicit TypeStringifier(StringifierState& state)
: state(state)
{
}
void stringify(TypeId tv)
{
if (state.opts.maxTypeLength > 0 && state.result.name.length() > state.opts.maxTypeLength)
return;
if (tv->ty.valueless_by_exception())
{
state.result.error = true;
state.emit("* VALUELESS BY EXCEPTION *");
return;
}
auto it = state.cycleNames.find(tv);
if (it != state.cycleNames.end())
{
state.emit(it->second);
return;
}
Luau::visit(
[this, tv](auto&& t) {
return (*this)(tv, t);
},
tv->ty);
}
void stringify(const std::string& name, const Property& prop)
{
if (isIdentifier(name))
state.emit(name);
else
{
state.emit("[\"");
state.emit(escape(name));
state.emit("\"]");
}
state.emit(": ");
if (FFlag::DebugLuauReadWriteProperties)
{
// We special case the stringification if the property's read and write types are shared.
if (prop.isShared())
return stringify(*prop.readType());
// Otherwise emit them separately.
if (auto ty = prop.readType())
{
state.emit("read ");
stringify(*ty);
}
if (prop.readType() && prop.writeType())
state.emit(" + ");
if (auto ty = prop.writeType())
{
state.emit("write ");
stringify(*ty);
}
}
else
stringify(prop.type());
}
void stringify(TypePackId tp);
void stringify(TypePackId tpid, const std::vector<std::optional<FunctionArgument>>& names);
void stringify(const std::vector<TypeId>& types, const std::vector<TypePackId>& typePacks)
{
if (types.size() == 0 && typePacks.size() == 0)
return;
if (types.size() || typePacks.size())
state.emit("<");
bool first = true;
for (TypeId ty : types)
{
if (!first)
state.emit(", ");
first = false;
stringify(ty);
}
bool singleTp = typePacks.size() == 1;
for (TypePackId tp : typePacks)
{
if (isEmpty(tp) && singleTp)
continue;
if (!first)
state.emit(", ");
else
first = false;
2022-03-04 16:36:33 +00:00
bool wrap = !singleTp && get<TypePack>(follow(tp));
2022-01-14 16:20:09 +00:00
2022-03-04 16:36:33 +00:00
if (wrap)
state.emit("(");
2022-01-14 16:20:09 +00:00
2022-03-04 16:36:33 +00:00
stringify(tp);
2022-01-14 16:20:09 +00:00
2022-03-04 16:36:33 +00:00
if (wrap)
state.emit(")");
}
if (types.size() || typePacks.size())
state.emit(">");
}
void operator()(TypeId ty, const FreeType& ftv)
{
state.result.invalid = true;
Sync to upstream/release/576 (#928) * `ClassType` can now have an indexer defined on it. This allows custom types to be used in `t[x]` expressions. * Fixed search for closest executable breakpoint line. Previously, breakpoints might have been skipped in `else` blocks at the end of a function * Fixed how unification is performed for two optional types `a? <: b?`, previously it might have unified either 'a' or 'b' with 'nil'. Note that this fix is not enabled by default yet (see the list in `ExperimentalFlags.h`) In the new type solver, a concept of 'Type Families' has been introduced. Type families can be thought of as type aliases with custom type inference/reduction logic included with them. For example, we can have an `Add<T, U>` type family that will resolve the type that is the result of adding two values together. This will help type inference to figure out what 'T' and 'U' might be when explicit type annotations are not provided. In this update we don't define any type families, but they will be added in the near future. It is also possible for Luau embedders to define their own type families in the global/environment scope. Other changes include: * Fixed scope used to find out which generic types should be included in the function generic type list * Fixed a crash after cyclic bound types were created during unification And in native code generation (jit): * Use of arm64 target on M1 now requires macOS 13 * Entry into native code has been optimized. This is especially important for coroutine call/pcall performance as they involve going through a C call frame * LOP_LOADK(X) translation into IR has been improved to enable type tag/constant propagation * arm64 can use integer immediate values to synthesize floating-point values * x64 assembler removes duplicate 64bit numbers from the data section to save space * Linux `perf` can now be used to profile native Luau code (when running with --codegen-perf CLI argument)
2023-05-12 18:50:47 +01:00
// TODO: ftv.lowerBound and ftv.upperBound should always be non-nil when
// the new solver is used. This can be replaced with an assert.
if (FFlag::DebugLuauDeferredConstraintResolution && ftv.lowerBound && ftv.upperBound)
{
const TypeId lowerBound = follow(ftv.lowerBound);
const TypeId upperBound = follow(ftv.upperBound);
if (get<NeverType>(lowerBound) && get<UnknownType>(upperBound))
{
state.emit("'");
state.emit(state.getName(ty));
}
else
{
state.emit("(");
if (!get<NeverType>(lowerBound))
{
stringify(lowerBound);
state.emit(" <: ");
}
state.emit("'");
state.emit(state.getName(ty));
if (!get<UnknownType>(upperBound))
{
state.emit(" <: ");
stringify(upperBound);
}
state.emit(")");
}
return;
}
Sync to upstream/release/576 (#928) * `ClassType` can now have an indexer defined on it. This allows custom types to be used in `t[x]` expressions. * Fixed search for closest executable breakpoint line. Previously, breakpoints might have been skipped in `else` blocks at the end of a function * Fixed how unification is performed for two optional types `a? <: b?`, previously it might have unified either 'a' or 'b' with 'nil'. Note that this fix is not enabled by default yet (see the list in `ExperimentalFlags.h`) In the new type solver, a concept of 'Type Families' has been introduced. Type families can be thought of as type aliases with custom type inference/reduction logic included with them. For example, we can have an `Add<T, U>` type family that will resolve the type that is the result of adding two values together. This will help type inference to figure out what 'T' and 'U' might be when explicit type annotations are not provided. In this update we don't define any type families, but they will be added in the near future. It is also possible for Luau embedders to define their own type families in the global/environment scope. Other changes include: * Fixed scope used to find out which generic types should be included in the function generic type list * Fixed a crash after cyclic bound types were created during unification And in native code generation (jit): * Use of arm64 target on M1 now requires macOS 13 * Entry into native code has been optimized. This is especially important for coroutine call/pcall performance as they involve going through a C call frame * LOP_LOADK(X) translation into IR has been improved to enable type tag/constant propagation * arm64 can use integer immediate values to synthesize floating-point values * x64 assembler removes duplicate 64bit numbers from the data section to save space * Linux `perf` can now be used to profile native Luau code (when running with --codegen-perf CLI argument)
2023-05-12 18:50:47 +01:00
if (FInt::DebugLuauVerboseTypeNames >= 1)
state.emit("free-");
Sync to upstream/release/576 (#928) * `ClassType` can now have an indexer defined on it. This allows custom types to be used in `t[x]` expressions. * Fixed search for closest executable breakpoint line. Previously, breakpoints might have been skipped in `else` blocks at the end of a function * Fixed how unification is performed for two optional types `a? <: b?`, previously it might have unified either 'a' or 'b' with 'nil'. Note that this fix is not enabled by default yet (see the list in `ExperimentalFlags.h`) In the new type solver, a concept of 'Type Families' has been introduced. Type families can be thought of as type aliases with custom type inference/reduction logic included with them. For example, we can have an `Add<T, U>` type family that will resolve the type that is the result of adding two values together. This will help type inference to figure out what 'T' and 'U' might be when explicit type annotations are not provided. In this update we don't define any type families, but they will be added in the near future. It is also possible for Luau embedders to define their own type families in the global/environment scope. Other changes include: * Fixed scope used to find out which generic types should be included in the function generic type list * Fixed a crash after cyclic bound types were created during unification And in native code generation (jit): * Use of arm64 target on M1 now requires macOS 13 * Entry into native code has been optimized. This is especially important for coroutine call/pcall performance as they involve going through a C call frame * LOP_LOADK(X) translation into IR has been improved to enable type tag/constant propagation * arm64 can use integer immediate values to synthesize floating-point values * x64 assembler removes duplicate 64bit numbers from the data section to save space * Linux `perf` can now be used to profile native Luau code (when running with --codegen-perf CLI argument)
2023-05-12 18:50:47 +01:00
state.emit(state.getName(ty));
Sync to upstream/release/576 (#928) * `ClassType` can now have an indexer defined on it. This allows custom types to be used in `t[x]` expressions. * Fixed search for closest executable breakpoint line. Previously, breakpoints might have been skipped in `else` blocks at the end of a function * Fixed how unification is performed for two optional types `a? <: b?`, previously it might have unified either 'a' or 'b' with 'nil'. Note that this fix is not enabled by default yet (see the list in `ExperimentalFlags.h`) In the new type solver, a concept of 'Type Families' has been introduced. Type families can be thought of as type aliases with custom type inference/reduction logic included with them. For example, we can have an `Add<T, U>` type family that will resolve the type that is the result of adding two values together. This will help type inference to figure out what 'T' and 'U' might be when explicit type annotations are not provided. In this update we don't define any type families, but they will be added in the near future. It is also possible for Luau embedders to define their own type families in the global/environment scope. Other changes include: * Fixed scope used to find out which generic types should be included in the function generic type list * Fixed a crash after cyclic bound types were created during unification And in native code generation (jit): * Use of arm64 target on M1 now requires macOS 13 * Entry into native code has been optimized. This is especially important for coroutine call/pcall performance as they involve going through a C call frame * LOP_LOADK(X) translation into IR has been improved to enable type tag/constant propagation * arm64 can use integer immediate values to synthesize floating-point values * x64 assembler removes duplicate 64bit numbers from the data section to save space * Linux `perf` can now be used to profile native Luau code (when running with --codegen-perf CLI argument)
2023-05-12 18:50:47 +01:00
if (FInt::DebugLuauVerboseTypeNames >= 2)
{
state.emit("-");
if (FFlag::DebugLuauDeferredConstraintResolution)
state.emitLevel(ftv.scope);
else
state.emit(ftv.level);
}
}
void operator()(TypeId, const BoundType& btv)
{
stringify(btv.boundTo);
}
void operator()(TypeId ty, const GenericType& gtv)
{
Sync to upstream/release/576 (#928) * `ClassType` can now have an indexer defined on it. This allows custom types to be used in `t[x]` expressions. * Fixed search for closest executable breakpoint line. Previously, breakpoints might have been skipped in `else` blocks at the end of a function * Fixed how unification is performed for two optional types `a? <: b?`, previously it might have unified either 'a' or 'b' with 'nil'. Note that this fix is not enabled by default yet (see the list in `ExperimentalFlags.h`) In the new type solver, a concept of 'Type Families' has been introduced. Type families can be thought of as type aliases with custom type inference/reduction logic included with them. For example, we can have an `Add<T, U>` type family that will resolve the type that is the result of adding two values together. This will help type inference to figure out what 'T' and 'U' might be when explicit type annotations are not provided. In this update we don't define any type families, but they will be added in the near future. It is also possible for Luau embedders to define their own type families in the global/environment scope. Other changes include: * Fixed scope used to find out which generic types should be included in the function generic type list * Fixed a crash after cyclic bound types were created during unification And in native code generation (jit): * Use of arm64 target on M1 now requires macOS 13 * Entry into native code has been optimized. This is especially important for coroutine call/pcall performance as they involve going through a C call frame * LOP_LOADK(X) translation into IR has been improved to enable type tag/constant propagation * arm64 can use integer immediate values to synthesize floating-point values * x64 assembler removes duplicate 64bit numbers from the data section to save space * Linux `perf` can now be used to profile native Luau code (when running with --codegen-perf CLI argument)
2023-05-12 18:50:47 +01:00
if (FInt::DebugLuauVerboseTypeNames >= 1)
state.emit("gen-");
if (gtv.explicitName)
{
2022-05-20 01:02:24 +01:00
state.usedNames.insert(gtv.name);
state.opts.nameMap.types[ty] = gtv.name;
state.emit(gtv.name);
}
else
state.emit(state.getName(ty));
Sync to upstream/release/576 (#928) * `ClassType` can now have an indexer defined on it. This allows custom types to be used in `t[x]` expressions. * Fixed search for closest executable breakpoint line. Previously, breakpoints might have been skipped in `else` blocks at the end of a function * Fixed how unification is performed for two optional types `a? <: b?`, previously it might have unified either 'a' or 'b' with 'nil'. Note that this fix is not enabled by default yet (see the list in `ExperimentalFlags.h`) In the new type solver, a concept of 'Type Families' has been introduced. Type families can be thought of as type aliases with custom type inference/reduction logic included with them. For example, we can have an `Add<T, U>` type family that will resolve the type that is the result of adding two values together. This will help type inference to figure out what 'T' and 'U' might be when explicit type annotations are not provided. In this update we don't define any type families, but they will be added in the near future. It is also possible for Luau embedders to define their own type families in the global/environment scope. Other changes include: * Fixed scope used to find out which generic types should be included in the function generic type list * Fixed a crash after cyclic bound types were created during unification And in native code generation (jit): * Use of arm64 target on M1 now requires macOS 13 * Entry into native code has been optimized. This is especially important for coroutine call/pcall performance as they involve going through a C call frame * LOP_LOADK(X) translation into IR has been improved to enable type tag/constant propagation * arm64 can use integer immediate values to synthesize floating-point values * x64 assembler removes duplicate 64bit numbers from the data section to save space * Linux `perf` can now be used to profile native Luau code (when running with --codegen-perf CLI argument)
2023-05-12 18:50:47 +01:00
if (FInt::DebugLuauVerboseTypeNames >= 2)
{
state.emit("-");
if (FFlag::DebugLuauDeferredConstraintResolution)
state.emitLevel(gtv.scope);
else
state.emit(gtv.level);
}
}
void operator()(TypeId, const BlockedType& btv)
2022-06-17 02:05:14 +01:00
{
state.emit("*blocked-");
state.emit(btv.index);
state.emit("*");
}
void operator()(TypeId ty, const PendingExpansionType& petv)
2022-08-04 23:35:33 +01:00
{
state.emit("*pending-expansion-");
state.emit(petv.index);
state.emit("*");
}
void operator()(TypeId, const PrimitiveType& ptv)
{
switch (ptv.type)
{
case PrimitiveType::NilType:
state.emit("nil");
return;
case PrimitiveType::Boolean:
state.emit("boolean");
return;
case PrimitiveType::Number:
state.emit("number");
return;
case PrimitiveType::String:
state.emit("string");
return;
case PrimitiveType::Thread:
state.emit("thread");
return;
Sync to upstream/release/603 (#1097) # What's changed? - Record the location of properties for table types (closes #802) - Implement stricter UTF-8 validations as per the RFC (https://github.com/luau-lang/rfcs/pull/1) - Implement `buffer` as a new type in both the old and new solvers. - Changed errors produced by some `buffer` builtins to be a bit more generic to avoid platform-dependent error messages. - Fixed a bug where `Unifier` would copy some persistent types, tripping some internal assertions. - Type checking rules on relational operators is now a little bit more lax. - Improve dead code elimination for some `if` statements with complex always-false conditions ## New type solver - Dataflow analysis now generates phi nodes on exit of branches. - Dataflow analysis avoids producing a new definition for locals or properties that are not owned by that loop. - If a function parameter has been constrained to `never`, report errors at all uses of that parameter within that function. - Switch to using the new `Luau::Set` to replace `std::unordered_set` to alleviate some poor allocation characteristics which was negatively affecting overall performance. - Subtyping can now report many failing reasons instead of just the first one that we happened to find during the test. - Subtyping now also report reasons for type pack mismatches. - When visiting `if` statements or expressions, the resulting context are the common terms in both branches. ## Native codegen - Implement support for `buffer` builtins to its IR for x64 and A64. - Optimized `table.insert` by not inserting a table barrier if it is fastcalled with a constant. ## Internal Contributors 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: Arseny Kapoulkine <arseny@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-11-10 21:10:07 +00:00
case PrimitiveType::Buffer:
state.emit("buffer");
return;
case PrimitiveType::Function:
state.emit("function");
return;
case PrimitiveType::Table:
state.emit("table");
return;
default:
LUAU_ASSERT(!"Unknown primitive type");
throw InternalCompilerError("Unknown primitive type " + std::to_string(ptv.type));
}
}
void operator()(TypeId, const SingletonType& stv)
{
if (const BooleanSingleton* bs = Luau::get<BooleanSingleton>(&stv))
state.emit(bs->value ? "true" : "false");
else if (const StringSingleton* ss = Luau::get<StringSingleton>(&stv))
{
state.emit("\"");
state.emit(escape(ss->value));
state.emit("\"");
}
else
{
LUAU_ASSERT(!"Unknown singleton type");
throw InternalCompilerError("Unknown singleton type");
}
}
void operator()(TypeId, const FunctionType& ftv)
{
if (state.hasSeen(&ftv))
{
state.result.cycle = true;
state.emit("*CYCLE*");
return;
}
// We should not be respecting opts.hideNamedFunctionTypeParameters here.
if (ftv.generics.size() > 0 || ftv.genericPacks.size() > 0)
{
state.emit("<");
bool comma = false;
for (auto it = ftv.generics.begin(); it != ftv.generics.end(); ++it)
{
if (comma)
state.emit(", ");
comma = true;
stringify(*it);
}
for (auto it = ftv.genericPacks.begin(); it != ftv.genericPacks.end(); ++it)
{
if (comma)
state.emit(", ");
comma = true;
stringify(*it);
}
state.emit(">");
}
Sync to upstream/release/600 (#1076) ### What's Changed - Improve readability of unions and intersections by limiting the number of elements of those types that can be presented on a single line (gated under `FFlag::LuauToStringSimpleCompositeTypesSingleLine`) - Adds a new option to the compiler `--record-stats` to record and output compilation statistics - `if...then...else` expressions are now optimized into `AND/OR` form when possible. ### VM - Add a new `buffer` type to Luau based on the [buffer RFC](https://github.com/Roblox/luau/pull/739) and additional C API functions to work with it; this release does not include the library. - Internal C API to work with string buffers has been updated to align with Lua version more closely ### Native Codegen - Added support for new X64 instruction (rev) and new A64 instruction (bswap) in the assembler - Simplified the way numerical loop condition is translated to IR ### New Type Solver - Operator inference now handled by type families - Created a new system called `Type Paths` to explain why subtyping tests fail in order to improve the quality of error messages. - Systematic changes to implement Data Flow analysis in the new solver (`Breadcrumb` removed and replaced with `RefinementKey`) --- 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: Lily Brown <lbrown@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com>
2023-10-21 02:10:30 +01:00
if (FFlag::DebugLuauDeferredConstraintResolution)
{
if (ftv.isCheckedFunction)
state.emit("@checked ");
}
state.emit("(");
if (state.opts.functionTypeArguments)
stringify(ftv.argTypes, ftv.argNames);
else
stringify(ftv.argTypes);
state.emit(") -> ");
bool plural = true;
2022-04-15 00:57:43 +01:00
auto retBegin = begin(ftv.retTypes);
auto retEnd = end(ftv.retTypes);
if (retBegin != retEnd)
2022-04-15 00:57:43 +01:00
{
++retBegin;
if (retBegin == retEnd && !retBegin.tail())
plural = false;
}
if (plural)
state.emit("(");
2022-06-17 02:05:14 +01:00
stringify(ftv.retTypes);
if (plural)
state.emit(")");
state.unsee(&ftv);
}
void operator()(TypeId, const TableType& ttv)
{
if (ttv.boundTo)
return stringify(*ttv.boundTo);
if (!state.exhaustive)
{
if (ttv.name)
{
// If scope if provided, add module name and check visibility
if (state.opts.scope)
{
auto [success, moduleName] = canUseTypeNameInScope(state.opts.scope, *ttv.name);
if (!success)
state.result.invalid = true;
if (moduleName)
{
state.emit(*moduleName);
state.emit(".");
}
}
state.emit(*ttv.name);
stringify(ttv.instantiatedTypeParams, ttv.instantiatedTypePackParams);
return;
}
if (ttv.syntheticName)
{
state.result.invalid = true;
state.emit(*ttv.syntheticName);
stringify(ttv.instantiatedTypeParams, ttv.instantiatedTypePackParams);
return;
}
}
if (state.hasSeen(&ttv))
{
state.result.cycle = true;
state.emit("*CYCLE*");
return;
}
std::string openbrace = "@@@";
std::string closedbrace = "@@@?!";
switch (state.opts.hideTableKind ? (FFlag::DebugLuauDeferredConstraintResolution ? TableState::Sealed : TableState::Unsealed) : ttv.state)
{
case TableState::Sealed:
if (FFlag::DebugLuauDeferredConstraintResolution)
{
openbrace = "{";
closedbrace = "}";
}
else
{
state.result.invalid = true;
openbrace = "{|";
closedbrace = "|}";
}
break;
case TableState::Unsealed:
if (FFlag::DebugLuauDeferredConstraintResolution)
{
state.result.invalid = true;
openbrace = "{|";
closedbrace = "|}";
}
else
{
openbrace = "{";
closedbrace = "}";
}
break;
case TableState::Free:
state.result.invalid = true;
2022-07-14 23:52:26 +01:00
openbrace = "{-";
closedbrace = "-}";
break;
case TableState::Generic:
state.result.invalid = true;
2022-07-14 23:52:26 +01:00
openbrace = "{+";
closedbrace = "+}";
break;
}
// If this appears to be an array, we want to stringify it using the {T} syntax.
if (ttv.indexer && ttv.props.empty() && isNumber(ttv.indexer->indexType))
{
state.emit("{");
stringify(ttv.indexer->indexResultType);
state.emit("}");
state.unsee(&ttv);
return;
}
state.emit(openbrace);
2022-04-15 00:57:43 +01:00
state.indent();
bool comma = false;
if (ttv.indexer)
{
2022-07-14 23:52:26 +01:00
state.newline();
state.emit("[");
stringify(ttv.indexer->indexType);
state.emit("]: ");
stringify(ttv.indexer->indexResultType);
comma = true;
}
size_t index = 0;
size_t oldLength = state.result.name.length();
for (const auto& [name, prop] : ttv.props)
{
if (comma)
2022-04-15 00:57:43 +01:00
{
state.emit(",");
state.newline();
}
2022-07-14 23:52:26 +01:00
else
state.newline();
size_t length = state.result.name.length() - oldLength;
if (state.opts.maxTableLength > 0 && (length - 2 * index) >= state.opts.maxTableLength)
{
state.emit("... ");
state.emit(std::to_string(ttv.props.size() - index));
state.emit(" more ...");
break;
}
stringify(name, prop);
comma = true;
++index;
}
2022-04-15 00:57:43 +01:00
state.dedent();
2022-07-14 23:52:26 +01:00
if (comma)
state.newline();
else
state.emit(" ");
state.emit(closedbrace);
state.unsee(&ttv);
}
void operator()(TypeId, const MetatableType& mtv)
{
state.result.invalid = true;
if (!state.exhaustive && mtv.syntheticName)
{
state.emit(*mtv.syntheticName);
return;
}
state.emit("{ @metatable ");
stringify(mtv.metatable);
2022-04-15 00:57:43 +01:00
state.emit(",");
state.newline();
stringify(mtv.table);
state.emit(" }");
}
void operator()(TypeId, const ClassType& ctv)
{
state.emit(ctv.name);
}
void operator()(TypeId, const AnyType&)
{
state.emit("any");
}
void operator()(TypeId, const UnionType& uv)
{
if (state.hasSeen(&uv))
{
state.result.cycle = true;
state.emit("*CYCLE*");
return;
}
bool optional = false;
bool hasNonNilDisjunct = false;
std::vector<std::string> results = {};
for (auto el : &uv)
{
el = follow(el);
if (isNil(el))
{
optional = true;
continue;
}
else
{
hasNonNilDisjunct = true;
}
std::string saved = std::move(state.result.name);
bool needParens = !state.cycleNames.count(el) && (get<IntersectionType>(el) || get<FunctionType>(el));
if (needParens)
state.emit("(");
stringify(el);
if (needParens)
state.emit(")");
results.push_back(std::move(state.result.name));
state.result.name = std::move(saved);
}
state.unsee(&uv);
if (!FFlag::DebugLuauToStringNoLexicalSort)
std::sort(results.begin(), results.end());
if (optional && results.size() > 1)
state.emit("(");
bool first = true;
bool shouldPlaceOnNewlines = results.size() > state.opts.compositeTypesSingleLineLimit;
for (std::string& ss : results)
{
if (!first)
{
if (shouldPlaceOnNewlines)
state.newline();
else
state.emit(" ");
state.emit("| ");
}
state.emit(ss);
first = false;
}
if (optional)
{
const char* s = "?";
if (results.size() > 1)
s = ")?";
if (!hasNonNilDisjunct)
s = "nil";
state.emit(s);
}
}
void operator()(TypeId ty, const IntersectionType& uv)
{
if (state.hasSeen(&uv))
{
state.result.cycle = true;
state.emit("*CYCLE*");
return;
}
std::vector<std::string> results = {};
for (auto el : uv.parts)
{
el = follow(el);
std::string saved = std::move(state.result.name);
bool needParens = !state.cycleNames.count(el) && (get<UnionType>(el) || get<FunctionType>(el));
if (needParens)
state.emit("(");
stringify(el);
if (needParens)
state.emit(")");
results.push_back(std::move(state.result.name));
state.result.name = std::move(saved);
}
state.unsee(&uv);
if (!FFlag::DebugLuauToStringNoLexicalSort)
std::sort(results.begin(), results.end());
bool first = true;
bool shouldPlaceOnNewlines = results.size() > state.opts.compositeTypesSingleLineLimit || isOverloadedFunction(ty);
for (std::string& ss : results)
{
if (!first)
{
if (shouldPlaceOnNewlines)
state.newline();
else
state.emit(" ");
state.emit("& ");
}
state.emit(ss);
first = false;
}
}
void operator()(TypeId, const ErrorType& tv)
{
state.result.error = true;
state.emit("*error-type*");
}
void operator()(TypeId, const LazyType& ltv)
{
if (TypeId unwrapped = ltv.unwrapped.load())
{
stringify(unwrapped);
}
else
{
state.result.invalid = true;
state.emit("lazy?");
}
}
void operator()(TypeId, const UnknownType& ttv)
2022-07-08 02:22:39 +01:00
{
state.emit("unknown");
}
void operator()(TypeId, const NeverType& ttv)
2022-07-08 02:22:39 +01:00
{
state.emit("never");
}
void operator()(TypeId, const NegationType& ntv)
{
state.emit("~");
// The precedence of `~` should be less than `|` and `&`.
TypeId followed = follow(ntv.ty);
bool parens = get<UnionType>(followed) || get<IntersectionType>(followed);
if (parens)
state.emit("(");
stringify(ntv.ty);
if (parens)
state.emit(")");
}
Sync to upstream/release/576 (#928) * `ClassType` can now have an indexer defined on it. This allows custom types to be used in `t[x]` expressions. * Fixed search for closest executable breakpoint line. Previously, breakpoints might have been skipped in `else` blocks at the end of a function * Fixed how unification is performed for two optional types `a? <: b?`, previously it might have unified either 'a' or 'b' with 'nil'. Note that this fix is not enabled by default yet (see the list in `ExperimentalFlags.h`) In the new type solver, a concept of 'Type Families' has been introduced. Type families can be thought of as type aliases with custom type inference/reduction logic included with them. For example, we can have an `Add<T, U>` type family that will resolve the type that is the result of adding two values together. This will help type inference to figure out what 'T' and 'U' might be when explicit type annotations are not provided. In this update we don't define any type families, but they will be added in the near future. It is also possible for Luau embedders to define their own type families in the global/environment scope. Other changes include: * Fixed scope used to find out which generic types should be included in the function generic type list * Fixed a crash after cyclic bound types were created during unification And in native code generation (jit): * Use of arm64 target on M1 now requires macOS 13 * Entry into native code has been optimized. This is especially important for coroutine call/pcall performance as they involve going through a C call frame * LOP_LOADK(X) translation into IR has been improved to enable type tag/constant propagation * arm64 can use integer immediate values to synthesize floating-point values * x64 assembler removes duplicate 64bit numbers from the data section to save space * Linux `perf` can now be used to profile native Luau code (when running with --codegen-perf CLI argument)
2023-05-12 18:50:47 +01:00
void operator()(TypeId, const TypeFamilyInstanceType& tfitv)
{
state.emit(tfitv.family->name);
state.emit("<");
bool comma = false;
for (TypeId ty : tfitv.typeArguments)
{
if (comma)
state.emit(", ");
comma = true;
stringify(ty);
}
for (TypePackId tp : tfitv.packArguments)
{
if (comma)
state.emit(", ");
comma = true;
stringify(tp);
}
state.emit(">");
}
2022-07-08 02:22:39 +01:00
};
struct TypePackStringifier
{
StringifierState& state;
const std::vector<std::optional<FunctionArgument>> elemNames;
static inline const std::vector<std::optional<FunctionArgument>> dummyElemNames = {};
unsigned elemIndex = 0;
explicit TypePackStringifier(StringifierState& state, const std::vector<std::optional<FunctionArgument>>& elemNames)
: state(state)
, elemNames(elemNames)
{
}
explicit TypePackStringifier(StringifierState& state)
: state(state)
, elemNames(dummyElemNames)
{
}
void stringify(TypeId tv)
{
TypeStringifier tvs{state};
tvs.stringify(tv);
}
void stringify(TypePackId tp)
{
if (state.opts.maxTypeLength > 0 && state.result.name.length() > state.opts.maxTypeLength)
return;
if (tp->ty.valueless_by_exception())
{
state.result.error = true;
state.emit("* VALUELESS TP BY EXCEPTION *");
return;
}
auto it = state.cycleTpNames.find(tp);
if (it != state.cycleTpNames.end())
{
state.emit(it->second);
return;
}
Luau::visit(
[this, tp](auto&& t) {
return (*this)(tp, t);
},
tp->ty);
}
void operator()(TypePackId, const TypePack& tp)
{
if (state.hasSeen(&tp))
{
state.result.cycle = true;
state.emit("*CYCLETP*");
return;
}
bool first = true;
for (const auto& typeId : tp.head)
{
if (first)
first = false;
else
state.emit(", ");
2022-03-24 22:04:14 +00:00
// Do not respect opts.namedFunctionOverrideArgNames here
if (elemIndex < elemNames.size() && elemNames[elemIndex])
{
state.emit(elemNames[elemIndex]->name);
state.emit(": ");
}
elemIndex++;
stringify(typeId);
}
if (tp.tail && !isEmpty(*tp.tail))
{
2022-04-15 00:57:43 +01:00
TypePackId tail = follow(*tp.tail);
Sync to upstream/release/576 (#928) * `ClassType` can now have an indexer defined on it. This allows custom types to be used in `t[x]` expressions. * Fixed search for closest executable breakpoint line. Previously, breakpoints might have been skipped in `else` blocks at the end of a function * Fixed how unification is performed for two optional types `a? <: b?`, previously it might have unified either 'a' or 'b' with 'nil'. Note that this fix is not enabled by default yet (see the list in `ExperimentalFlags.h`) In the new type solver, a concept of 'Type Families' has been introduced. Type families can be thought of as type aliases with custom type inference/reduction logic included with them. For example, we can have an `Add<T, U>` type family that will resolve the type that is the result of adding two values together. This will help type inference to figure out what 'T' and 'U' might be when explicit type annotations are not provided. In this update we don't define any type families, but they will be added in the near future. It is also possible for Luau embedders to define their own type families in the global/environment scope. Other changes include: * Fixed scope used to find out which generic types should be included in the function generic type list * Fixed a crash after cyclic bound types were created during unification And in native code generation (jit): * Use of arm64 target on M1 now requires macOS 13 * Entry into native code has been optimized. This is especially important for coroutine call/pcall performance as they involve going through a C call frame * LOP_LOADK(X) translation into IR has been improved to enable type tag/constant propagation * arm64 can use integer immediate values to synthesize floating-point values * x64 assembler removes duplicate 64bit numbers from the data section to save space * Linux `perf` can now be used to profile native Luau code (when running with --codegen-perf CLI argument)
2023-05-12 18:50:47 +01:00
if (auto vtp = get<VariadicTypePack>(tail); !vtp || (FInt::DebugLuauVerboseTypeNames < 1 && !vtp->hidden))
2022-04-15 00:57:43 +01:00
{
if (first)
first = false;
else
state.emit(", ");
2022-04-15 00:57:43 +01:00
stringify(tail);
}
}
state.unsee(&tp);
}
void operator()(TypePackId, const Unifiable::Error& error)
{
state.result.error = true;
state.emit("*error-type*");
}
void operator()(TypePackId, const VariadicTypePack& pack)
{
state.emit("...");
Sync to upstream/release/576 (#928) * `ClassType` can now have an indexer defined on it. This allows custom types to be used in `t[x]` expressions. * Fixed search for closest executable breakpoint line. Previously, breakpoints might have been skipped in `else` blocks at the end of a function * Fixed how unification is performed for two optional types `a? <: b?`, previously it might have unified either 'a' or 'b' with 'nil'. Note that this fix is not enabled by default yet (see the list in `ExperimentalFlags.h`) In the new type solver, a concept of 'Type Families' has been introduced. Type families can be thought of as type aliases with custom type inference/reduction logic included with them. For example, we can have an `Add<T, U>` type family that will resolve the type that is the result of adding two values together. This will help type inference to figure out what 'T' and 'U' might be when explicit type annotations are not provided. In this update we don't define any type families, but they will be added in the near future. It is also possible for Luau embedders to define their own type families in the global/environment scope. Other changes include: * Fixed scope used to find out which generic types should be included in the function generic type list * Fixed a crash after cyclic bound types were created during unification And in native code generation (jit): * Use of arm64 target on M1 now requires macOS 13 * Entry into native code has been optimized. This is especially important for coroutine call/pcall performance as they involve going through a C call frame * LOP_LOADK(X) translation into IR has been improved to enable type tag/constant propagation * arm64 can use integer immediate values to synthesize floating-point values * x64 assembler removes duplicate 64bit numbers from the data section to save space * Linux `perf` can now be used to profile native Luau code (when running with --codegen-perf CLI argument)
2023-05-12 18:50:47 +01:00
if (FInt::DebugLuauVerboseTypeNames >= 1 && pack.hidden)
2022-07-29 05:24:07 +01:00
{
state.emit("*hidden*");
2022-07-29 05:24:07 +01:00
}
stringify(pack.ty);
}
void operator()(TypePackId tp, const GenericTypePack& pack)
{
Sync to upstream/release/576 (#928) * `ClassType` can now have an indexer defined on it. This allows custom types to be used in `t[x]` expressions. * Fixed search for closest executable breakpoint line. Previously, breakpoints might have been skipped in `else` blocks at the end of a function * Fixed how unification is performed for two optional types `a? <: b?`, previously it might have unified either 'a' or 'b' with 'nil'. Note that this fix is not enabled by default yet (see the list in `ExperimentalFlags.h`) In the new type solver, a concept of 'Type Families' has been introduced. Type families can be thought of as type aliases with custom type inference/reduction logic included with them. For example, we can have an `Add<T, U>` type family that will resolve the type that is the result of adding two values together. This will help type inference to figure out what 'T' and 'U' might be when explicit type annotations are not provided. In this update we don't define any type families, but they will be added in the near future. It is also possible for Luau embedders to define their own type families in the global/environment scope. Other changes include: * Fixed scope used to find out which generic types should be included in the function generic type list * Fixed a crash after cyclic bound types were created during unification And in native code generation (jit): * Use of arm64 target on M1 now requires macOS 13 * Entry into native code has been optimized. This is especially important for coroutine call/pcall performance as they involve going through a C call frame * LOP_LOADK(X) translation into IR has been improved to enable type tag/constant propagation * arm64 can use integer immediate values to synthesize floating-point values * x64 assembler removes duplicate 64bit numbers from the data section to save space * Linux `perf` can now be used to profile native Luau code (when running with --codegen-perf CLI argument)
2023-05-12 18:50:47 +01:00
if (FInt::DebugLuauVerboseTypeNames >= 1)
state.emit("gen-");
if (pack.explicitName)
{
2022-05-20 01:02:24 +01:00
state.usedNames.insert(pack.name);
state.opts.nameMap.typePacks[tp] = pack.name;
state.emit(pack.name);
}
else
{
state.emit(state.getName(tp));
}
Sync to upstream/release/576 (#928) * `ClassType` can now have an indexer defined on it. This allows custom types to be used in `t[x]` expressions. * Fixed search for closest executable breakpoint line. Previously, breakpoints might have been skipped in `else` blocks at the end of a function * Fixed how unification is performed for two optional types `a? <: b?`, previously it might have unified either 'a' or 'b' with 'nil'. Note that this fix is not enabled by default yet (see the list in `ExperimentalFlags.h`) In the new type solver, a concept of 'Type Families' has been introduced. Type families can be thought of as type aliases with custom type inference/reduction logic included with them. For example, we can have an `Add<T, U>` type family that will resolve the type that is the result of adding two values together. This will help type inference to figure out what 'T' and 'U' might be when explicit type annotations are not provided. In this update we don't define any type families, but they will be added in the near future. It is also possible for Luau embedders to define their own type families in the global/environment scope. Other changes include: * Fixed scope used to find out which generic types should be included in the function generic type list * Fixed a crash after cyclic bound types were created during unification And in native code generation (jit): * Use of arm64 target on M1 now requires macOS 13 * Entry into native code has been optimized. This is especially important for coroutine call/pcall performance as they involve going through a C call frame * LOP_LOADK(X) translation into IR has been improved to enable type tag/constant propagation * arm64 can use integer immediate values to synthesize floating-point values * x64 assembler removes duplicate 64bit numbers from the data section to save space * Linux `perf` can now be used to profile native Luau code (when running with --codegen-perf CLI argument)
2023-05-12 18:50:47 +01:00
if (FInt::DebugLuauVerboseTypeNames >= 2)
{
state.emit("-");
if (FFlag::DebugLuauDeferredConstraintResolution)
state.emitLevel(pack.scope);
else
state.emit(pack.level);
}
Sync to upstream/release/576 (#928) * `ClassType` can now have an indexer defined on it. This allows custom types to be used in `t[x]` expressions. * Fixed search for closest executable breakpoint line. Previously, breakpoints might have been skipped in `else` blocks at the end of a function * Fixed how unification is performed for two optional types `a? <: b?`, previously it might have unified either 'a' or 'b' with 'nil'. Note that this fix is not enabled by default yet (see the list in `ExperimentalFlags.h`) In the new type solver, a concept of 'Type Families' has been introduced. Type families can be thought of as type aliases with custom type inference/reduction logic included with them. For example, we can have an `Add<T, U>` type family that will resolve the type that is the result of adding two values together. This will help type inference to figure out what 'T' and 'U' might be when explicit type annotations are not provided. In this update we don't define any type families, but they will be added in the near future. It is also possible for Luau embedders to define their own type families in the global/environment scope. Other changes include: * Fixed scope used to find out which generic types should be included in the function generic type list * Fixed a crash after cyclic bound types were created during unification And in native code generation (jit): * Use of arm64 target on M1 now requires macOS 13 * Entry into native code has been optimized. This is especially important for coroutine call/pcall performance as they involve going through a C call frame * LOP_LOADK(X) translation into IR has been improved to enable type tag/constant propagation * arm64 can use integer immediate values to synthesize floating-point values * x64 assembler removes duplicate 64bit numbers from the data section to save space * Linux `perf` can now be used to profile native Luau code (when running with --codegen-perf CLI argument)
2023-05-12 18:50:47 +01:00
state.emit("...");
}
void operator()(TypePackId tp, const FreeTypePack& pack)
{
state.result.invalid = true;
Sync to upstream/release/576 (#928) * `ClassType` can now have an indexer defined on it. This allows custom types to be used in `t[x]` expressions. * Fixed search for closest executable breakpoint line. Previously, breakpoints might have been skipped in `else` blocks at the end of a function * Fixed how unification is performed for two optional types `a? <: b?`, previously it might have unified either 'a' or 'b' with 'nil'. Note that this fix is not enabled by default yet (see the list in `ExperimentalFlags.h`) In the new type solver, a concept of 'Type Families' has been introduced. Type families can be thought of as type aliases with custom type inference/reduction logic included with them. For example, we can have an `Add<T, U>` type family that will resolve the type that is the result of adding two values together. This will help type inference to figure out what 'T' and 'U' might be when explicit type annotations are not provided. In this update we don't define any type families, but they will be added in the near future. It is also possible for Luau embedders to define their own type families in the global/environment scope. Other changes include: * Fixed scope used to find out which generic types should be included in the function generic type list * Fixed a crash after cyclic bound types were created during unification And in native code generation (jit): * Use of arm64 target on M1 now requires macOS 13 * Entry into native code has been optimized. This is especially important for coroutine call/pcall performance as they involve going through a C call frame * LOP_LOADK(X) translation into IR has been improved to enable type tag/constant propagation * arm64 can use integer immediate values to synthesize floating-point values * x64 assembler removes duplicate 64bit numbers from the data section to save space * Linux `perf` can now be used to profile native Luau code (when running with --codegen-perf CLI argument)
2023-05-12 18:50:47 +01:00
if (FInt::DebugLuauVerboseTypeNames >= 1)
state.emit("free-");
state.emit(state.getName(tp));
Sync to upstream/release/576 (#928) * `ClassType` can now have an indexer defined on it. This allows custom types to be used in `t[x]` expressions. * Fixed search for closest executable breakpoint line. Previously, breakpoints might have been skipped in `else` blocks at the end of a function * Fixed how unification is performed for two optional types `a? <: b?`, previously it might have unified either 'a' or 'b' with 'nil'. Note that this fix is not enabled by default yet (see the list in `ExperimentalFlags.h`) In the new type solver, a concept of 'Type Families' has been introduced. Type families can be thought of as type aliases with custom type inference/reduction logic included with them. For example, we can have an `Add<T, U>` type family that will resolve the type that is the result of adding two values together. This will help type inference to figure out what 'T' and 'U' might be when explicit type annotations are not provided. In this update we don't define any type families, but they will be added in the near future. It is also possible for Luau embedders to define their own type families in the global/environment scope. Other changes include: * Fixed scope used to find out which generic types should be included in the function generic type list * Fixed a crash after cyclic bound types were created during unification And in native code generation (jit): * Use of arm64 target on M1 now requires macOS 13 * Entry into native code has been optimized. This is especially important for coroutine call/pcall performance as they involve going through a C call frame * LOP_LOADK(X) translation into IR has been improved to enable type tag/constant propagation * arm64 can use integer immediate values to synthesize floating-point values * x64 assembler removes duplicate 64bit numbers from the data section to save space * Linux `perf` can now be used to profile native Luau code (when running with --codegen-perf CLI argument)
2023-05-12 18:50:47 +01:00
if (FInt::DebugLuauVerboseTypeNames >= 2)
{
state.emit("-");
if (FFlag::DebugLuauDeferredConstraintResolution)
state.emitLevel(pack.scope);
else
state.emit(pack.level);
}
state.emit("...");
}
void operator()(TypePackId, const BoundTypePack& btv)
{
stringify(btv.boundTo);
}
void operator()(TypePackId, const BlockedTypePack& btp)
{
state.emit("*blocked-tp-");
state.emit(btp.index);
state.emit("*");
}
Sync to upstream/release/576 (#928) * `ClassType` can now have an indexer defined on it. This allows custom types to be used in `t[x]` expressions. * Fixed search for closest executable breakpoint line. Previously, breakpoints might have been skipped in `else` blocks at the end of a function * Fixed how unification is performed for two optional types `a? <: b?`, previously it might have unified either 'a' or 'b' with 'nil'. Note that this fix is not enabled by default yet (see the list in `ExperimentalFlags.h`) In the new type solver, a concept of 'Type Families' has been introduced. Type families can be thought of as type aliases with custom type inference/reduction logic included with them. For example, we can have an `Add<T, U>` type family that will resolve the type that is the result of adding two values together. This will help type inference to figure out what 'T' and 'U' might be when explicit type annotations are not provided. In this update we don't define any type families, but they will be added in the near future. It is also possible for Luau embedders to define their own type families in the global/environment scope. Other changes include: * Fixed scope used to find out which generic types should be included in the function generic type list * Fixed a crash after cyclic bound types were created during unification And in native code generation (jit): * Use of arm64 target on M1 now requires macOS 13 * Entry into native code has been optimized. This is especially important for coroutine call/pcall performance as they involve going through a C call frame * LOP_LOADK(X) translation into IR has been improved to enable type tag/constant propagation * arm64 can use integer immediate values to synthesize floating-point values * x64 assembler removes duplicate 64bit numbers from the data section to save space * Linux `perf` can now be used to profile native Luau code (when running with --codegen-perf CLI argument)
2023-05-12 18:50:47 +01:00
void operator()(TypePackId, const TypeFamilyInstanceTypePack& tfitp)
{
state.emit(tfitp.family->name);
state.emit("<");
bool comma = false;
for (TypeId p : tfitp.typeArguments)
{
if (comma)
state.emit(", ");
comma = true;
stringify(p);
}
for (TypePackId p : tfitp.packArguments)
{
if (comma)
state.emit(", ");
comma = true;
stringify(p);
}
state.emit(">");
}
};
void TypeStringifier::stringify(TypePackId tp)
{
TypePackStringifier tps(state);
tps.stringify(tp);
}
void TypeStringifier::stringify(TypePackId tpid, const std::vector<std::optional<FunctionArgument>>& names)
{
TypePackStringifier tps(state, names);
tps.stringify(tpid);
}
2022-04-15 00:57:43 +01:00
static void assignCycleNames(const std::set<TypeId>& cycles, const std::set<TypePackId>& cycleTPs,
std::unordered_map<TypeId, std::string>& cycleNames, std::unordered_map<TypePackId, std::string>& cycleTpNames, bool exhaustive)
{
int nextIndex = 1;
2022-04-15 00:57:43 +01:00
for (TypeId cycleTy : cycles)
{
std::string name;
// TODO: use the stringified type list if there are no cycles
if (auto ttv = get<TableType>(follow(cycleTy)); !exhaustive && ttv && (ttv->syntheticName || ttv->name))
{
// If we have a cycle type in type parameters, assign a cycle name for this named table
if (std::find_if(ttv->instantiatedTypeParams.begin(), ttv->instantiatedTypeParams.end(), [&](auto&& el) {
return cycles.count(follow(el));
}) != ttv->instantiatedTypeParams.end())
cycleNames[cycleTy] = ttv->name ? *ttv->name : *ttv->syntheticName;
continue;
}
name = "t" + std::to_string(nextIndex);
++nextIndex;
cycleNames[cycleTy] = std::move(name);
}
2022-04-15 00:57:43 +01:00
for (TypePackId tp : cycleTPs)
{
std::string name = "tp" + std::to_string(nextIndex);
++nextIndex;
cycleTpNames[tp] = std::move(name);
}
}
ToStringResult toStringDetailed(TypeId ty, ToStringOptions& opts)
{
/*
* 1. Walk the Type and track seen TypeIds. When you reencounter a TypeId, add it to a set of seen cycles.
* 2. Generate some names for each cycle. For a starting point, we can just call them t0, t1 and so on.
* 3. For each seen cycle, stringify it like we do now, but replace each known cycle with its name.
* 4. Print out the root of the type using the same algorithm as step 3.
*/
ty = follow(ty);
ToStringResult result;
StringifierState state{opts, result};
2022-04-15 00:57:43 +01:00
std::set<TypeId> cycles;
std::set<TypePackId> cycleTPs;
findCyclicTypes(cycles, cycleTPs, ty, opts.exhaustive);
assignCycleNames(cycles, cycleTPs, state.cycleNames, state.cycleTpNames, opts.exhaustive);
TypeStringifier tvs{state};
if (!opts.exhaustive)
{
if (auto ttv = get<TableType>(ty); ttv && (ttv->name || ttv->syntheticName))
{
if (ttv->syntheticName)
result.invalid = true;
// If scope if provided, add module name and check visibility
if (ttv->name && opts.scope)
{
auto [success, moduleName] = canUseTypeNameInScope(opts.scope, *ttv->name);
if (!success)
result.invalid = true;
if (moduleName)
result.name = format("%s.", moduleName->c_str());
}
result.name += ttv->name ? *ttv->name : *ttv->syntheticName;
tvs.stringify(ttv->instantiatedTypeParams, ttv->instantiatedTypePackParams);
return result;
}
else if (auto mtv = get<MetatableType>(ty); mtv && mtv->syntheticName)
{
result.invalid = true;
result.name = *mtv->syntheticName;
return result;
}
}
/* If the root itself is a cycle, we special case a little.
* We go out of our way to print the following:
*
* t1 where t1 = the_whole_root_type
*/
auto it = state.cycleNames.find(ty);
if (it != state.cycleNames.end())
state.emit(it->second);
else
tvs.stringify(ty);
if (!state.cycleNames.empty() || !state.cycleTpNames.empty())
{
result.cycle = true;
state.emit(" where ");
}
state.exhaustive = true;
std::vector<std::pair<TypeId, std::string>> sortedCycleNames{state.cycleNames.begin(), state.cycleNames.end()};
std::sort(sortedCycleNames.begin(), sortedCycleNames.end(), [](const auto& a, const auto& b) {
return a.second < b.second;
});
bool semi = false;
for (const auto& [cycleTy, name] : sortedCycleNames)
{
if (semi)
state.emit(" ; ");
state.emit(name);
state.emit(" = ");
Luau::visit(
[&tvs, cycleTy = cycleTy](auto&& t) {
return tvs(cycleTy, t);
},
cycleTy->ty);
semi = true;
}
std::vector<std::pair<TypePackId, std::string>> sortedCycleTpNames(state.cycleTpNames.begin(), state.cycleTpNames.end());
std::sort(sortedCycleTpNames.begin(), sortedCycleTpNames.end(), [](const auto& a, const auto& b) {
return a.second < b.second;
});
TypePackStringifier tps{state};
for (const auto& [cycleTp, name] : sortedCycleTpNames)
{
if (semi)
state.emit(" ; ");
state.emit(name);
state.emit(" = ");
Luau::visit(
[&tps, cycleTy = cycleTp](auto&& t) {
return tps(cycleTy, t);
},
cycleTp->ty);
semi = true;
}
if (opts.maxTypeLength > 0 && result.name.length() > opts.maxTypeLength)
{
result.truncated = true;
2022-07-29 05:24:07 +01:00
result.name += "... *TRUNCATED*";
}
return result;
}
ToStringResult toStringDetailed(TypePackId tp, ToStringOptions& opts)
{
/*
* 1. Walk the Type and track seen TypeIds. When you reencounter a TypeId, add it to a set of seen cycles.
* 2. Generate some names for each cycle. For a starting point, we can just call them t0, t1 and so on.
* 3. For each seen cycle, stringify it like we do now, but replace each known cycle with its name.
* 4. Print out the root of the type using the same algorithm as step 3.
*/
ToStringResult result;
StringifierState state{opts, result};
2022-04-15 00:57:43 +01:00
std::set<TypeId> cycles;
std::set<TypePackId> cycleTPs;
findCyclicTypes(cycles, cycleTPs, tp, opts.exhaustive);
assignCycleNames(cycles, cycleTPs, state.cycleNames, state.cycleTpNames, opts.exhaustive);
TypeStringifier tvs{state};
/* If the root itself is a cycle, we special case a little.
* We go out of our way to print the following:
*
* t1 where t1 = the_whole_root_type
*/
auto it = state.cycleTpNames.find(tp);
if (it != state.cycleTpNames.end())
state.emit(it->second);
else
tvs.stringify(tp);
if (!cycles.empty())
{
result.cycle = true;
state.emit(" where ");
}
state.exhaustive = true;
std::vector<std::pair<TypeId, std::string>> sortedCycleNames{state.cycleNames.begin(), state.cycleNames.end()};
std::sort(sortedCycleNames.begin(), sortedCycleNames.end(), [](const auto& a, const auto& b) {
return a.second < b.second;
});
bool semi = false;
for (const auto& [cycleTy, name] : sortedCycleNames)
{
if (semi)
state.emit(" ; ");
state.emit(name);
state.emit(" = ");
Luau::visit(
[&tvs, cycleTy = cycleTy](auto t) {
return tvs(cycleTy, t);
},
cycleTy->ty);
semi = true;
}
if (opts.maxTypeLength > 0 && result.name.length() > opts.maxTypeLength)
2022-07-29 05:24:07 +01:00
{
result.name += "... *TRUNCATED*";
2022-07-29 05:24:07 +01:00
}
return result;
}
std::string toString(TypeId ty, ToStringOptions& opts)
{
return toStringDetailed(ty, opts).name;
}
std::string toString(TypePackId tp, ToStringOptions& opts)
{
return toStringDetailed(tp, opts).name;
}
std::string toString(const Type& tv, ToStringOptions& opts)
{
return toString(const_cast<TypeId>(&tv), opts);
}
std::string toString(const TypePackVar& tp, ToStringOptions& opts)
{
return toString(const_cast<TypePackId>(&tp), opts);
}
std::string toStringNamedFunction(const std::string& funcName, const FunctionType& ftv, ToStringOptions& opts)
2022-01-14 16:20:09 +00:00
{
ToStringResult result;
StringifierState state{opts, result};
TypeStringifier tvs{state};
2022-01-14 16:20:09 +00:00
2022-03-24 22:04:14 +00:00
state.emit(funcName);
2022-01-14 16:20:09 +00:00
if (!opts.hideNamedFunctionTypeParameters)
tvs.stringify(ftv.generics, ftv.genericPacks);
state.emit("(");
auto argPackIter = begin(ftv.argTypes);
bool first = true;
size_t idx = 0;
while (argPackIter != end(ftv.argTypes))
2022-01-14 16:20:09 +00:00
{
// ftv takes a self parameter as the first argument, skip it if specified in option
if (idx == 0 && ftv.hasSelf && opts.hideFunctionSelfArgument)
2022-01-14 16:20:09 +00:00
{
2022-03-24 22:04:14 +00:00
++argPackIter;
++idx;
continue;
2022-01-14 16:20:09 +00:00
}
if (!first)
state.emit(", ");
first = false;
2022-03-24 22:04:14 +00:00
// We don't respect opts.functionTypeArguments
if (idx < opts.namedFunctionOverrideArgNames.size())
{
state.emit(opts.namedFunctionOverrideArgNames[idx] + ": ");
}
else if (idx < ftv.argNames.size() && ftv.argNames[idx])
{
state.emit(ftv.argNames[idx]->name + ": ");
}
else
{
state.emit("_: ");
2022-03-24 22:04:14 +00:00
}
tvs.stringify(*argPackIter);
++argPackIter;
++idx;
2022-01-14 16:20:09 +00:00
}
if (argPackIter.tail())
{
2022-04-15 00:57:43 +01:00
if (auto vtp = get<VariadicTypePack>(*argPackIter.tail()); !vtp || !vtp->hidden)
{
if (!first)
state.emit(", ");
2022-01-14 16:20:09 +00:00
2022-04-15 00:57:43 +01:00
state.emit("...: ");
if (vtp)
tvs.stringify(vtp->ty);
else
tvs.stringify(*argPackIter.tail());
}
2022-01-14 16:20:09 +00:00
}
state.emit("): ");
2022-06-17 02:05:14 +01:00
size_t retSize = size(ftv.retTypes);
bool hasTail = !finite(ftv.retTypes);
bool wrap = get<TypePack>(follow(ftv.retTypes)) && (hasTail ? retSize != 0 : retSize != 1);
2022-01-14 16:20:09 +00:00
if (wrap)
state.emit("(");
2022-06-17 02:05:14 +01:00
tvs.stringify(ftv.retTypes);
2022-01-14 16:20:09 +00:00
if (wrap)
state.emit(")");
return result.name;
}
static ToStringOptions& dumpOptions()
{
static ToStringOptions opts = ([]() {
ToStringOptions o;
o.exhaustive = true;
o.functionTypeArguments = true;
o.maxTableLength = 0;
o.maxTypeLength = 0;
return o;
})();
return opts;
}
std::string dump(TypeId ty)
{
std::string s = toString(ty, dumpOptions());
printf("%s\n", s.c_str());
return s;
}
std::string dump(const std::optional<TypeId>& ty)
{
if (ty)
return dump(*ty);
printf("nullopt\n");
return "nullopt";
}
std::string dump(TypePackId ty)
{
std::string s = toString(ty, dumpOptions());
printf("%s\n", s.c_str());
return s;
}
std::string dump(const std::optional<TypePackId>& ty)
{
if (ty)
return dump(*ty);
printf("nullopt\n");
return "nullopt";
}
2022-04-15 00:57:43 +01:00
std::string dump(const ScopePtr& scope, const char* name)
{
auto binding = scope->linearSearchForBinding(name);
if (!binding)
{
printf("No binding %s\n", name);
return {};
}
TypeId ty = binding->typeId;
std::string s = toString(ty, dumpOptions());
2022-04-15 00:57:43 +01:00
printf("%s\n", s.c_str());
return s;
}
std::string generateName(size_t i)
{
std::string n;
n = char('a' + i % 26);
if (i >= 26)
n += std::to_string(i / 26);
return n;
}
2022-07-01 00:52:43 +01:00
std::string toString(const Constraint& constraint, ToStringOptions& opts)
{
auto go = [&opts](auto&& c) -> std::string {
2022-07-01 00:52:43 +01:00
using T = std::decay_t<decltype(c)>;
auto tos = [&opts](auto&& a) {
return toString(a, opts);
};
2022-07-01 00:52:43 +01:00
if constexpr (std::is_same_v<T, SubtypeConstraint>)
{
std::string subStr = tos(c.subType);
std::string superStr = tos(c.superType);
return subStr + " <: " + superStr;
2022-07-01 00:52:43 +01:00
}
else if constexpr (std::is_same_v<T, PackSubtypeConstraint>)
{
std::string subStr = tos(c.subPack);
std::string superStr = tos(c.superPack);
return subStr + " <: " + superStr;
2022-07-01 00:52:43 +01:00
}
else if constexpr (std::is_same_v<T, GeneralizationConstraint>)
{
std::string subStr = tos(c.generalizedType);
std::string superStr = tos(c.sourceType);
return subStr + " ~ gen " + superStr;
2022-07-01 00:52:43 +01:00
}
else if constexpr (std::is_same_v<T, InstantiationConstraint>)
{
std::string subStr = tos(c.subType);
std::string superStr = tos(c.superType);
return subStr + " ~ inst " + superStr;
2022-07-01 00:52:43 +01:00
}
else if constexpr (std::is_same_v<T, IterableConstraint>)
{
std::string iteratorStr = tos(c.iterator);
std::string variableStr = tos(c.variables);
return variableStr + " ~ Iterate<" + iteratorStr + ">";
}
2022-07-01 00:52:43 +01:00
else if constexpr (std::is_same_v<T, NameConstraint>)
{
std::string namedStr = tos(c.namedType);
return "@name(" + namedStr + ") = " + c.name;
2022-07-01 00:52:43 +01:00
}
2022-08-04 23:35:33 +01:00
else if constexpr (std::is_same_v<T, TypeAliasExpansionConstraint>)
{
std::string targetStr = tos(c.target);
return "expand " + targetStr;
2022-08-04 23:35:33 +01:00
}
else if constexpr (std::is_same_v<T, FunctionCallConstraint>)
{
return "call " + tos(c.fn) + "( " + tos(c.argsPack) + " )" + " with { result = " + tos(c.result) + " }";
}
2022-09-23 20:17:25 +01:00
else if constexpr (std::is_same_v<T, PrimitiveTypeConstraint>)
{
return tos(c.resultType) + " ~ prim " + tos(c.expectedType) + ", " + tos(c.singletonType) + ", " + tos(c.multitonType);
2022-09-23 20:17:25 +01:00
}
else if constexpr (std::is_same_v<T, HasPropConstraint>)
{
return tos(c.resultType) + " ~ hasProp " + tos(c.subjectType) + ", \"" + c.prop + "\"";
2022-09-23 20:17:25 +01:00
}
else if constexpr (std::is_same_v<T, SetPropConstraint>)
{
const std::string pathStr = c.path.size() == 1 ? "\"" + c.path[0] + "\"" : "[\"" + join(c.path, "\", \"") + "\"]";
return tos(c.resultType) + " ~ setProp " + tos(c.subjectType) + ", " + pathStr + " " + tos(c.propType);
}
else if constexpr (std::is_same_v<T, SetIndexerConstraint>)
{
return tos(c.resultType) + " ~ setIndexer " + tos(c.subjectType) + " [ " + tos(c.indexType) + " ] " + tos(c.propType);
}
else if constexpr (std::is_same_v<T, SingletonOrTopTypeConstraint>)
{
std::string result = tos(c.resultType);
std::string discriminant = tos(c.discriminantType);
if (c.negated)
return result + " ~ if isSingleton D then ~D else unknown where D = " + discriminant;
else
return result + " ~ if isSingleton D then D else unknown where D = " + discriminant;
}
else if constexpr (std::is_same_v<T, UnpackConstraint>)
return tos(c.resultPack) + " ~ unpack " + tos(c.sourcePack);
Sync to upstream/release/577 (#934) Lots of things going on this week: * Fix a crash that could occur in the presence of a cyclic union. We shouldn't be creating cyclic unions, but we shouldn't be crashing when they arise either. * Minor cleanup of `luau_precall` * Internal change to make L->top handling slightly more uniform * Optimize SETGLOBAL & GETGLOBAL fallback C functions. * https://github.com/Roblox/luau/pull/929 * The syntax to the `luau-reduce` commandline tool has changed. It now accepts a script, a command to execute, and an error to search for. It no longer automatically passes the script to the command which makes it a lot more flexible. Also be warned that it edits the script it is passed **in place**. Do not point it at something that is not in source control! New solver * Switch to a greedier but more fallible algorithm for simplifying union and intersection types that are created as part of refinement calculation. This has much better and more predictable performance. * Fix a constraint cycle in recursive function calls. * Much improved inference of binary addition. Functions like `function add(x, y) return x + y end` can now be inferred without annotations. We also accurately typecheck calls to functions like this. * Many small bugfixes surrounding things like table indexers * Add support for indexers on class types. This was previously added to the old solver; we now add it to the new one for feature parity. JIT * https://github.com/Roblox/luau/pull/931 * Fuse key.value and key.tt loads for CEHCK_SLOT_MATCH in A64 * Implement remaining aliases of BFM for A64 * Implement new callinfo flag for A64 * Add instruction simplification for int->num->int conversion chains * Don't even load execdata for X64 calls * Treat opcode fallbacks the same as manually written fallbacks --------- Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-05-19 20:37:30 +01:00
else if constexpr (std::is_same_v<T, RefineConstraint>)
{
const char* op = c.mode == RefineConstraint::Union ? "union" : "intersect";
return tos(c.resultType) + " ~ refine " + tos(c.type) + " " + op + " " + tos(c.discriminant);
}
Sync to upstream/release/600 (#1076) ### What's Changed - Improve readability of unions and intersections by limiting the number of elements of those types that can be presented on a single line (gated under `FFlag::LuauToStringSimpleCompositeTypesSingleLine`) - Adds a new option to the compiler `--record-stats` to record and output compilation statistics - `if...then...else` expressions are now optimized into `AND/OR` form when possible. ### VM - Add a new `buffer` type to Luau based on the [buffer RFC](https://github.com/Roblox/luau/pull/739) and additional C API functions to work with it; this release does not include the library. - Internal C API to work with string buffers has been updated to align with Lua version more closely ### Native Codegen - Added support for new X64 instruction (rev) and new A64 instruction (bswap) in the assembler - Simplified the way numerical loop condition is translated to IR ### New Type Solver - Operator inference now handled by type families - Created a new system called `Type Paths` to explain why subtyping tests fail in order to improve the quality of error messages. - Systematic changes to implement Data Flow analysis in the new solver (`Breadcrumb` removed and replaced with `RefinementKey`) --- 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: Lily Brown <lbrown@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com>
2023-10-21 02:10:30 +01:00
else if constexpr (std::is_same_v<T, SetOpConstraint>)
{
const char* op = c.mode == SetOpConstraint::Union ? " | " : " & ";
std::string res = tos(c.resultType) + " ~ ";
bool first = true;
for (TypeId t : c.types)
{
if (first)
first = false;
else
res += op;
res += tos(t);
}
return res;
}
Sync to upstream/release/576 (#928) * `ClassType` can now have an indexer defined on it. This allows custom types to be used in `t[x]` expressions. * Fixed search for closest executable breakpoint line. Previously, breakpoints might have been skipped in `else` blocks at the end of a function * Fixed how unification is performed for two optional types `a? <: b?`, previously it might have unified either 'a' or 'b' with 'nil'. Note that this fix is not enabled by default yet (see the list in `ExperimentalFlags.h`) In the new type solver, a concept of 'Type Families' has been introduced. Type families can be thought of as type aliases with custom type inference/reduction logic included with them. For example, we can have an `Add<T, U>` type family that will resolve the type that is the result of adding two values together. This will help type inference to figure out what 'T' and 'U' might be when explicit type annotations are not provided. In this update we don't define any type families, but they will be added in the near future. It is also possible for Luau embedders to define their own type families in the global/environment scope. Other changes include: * Fixed scope used to find out which generic types should be included in the function generic type list * Fixed a crash after cyclic bound types were created during unification And in native code generation (jit): * Use of arm64 target on M1 now requires macOS 13 * Entry into native code has been optimized. This is especially important for coroutine call/pcall performance as they involve going through a C call frame * LOP_LOADK(X) translation into IR has been improved to enable type tag/constant propagation * arm64 can use integer immediate values to synthesize floating-point values * x64 assembler removes duplicate 64bit numbers from the data section to save space * Linux `perf` can now be used to profile native Luau code (when running with --codegen-perf CLI argument)
2023-05-12 18:50:47 +01:00
else if constexpr (std::is_same_v<T, ReduceConstraint>)
return "reduce " + tos(c.ty);
else if constexpr (std::is_same_v<T, ReducePackConstraint>)
{
return "reduce " + tos(c.tp);
}
2022-07-01 00:52:43 +01:00
else
static_assert(always_false_v<T>, "Non-exhaustive constraint switch");
};
return visit(go, constraint.c);
}
Sync to upstream/release/577 (#934) Lots of things going on this week: * Fix a crash that could occur in the presence of a cyclic union. We shouldn't be creating cyclic unions, but we shouldn't be crashing when they arise either. * Minor cleanup of `luau_precall` * Internal change to make L->top handling slightly more uniform * Optimize SETGLOBAL & GETGLOBAL fallback C functions. * https://github.com/Roblox/luau/pull/929 * The syntax to the `luau-reduce` commandline tool has changed. It now accepts a script, a command to execute, and an error to search for. It no longer automatically passes the script to the command which makes it a lot more flexible. Also be warned that it edits the script it is passed **in place**. Do not point it at something that is not in source control! New solver * Switch to a greedier but more fallible algorithm for simplifying union and intersection types that are created as part of refinement calculation. This has much better and more predictable performance. * Fix a constraint cycle in recursive function calls. * Much improved inference of binary addition. Functions like `function add(x, y) return x + y end` can now be inferred without annotations. We also accurately typecheck calls to functions like this. * Many small bugfixes surrounding things like table indexers * Add support for indexers on class types. This was previously added to the old solver; we now add it to the new one for feature parity. JIT * https://github.com/Roblox/luau/pull/931 * Fuse key.value and key.tt loads for CEHCK_SLOT_MATCH in A64 * Implement remaining aliases of BFM for A64 * Implement new callinfo flag for A64 * Add instruction simplification for int->num->int conversion chains * Don't even load execdata for X64 calls * Treat opcode fallbacks the same as manually written fallbacks --------- Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-05-19 20:37:30 +01:00
std::string toString(const Constraint& constraint)
{
return toString(constraint, ToStringOptions{});
}
std::string dump(const Constraint& c)
{
ToStringOptions opts;
opts.exhaustive = true;
opts.functionTypeArguments = true;
std::string s = toString(c, opts);
printf("%s\n", s.c_str());
return s;
}
std::optional<std::string> getFunctionNameAsString(const AstExpr& expr)
{
const AstExpr* curr = &expr;
std::string s;
for (;;)
{
if (auto local = curr->as<AstExprLocal>())
return local->local->name.value + s;
if (auto global = curr->as<AstExprGlobal>())
return global->name.value + s;
if (auto indexname = curr->as<AstExprIndexName>())
{
curr = indexname->expr;
s = "." + std::string(indexname->index.value) + s;
}
else if (auto group = curr->as<AstExprGroup>())
{
curr = group->expr;
}
else
{
return std::nullopt;
}
}
return s;
}
std::string toString(const Position& position)
{
return "{ line = " + std::to_string(position.line) + ", col = " + std::to_string(position.column) + " }";
}
std::string toString(const Location& location, int offset, bool useBegin)
{
if (FFlag::LuauToStringPrettifyLocation)
{
return "(" + std::to_string(location.begin.line + offset) + ", " + std::to_string(location.begin.column + offset) + ") - (" +
std::to_string(location.end.line + offset) + ", " + std::to_string(location.end.column + offset) + ")";
}
else
{
return "Location { " + toString(location.begin) + ", " + toString(location.end) + " }";
}
}
Sync to upstream/release/600 (#1076) ### What's Changed - Improve readability of unions and intersections by limiting the number of elements of those types that can be presented on a single line (gated under `FFlag::LuauToStringSimpleCompositeTypesSingleLine`) - Adds a new option to the compiler `--record-stats` to record and output compilation statistics - `if...then...else` expressions are now optimized into `AND/OR` form when possible. ### VM - Add a new `buffer` type to Luau based on the [buffer RFC](https://github.com/Roblox/luau/pull/739) and additional C API functions to work with it; this release does not include the library. - Internal C API to work with string buffers has been updated to align with Lua version more closely ### Native Codegen - Added support for new X64 instruction (rev) and new A64 instruction (bswap) in the assembler - Simplified the way numerical loop condition is translated to IR ### New Type Solver - Operator inference now handled by type families - Created a new system called `Type Paths` to explain why subtyping tests fail in order to improve the quality of error messages. - Systematic changes to implement Data Flow analysis in the new solver (`Breadcrumb` removed and replaced with `RefinementKey`) --- 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: Lily Brown <lbrown@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com>
2023-10-21 02:10:30 +01:00
std::string toString(const TypeOrPack& tyOrTp, ToStringOptions& opts)
{
if (const TypeId* ty = get<TypeId>(tyOrTp))
return toString(*ty, opts);
else if (const TypePackId* tp = get<TypePackId>(tyOrTp))
return toString(*tp, opts);
else
LUAU_UNREACHABLE();
}
std::string dump(const TypeOrPack& tyOrTp)
{
ToStringOptions opts;
opts.exhaustive = true;
opts.functionTypeArguments = true;
std::string s = toString(tyOrTp, opts);
printf("%s\n", s.c_str());
return s;
}
} // namespace Luau