Skip to content

Commit e0b55a9

Browse files
hgoldsteinvrn-snayoungbloodrbxmenarulalamaviralg
authored
Sync to upstream/release/665 (#1732)
Hello all! Another week, another Luau release! # Change to `lua_setuserdatametatable` This release fixes #1710: `lua_setuserdatametatable` is being changed so that it _only_ operates on the top of the stack: the `idx` parameter is being removed. Prior to this, `lua_setuserdatametable` would set the metatable of the value in the stack at `idx`, but _always_ pop the top of the stack. The old behavior is available in this release as `lua_setuserdatametatable_DEPRECATED`. # General This release exposes a generalized implementation of require-by-string's autocomplete logic. `FileResolver` can now be optionally constructed with a `RequireSuggester`, which provides an interface for converting a given module to a `RequireNode`. Consumers of this new API implement a `RequireNode` to define how modules are represented in their embedded context, and the new API manages the logic specific to require-by-string, including providing suggestions for require aliases. This enhancement moves toward integrating require-by-string's semantics into the language itself, rather than merely providing a specification for community members to implement themselves. # New Type Solver * Fixed a source of potential `Luau::follow detected a Type cycle` internal compiler exceptions when assigning a global to itself. * Fixed an issue whereby `*no-refine*` (a type which should not be visible at the end of type checking) was not being properly elided, causing inference of class-like tables to become unreadable / induce crashes in autocomplete. * Fixed a case of incomplete constraint solving when performing basic math in a loop # Fragment Autocomplete * Fixed several crashes related to not properly filling in scope information for the fragments * Fixed a source of memory corruption by isolating the return type of a fragment when it is type checked. * Improved performance by opting not to clone persistent types for the fragment (e.g.: built in types) # Internal Contributors Co-authored-by: Andy Friesen <[email protected]> Co-authored-by: Hunter Goldstein <[email protected]> Co-authored-by: Talha Pathan <[email protected]> Co-authored-by: Varun Saini <[email protected]> Co-authored-by: Vighnesh Vijay <[email protected]> Co-authored-by: Vyacheslav Egorov <[email protected]> --------- Co-authored-by: Varun Saini <[email protected]> Co-authored-by: Alexander Youngblood <[email protected]> Co-authored-by: Menarul Alam <[email protected]> Co-authored-by: Aviral Goel <[email protected]> Co-authored-by: Vighnesh <[email protected]> Co-authored-by: Vyacheslav Egorov <[email protected]> Co-authored-by: Ariel Weiss <[email protected]>
1 parent b0c3f40 commit e0b55a9

32 files changed

+1056
-409
lines changed

Analysis/include/Luau/FileResolver.h

+62-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
22
#pragma once
33

4-
#include <string>
4+
#include <memory>
55
#include <optional>
6+
#include <string>
67
#include <vector>
78

89
namespace Luau
@@ -32,15 +33,71 @@ struct ModuleInfo
3233
bool optional = false;
3334
};
3435

36+
struct RequireAlias
37+
{
38+
std::string alias; // Unprefixed alias name (no leading `@`).
39+
std::vector<std::string> tags = {};
40+
};
41+
42+
struct RequireNode
43+
{
44+
virtual ~RequireNode() {}
45+
46+
// Get the path component representing this node.
47+
virtual std::string getPathComponent() const = 0;
48+
49+
// Get the displayed user-facing label for this node, defaults to getPathComponent()
50+
virtual std::string getLabel() const
51+
{
52+
return getPathComponent();
53+
}
54+
55+
// Get tags to attach to this node's RequireSuggestion (defaults to none).
56+
virtual std::vector<std::string> getTags() const
57+
{
58+
return {};
59+
}
60+
61+
// TODO: resolvePathToNode() can ultimately be replaced with a call into
62+
// require-by-string's path resolution algorithm. This will first require
63+
// generalizing that algorithm to work with a virtual file system.
64+
virtual std::unique_ptr<RequireNode> resolvePathToNode(const std::string& path) const = 0;
65+
66+
// Get children of this node, if any (if this node represents a directory).
67+
virtual std::vector<std::unique_ptr<RequireNode>> getChildren() const = 0;
68+
69+
// A list of the aliases available to this node.
70+
virtual std::vector<RequireAlias> getAvailableAliases() const = 0;
71+
};
72+
3573
struct RequireSuggestion
3674
{
3775
std::string label;
3876
std::string fullPath;
77+
std::vector<std::string> tags;
3978
};
4079
using RequireSuggestions = std::vector<RequireSuggestion>;
4180

