Skip to content

Commit

Permalink
Sync to upstream/release/654 (#1552)
Browse files Browse the repository at this point in the history
# What's Changed

* Support dead store elimination for `STORE_VECTOR` instruction
* Fix parser hang when a separator is used between Luau class
declaration properties
* Provide properties and metatable for built-in vector type definition
to fix type errors
* Fix Fragment Autocomplete to ensure correct parentheses insertion
behavior.
* Add support for 'thread' and 'buffer' primitive types in user-defined
type functions

---------

Co-authored-by: Andy Friesen <[email protected]>
Co-authored-by: Hunter Goldstein <[email protected]>
Co-authored-by: Vighnesh Vijay <[email protected]>
Co-authored-by: Vyacheslav Egorov <[email protected]>
  • Loading branch information
5 people authored Dec 3, 2024
1 parent 8cc289f commit d0222bb
Show file tree
Hide file tree
Showing 27 changed files with 825 additions and 95 deletions.
1 change: 1 addition & 0 deletions Analysis/include/Luau/Frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ struct Frontend
);

std::optional<CheckResult> getCheckResult(const ModuleName& name, bool accumulateNested, bool forAutocomplete = false);
std::vector<ModuleName> getRequiredScripts(const ModuleName& name);

private:
ModulePtr check(
Expand Down
2 changes: 2 additions & 0 deletions Analysis/include/Luau/TypeFunctionRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ struct TypeFunctionPrimitiveType
Boolean,
Number,
String,
Thread,
Buffer,
};

