Skip to content

Commit

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

* Light update this week, mostly fast flag cleanups.

### New Solver

* Rename flag to enable new solver from
`DebugLuauDeferredConstraintResolution` to `LuauSolverV2`
* Added support for magic functions for the new type checker (as opposed
to the type inference component)
* Improved handling of `string.format` with magic function improvements
* Cleaning up some of the reported errors by the new type checker
* Minor refactoring of `TypeChecker2.cpp` that happens to make the diff
very hard to read.

---

### Internal Contributors

Co-authored-by: Aaron Weiss <[email protected]>
Co-authored-by: Andy Friesen <[email protected]>
Co-authored-by: Vighnesh Vijay <[email protected]>
Co-authored-by: Vyacheslav Egorov <[email protected]>

---------

Co-authored-by: Alexander McCord <[email protected]>
Co-authored-by: Andy Friesen <[email protected]>
Co-authored-by: Vighnesh <[email protected]>
Co-authored-by: Aviral Goel <[email protected]>
Co-authored-by: David Cope <[email protected]>
Co-authored-by: Lily Brown <[email protected]>
Co-authored-by: Vyacheslav Egorov <[email protected]>
Co-authored-by: Junseo Yoo <[email protected]>
  • Loading branch information
9 people authored Aug 30, 2024
1 parent d518d14 commit b23d434
Show file tree
Hide file tree
Showing 92 changed files with 3,555 additions and 3,471 deletions.
3 changes: 2 additions & 1 deletion Analysis/include/Luau/BuiltinDefinitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ struct Frontend;
struct GlobalTypes;
struct TypeChecker;
struct TypeArena;
struct Subtyping;