81+
struct RequireSuggester
82+
{
83+
virtual ~RequireSuggester() {}
84+
std::optional<RequireSuggestions> getRequireSuggestions(const ModuleName& requirer, const std::optional<std::string>& pathString) const;
85+
86+
protected:
87+
virtual std::unique_ptr<RequireNode> getNode(const ModuleName& name) const = 0;
88+
89+
private:
90+
std::optional<RequireSuggestions> getRequireSuggestionsImpl(const ModuleName& requirer, const std::optional<std::string>& path) const;
91+
};
92+
4293
struct FileResolver
4394
{
95+
FileResolver() = default;
96+
FileResolver(std::shared_ptr<RequireSuggester> requireSuggester)
97+
: requireSuggester(std::move(requireSuggester))
98+
{
99+
}
100+
44101
virtual ~FileResolver() {}
45102

46103
virtual std::optional<SourceCode> readSource(const ModuleName& name) = 0;
@@ -60,10 +117,10 @@ struct FileResolver
60117
return std::nullopt;
61118
}
62119

63-
virtual std::optional<RequireSuggestions> getRequireSuggestions(const ModuleName& requirer, const std::optional<std::string>& pathString) const
64-
{
65-
return std::nullopt;
66-
}
120+
// Make non-virtual when removing FFlagLuauImproveRequireByStringAutocomplete.
121+
virtual std::optional<RequireSuggestions> getRequireSuggestions(const ModuleName& requirer, const std::optional<std::string>& pathString) const;
122+
123+
std::shared_ptr<RequireSuggester> requireSuggester;
67124
};
68125

69126
struct NullFileResolver : FileResolver

Analysis/include/Luau/FragmentAutocomplete.h

