Skip to content

Commit

Permalink
Sync to upstream/release/555 (#768)
Browse files Browse the repository at this point in the history
* Type mismatch errors now mention if unification failed in covariant or
invariant context, to explain why sometimes derived class can't be
converted to base class or why `T` can't be converted into `T?` and so
on
* Class type indexing is no longer an error in non-strict mode (still an
error in strict mode)
* Fixed cyclic type packs not being displayed in the type
* Added an error when unrelated types are compared with `==`/`~=`
* Fixed false positive errors involving sub-type tests an `never` type
* Fixed miscompilation of multiple assignment statements (Fixes
#754)
* Type inference stability improvements
  • Loading branch information
vegorov-rbx authored Dec 2, 2022
1 parent 91302d1 commit 59ae47d
Show file tree
Hide file tree
Showing 72 changed files with 2,303 additions and 1,026 deletions.
1 change: 1 addition & 0 deletions Analysis/include/Luau/BuiltinDefinitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ TypeId makeFunction( // Polymorphic

void attachMagicFunction(TypeId ty, MagicFunction fn);
void attachDcrMagicFunction(TypeId ty, DcrMagicFunction fn);
void attachDcrMagicRefinement(TypeId ty, DcrMagicRefinement fn);

Property makeProperty(TypeId ty, std::optional<std::string> documentationSymbol = std::nullopt);
void assignPropDocumentationSymbols(TableTypeVar::Props& props, const std::string& baseName);
Expand Down
4 changes: 3 additions & 1 deletion Analysis/include/Luau/Connective.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@

#include "Luau/Def.h"
#include "Luau/TypedAllocator.h"
#include "Luau/TypeVar.h"
#include "Luau/Variant.h"

#include <memory>

namespace Luau
{

struct TypeVar;
using TypeId = const TypeVar*;

struct Negation;
struct Conjunction;
struct Disjunction;
Expand Down
6 changes: 5 additions & 1 deletion Analysis/include/Luau/Constraint.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,15 @@ struct SetPropConstraint
TypeId propType;
};

// result ~ if isSingleton D then ~D else unknown where D = discriminantType
// if negation:
// result ~ if isSingleton D then ~D else unknown where D = discriminantType
// if not negation:
// result ~ if isSingleton D then D else unknown where D = discriminantType
struct SingletonOrTopTypeConstraint
{
TypeId resultType;
TypeId discriminantType;
bool negated;
};

using ConstraintV = Variant<SubtypeConstraint, PackSubtypeConstraint, GeneralizationConstraint, InstantiationConstraint, UnaryConstraint,
Expand Down
4 changes: 2 additions & 2 deletions Analysis/include/Luau/ConstraintGraphBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include "Luau/Ast.h"
#include "Luau/Connective.h"
#include "Luau/Constraint.h"
#include "Luau/DataFlowGraphBuilder.h"
#include "Luau/DataFlowGraph.h"
#include "Luau/Module.h"
#include "Luau/ModuleResolver.h"
#include "Luau/NotNull.h"
Expand Down Expand Up @@ -215,7 +215,7 @@ struct ConstraintGraphBuilder
ScopePtr bodyScope;
};

FunctionSignature checkFunctionSignature(const ScopePtr& parent, AstExprFunction* fn);
FunctionSignature checkFunctionSignature(const ScopePtr& parent, AstExprFunction* fn, std::optional<TypeId> expectedType = {});

/**
* Checks the body of a function expression.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,14 @@ struct DataFlowGraphBuilder
struct InternalErrorReporter* handle;
std::vector<std::unique_ptr<DfgScope>> scopes;

// Does not belong in DataFlowGraphBuilder, but the old solver allows properties to escape the scope they were defined in,
// so we will need to be able to emulate this same behavior here too. We can kill this once we have better flow sensitivity.
DenseHashMap<const Def*, std::unordered_map<std::string, const Def*>> props{nullptr};

DfgScope* childScope(DfgScope* scope);

std::optional<DefId> use(DfgScope* scope, Symbol symbol, AstExpr* e);
DefId use(DefId def, AstExprIndexName* e);

void visit(DfgScope* scope, AstStatBlock* b);
void visitBlockWithoutChildScope(DfgScope* scope, AstStatBlock* b);
Expand Down
53 changes: 33 additions & 20 deletions Analysis/include/Luau/Def.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,38 @@
#include "Luau/TypedAllocator.h"
#include "Luau/Variant.h"

#include <string>
#include <optional>

namespace Luau
{

using Def = Variant<struct Undefined, struct Phi>;

/**
* We statically approximate a value at runtime using a symbolic value, which we call a Def.
*
* DataFlowGraphBuilder will allocate these defs as a stand-in for some Luau values, and bind them to places that
* can hold a Luau value, and then observes how those defs will commute as it statically evaluate the program.
*
* It must also be noted that defs are a cyclic graph, so it is not safe to recursively traverse into it expecting it to terminate.
*/
struct Def;
using DefId = NotNull<const Def>;

struct FieldMetadata
{
DefId parent;
std::string propName;
};

/**
* A "single-object" value.
* A cell is a "single-object" value.
*
* Leaky implementation note: sometimes "multiple-object" values, but none of which were interesting enough to warrant creating a phi node instead.
* That can happen because there's no point in creating a phi node that points to either resultant in `if math.random() > 0.5 then 5 else "hello"`.
* This might become of utmost importance if we wanted to do some backward reasoning, e.g. if `5` is taken, then `cond` must be `truthy`.
*/
struct Undefined
struct Cell
{
std::optional<struct FieldMetadata> field;
};

/**
* A phi node is a union of defs.
* A phi node is a union of cells.
*
* We need this because we're statically evaluating a program, and sometimes a place may be assigned with
* different defs, and when that happens, we need a special data type that merges in all the defs
* different cells, and when that happens, we need a special data type that merges in all the cells
* that will flow into that specific place. For example, consider this simple program:
*
* ```
Expand All @@ -56,23 +57,35 @@ struct Phi
std::vector<DefId> operands;
};

template<typename T>
T* getMutable(DefId def)
/**
* We statically approximate a value at runtime using a symbolic value, which we call a Def.
*
* DataFlowGraphBuilder will allocate these defs as a stand-in for some Luau values, and bind them to places that
* can hold a Luau value, and then observes how those defs will commute as it statically evaluate the program.
*
* It must also be noted that defs are a cyclic graph, so it is not safe to recursively traverse into it expecting it to terminate.
*/
struct Def
{
return get_if<T>(def.get());
}
using V = Variant<struct Cell, struct Phi>;

V v;
};

template<typename T>
const T* get(DefId def)
{
return getMutable<T>(def);
return get_if<T>(&def->v);
}

struct DefArena
{
TypedAllocator<Def> allocator;

DefId freshDef();
DefId freshCell();
DefId freshCell(DefId parent, const std::string& prop);
// TODO: implement once we have cases where we need to merge in definitions
// DefId phi(const std::vector<DefId>& defs);
};

} // namespace Luau
46 changes: 37 additions & 9 deletions Analysis/include/Luau/Error.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,31 @@
#include "Luau/Variant.h"
#include "Luau/TypeArena.h"

