Skip to content

Commit

Permalink
Sync to upstream/release/662 (#1681)
Browse files Browse the repository at this point in the history
## What's new

This update brings improvements to the new type solver, roundtrippable
AST parsing mode and closes multiple issues reported in this repository.

* `require` dependency tracing for non-string requires now supports `()`
groups in expressions and types as well as an ability to type annotate a
value with a `typeof` of a different module path
* Fixed rare misaligned memory access in Compiler/Typechecker on 32 bit
platforms (Closes #1572)

## New Solver

* Fixed crash/UB in subtyping of type packs (Closes #1449)
* Fixed incorrect type errors when calling `debug.info` (Closes #1534
and Resolves #966)
* Fixed incorrect boolean and string equality comparison result in
user-defined type functions (Closes #1623)
* Fixed incorrect class types being produced in user-defined type
functions when multiple classes share the same name (Closes #1639)
* Improved bidirectional typechecking for table literals containing
elements that have not been solved yet (Closes #1641)

## Roundtrippable AST

* Added source information for `AstStatTypeAlias`
* Fixed an issue with `AstTypeGroup` node (added in #1643) producing
invalid AST json. Contained type is now named 'inner' instead of 'type'
* Fixed end location of the `do ... end` statement

---

Internal Contributors:

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]>
  • Loading branch information
6 people authored Feb 21, 2025
1 parent 86bf4ae commit c2e7266
Show file tree
Hide file tree
Showing 51 changed files with 1,047 additions and 290 deletions.
18 changes: 17 additions & 1 deletion Analysis/include/Luau/Constraint.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,21 @@ struct FunctionCheckConstraint
NotNull<DenseHashMap<const AstExpr*, TypeId>> astExpectedTypes;
};

// table_check expectedType exprType
//
// If `expectedType` is a table type and `exprType` is _also_ a table type,
// propogate the member types of `expectedType` into the types of `exprType`.
// This is used to implement bidirectional inference on table assignment.
// Also see: FunctionCheckConstraint.
struct TableCheckConstraint
{
TypeId expectedType;
TypeId exprType;
AstExprTable* table = nullptr;
NotNull<DenseHashMap<const AstExpr*, TypeId>> astTypes;
NotNull<DenseHashMap<const AstExpr*, TypeId>> astExpectedTypes;
};

// prim FreeType ExpectedType PrimitiveType
//
// FreeType is bounded below by the singleton type and above by PrimitiveType
Expand Down Expand Up @@ -273,7 +288,8 @@ using ConstraintV = Variant<
UnpackConstraint,
ReduceConstraint,
ReducePackConstraint,
EqualityConstraint>;
EqualityConstraint,
TableCheckConstraint>;

struct Constraint
{
Expand Down
8 changes: 4 additions & 4 deletions Analysis/include/Luau/ConstraintSolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ struct ConstraintSolver
// A mapping from free types to the number of unresolved constraints that mention them.
DenseHashMap<TypeId, size_t> unresolvedConstraints{{}};

std::unordered_map<NotNull<const Constraint>, DenseHashSet<TypeId>> maybeMutatedFreeTypes;

// Irreducible/uninhabited type functions or type pack functions.
DenseHashSet<const void*> uninhabitedTypeFunctions{{}};

Expand Down Expand Up @@ -201,6 +203,7 @@ struct ConstraintSolver
bool tryDispatch(const NameConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const TypeAliasExpansionConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const FunctionCallConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const TableCheckConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const FunctionCheckConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const PrimitiveTypeConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const HasPropConstraint& c, NotNull<const Constraint> constraint);
Expand Down Expand Up @@ -421,10 +424,7 @@ struct ConstraintSolver

ToStringOptions opts;

void fillInDiscriminantTypes(
NotNull<const Constraint> constraint,
const std::vector<std::optional<TypeId>>& discriminantTypes
);
void fillInDiscriminantTypes(NotNull<const Constraint> constraint, const std::vector<std::optional<TypeId>>& discriminantTypes);
};

void dump(NotNull<Scope> rootScope, struct ToStringOptions& opts);
Expand Down
2 changes: 1 addition & 1 deletion Analysis/include/Luau/EqSatSimplificationImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ struct TTable
std::vector<Id> storage;
};

template <typename L>
template<typename L>
using Node = EqSat::Node<L>;

using EType = EqSat::Language<
Expand Down
6 changes: 2 additions & 4 deletions Analysis/include/Luau/RequireTracer.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,12 @@
namespace Luau
{

class AstStat;
class AstExpr;
class AstNode;
class AstStatBlock;
struct AstLocal;

struct RequireTraceResult
{
DenseHashMap<const AstExpr*, ModuleInfo> exprs{nullptr};
DenseHashMap<const AstNode*, ModuleInfo> exprs{nullptr};

std::vector<std::pair<ModuleName, Location>> requireList;
};
Expand Down
3 changes: 2 additions & 1 deletion Analysis/include/Luau/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,8 @@ struct MagicFunctionTypeCheckContext

struct MagicFunction
{
virtual std::optional<WithPredicate<TypePackId>> handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) = 0;
virtual std::optional<WithPredicate<TypePackId>>
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) = 0;

// Callback to allow custom typechecking of builtin function calls whose argument types
// will only be resolved after constraint solving. For example, the arguments to string.format
Expand Down
5 changes: 4 additions & 1 deletion Analysis/include/Luau/TypeFunctionRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "Luau/Common.h"
#include "Luau/Variant.h"
#include "Luau/TypeFwd.h"

#include <optional>
#include <string>
Expand Down Expand Up @@ -217,7 +218,9 @@ struct TypeFunctionClassType

std::optional<TypeFunctionTypeId> parent;

std::string name;
TypeId classTy;

std::string name_DEPRECATED;
};

struct TypeFunctionGenericType
Expand Down
4 changes: 1 addition & 3 deletions Analysis/include/Luau/TypeFunctionRuntimeBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,14 @@ struct TypeFunctionRuntimeBuilderState
// Invariant: users can not create a new class types -> any class types that get deserialized must have been an argument to the type function
// Using this invariant, whenever a ClassType is serialized, we can put it into this map
// whenever a ClassType is deserialized, we can use this map to return the corresponding value
DenseHashMap<std::string, TypeId> classesSerialized{{}};
DenseHashMap<std::string, TypeId> classesSerialized_DEPRECATED{{}};

// List of errors that occur during serialization/deserialization
// At every iteration of serialization/deserialzation, if this list.size() != 0, we halt the process
std::vector<std::string> errors{};

TypeFunctionRuntimeBuilderState(NotNull<TypeFunctionContext> ctx)
: ctx(ctx)
, classesSerialized({})
, errors({})
{
}
};
Expand Down
1 change: 0 additions & 1 deletion Analysis/include/Luau/UnifierSharedState.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ struct UnifierSharedState
UnifierCounters counters;

bool reentrantTypeReduction = false;

};

struct TypeReductionRentrancyGuard final
Expand Down
2 changes: 1 addition & 1 deletion Analysis/src/AstJsonEncoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1168,7 +1168,7 @@ struct AstJsonEncoder : public AstVisitor
"AstTypeGroup",
[&]()
{
write("type", node->type);
write("inner", node->type);
}
);
return false;
Expand Down
8 changes: 2 additions & 6 deletions Analysis/src/AutocompleteCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
#include <utility>

LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAGVARIABLE(AutocompleteRequirePathSuggestions2)
LUAU_FASTINT(LuauTypeInferIterationLimit)
LUAU_FASTINT(LuauTypeInferRecursionLimit)

Expand Down Expand Up @@ -1521,12 +1520,9 @@ static std::optional<AutocompleteEntryMap> autocompleteStringParams(
{
for (const std::string& tag : funcType->tags)
{
if (FFlag::AutocompleteRequirePathSuggestions2)
if (tag == kRequireTagName && fileResolver)
{
if (tag == kRequireTagName && fileResolver)
{
return convertRequireSuggestionsToAutocompleteEntryMap(fileResolver->getRequireSuggestions(module->name, candidateString));
}
return convertRequireSuggestionsToAutocompleteEntryMap(fileResolver->getRequireSuggestions(module->name, candidateString));
}
if (std::optional<AutocompleteEntryMap> ret = callback(tag, getMethodContainingClass(module, candidate->func), candidateString))
{
Expand Down
68 changes: 36 additions & 32 deletions Analysis/src/BuiltinDefinitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@

LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAGVARIABLE(LuauStringFormatErrorSuppression)
LUAU_FASTFLAG(AutocompleteRequirePathSuggestions2)
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
LUAU_FASTFLAGVARIABLE(LuauFreezeIgnorePersistent)
Expand All @@ -41,68 +40,79 @@ namespace Luau

struct MagicSelect final : MagicFunction
{
std::optional<WithPredicate<TypePackId>> handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
std::optional<WithPredicate<TypePackId>>
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
bool infer(const MagicFunctionCallContext& ctx) override;
};

struct MagicSetMetatable final : MagicFunction
{
std::optional<WithPredicate<TypePackId>> handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
std::optional<WithPredicate<TypePackId>>
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
bool infer(const MagicFunctionCallContext& ctx) override;
};

struct MagicAssert final : MagicFunction
{
std::optional<WithPredicate<TypePackId>> handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
std::optional<WithPredicate<TypePackId>>
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
bool infer(const MagicFunctionCallContext& ctx) override;
};

struct MagicPack final : MagicFunction
{
std::optional<WithPredicate<TypePackId>> handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
std::optional<WithPredicate<TypePackId>>
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
bool infer(const MagicFunctionCallContext& ctx) override;
};

struct MagicRequire final : MagicFunction
{
std::optional<WithPredicate<TypePackId>> handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
std::optional<WithPredicate<TypePackId>>
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
bool infer(const MagicFunctionCallContext& ctx) override;
};

struct MagicClone final : MagicFunction
{
std::optional<WithPredicate<TypePackId>> handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
std::optional<WithPredicate<TypePackId>>
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
bool infer(const MagicFunctionCallContext& ctx) override;
};

struct MagicFreeze final : MagicFunction
{
std::optional<WithPredicate<TypePackId>> handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
std::optional<WithPredicate<TypePackId>>
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
bool infer(const MagicFunctionCallContext& ctx) override;
};

struct MagicFormat final : MagicFunction
{
std::optional<WithPredicate<TypePackId>> handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
std::optional<WithPredicate<TypePackId>>
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
bool infer(const MagicFunctionCallContext& ctx) override;
bool typeCheck(const MagicFunctionTypeCheckContext& ctx) override;
};

struct MagicMatch final : MagicFunction
{
std::optional<WithPredicate<TypePackId>> handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
std::optional<WithPredicate<TypePackId>>
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
bool infer(const MagicFunctionCallContext& ctx) override;
};

struct MagicGmatch final : MagicFunction
{
std::optional<WithPredicate<TypePackId>> handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
std::optional<WithPredicate<TypePackId>>
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
bool infer(const MagicFunctionCallContext& ctx) override;
};

struct MagicFind final : MagicFunction
{
std::optional<WithPredicate<TypePackId>> handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
std::optional<WithPredicate<TypePackId>>
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>) override;
bool infer(const MagicFunctionCallContext& ctx) override;
};

Expand Down Expand Up @@ -454,16 +464,9 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
attachMagicFunction(ttv->props["freeze"].type(), std::make_shared<MagicFreeze>());
}

if (FFlag::AutocompleteRequirePathSuggestions2)
{
TypeId requireTy = getGlobalBinding(globals, "require");
attachTag(requireTy, kRequireTagName);
attachMagicFunction(requireTy, std::make_shared<MagicRequire>());
}
else
{
attachMagicFunction(getGlobalBinding(globals, "require"), std::make_shared<MagicRequire>());
}
TypeId requireTy = getGlobalBinding(globals, "require");
attachTag(requireTy, kRequireTagName);
attachMagicFunction(requireTy, std::make_shared<MagicRequire>());
}

static std::vector<TypeId> parseFormatString(NotNull<BuiltinTypes> builtinTypes, const char* data, size_t size)
Expand Down Expand Up @@ -637,15 +640,15 @@ bool MagicFormat::typeCheck(const MagicFunctionTypeCheckContext& context)
{
switch (shouldSuppressErrors(NotNull{&context.typechecker->normalizer}, actualTy))
{
case ErrorSuppression::Suppress:
break;
case ErrorSuppression::NormalizationFailed:
break;
case ErrorSuppression::DoNotSuppress:
Reasonings reasonings = context.typechecker->explainReasonings(actualTy, expectedTy, location, result);

if (!reasonings.suppressed)
context.typechecker->reportError(TypeMismatch{expectedTy, actualTy, reasonings.toString()}, location);
case ErrorSuppression::Suppress:
break;
case ErrorSuppression::NormalizationFailed:
break;
case ErrorSuppression::DoNotSuppress:
Reasonings reasonings = context.typechecker->explainReasonings(actualTy, expectedTy, location, result);

if (!reasonings.suppressed)
context.typechecker->reportError(TypeMismatch{expectedTy, actualTy, reasonings.toString()}, location);
}
}
else
Expand Down Expand Up @@ -1503,7 +1506,8 @@ static std::optional<TypeId> freezeTable(TypeId inputType, const MagicFunctionCa
return std::nullopt;
}

std::optional<WithPredicate<TypePackId>> MagicFreeze::handleOldSolver(struct TypeChecker &, const std::shared_ptr<struct Scope> &, const class AstExprCall &, WithPredicate<TypePackId>)
std::optional<WithPredicate<TypePackId>> MagicFreeze::
handleOldSolver(struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>)
{
return std::nullopt;
}
Expand Down
4 changes: 4 additions & 0 deletions Analysis/src/Constraint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ DenseHashSet<TypeId> Constraint::getMaybeMutatedFreeTypes() const
{
rci.traverse(rpc->tp);
}
else if (auto tcc = get<TableCheckConstraint>(*this))
{
rci.traverse(tcc->exprType);
}

return types;
}
Expand Down
Loading

0 comments on commit c2e7266

Please sign in to comment.