+27-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,28 @@ namespace Luau
1515
{
1616
struct FrontendOptions;
1717

18+
enum class FragmentAutocompleteWaypoint
19+
{
20+
ParseFragmentEnd,
21+
CloneModuleStart,
22+
CloneModuleEnd,
23+
DfgBuildEnd,
24+
CloneAndSquashScopeStart,
25+
CloneAndSquashScopeEnd,
26+
ConstraintSolverStart,
27+
ConstraintSolverEnd,
28+
TypecheckFragmentEnd,
29+
AutocompleteEnd,
30+
COUNT,
31+
};
32+
33+
class IFragmentAutocompleteReporter
34+
{
35+
public:
36+
virtual void reportWaypoint(FragmentAutocompleteWaypoint) = 0;
37+
virtual void reportFragmentString(std::string_view) = 0;
38+
};
39+
1840
enum class FragmentTypeCheckStatus
1941
{
2042
SkipAutocomplete,
@@ -70,7 +92,8 @@ std::pair<FragmentTypeCheckStatus, FragmentTypeCheckResult> typecheckFragment(
7092
const Position& cursorPos,
7193
std::optional<FrontendOptions> opts,
7294
std::string_view src,
73-
std::optional<Position> fragmentEndPosition
95+
std::optional<Position> fragmentEndPosition,
96+
IFragmentAutocompleteReporter* reporter = nullptr
7497
);
7598

7699
FragmentAutocompleteResult fragmentAutocomplete(
@@ -80,7 +103,8 @@ FragmentAutocompleteResult fragmentAutocomplete(
80103
Position cursorPosition,
81104
std::optional<FrontendOptions> opts,
82105
StringCompletionCallback callback,
83-
std::optional<Position> fragmentEndPosition = std::nullopt
106+
std::optional<Position> fragmentEndPosition = std::nullopt,
107+
IFragmentAutocompleteReporter* reporter = nullptr
84108
);
85109

86110
enum class FragmentAutocompleteStatus
@@ -102,6 +126,7 @@ struct FragmentContext
102126
const ParseResult& freshParse;
103127
std::optional<FrontendOptions> opts;
104128
std::optional<Position> DEPRECATED_fragmentEndPosition;
129+
IFragmentAutocompleteReporter* reporter = nullptr;
105130
};
106131

107132
/**

Analysis/include/Luau/Quantify.h

-9
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,4 @@ struct OrderedMap
3131
}
3232
};
3333

34-
struct QuantifierResult
35-
{
36-
TypeId result;
37-
OrderedMap<TypeId, TypeId> insertedGenerics;
38-
OrderedMap<TypePackId, TypePackId> insertedGenericPacks;
39-
};
40-
41-
std::optional<QuantifierResult> quantify(TypeArena* arena, TypeId ty, Scope* scope);
42-
4334
} // namespace Luau

Analysis/src/AutocompleteCore.cpp

+7-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ LUAU_FASTINT(LuauTypeInferIterationLimit)
2525
LUAU_FASTINT(LuauTypeInferRecursionLimit)
2626
LUAU_FASTFLAGVARIABLE(DebugLuauMagicVariableNames)
2727

28+
LUAU_FASTFLAG(LuauExposeRequireByStringAutocomplete)
29+
2830
LUAU_FASTFLAGVARIABLE(LuauAutocompleteRefactorsForIncrementalAutocomplete)
2931

3032
LUAU_FASTFLAGVARIABLE(LuauAutocompleteUsesModuleForTypeCompatibility)
@@ -1519,10 +1521,14 @@ static std::optional<AutocompleteEntryMap> convertRequireSuggestionsToAutocomple
15191521
return std::nullopt;
15201522

15211523
AutocompleteEntryMap result;
1522-
for (const RequireSuggestion& suggestion : *suggestions)
1524+
for (RequireSuggestion& suggestion : *suggestions)
15231525
{
15241526
AutocompleteEntry entry = {AutocompleteEntryKind::RequirePath};
15251527
entry.insertText = std::move(suggestion.fullPath);
1528+
if (FFlag::LuauExposeRequireByStringAutocomplete)
1529+
{
1530+
entry.tags = std::move(suggestion.tags);
1531+
}
15261532
result[std::move(suggestion.label)] = std::move(entry);
15271533
}
15281534
return result;

Analysis/src/Clone.cpp

+19-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
22
#include "Luau/Clone.h"
33

4+
#include "Luau/Common.h"
45
#include "Luau/NotNull.h"
56
#include "Luau/Type.h"
67
#include "Luau/TypePack.h"
@@ -12,6 +13,8 @@ LUAU_FASTFLAG(LuauFreezeIgnorePersistent)
1213

1314
// For each `Luau::clone` call, we will clone only up to N amount of types _and_ packs, as controlled by this limit.
1415
LUAU_FASTINTVARIABLE(LuauTypeCloneIterationLimit, 100'000)
16+
LUAU_FASTFLAGVARIABLE(LuauClonedTableAndFunctionTypesMustHaveScopes)
17+
LUAU_FASTFLAGVARIABLE(LuauDoNotClonePersistentBindings)
1518

1619
namespace Luau
1720
{
@@ -477,7 +480,7 @@ class TypeCloner
477480

478481
class FragmentAutocompleteTypeCloner final : public TypeCloner
479482
{
480-
Scope* freeTypeReplacementScope = nullptr;
483+
Scope* replacementForNullScope = nullptr;
481484

482485
public:
483486
FragmentAutocompleteTypeCloner(
@@ -487,12 +490,12 @@ class FragmentAutocompleteTypeCloner final : public TypeCloner
487490
NotNull<SeenTypePacks> packs,
488491
TypeId forceTy,
489492
TypePackId forceTp,
490-
Scope* freeTypeReplacementScope
493+
Scope* replacementForNullScope
491494
)
492495
: TypeCloner(arena, builtinTypes, types, packs, forceTy, forceTp)
493-
, freeTypeReplacementScope(freeTypeReplacementScope)
496+
, replacementForNullScope(replacementForNullScope)
494497
{
495-
LUAU_ASSERT(freeTypeReplacementScope);
498+
LUAU_ASSERT(replacementForNullScope);
496499
}
497500

498501
TypeId shallowClone(TypeId ty) override
@@ -512,12 +515,18 @@ class FragmentAutocompleteTypeCloner final : public TypeCloner
512515
generic->scope = nullptr;
513516
else if (auto free = getMutable<FreeType>(target))
514517
{
515-
free->scope = freeTypeReplacementScope;
518+
free->scope = replacementForNullScope;
519+
}
520+
else if (auto tt = getMutable<TableType>(target))
521+
{
522+
if (FFlag::LuauClonedTableAndFunctionTypesMustHaveScopes)
523+
tt->scope = replacementForNullScope;
516524
}
517525
else if (auto fn = getMutable<FunctionType>(target))
518-
fn->scope = nullptr;
519-
else if (auto table = getMutable<TableType>(target))
520-
table->scope = nullptr;
526+
{
527+
if (FFlag::LuauClonedTableAndFunctionTypesMustHaveScopes)
528+
fn->scope = replacementForNullScope;
529+
}
521530

522531
(*types)[ty] = target;
523532
queue.emplace_back(target);
@@ -538,7 +547,7 @@ class FragmentAutocompleteTypeCloner final : public TypeCloner
538547
if (auto generic = getMutable<GenericTypePack>(target))
539548
generic->scope = nullptr;
540549
else if (auto free = getMutable<FreeTypePack>(target))
541-
free->scope = freeTypeReplacementScope;
550+
free->scope = replacementForNullScope;
542551

543552
(*packs)[tp] = target;
544553
queue.emplace_back(target);
@@ -728,7 +737,7 @@ Binding cloneIncremental(const Binding& binding, TypeArena& dest, CloneState& cl
728737
b.deprecatedSuggestion = binding.deprecatedSuggestion;
729738
b.documentationSymbol = binding.documentationSymbol;
730739
b.location = binding.location;
731-
b.typeId = cloner.clone(binding.typeId);
740+
b.typeId = FFlag::LuauDoNotClonePersistentBindings && binding.typeId->persistent ? binding.typeId : cloner.clone(binding.typeId);
732741

733742
return b;
734743
}

Analysis/src/ConstraintGenerator.cpp

+10-1
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,12 @@ LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
3838
LUAU_FASTFLAGVARIABLE(LuauTrackInteriorFreeTypesOnScope)
3939
LUAU_FASTFLAGVARIABLE(LuauDeferBidirectionalInferenceForTableAssignment)
4040
LUAU_FASTFLAGVARIABLE(LuauUngeneralizedTypesForRecursiveFunctions)
41+
LUAU_FASTFLAGVARIABLE(LuauGlobalSelfAssignmentCycle)
4142

4243
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
4344
LUAU_FASTFLAGVARIABLE(LuauInferLocalTypesInMultipleAssignments)
4445
LUAU_FASTFLAGVARIABLE(LuauDoNotLeakNilInRefinement)
46+
LUAU_FASTFLAGVARIABLE(LuauExtraFollows)
4547

4648
namespace Luau
4749
{
@@ -2082,7 +2084,7 @@ InferencePack ConstraintGenerator::checkPack(const ScopePtr& scope, AstExprCall*
20822084
{
20832085
std::vector<TypeId> unpackedTypes;
20842086
if (args.size() > 0)
2085-
target = args[0];
2087+
target = FFlag::LuauExtraFollows ? follow(args[0]) : args[0];
20862088
else
20872089
{
20882090
target = arena->addType(BlockedType{});
@@ -2891,6 +2893,13 @@ void ConstraintGenerator::visitLValue(const ScopePtr& scope, AstExprGlobal* glob
28912893
DefId def = dfg->getDef(global);
28922894
rootScope->lvalueTypes[def] = rhsType;
28932895

2896+
if (FFlag::LuauGlobalSelfAssignmentCycle)
2897+
{
2898+
// Ignore possible self-assignment, it doesn't create a new constraint
2899+
if (annotatedTy == follow(rhsType))
2900+
return;
2901+
}
2902+
28942903
// Sketchy: We're specifically looking for BlockedTypes that were
28952904
// initially created by ConstraintGenerator::prepopulateGlobalScope.
28962905
if (auto bt = get<BlockedType>(follow(*annotatedTy)); bt && !bt->getOwner())

Analysis/src/ConstraintSolver.cpp

+24-19
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
3737
LUAU_FASTFLAGVARIABLE(LuauTrackInteriorFreeTablesOnScope)
3838
LUAU_FASTFLAGVARIABLE(LuauPrecalculateMutatedFreeTypes2)
3939
LUAU_FASTFLAGVARIABLE(DebugLuauGreedyGeneralization)
40+
LUAU_FASTFLAG(LuauSearchForRefineableType)
4041

4142
namespace Luau
4243
{
@@ -907,26 +908,16 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
907908
else if (get<PendingExpansionType>(generalizedType))
908909
return block(generalizedType, constraint);
909910

910-
std::optional<QuantifierResult> generalized;
911-
912911
std::optional<TypeId> generalizedTy = generalize(NotNull{arena}, builtinTypes, constraint->scope, generalizedTypes, c.sourceType);
913-
if (generalizedTy)
914-
generalized = QuantifierResult{*generalizedTy}; // FIXME insertedGenerics and insertedGenericPacks
915-
else
912+
if (!generalizedTy)
916913
reportError(CodeTooComplex{}, constraint->location);
917914

918-
if (generalized)
915+
if (generalizedTy)
919916
{
920917
if (get<BlockedType>(generalizedType))
921-
bind(constraint, generalizedType, generalized->result);
918+
bind(constraint, generalizedType, *generalizedTy);
922919
else
923-
unify(constraint, generalizedType, generalized->result);
924-
925-
for (auto [free, gen] : generalized->insertedGenerics.pairings)
926-
unify(constraint, free, gen);
927-
928-
for (auto [free, gen] : generalized->insertedGenericPacks.pairings)
929-
unify(constraint, free, gen);
920+
unify(constraint, generalizedType, *generalizedTy);
930921
}
931922
else
932923
{
@@ -1356,15 +1347,29 @@ void ConstraintSolver::fillInDiscriminantTypes(NotNull<const Constraint> constra
13561347
if (!ty)
13571348
continue;
13581349

1359-
// If the discriminant type has been transmuted, we need to unblock them.
1360-
if (!isBlocked(*ty))
1350+
if (FFlag::LuauSearchForRefineableType)
13611351
{
1352+
if (isBlocked(*ty))
1353+
// We bind any unused discriminants to the `*no-refine*` type indicating that it can be safely ignored.
1354+
emplaceType<BoundType>(asMutable(follow(*ty)), builtinTypes->noRefineType);
1355+
1356+
// We also need to unconditionally unblock these types, otherwise
1357+
// you end up with funky looking "Blocked on *no-refine*."
13621358
unblock(*ty, constraint->location);
1363-
continue;
13641359
}
1360+
else
1361+
{
13651362

1366-
// We bind any unused discriminants to the `*no-refine*` type indicating that it can be safely ignored.
1367-
emplaceType<BoundType>(asMutable(follow(*ty)), builtinTypes->noRefineType);
1363+
// If the discriminant type has been transmuted, we need to unblock them.
1364+
if (!isBlocked(*ty))
1365+
{
1366+
unblock(*ty, constraint->location);
1367+
continue;
1368+
}
1369+
1370+
// We bind any unused discriminants to the `*no-refine*` type indicating that it can be safely ignored.
1371+
emplaceType<BoundType>(asMutable(follow(*ty)), builtinTypes->noRefineType);
1372+
}
13681373
}
13691374
}
13701375

0 commit comments

Comments
 (0)