LUAU_FASTFLAG(LuauIceExceptionInheritanceChange)

namespace Luau
{
struct TypeError;


struct TypeMismatch
{
enum Context
{
CovariantContext,
InvariantContext
};

TypeMismatch() = default;
TypeMismatch(TypeId wantedType, TypeId givenType);
TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason);
TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason, std::optional<TypeError> error);

TypeMismatch(TypeId wantedType, TypeId givenType, Context context);
TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason, Context context);
TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason, std::optional<TypeError> error, Context context);

TypeId wantedType = nullptr;
TypeId givenType = nullptr;
Context context = CovariantContext;

std::string reason;
std::shared_ptr<TypeError> error;
Expand Down Expand Up @@ -312,19 +322,41 @@ struct TypePackMismatch
bool operator==(const TypePackMismatch& rhs) const;
};

struct DynamicPropertyLookupOnClassesUnsafe
{
TypeId ty;

bool operator==(const DynamicPropertyLookupOnClassesUnsafe& rhs) const;
};

using TypeErrorData = Variant<TypeMismatch, UnknownSymbol, UnknownProperty, NotATable, CannotExtendTable, OnlyTablesCanHaveMethods,
DuplicateTypeDefinition, CountMismatch, FunctionDoesNotTakeSelf, FunctionRequiresSelf, OccursCheckFailed, UnknownRequire,
IncorrectGenericParameterCount, SyntaxError, CodeTooComplex, UnificationTooComplex, UnknownPropButFoundLikeProp, GenericError, InternalError,
CannotCallNonFunction, ExtraInformation, DeprecatedApiUsed, ModuleHasCyclicDependency, IllegalRequire, FunctionExitsWithoutReturning,
DuplicateGenericParameter, CannotInferBinaryOperation, MissingProperties, SwappedGenericTypeParameter, OptionalValueAccess, MissingUnionProperty,
TypesAreUnrelated, NormalizationTooComplex, TypePackMismatch>;
TypesAreUnrelated, NormalizationTooComplex, TypePackMismatch, DynamicPropertyLookupOnClassesUnsafe>;