Type type;
Expand Down
2 changes: 1 addition & 1 deletion Analysis/src/AutocompleteCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1701,7 +1701,7 @@ AutocompleteResult autocomplete_(
NotNull<BuiltinTypes> builtinTypes,
TypeArena* typeArena,
std::vector<AstNode*>& ancestry,
Scope* globalScope,
Scope* globalScope, // [TODO] This is unused argument, do we really need this?
const ScopePtr& scopeAtPosition,
Position position,
FileResolver* fileResolver,
Expand Down
29 changes: 27 additions & 2 deletions Analysis/src/BuiltinDefinitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAGVARIABLE(LuauTypestateBuiltins2)
LUAU_FASTFLAGVARIABLE(LuauStringFormatArityFix)

LUAU_FASTFLAG(AutocompleteRequirePathSuggestions2);
LUAU_FASTFLAG(AutocompleteRequirePathSuggestions2)
LUAU_FASTFLAG(LuauVectorDefinitionsExtra)

namespace Luau
{
Expand Down Expand Up @@ -300,6 +300,31 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC

addGlobalBinding(globals, "string", it->second.type(), "@luau");

// Setup 'vector' metatable
if (FFlag::LuauVectorDefinitionsExtra)
{
if (auto it = globals.globalScope->exportedTypeBindings.find("vector"); it != globals.globalScope->exportedTypeBindings.end())
{
TypeId vectorTy = it->second.type;
ClassType* vectorCls = getMutable<ClassType>(vectorTy);

vectorCls->metatable = arena.addType(TableType{{}, std::nullopt, TypeLevel{}, TableState::Sealed});
TableType* metatableTy = Luau::getMutable<TableType>(vectorCls->metatable);

metatableTy->props["__add"] = {makeFunction(arena, vectorTy, {vectorTy}, {vectorTy})};
metatableTy->props["__sub"] = {makeFunction(arena, vectorTy, {vectorTy}, {vectorTy})};
metatableTy->props["__unm"] = {makeFunction(arena, vectorTy, {}, {vectorTy})};

std::initializer_list<TypeId> mulOverloads{
makeFunction(arena, vectorTy, {vectorTy}, {vectorTy}),
makeFunction(arena, vectorTy, {builtinTypes->numberType}, {vectorTy}),
};
metatableTy->props["__mul"] = {makeIntersection(arena, mulOverloads)};
metatableTy->props["__div"] = {makeIntersection(arena, mulOverloads)};
metatableTy->props["__idiv"] = {makeIntersection(arena, mulOverloads)};
}
}

// next<K, V>(t: Table<K, V>, i: K?) -> (K?, V)
TypePackId nextArgsTypePack = arena.addTypePack(TypePack{{mapOfKtoV, makeOption(builtinTypes, arena, genericK)}});
TypePackId nextRetsTypePack = arena.addTypePack(TypePack{{makeOption(builtinTypes, arena, genericK), genericV}});
Expand Down
37 changes: 35 additions & 2 deletions Analysis/src/EmbeddedBuiltinDefinitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
LUAU_FASTFLAG(LuauMathMap)

LUAU_FASTFLAGVARIABLE(LuauVectorDefinitions)
LUAU_FASTFLAGVARIABLE(LuauVectorDefinitionsExtra)

namespace Luau
{
Expand Down Expand Up @@ -452,7 +453,7 @@ declare buffer: {
)BUILTIN_SRC";

static const std::string kBuiltinDefinitionVectorSrc = R"BUILTIN_SRC(
static const std::string kBuiltinDefinitionVectorSrc_DEPRECATED = R"BUILTIN_SRC(
-- TODO: this will be replaced with a built-in primitive type
declare class vector end
Expand All @@ -478,12 +479,44 @@ declare vector: {
)BUILTIN_SRC";

static const std::string kBuiltinDefinitionVectorSrc = R"BUILTIN_SRC(
-- While vector would have been better represented as a built-in primitive type, type solver class handling covers most of the properties
declare class vector
x: number
y: number
z: number
end
declare vector: {
create: @checked (x: number, y: number, z: number) -> vector,
magnitude: @checked (vec: vector) -> number,
normalize: @checked (vec: vector) -> vector,
cross: @checked (vec1: vector, vec2: vector) -> vector,
dot: @checked (vec1: vector, vec2: vector) -> number,
angle: @checked (vec1: vector, vec2: vector, axis: vector?) -> number,
floor: @checked (vec: vector) -> vector,
ceil: @checked (vec: vector) -> vector,
abs: @checked (vec: vector) -> vector,
sign: @checked (vec: vector) -> vector,
clamp: @checked (vec: vector, min: vector, max: vector) -> vector,
max: @checked (vector, ...vector) -> vector,
min: @checked (vector, ...vector) -> vector,
zero: vector,
one: vector,
}
)BUILTIN_SRC";

std::string getBuiltinDefinitionSource()
{
std::string result = FFlag::LuauMathMap ? kBuiltinDefinitionLuaSrcChecked : kBuiltinDefinitionLuaSrcChecked_DEPRECATED;

if (FFlag::LuauVectorDefinitions)
if (FFlag::LuauVectorDefinitionsExtra)
result += kBuiltinDefinitionVectorSrc;
else if (FFlag::LuauVectorDefinitions)
result += kBuiltinDefinitionVectorSrc_DEPRECATED;

return result;
}
Expand Down
56 changes: 16 additions & 40 deletions Analysis/src/FragmentAutocomplete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,19 +121,13 @@ FragmentAutocompleteAncestryResult findAncestryForFragmentParse(AstStatBlock* ro

/**
* Get document offsets is a function that takes a source text document as well as a start position and end position(line, column) in that
* document and attempts to get the concrete text between those points. It returns a tuple of:
* document and attempts to get the concrete text between those points. It returns a pair of:
* - start offset that represents an index in the source `char*` corresponding to startPos
* - length, that represents how many more bytes to read to get to endPos.
* - cursorPos, that represents the position of the cursor relative to the start offset.
* Example - your document is "foo bar baz" and getDocumentOffsets is passed (0, 4), (0, 7), (0, 8). This function returns the tuple {3, 5,
* Position{0, 4}}, which corresponds to the string " bar "
* Example - your document is "foo bar baz" and getDocumentOffsets is passed (0, 4), (0, 8). This function returns the pair {3, 5}
* which corresponds to the string " bar "
*/
std::tuple<size_t, size_t, Position> getDocumentOffsets(
const std::string_view& src,
const Position& startPos,
Position cursorPos,
const Position& endPos
)
std::pair<size_t, size_t> getDocumentOffsets(const std::string_view& src, const Position& startPos, const Position& endPos)
{
size_t lineCount = 0;
size_t colCount = 0;
Expand All @@ -142,12 +136,8 @@ std::tuple<size_t, size_t, Position> getDocumentOffsets(
size_t startOffset = 0;
size_t endOffset = 0;
bool foundStart = false;
bool foundCursor = false;
bool foundEnd = false;

unsigned int colOffsetFromStart = 0;
unsigned int lineOffsetFromStart = 0;

for (char c : src)
{
if (foundStart && foundEnd)
Expand All @@ -159,15 +149,11 @@ std::tuple<size_t, size_t, Position> getDocumentOffsets(
startOffset = docOffset;
}

if (cursorPos.line == lineCount && cursorPos.column == colCount)
{
foundCursor = true;
cursorPos = {lineOffsetFromStart, colOffsetFromStart};
}

if (endPos.line == lineCount && endPos.column == colCount)
{
endOffset = docOffset;
while (endOffset < src.size() && src[endOffset] != '\n')
endOffset++;
foundEnd = true;
}

Expand All @@ -180,18 +166,11 @@ std::tuple<size_t, size_t, Position> getDocumentOffsets(

if (c == '\n')
{
if (foundStart)
{
lineOffsetFromStart++;
colOffsetFromStart = 0;
}
lineCount++;
colCount = 0;
}
else
{
if (foundStart)
colOffsetFromStart++;
colCount++;
}
docOffset++;
Expand All @@ -200,12 +179,9 @@ std::tuple<size_t, size_t, Position> getDocumentOffsets(
if (foundStart && !foundEnd)
endOffset = src.length();

if (foundStart && !foundCursor)
cursorPos = {lineOffsetFromStart, colOffsetFromStart};

size_t min = std::min(startOffset, endOffset);
size_t len = std::max(startOffset, endOffset) - min;
return {min, len, cursorPos};
return {min, len};
}

ScopePtr findClosestScope(const ModulePtr& module, const AstStat* nearestStatement)
Expand All @@ -232,10 +208,6 @@ FragmentParseResult parseFragment(
)
{
FragmentAutocompleteAncestryResult result = findAncestryForFragmentParse(srcModule.root, cursorPos);
ParseOptions opts;
opts.allowDeclarationSyntax = false;
opts.captureComments = true;
opts.parseFragment = FragmentParseResumeSettings{std::move(result.localMap), std::move(result.localStack)};
AstStat* nearestStatement = result.nearestStatement;

const Location& rootSpan = srcModule.root->location;
Expand All @@ -260,23 +232,26 @@ FragmentParseResult parseFragment(
else
startPos = nearestStatement->location.begin;

auto [offsetStart, parseLength, cursorInFragment] = getDocumentOffsets(src, startPos, cursorPos, endPos);


auto [offsetStart, parseLength] = getDocumentOffsets(src, startPos, endPos);
const char* srcStart = src.data() + offsetStart;
std::string_view dbg = src.substr(offsetStart, parseLength);
const std::shared_ptr<AstNameTable>& nameTbl = srcModule.names;
FragmentParseResult fragmentResult;
fragmentResult.fragmentToParse = std::string(dbg.data(), parseLength);
// For the duration of the incremental parse, we want to allow the name table to re-use duplicate names

ParseOptions opts;
opts.allowDeclarationSyntax = false;
opts.captureComments = true;
opts.parseFragment = FragmentParseResumeSettings{std::move(result.localMap), std::move(result.localStack), startPos};
ParseResult p = Luau::Parser::parse(srcStart, parseLength, *nameTbl, *fragmentResult.alloc.get(), opts);

std::vector<AstNode*> fabricatedAncestry = std::move(result.ancestry);

// Get the ancestry for the fragment at the offset cursor position.
// Consumers have the option to request with fragment end position, so we cannot just use the end position of our parse result as the
// cursor position. Instead, use the cursor position calculated as an offset from our start position.
std::vector<AstNode*> fragmentAncestry = findAncestryAtPositionForAutocomplete(p.root, cursorInFragment);
std::vector<AstNode*> fragmentAncestry = findAncestryAtPositionForAutocomplete(p.root, cursorPos);
fabricatedAncestry.insert(fabricatedAncestry.end(), fragmentAncestry.begin(), fragmentAncestry.end());
if (nearestStatement == nullptr)
nearestStatement = p.root;
Expand Down Expand Up @@ -524,14 +499,15 @@ FragmentAutocompleteResult fragmentAutocomplete(
}

auto tcResult = typecheckFragment(frontend, moduleName, cursorPosition, opts, src, fragmentEndPosition);
auto globalScope = (opts && opts->forAutocomplete) ? frontend.globalsForAutocomplete.globalScope.get() : frontend.globals.globalScope.get();

TypeArena arenaForFragmentAutocomplete;
auto result = Luau::autocomplete_(
tcResult.incrementalModule,
frontend.builtinTypes,
&arenaForFragmentAutocomplete,
tcResult.ancestry,
frontend.globals.globalScope.get(),
globalScope,
tcResult.freshScope,
cursorPosition,
frontend.fileResolver,
Expand Down
26 changes: 26 additions & 0 deletions Analysis/src/Frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,32 @@ std::optional<CheckResult> Frontend::getCheckResult(const ModuleName& name, bool
return checkResult;
}

std::vector<ModuleName> Frontend::getRequiredScripts(const ModuleName& name)
{
RequireTraceResult require = requireTrace[name];
if (isDirty(name))
{
std::optional<SourceCode> source = fileResolver->readSource(name);
if (!source)
{
return {};
}
const Config& config = configResolver->getConfig(name);
ParseOptions opts = config.parseOptions;
opts.captureComments = true;
SourceModule result = parse(name, source->source, opts);
result.type = source->type;
require = traceRequires(fileResolver, result.root, name);
}
std::vector<std::string> requiredModuleNames;
requiredModuleNames.reserve(require.requireList.size());
for (const auto& [moduleName, _] : require.requireList)
{
requiredModuleNames.push_back(moduleName);
}
return requiredModuleNames;
}

bool Frontend::parseGraph(
std::vector<ModuleName>& buildQueue,
const ModuleName& root,
Expand Down
37 changes: 35 additions & 2 deletions Analysis/src/TypeFunctionRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
LUAU_DYNAMIC_FASTINT(LuauTypeFunctionSerdeIterationLimit)
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunFixRegister)
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunFixNoReadWrite)
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunThreadBuffer)

namespace Luau
{
Expand Down Expand Up @@ -133,6 +134,12 @@ static std::string getTag(lua_State* L, TypeFunctionTypeId ty)
return "number";
else if (auto s = get<TypeFunctionPrimitiveType>(ty); s && s->type == TypeFunctionPrimitiveType::Type::String)
return "string";
else if (auto s = get<TypeFunctionPrimitiveType>(ty);
FFlag::LuauUserTypeFunThreadBuffer && s && s->type == TypeFunctionPrimitiveType::Type::Thread)
return "thread";
else if (auto s = get<TypeFunctionPrimitiveType>(ty);
FFlag::LuauUserTypeFunThreadBuffer && s && s->type == TypeFunctionPrimitiveType::Type::Buffer)
return "buffer";
else if (get<TypeFunctionUnknownType>(ty))
return "unknown";
else if (get<TypeFunctionNeverType>(ty))
Expand Down Expand Up @@ -212,6 +219,22 @@ static int createString(lua_State* L)
return 1;
}

// Luau: `type.thread`
static int createThread(lua_State* L)
{
allocTypeUserData(L, TypeFunctionPrimitiveType{TypeFunctionPrimitiveType::Thread});

return 1;
}

// Luau: `type.buffer`
static int createBuffer(lua_State* L)
{
allocTypeUserData(L, TypeFunctionPrimitiveType{TypeFunctionPrimitiveType::Buffer});

return 1;
}

// Luau: `type.singleton(value: string | boolean | nil) -> type`
// Returns the type instance representing string or boolean singleton or nil
static int createSingleton(lua_State* L)
Expand Down Expand Up @@ -1394,6 +1417,8 @@ void registerTypesLibrary(lua_State* L)
{"boolean", createBoolean},
{"number", createNumber},
{"string", createString},
{FFlag::LuauUserTypeFunThreadBuffer ? "thread" : nullptr, FFlag::LuauUserTypeFunThreadBuffer ? createThread : nullptr},
{FFlag::LuauUserTypeFunThreadBuffer ? "buffer" : nullptr, FFlag::LuauUserTypeFunThreadBuffer ? createBuffer : nullptr},
{nullptr, nullptr}
};

Expand Down Expand Up @@ -2118,10 +2143,10 @@ class TypeFunctionCloner
{
switch (p->type)
{
case TypeFunctionPrimitiveType::Type::NilType:
case TypeFunctionPrimitiveType::NilType:
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionPrimitiveType(TypeFunctionPrimitiveType::NilType));
break;
case TypeFunctionPrimitiveType::Type::Boolean:
case TypeFunctionPrimitiveType::Boolean:
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionPrimitiveType(TypeFunctionPrimitiveType::Boolean));
break;
case TypeFunctionPrimitiveType::Number:
Expand All @@ -2130,6 +2155,14 @@ class TypeFunctionCloner
case TypeFunctionPrimitiveType::String:
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionPrimitiveType(TypeFunctionPrimitiveType::String));
break;
case TypeFunctionPrimitiveType::Thread:
if (FFlag::LuauUserTypeFunThreadBuffer)
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionPrimitiveType(TypeFunctionPrimitiveType::Thread));
break;
case TypeFunctionPrimitiveType::Buffer:
if (FFlag::LuauUserTypeFunThreadBuffer)
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionPrimitiveType(TypeFunctionPrimitiveType::Buffer));
break;
default:
break;
}
Expand Down
Loading

0 comments on commit d0222bb

Please sign in to comment.