Skip to content

Commit 640ebbc

Browse files
aatxehgoldsteinvrn-snayoungbloodrbxmenarulalam
authored
Sync to upstream/release/663 (#1699)
Hey folks, another week means another Luau release! This one features a number of bug fixes in the New Type Solver including improvements to user-defined type functions and a bunch of work to untangle some of the outstanding issues we've been seeing with constraint solving not completing in real world use. We're also continuing to make progress on crashes and other problems that affect the stability of fragment autocomplete, as we work towards delivering consistent, low-latency autocomplete for any editor environment. ## New Type Solver - Fix a bug in user-defined type functions where `print` would incorrectly insert `\1` a number of times. - Fix a bug where attempting to refine an optional generic with a type test will cause a false positive type error (fixes #1666) - Fix a bug where the `refine` type family would not skip over `*no-refine*` discriminants (partial resolution for #1424) - Fix a constraint solving bug where recursive function calls would consistently produce cyclic constraints leading to incomplete or inaccurate type inference. - Implement `readparent` and `writeparent` for class types in user-defined type functions, replacing the incorrectly included `parent` method. - Add initial groundwork (under a debug flag) for eager free type generalization, moving us towards further improvements to constraint solving incomplete errors. ## Fragment Autocomplete - Ease up some assertions to improve stability of mixed-mode use of the two type solvers (i.e. using Fragment Autocomplete on a type graph originally produced by the old type solver) - Resolve a bug with type compatibility checks causing internal compiler errors in autocomplete. ## Lexer and Parser - Improve the accuracy of the roundtrippable AST parsing mode by correctly placing closing parentheses on type groupings. - Add a getter for `offset` in the Lexer by @aduermael in #1688 - Add a second entry point to the parser to parse an expression, `parseExpr` ## Internal Contributors Co-authored-by: Andy Friesen <[email protected]> Co-authored-by: Ariel Weiss <[email protected]> Co-authored-by: Aviral Goel <[email protected]> Co-authored-by: Hunter Goldstein <[email protected]> Co-authored-by: James McNellis <[email protected]> Co-authored-by: Talha Pathan <[email protected]> Co-authored-by: Vighnesh Vijay <[email protected]> Co-authored-by: Vyacheslav Egorov <[email protected]> --------- Co-authored-by: Hunter Goldstein <[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]>
1 parent 6a21dba commit 640ebbc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+924
-1066
lines changed

Analysis/include/Luau/ConstraintGenerator.h

+3
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ struct ConstraintGenerator
9696
// will enqueue them during solving.
9797
std::vector<ConstraintPtr> unqueuedConstraints;
9898

99+
// Map a function's signature scope back to its signature type.
100+
DenseHashMap<Scope*, TypeId> scopeToFunction{nullptr};
101+
99102
// The private scope of type aliases for which the type parameters belong to.
100103
DenseHashMap<const AstStatTypeAlias*, ScopePtr> astTypeAliasDefiningScopes{nullptr};
101104

Analysis/include/Luau/ConstraintSolver.h

+5
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ struct ConstraintSolver
8888
NotNull<TypeFunctionRuntime> typeFunctionRuntime;
8989
// The entire set of constraints that the solver is trying to resolve.
9090
std::vector<NotNull<Constraint>> constraints;
91+
NotNull<DenseHashMap<Scope*, TypeId>> scopeToFunction;
9192
NotNull<Scope> rootScope;
9293
ModuleName currentModuleName;
9394

@@ -119,6 +120,7 @@ struct ConstraintSolver
119120
DenseHashMap<TypeId, size_t> unresolvedConstraints{{}};
120121

121122
std::unordered_map<NotNull<const Constraint>, DenseHashSet<TypeId>> maybeMutatedFreeTypes;
123+
std::unordered_map<TypeId, DenseHashSet<const Constraint*>> mutatedFreeTypeToConstraint;
122124

123125
// Irreducible/uninhabited type functions or type pack functions.
124126
DenseHashSet<const void*> uninhabitedTypeFunctions{{}};
@@ -144,6 +146,7 @@ struct ConstraintSolver
144146
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
145147
NotNull<Scope> rootScope,
146148
std::vector<NotNull<Constraint>> constraints,
149+
NotNull<DenseHashMap<Scope*, TypeId>> scopeToFunction,
147150
ModuleName moduleName,
148151
NotNull<ModuleResolver> moduleResolver,
149152
std::vector<RequireCycle> requireCycles,
@@ -171,6 +174,8 @@ struct ConstraintSolver
171174
bool isDone() const;
172175

173176
private:
177+
void generalizeOneType(TypeId ty);
178+
174179
/**
175180
* Bind a type variable to another type.
176181
*

Analysis/include/Luau/FragmentAutocomplete.h

+60
Original file line numberDiff line numberDiff line change
@@ -82,5 +82,65 @@ FragmentAutocompleteResult fragmentAutocomplete(
8282
std::optional<Position> fragmentEndPosition = std::nullopt
8383
);
8484

85+
enum class FragmentAutocompleteStatus
86+
{
87+
Success,
88+
FragmentTypeCheckFail,
89+
InternalIce
90+
};
91+
92+
struct FragmentAutocompleteStatusResult
93+
{
94+
FragmentAutocompleteStatus status;
95+
std::optional<FragmentAutocompleteResult> result;
96+
};
97+
98+
struct FragmentContext
99+
{
100+
std::string_view newSrc;
101+
const ParseResult& newAstRoot;
102+
std::optional<FrontendOptions> opts;
103+
std::optional<Position> DEPRECATED_fragmentEndPosition;
104+
};
105+
106+
/**
107+
* @brief Attempts to compute autocomplete suggestions from the fragment context.
108+
*
109+
* This function computes autocomplete suggestions using outdated frontend typechecking data
110+
* by patching the fragment context of the new script source content.
111+
*
112+
* @param frontend The Luau Frontend data structure, which may contain outdated typechecking data.
113+
*
114+
* @param moduleName The name of the target module, specifying which script the caller wants to request autocomplete for.
115+
*
116+
* @param cursorPosition The position in the script where the caller wants to trigger autocomplete.
117+
*
118+
* @param context The fragment context that this API will use to patch the outdated typechecking data.
119+
*
120+
* @param stringCompletionCB A callback function that provides autocomplete suggestions for string contexts.
121+
*
122+
* @return
123+
* The status indicating whether `fragmentAutocomplete` ran successfully or failed, along with the reason for failure.
124+
* Also includes autocomplete suggestions if the status is successful.
125+
*
126+
* @usage
127+
* FragmentAutocompleteStatusResult acStatusResult;
128+
* if (shouldFragmentAC)
129+
* acStatusResult = Luau::tryFragmentAutocomplete(...);
130+
*
131+
* if (acStatusResult.status != Successful)
132+
* {
133+
* frontend.check(moduleName, options);
134+
* acStatusResult.acResult = Luau::autocomplete(...);
135+
* }
136+
* return convertResultWithContext(acStatusResult.acResult);
137+
*/
138+
FragmentAutocompleteStatusResult tryFragmentAutocomplete(
139+
Frontend& frontend,
140+
const ModuleName& moduleName,
141+
Position cursorPosition,
142+
FragmentContext context,
143+
StringCompletionCallback stringCompletionCB
144+
);
85145

86146
} // namespace Luau