struct TypeErrorSummary
{
Location location;
ModuleName moduleName;
int code;

TypeErrorSummary(const Location& location, const ModuleName& moduleName, int code)
: location(location)
, moduleName(moduleName)
, code(code)
{
}
};

struct TypeError
{
Location location;
ModuleName moduleName;
TypeErrorData data;

static int minCode();
int code() const;

TypeError() = default;
Expand All @@ -342,6 +374,8 @@ struct TypeError
}

bool operator==(const TypeError& rhs) const;

TypeErrorSummary summary() const;
};

template<typename T>
Expand Down Expand Up @@ -406,10 +440,4 @@ class InternalCompilerError : public std::exception
const std::optional<Location> location;
};

// These two function overloads only exist to facilitate fast flagging a change to InternalCompilerError
// Both functions can be removed when FFlagLuauIceExceptionInheritanceChange is removed and calling code
// can directly throw InternalCompilerError.
[[noreturn]] void throwRuntimeError(const std::string& message);
[[noreturn]] void throwRuntimeError(const std::string& message, const std::string& moduleName);

} // namespace Luau
6 changes: 6 additions & 0 deletions Analysis/include/Luau/Normalize.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ struct NormalizedFunctionType
struct NormalizedType;
using NormalizedTyvars = std::unordered_map<TypeId, std::unique_ptr<NormalizedType>>;

bool isInhabited_DEPRECATED(const NormalizedType& norm);

// A normalized type is either any, unknown, or one of the form P | T | F | G where
// * P is a union of primitive types (including singletons, classes and the error type)
// * T is a union of table types
Expand Down Expand Up @@ -328,6 +330,10 @@ class Normalizer
bool intersectNormals(NormalizedType& here, const NormalizedType& there, int ignoreSmallerTyvars = -1);
bool intersectNormalWithTy(NormalizedType& here, TypeId there);

// Check for inhabitance
bool isInhabited(TypeId ty, std::unordered_set<TypeId> seen = {});
bool isInhabited(const NormalizedType* norm, std::unordered_set<TypeId> seen = {});

// -------- Convert back from a normalized type to a type
TypeId typeFromNormal(const NormalizedType& norm);
};
Expand Down
14 changes: 14 additions & 0 deletions Analysis/include/Luau/NotNull.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,20 @@ struct NotNull
return ptr;
}

template<typename U>
bool operator==(NotNull<U> other) const noexcept
{
return get() == other.get();
}

template<typename U>
bool operator!=(NotNull<U> other) const noexcept
{
return get() != other.get();
}

operator bool() const noexcept = delete;

T& operator[](int) = delete;

T& operator+(int) = delete;
Expand Down
19 changes: 1 addition & 18 deletions Analysis/include/Luau/RecursionCounter.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,6 @@ struct RecursionLimitException : public InternalCompilerError
RecursionLimitException()
: InternalCompilerError("Internal recursion counter limit exceeded")
{
LUAU_ASSERT(FFlag::LuauIceExceptionInheritanceChange);
}
};