void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeCheckForAutocomplete = false);
TypeId makeUnion(TypeArena& arena, std::vector<TypeId>&& types);
Expand Down Expand Up @@ -65,7 +66,7 @@ TypeId makeFunction( // Polymorphic
void attachMagicFunction(TypeId ty, MagicFunction fn);
void attachDcrMagicFunction(TypeId ty, DcrMagicFunction fn);
void attachDcrMagicRefinement(TypeId ty, DcrMagicRefinement fn);

void attachDcrMagicFunctionTypeCheck(TypeId ty, DcrMagicFunctionTypeCheck fn);
Property makeProperty(TypeId ty, std::optional<std::string> documentationSymbol = std::nullopt);
void assignPropDocumentationSymbols(TableType::Props& props, const std::string& baseName);

Expand Down
2 changes: 1 addition & 1 deletion Analysis/include/Luau/Set.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include "Luau/Common.h"
#include "Luau/DenseHash.h"

LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
LUAU_FASTFLAG(LuauSolverV2)

namespace Luau
{
Expand Down
22 changes: 20 additions & 2 deletions Analysis/include/Luau/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ using ScopePtr = std::shared_ptr<Scope>;

struct TypeFunction;
struct Constraint;
struct Subtyping;
struct TypeChecker2;

/**
* There are three kinds of type variables:
Expand Down Expand Up @@ -289,16 +291,24 @@ struct MagicFunctionCallContext
};

using DcrMagicFunction = std::function<bool(MagicFunctionCallContext)>;

struct MagicRefinementContext
{
NotNull<Scope> scope;
const class AstExprCall* callSite;
std::vector<std::optional<TypeId>> discriminantTypes;
};

using DcrMagicRefinement = void (*)(const MagicRefinementContext&);
struct MagicFunctionTypeCheckContext
{
NotNull<TypeChecker2> typechecker;
NotNull<BuiltinTypes> builtinTypes;
const class AstExprCall* callSite;
TypePackId arguments;
NotNull<Scope> checkScope;
};

using DcrMagicRefinement = void (*)(const MagicRefinementContext&);
using DcrMagicFunctionTypeCheck = std::function<void(const MagicFunctionTypeCheckContext&)>;
struct FunctionType
{
// Global monomorphic function
Expand Down Expand Up @@ -359,6 +369,14 @@ struct FunctionType
MagicFunction magicFunction = nullptr;
DcrMagicFunction dcrMagicFunction = nullptr;
DcrMagicRefinement dcrMagicRefinement = nullptr;

// 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
// have types that can only be decided after parsing the format string and unifying
// with the passed in values, but the correctness of the call can only be decided after
// all the types have been finalized.
DcrMagicFunctionTypeCheck dcrMagicTypeCheck = nullptr;

bool hasSelf;
// `hasNoFreeOrGenericTypes` should be true if and only if the type does not have any free or generic types present inside it.
// this flag is used as an optimization to exit early from procedures that manipulate free or generic types.
Expand Down
194 changes: 194 additions & 0 deletions Analysis/include/Luau/TypeChecker2.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@

#pragma once

#include "Luau/Error.h"
#include "Luau/NotNull.h"
#include "Luau/Common.h"
#include "Luau/TypeUtils.h"
#include "Luau/Type.h"
#include "Luau/TypeFwd.h"
#include "Luau/TypeOrPack.h"
#include "Luau/Normalize.h"
#include "Luau/Subtyping.h"

namespace Luau
{
Expand All @@ -13,6 +21,42 @@ struct TypeCheckLimits;
struct UnifierSharedState;
struct SourceModule;
struct Module;
struct InternalErrorReporter;
struct Scope;
struct PropertyType;
struct PropertyTypes;
struct StackPusher;

struct Reasonings
{
// the list of reasons
std::vector<std::string> reasons;

// this should be true if _all_ of the reasons have an error suppressing type, and false otherwise.
bool suppressed;

std::string toString()
{
// DenseHashSet ordering is entirely undefined, so we want to
// sort the reasons here to achieve a stable error
// stringification.
std::sort(reasons.begin(), reasons.end());
std::string allReasons;
bool first = true;
for (const std::string& reason : reasons)
{
if (first)
first = false;
else
allReasons += "\n\t";

allReasons += reason;
}

return allReasons;
}
};


void check(
NotNull<BuiltinTypes> builtinTypes,
Expand All @@ -23,4 +67,154 @@ void check(
Module* module
);

struct TypeChecker2
{
NotNull<BuiltinTypes> builtinTypes;
DcrLogger* logger;
const NotNull<TypeCheckLimits> limits;
const NotNull<InternalErrorReporter> ice;
const SourceModule* sourceModule;
Module* module;

TypeContext typeContext = TypeContext::Default;
std::vector<NotNull<Scope>> stack;
std::vector<TypeId> functionDeclStack;

DenseHashSet<TypeId> seenTypeFunctionInstances{nullptr};

Normalizer normalizer;
Subtyping _subtyping;
NotNull<Subtyping> subtyping;

TypeChecker2(
NotNull<BuiltinTypes> builtinTypes,
NotNull<UnifierSharedState> unifierState,
NotNull<TypeCheckLimits> limits,
DcrLogger* logger,
const SourceModule* sourceModule,
Module* module
);

void visit(AstStatBlock* block);
void reportError(TypeErrorData data, const Location& location);
Reasonings explainReasonings(TypeId subTy, TypeId superTy, Location location, const SubtypingResult& r);
Reasonings explainReasonings(TypePackId subTp, TypePackId superTp, Location location, const SubtypingResult& r);

private:
static bool allowsNoReturnValues(const TypePackId tp);
static Location getEndLocation(const AstExprFunction* function);
bool isErrorCall(const AstExprCall* call);
bool hasBreak(AstStat* node);
const AstStat* getFallthrough(const AstStat* node);
std::optional<StackPusher> pushStack(AstNode* node);
void checkForInternalTypeFunction(TypeId ty, Location location);
TypeId checkForTypeFunctionInhabitance(TypeId instance, Location location);
TypePackId lookupPack(AstExpr* expr);
TypeId lookupType(AstExpr* expr);
TypeId lookupAnnotation(AstType* annotation);
std::optional<TypePackId> lookupPackAnnotation(AstTypePack* annotation);
TypeId lookupExpectedType(AstExpr* expr);
TypePackId lookupExpectedPack(AstExpr* expr, TypeArena& arena);
TypePackId reconstructPack(AstArray<AstExpr*> exprs, TypeArena& arena);
Scope* findInnermostScope(Location location);
void visit(AstStat* stat);
void visit(AstStatIf* ifStatement);
void visit(AstStatWhile* whileStatement);
void visit(AstStatRepeat* repeatStatement);
void visit(AstStatBreak*);
void visit(AstStatContinue*);
void visit(AstStatReturn* ret);
void visit(AstStatExpr* expr);
void visit(AstStatLocal* local);
void visit(AstStatFor* forStatement);
void visit(AstStatForIn* forInStatement);
std::optional<TypeId> getBindingType(AstExpr* expr);
void reportErrorsFromAssigningToNever(AstExpr* lhs, TypeId rhsType);
void visit(AstStatAssign* assign);
void visit(AstStatCompoundAssign* stat);
void visit(AstStatFunction* stat);
void visit(AstStatLocalFunction* stat);
void visit(const AstTypeList* typeList);
void visit(AstStatTypeAlias* stat);
void visit(AstStatTypeFunction* stat);
void visit(AstTypeList types);
void visit(AstStatDeclareFunction* stat);
void visit(AstStatDeclareGlobal* stat);
void visit(AstStatDeclareClass* stat);
void visit(AstStatError* stat);
void visit(AstExpr* expr, ValueContext context);
void visit(AstExprGroup* expr, ValueContext context);
void visit(AstExprConstantNil* expr);
void visit(AstExprConstantBool* expr);
void visit(AstExprConstantNumber* expr);
void visit(AstExprConstantString* expr);
void visit(AstExprLocal* expr);
void visit(AstExprGlobal* expr);
void visit(AstExprVarargs* expr);
void visitCall(AstExprCall* call);
void visit(AstExprCall* call);
std::optional<TypeId> tryStripUnionFromNil(TypeId ty);
TypeId stripFromNilAndReport(TypeId ty, const Location& location);
void visitExprName(AstExpr* expr, Location location, const std::string& propName, ValueContext context, TypeId astIndexExprTy);
void visit(AstExprIndexName* indexName, ValueContext context);
void indexExprMetatableHelper(AstExprIndexExpr* indexExpr, const MetatableType* metaTable, TypeId exprType, TypeId indexType);
void visit(AstExprIndexExpr* indexExpr, ValueContext context);
void visit(AstExprFunction* fn);
void visit(AstExprTable* expr);
void visit(AstExprUnary* expr);
TypeId visit(AstExprBinary* expr, AstNode* overrideKey = nullptr);
void visit(AstExprTypeAssertion* expr);
void visit(AstExprIfElse* expr);
void visit(AstExprInterpString* interpString);
void visit(AstExprError* expr);
TypeId flattenPack(TypePackId pack);
void visitGenerics(AstArray<AstGenericType> generics, AstArray<AstGenericTypePack> genericPacks);
void visit(AstType* ty);
void visit(AstTypeReference* ty);
void visit(AstTypeTable* table);
void visit(AstTypeFunction* ty);
void visit(AstTypeTypeof* ty);
void visit(AstTypeUnion* ty);
void visit(AstTypeIntersection* ty);
void visit(AstTypePack* pack);
void visit(AstTypePackExplicit* tp);
void visit(AstTypePackVariadic* tp);
void visit(AstTypePackGeneric* tp);

template<typename TID>
Reasonings explainReasonings_(TID subTy, TID superTy, Location location, const SubtypingResult& r);

void explainError(TypeId subTy, TypeId superTy, Location location, const SubtypingResult& result);
void explainError(TypePackId subTy, TypePackId superTy, Location location, const SubtypingResult& result);
bool testIsSubtype(TypeId subTy, TypeId superTy, Location location);
bool testIsSubtype(TypePackId subTy, TypePackId superTy, Location location);
void reportError(TypeError e);
void reportErrors(ErrorVec errors);
PropertyTypes lookupProp(
const NormalizedType* norm,
const std::string& prop,
ValueContext context,
const Location& location,
TypeId astIndexExprType,
std::vector<TypeError>& errors
);
// If the provided type does not have the named property, report an error.
void checkIndexTypeFromType(TypeId tableTy, const std::string& prop, ValueContext context, const Location& location, TypeId astIndexExprType);
PropertyType hasIndexTypeFromType(
TypeId ty,
const std::string& prop,
ValueContext context,
const Location& location,
DenseHashSet<TypeId>& seen,
TypeId astIndexExprType,
std::vector<TypeError>& errors
);

void diagnoseMissingTableKey(UnknownProperty* utk, TypeErrorData& data) const;
bool isErrorSuppressing(Location loc, TypeId ty);
bool isErrorSuppressing(Location loc1, TypeId ty1, Location loc2, TypeId ty2);
bool isErrorSuppressing(Location loc, TypePackId tp);
bool isErrorSuppressing(Location loc1, TypePackId tp1, Location loc2, TypePackId tp2);
};

} // namespace Luau
10 changes: 5 additions & 5 deletions Analysis/include/Luau/VisitType.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

LUAU_FASTINT(LuauVisitRecursionLimit)
LUAU_FASTFLAG(LuauBoundLazyTypes2)
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
LUAU_FASTFLAG(LuauSolverV2)

namespace Luau
{
Expand Down Expand Up @@ -226,12 +226,12 @@ struct GenericTypeVisitor
}
else if (auto ftv = get<FreeType>(ty))
{
if (FFlag::DebugLuauDeferredConstraintResolution)
if (FFlag::LuauSolverV2)
{
if (visit(ty, *ftv))
{
// TODO: Replace these if statements with assert()s when we
// delete FFlag::DebugLuauDeferredConstraintResolution.
// delete FFlag::LuauSolverV2.
//
// When the old solver is used, these pointers are always
// unused. When the new solver is used, they are never null.
Expand Down Expand Up @@ -276,7 +276,7 @@ struct GenericTypeVisitor
{
for (auto& [_name, prop] : ttv->props)
{
if (FFlag::DebugLuauDeferredConstraintResolution)
if (FFlag::LuauSolverV2)
{
if (auto ty = prop.readTy)
traverse(*ty);
Expand Down Expand Up @@ -314,7 +314,7 @@ struct GenericTypeVisitor
{
for (const auto& [name, prop] : ctv->props)
{
if (FFlag::DebugLuauDeferredConstraintResolution)
if (FFlag::LuauSolverV2)
{
if (auto ty = prop.readTy)
traverse(*ty);
Expand Down
2 changes: 1 addition & 1 deletion Analysis/src/AnyTypeSummary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -692,7 +692,7 @@ bool AnyTypeSummary::containsAny(TypeId typ)
{
for (auto& [_name, prop] : ty->props)
{
if (FFlag::DebugLuauDeferredConstraintResolution)
if (FFlag::LuauSolverV2)
{
if (auto newT = follow(prop.readTy))
{
Expand Down
9 changes: 4 additions & 5 deletions Analysis/src/AstQuery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@

#include <algorithm>

LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
LUAU_FASTFLAGVARIABLE(LuauFixBindingForGlobalPos, false);
LUAU_FASTFLAG(LuauSolverV2)

namespace Luau
{
Expand Down Expand Up @@ -327,7 +326,7 @@ static std::optional<AstStatLocal*> findBindingLocalStatement(const SourceModule
{
// Bindings coming from global sources (e.g., definition files) have a zero position.
// They cannot be defined from a local statement
if (FFlag::LuauFixBindingForGlobalPos && binding.location == Location{{0, 0}, {0, 0}})
if (binding.location == Location{{0, 0}, {0, 0}})
return std::nullopt;

std::vector<AstNode*> nodes = findAstAncestryOfPosition(source, binding.location.begin);
Expand Down Expand Up @@ -531,7 +530,7 @@ std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const Source
{
if (auto propIt = ttv->props.find(indexName->index.value); propIt != ttv->props.end())
{
if (FFlag::DebugLuauDeferredConstraintResolution)
if (FFlag::LuauSolverV2)
{
if (auto ty = propIt->second.readTy)
return checkOverloadedDocumentationSymbol(module, *ty, parentExpr, propIt->second.documentationSymbol);
Expand All @@ -544,7 +543,7 @@ std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const Source
{
if (auto propIt = ctv->props.find(indexName->index.value); propIt != ctv->props.end())
{
if (FFlag::DebugLuauDeferredConstraintResolution)
if (FFlag::LuauSolverV2)
{
if (auto ty = propIt->second.readTy)
return checkOverloadedDocumentationSymbol(module, *ty, parentExpr, propIt->second.documentationSymbol);
Expand Down
Loading

0 comments on commit b23d434

Please sign in to comment.