Analysis/include/Luau/TableLiteralInference.h

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#include "Luau/NotNull.h"
77
#include "Luau/TypeFwd.h"
88

9+
#include <vector>
10+
911
namespace Luau
1012
{
1113

Analysis/include/Luau/TxnLog.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,10 @@ T* getMutable(PendingTypePack* pending)
6565
// Log of what TypeIds we are rebinding, to be committed later.
6666
struct TxnLog
6767
{
68-
explicit TxnLog(bool useScopes = false)
68+
explicit TxnLog()
6969
: typeVarChanges(nullptr)
7070
, typePackChanges(nullptr)
7171
, ownedSeen()
72-
, useScopes(useScopes)
7372
, sharedSeen(&ownedSeen)
7473
{
7574
}

Analysis/include/Luau/TypeFunctionRuntime.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,11 @@ struct TypeFunctionClassType
216216

217217
std::optional<TypeFunctionTypeId> metatable; // metaclass?
218218

219-
std::optional<TypeFunctionTypeId> parent;
219+
// this was mistaken, and we should actually be keeping separate read/write types here.
220+
std::optional<TypeFunctionTypeId> parent_DEPRECATED;
221+
222+
std::optional<TypeFunctionTypeId> readParent;
223+
std::optional<TypeFunctionTypeId> writeParent;
220224

221225
TypeId classTy;
222226

Analysis/include/Luau/Unifier.h

-10
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,6 @@ struct Unifier
9393

9494
Unifier(NotNull<Normalizer> normalizer, NotNull<Scope> scope, const Location& location, Variance variance, TxnLog* parentLog = nullptr);
9595

96-
// Configure the Unifier to test for scope subsumption via embedded Scope
97-
// pointers rather than TypeLevels.
98-
void enableNewSolver();
99-
10096
// Test whether the two type vars unify. Never commits the result.
10197
ErrorVec canUnify(TypeId subTy, TypeId superTy);
10298
ErrorVec canUnify(TypePackId subTy, TypePackId superTy, bool isFunctionCall = false);
@@ -169,7 +165,6 @@ struct Unifier
169165

170166
std::optional<TypeId> findTablePropertyRespectingMeta(TypeId lhsType, Name name);
171167

172-
TxnLog combineLogsIntoIntersection(std::vector<TxnLog> logs);
173168
TxnLog combineLogsIntoUnion(std::vector<TxnLog> logs);
174169

175170
public:
@@ -195,11 +190,6 @@ struct Unifier
195190

196191
// Available after regular type pack unification errors
197192
std::optional<int> firstPackErrorPos;
198-
199-
// If true, we do a bunch of small things differently to work better with
200-
// the new type inference engine. Most notably, we use the Scope hierarchy
201-
// directly rather than using TypeLevels.
202-
bool useNewSolver = false;
203193
};
204194

205195
void promoteTypeLevels(TxnLog& log, const TypeArena* arena, TypeLevel minLevel, Scope* outerScope, bool useScope, TypePackId tp);

Analysis/src/Autocomplete.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "Luau/Autocomplete.h"
33

44
#include "Luau/AstQuery.h"
5+
#include "Luau/TimeTrace.h"
56
#include "Luau/TypeArena.h"
67
#include "Luau/Module.h"
78
#include "Luau/Frontend.h"
@@ -15,6 +16,9 @@ namespace Luau
1516

1617
AutocompleteResult autocomplete(Frontend& frontend, const ModuleName& moduleName, Position position, StringCompletionCallback callback)
1718
{
19+
LUAU_TIMETRACE_SCOPE("Luau::autocomplete", "Autocomplete");
20+
LUAU_TIMETRACE_ARGUMENT("name", moduleName.c_str());
21+
1822
const SourceModule* sourceModule = frontend.getSourceModule(moduleName);
1923
if (!sourceModule)
2024
return {};

Analysis/src/AutocompleteCore.cpp

+75-25
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "Luau/Common.h"
1111
#include "Luau/FileResolver.h"
1212
#include "Luau/Frontend.h"
13+
#include "Luau/TimeTrace.h"
1314
#include "Luau/ToString.h"
1415
#include "Luau/Subtyping.h"
1516
#include "Luau/TypeInfer.h"
@@ -24,7 +25,8 @@ LUAU_FASTINT(LuauTypeInferIterationLimit)
2425
LUAU_FASTINT(LuauTypeInferRecursionLimit)
2526

2627
LUAU_FASTFLAGVARIABLE(LuauAutocompleteRefactorsForIncrementalAutocomplete)
27-
LUAU_FASTFLAGVARIABLE(LuauAutocompleteUseLimits)
28+
29+
LUAU_FASTFLAGVARIABLE(LuauAutocompleteUsesModuleForTypeCompatibility)
2830

2931
static const std::unordered_set<std::string> kStatementStartingKeywords =
3032
{"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
@@ -146,44 +148,91 @@ static std::optional<TypeId> findExpectedTypeAt(const Module& module, AstNode* n
146148
return *it;
147149
}
148150

149-
static bool checkTypeMatch(TypeId subTy, TypeId superTy, NotNull<Scope> scope, TypeArena* typeArena, NotNull<BuiltinTypes> builtinTypes)
151+
static bool checkTypeMatch(
152+
const Module& module,
153+
TypeId subTy,
154+
TypeId superTy,
155+
NotNull<Scope> scope,
156+
TypeArena* typeArena,
157+
NotNull<BuiltinTypes> builtinTypes
158+
)
150159
{
151160
InternalErrorReporter iceReporter;
152161
UnifierSharedState unifierState(&iceReporter);
153162
SimplifierPtr simplifier = newSimplifier(NotNull{typeArena}, builtinTypes);
154163
Normalizer normalizer{typeArena, builtinTypes, NotNull{&unifierState}};
155-
156-
if (FFlag::LuauSolverV2)
164+
if (FFlag::LuauAutocompleteUsesModuleForTypeCompatibility)
157165
{
158-
TypeCheckLimits limits;
159-
TypeFunctionRuntime typeFunctionRuntime{
160-
NotNull{&iceReporter}, NotNull{&limits}
161-
}; // TODO: maybe subtyping checks should not invoke user-defined type function runtime
166+
if (module.checkedInNewSolver)
167+
{
168+
TypeCheckLimits limits;
169+
TypeFunctionRuntime typeFunctionRuntime{
170+
NotNull{&iceReporter}, NotNull{&limits}
171+
}; // TODO: maybe subtyping checks should not invoke user-defined type function runtime
172+
173+
unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit;
174+
unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit;
162175

163-
unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit;
164-
unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit;
176+
Subtyping subtyping{
177+
builtinTypes,
178+
NotNull{typeArena},
179+
NotNull{simplifier.get()},
180+
NotNull{&normalizer},
181+
NotNull{&typeFunctionRuntime},
182+
NotNull{&iceReporter}
183+
};
165184

166-
Subtyping subtyping{
167-
builtinTypes, NotNull{typeArena}, NotNull{simplifier.get()}, NotNull{&normalizer}, NotNull{&typeFunctionRuntime}, NotNull{&iceReporter}
168-
};
185+
return subtyping.isSubtype(subTy, superTy, scope).isSubtype;
186+
}
187+
else
188+
{
189+
Unifier unifier(NotNull<Normalizer>{&normalizer}, scope, Location(), Variance::Covariant);
190+
191+
// Cost of normalization can be too high for autocomplete response time requirements
192+
unifier.normalize = false;
193+
unifier.checkInhabited = false;
169194

170-
return subtyping.isSubtype(subTy, superTy, scope).isSubtype;
195+
unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit;
196+
unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit;
197+
198+
return unifier.canUnify(subTy, superTy).empty();
199+
}
171200
}
172201
else
173202
{
174-
Unifier unifier(NotNull<Normalizer>{&normalizer}, scope, Location(), Variance::Covariant);
203+
if (FFlag::LuauSolverV2)
204+
{
205+
TypeCheckLimits limits;
206+
TypeFunctionRuntime typeFunctionRuntime{
207+
NotNull{&iceReporter}, NotNull{&limits}
208+
}; // TODO: maybe subtyping checks should not invoke user-defined type function runtime
175209

176-
// Cost of normalization can be too high for autocomplete response time requirements
177-
unifier.normalize = false;
178-
unifier.checkInhabited = false;
210+
unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit;
211+
unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit;
179212

180-
if (FFlag::LuauAutocompleteUseLimits)
213+
Subtyping subtyping{
214+
builtinTypes,
215+
NotNull{typeArena},
216+
NotNull{simplifier.get()},
217+
NotNull{&normalizer},
218+
NotNull{&typeFunctionRuntime},
219+
NotNull{&iceReporter}
220+
};
221+
222+
return subtyping.isSubtype(subTy, superTy, scope).isSubtype;
223+
}
224+
else
181225
{
226+
Unifier unifier(NotNull<Normalizer>{&normalizer}, scope, Location(), Variance::Covariant);
227+
228+
// Cost of normalization can be too high for autocomplete response time requirements
229+
unifier.normalize = false;
230+
unifier.checkInhabited = false;
182231
unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit;
183232
unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit;
184-
}
185233

186-
return unifier.canUnify(subTy, superTy).empty();
234+
return unifier.canUnify(subTy, superTy).empty();
235+
}
187236
}
188237
}
189238

@@ -209,10 +258,10 @@ static TypeCorrectKind checkTypeCorrectKind(
209258

210259
TypeId expectedType = follow(*typeAtPosition);
211260

212-
auto checkFunctionType = [typeArena, builtinTypes, moduleScope, &expectedType](const FunctionType* ftv)
261+
auto checkFunctionType = [typeArena, builtinTypes, moduleScope, &expectedType, &module](const FunctionType* ftv)
213262
{
214263
if (std::optional<TypeId> firstRetTy = first(ftv->retTypes))
215-
return checkTypeMatch(*firstRetTy, expectedType, moduleScope, typeArena, builtinTypes);
264+
return checkTypeMatch(module, *firstRetTy, expectedType, moduleScope, typeArena, builtinTypes);
216265

217266
return false;
218267
};
@@ -235,7 +284,7 @@ static TypeCorrectKind checkTypeCorrectKind(
235284
}
236285
}
237286

238-
return checkTypeMatch(ty, expectedType, moduleScope, typeArena, builtinTypes) ? TypeCorrectKind::Correct : TypeCorrectKind::None;
287+
return checkTypeMatch(module, ty, expectedType, moduleScope, typeArena, builtinTypes) ? TypeCorrectKind::Correct : TypeCorrectKind::None;
239288
}
240289

241290
enum class PropIndexType
@@ -286,7 +335,7 @@ static void autocompleteProps(
286335
// When called with '.', but declared with 'self', it is considered invalid if first argument is compatible
287336
if (std::optional<TypeId> firstArgTy = first(ftv->argTypes))
288337
{
289-
if (checkTypeMatch(rootTy, *firstArgTy, NotNull{module.getModuleScope().get()}, typeArena, builtinTypes))
338+
if (checkTypeMatch(module, rootTy, *firstArgTy, NotNull{module.getModuleScope().get()}, typeArena, builtinTypes))
290339
return calledWithSelf;
291340
}
292341

@@ -1714,6 +1763,7 @@ AutocompleteResult autocomplete_(
17141763
StringCompletionCallback callback
17151764
)
17161765
{
1766+
LUAU_TIMETRACE_SCOPE("Luau::autocomplete_", "AutocompleteCore");
17171767
AstNode* node = ancestry.back();
17181768

17191769
AstExprConstantNil dummy{Location{}};

Analysis/src/Constraint.cpp

+9-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#include "Luau/Constraint.h"
44
#include "Luau/VisitType.h"
55

6+
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
7+
68
namespace Luau
79
{
810

@@ -111,14 +113,20 @@ DenseHashSet<TypeId> Constraint::getMaybeMutatedFreeTypes() const
111113
{
112114
rci.traverse(fchc->argsPack);
113115
}
116+
else if (auto fcc = get<FunctionCallConstraint>(*this); fcc && FFlag::DebugLuauGreedyGeneralization)
117+
{
118+
rci.traverse(fcc->fn);
119+
rci.traverse(fcc->argsPack);
120+
}
114121
else if (auto ptc = get<PrimitiveTypeConstraint>(*this))
115122
{
116123
rci.traverse(ptc->freeType);
117124
}
118125
else if (auto hpc = get<HasPropConstraint>(*this))
119126
{
120127
rci.traverse(hpc->resultType);
121-
// `HasPropConstraints` should not mutate `subjectType`.
128+
if (FFlag::DebugLuauGreedyGeneralization)
129+
rci.traverse(hpc->subjectType);
122130
}
123131
else if (auto hic = get<HasIndexerConstraint>(*this))
124132
{

0 commit comments

Comments
 (0)