struct RecursionLimitException_DEPRECATED : public std::exception
{
const char* what() const noexcept
{
LUAU_ASSERT(!FFlag::LuauIceExceptionInheritanceChange);
return "Internal recursion counter limit exceeded";
}
};

Expand Down Expand Up @@ -53,14 +43,7 @@ struct RecursionLimiter : RecursionCounter
{
if (limit > 0 && *count > limit)
{
if (FFlag::LuauIceExceptionInheritanceChange)
{
throw RecursionLimitException();
}
else
{
throw RecursionLimitException_DEPRECATED();
}
throw RecursionLimitException();
}
}
};
Expand Down
4 changes: 1 addition & 3 deletions Analysis/include/Luau/ToString.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ struct ToStringOptions
bool hideTableKind = false; // If true, all tables will be surrounded with plain '{}'
bool hideNamedFunctionTypeParameters = false; // If true, type parameters of functions will be hidden at top-level.
bool hideFunctionSelfArgument = false; // If true, `self: X` will be omitted from the function signature if the function has self
bool indent = false;
bool DEPRECATED_indent = false; // TODO Deprecated field, prune when clipping flag FFlagLuauLineBreaksDeterminIndents
size_t maxTableLength = size_t(FInt::LuauTableTypeMaximumStringifierLength); // Only applied to TableTypeVars
size_t maxTypeLength = size_t(FInt::LuauTypeMaximumStringifierLength);
ToStringNameMap nameMap;
Expand Down Expand Up @@ -90,8 +90,6 @@ inline std::string toString(const Constraint& c)
return toString(c, ToStringOptions{});
}

std::string toString(const LValue& lvalue);

std::string toString(const TypeVar& tv, ToStringOptions& opts);
std::string toString(const TypePackVar& tp, ToStringOptions& opts);

Expand Down
2 changes: 2 additions & 0 deletions Analysis/include/Luau/TxnLog.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ struct TxnLog
// If both logs talk about the same type, pack, or table, the rhs takes
// priority.
void concat(TxnLog rhs);
void concatAsIntersections(TxnLog rhs, NotNull<TypeArena> arena);
void concatAsUnion(TxnLog rhs, NotNull<TypeArena> arena);

// Commits the TxnLog, rebinding all type pointers to their pending states.
// Clears the TxnLog afterwards.
Expand Down
7 changes: 0 additions & 7 deletions Analysis/include/Luau/TypeInfer.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,9 @@ class TimeLimitError : public InternalCompilerError
explicit TimeLimitError(const std::string& moduleName)
: InternalCompilerError("Typeinfer failed to complete in allotted time", moduleName)
{
LUAU_ASSERT(FFlag::LuauIceExceptionInheritanceChange);
}
};

class TimeLimitError_DEPRECATED : public std::exception
{
public:
virtual const char* what() const throw();
};

// All TypeVars are retained via Environment::typeVars. All TypeIds
// within a program are borrowed pointers into this set.
struct TypeChecker
Expand Down
6 changes: 3 additions & 3 deletions Analysis/include/Luau/TypeUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ std::optional<TypeId> getIndexTypeFromType(const ScopePtr& scope, ErrorVec& erro
// Returns the minimum and maximum number of types the argument list can accept.
std::pair<size_t, std::optional<size_t>> getParameterExtents(const TxnLog* log, TypePackId tp, bool includeHiddenVariadics = false);

// "Render" a type pack out to an array of a given length. Expands variadics and
// various other things to get there.
std::vector<TypeId> flatten(TypeArena& arena, NotNull<SingletonTypes> singletonTypes, TypePackId pack, size_t length);
// Extend the provided pack to at least `length` types.
// Returns a temporary TypePack that contains those types plus a tail.
TypePack extendTypePack(TypeArena& arena, NotNull<SingletonTypes> singletonTypes, TypePackId pack, size_t length);

/**
* Reduces a union by decomposing to the any/error type if it appears in the
Expand Down
Loading

0 comments on commit 59ae47d

Please sign in to comment.