Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync to upstream/release/505 #216

Merged
merged 14 commits into from Nov 19, 2021
11 changes: 10 additions & 1 deletion Analysis/include/Luau/Documentation.h
Expand Up @@ -12,10 +12,17 @@ namespace Luau
struct FunctionDocumentation;
struct TableDocumentation;
struct OverloadedFunctionDocumentation;
struct BasicDocumentation;

using Documentation = Luau::Variant<std::string, FunctionDocumentation, TableDocumentation, OverloadedFunctionDocumentation>;
using Documentation = Luau::Variant<BasicDocumentation, FunctionDocumentation, TableDocumentation, OverloadedFunctionDocumentation>;
using DocumentationSymbol = std::string;

struct BasicDocumentation
{
std::string documentation;
std::string learnMoreLink;
};

struct FunctionParameterDocumentation
{
std::string name;
Expand All @@ -29,6 +36,7 @@ struct FunctionDocumentation
std::string documentation;
std::vector<FunctionParameterDocumentation> parameters;
std::vector<DocumentationSymbol> returns;
std::string learnMoreLink;
};

struct OverloadedFunctionDocumentation
Expand All @@ -43,6 +51,7 @@ struct TableDocumentation
{
std::string documentation;
Luau::DenseHashMap<std::string, DocumentationSymbol> keys;
std::string learnMoreLink;
};

using DocumentationDatabase = Luau::DenseHashMap<DocumentationSymbol, Documentation>;
Expand Down
11 changes: 7 additions & 4 deletions Analysis/include/Luau/ToString.h
Expand Up @@ -23,10 +23,11 @@ struct ToStringNameMap

struct ToStringOptions
{
bool exhaustive = false; // If true, we produce complete output rather than comprehensible output
bool useLineBreaks = false; // If true, we insert new lines to separate long results such as table entries/metatable.
bool functionTypeArguments = false; // If true, output function type argument names when they are available
bool hideTableKind = false; // If true, all tables will be surrounded with plain '{}'
bool exhaustive = false; // If true, we produce complete output rather than comprehensible output
bool useLineBreaks = false; // If true, we insert new lines to separate long results such as table entries/metatable.
bool functionTypeArguments = false; // If true, output function type argument names when they are available
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.
size_t maxTableLength = size_t(FInt::LuauTableTypeMaximumStringifierLength); // Only applied to TableTypeVars
size_t maxTypeLength = size_t(FInt::LuauTypeMaximumStringifierLength);
std::optional<ToStringNameMap> nameMap;
Expand Down Expand Up @@ -64,6 +65,8 @@ inline std::string toString(TypePackId ty)
std::string toString(const TypeVar& tv, const ToStringOptions& opts = {});
std::string toString(const TypePackVar& tp, const ToStringOptions& opts = {});

std::string toStringNamedFunction(const std::string& prefix, const FunctionTypeVar& ftv, ToStringOptions opts = {});

// It could be useful to see the text representation of a type during a debugging session instead of exploring the content of the class
// These functions will dump the type to stdout and can be evaluated in Watch/Immediate windows or as gdb/lldb expression
void dump(TypeId ty);
Expand Down
24 changes: 16 additions & 8 deletions Analysis/include/Luau/TypeInfer.h
Expand Up @@ -175,10 +175,10 @@ struct TypeChecker
std::vector<std::optional<TypeId>> getExpectedTypesForCall(const std::vector<TypeId>& overloads, size_t argumentCount, bool selfCall);
std::optional<ExprResult<TypePackId>> checkCallOverload(const ScopePtr& scope, const AstExprCall& expr, TypeId fn, TypePackId retPack,
TypePackId argPack, TypePack* args, const std::vector<Location>& argLocations, const ExprResult<TypePackId>& argListResult,
std::vector<TypeId>& overloadsThatMatchArgCount, std::vector<OverloadErrorEntry>& errors);
std::vector<TypeId>& overloadsThatMatchArgCount, std::vector<TypeId>& overloadsThatDont, std::vector<OverloadErrorEntry>& errors);
bool handleSelfCallMismatch(const ScopePtr& scope, const AstExprCall& expr, TypePack* args, const std::vector<Location>& argLocations,
const std::vector<OverloadErrorEntry>& errors);
ExprResult<TypePackId> reportOverloadResolutionError(const ScopePtr& scope, const AstExprCall& expr, TypePackId retPack, TypePackId argPack,
void reportOverloadResolutionError(const ScopePtr& scope, const AstExprCall& expr, TypePackId retPack, TypePackId argPack,
const std::vector<Location>& argLocations, const std::vector<TypeId>& overloads, const std::vector<TypeId>& overloadsThatMatchArgCount,
const std::vector<OverloadErrorEntry>& errors);

Expand Down Expand Up @@ -282,6 +282,14 @@ struct TypeChecker
// Wrapper for merge(l, r, toUnion) but without the lambda junk.
void merge(RefinementMap& l, const RefinementMap& r);

// Produce an "emergency backup type" for recovery from type errors.
// This comes in two flavours, depening on whether or not we can make a good guess
// for an error recovery type.
TypeId errorRecoveryType(TypeId guess);
TypePackId errorRecoveryTypePack(TypePackId guess);
TypeId errorRecoveryType(const ScopePtr& scope);
TypePackId errorRecoveryTypePack(const ScopePtr& scope);

private:
void prepareErrorsForDisplay(ErrorVec& errVec);
void diagnoseMissingTableKey(UnknownProperty* utk, TypeErrorData& data);
Expand All @@ -297,6 +305,10 @@ struct TypeChecker
TypeId freshType(const ScopePtr& scope);
TypeId freshType(TypeLevel level);

// Produce a new singleton type var.
TypeId singletonType(bool value);
TypeId singletonType(std::string value);

// Returns nullopt if the predicate filters down the TypeId to 0 options.
std::optional<TypeId> filterMap(TypeId type, TypeIdPredicate predicate);

Expand Down Expand Up @@ -330,8 +342,8 @@ struct TypeChecker
const std::vector<TypePackId>& typePackParams, const Location& location);

// Note: `scope` must be a fresh scope.
std::pair<std::vector<TypeId>, std::vector<TypePackId>> createGenericTypes(
const ScopePtr& scope, std::optional<TypeLevel> levelOpt, const AstNode& node, const AstArray<AstName>& genericNames, const AstArray<AstName>& genericPackNames);
std::pair<std::vector<TypeId>, std::vector<TypePackId>> createGenericTypes(const ScopePtr& scope, std::optional<TypeLevel> levelOpt,
const AstNode& node, const AstArray<AstName>& genericNames, const AstArray<AstName>& genericPackNames);

public:
ErrorVec resolve(const PredicateVec& predicates, const ScopePtr& scope, bool sense);
Expand All @@ -347,7 +359,6 @@ struct TypeChecker
void resolve(const OrPredicate& orP, ErrorVec& errVec, RefinementMap& refis, const ScopePtr& scope, bool sense);
void resolve(const IsAPredicate& isaP, ErrorVec& errVec, RefinementMap& refis, const ScopePtr& scope, bool sense);
void resolve(const TypeGuardPredicate& typeguardP, ErrorVec& errVec, RefinementMap& refis, const ScopePtr& scope, bool sense);
void DEPRECATED_resolve(const TypeGuardPredicate& typeguardP, ErrorVec& errVec, RefinementMap& refis, const ScopePtr& scope, bool sense);
void resolve(const EqPredicate& eqP, ErrorVec& errVec, RefinementMap& refis, const ScopePtr& scope, bool sense);

bool isNonstrictMode() const;
Expand Down Expand Up @@ -387,12 +398,9 @@ struct TypeChecker
const TypeId booleanType;
const TypeId threadType;
const TypeId anyType;

const TypeId errorType;
const TypeId optionalNumberType;

const TypePackId anyTypePack;
const TypePackId errorTypePack;

private:
int checkRecursionCount = 0;
Expand Down
87 changes: 83 additions & 4 deletions Analysis/include/Luau/TypeVar.h
Expand Up @@ -108,6 +108,79 @@ struct PrimitiveTypeVar
}
};

// Singleton types https://github.com/Roblox/luau/blob/master/rfcs/syntax-singleton-types.md
// Types for true and false
struct BoolSingleton
{
bool value;

bool operator==(const BoolSingleton& rhs) const
{
return value == rhs.value;
}

bool operator!=(const BoolSingleton& rhs) const
{
return !(*this == rhs);
}
};

// Types for "foo", "bar" etc.
struct StringSingleton
{
std::string value;

bool operator==(const StringSingleton& rhs) const
{
return value == rhs.value;
}

bool operator!=(const StringSingleton& rhs) const
{
return !(*this == rhs);
}
};

// No type for float singletons, partly because === isn't any equalivalence on floats
// (NaN != NaN).

using SingletonVariant = Luau::Variant<BoolSingleton, StringSingleton>;

struct SingletonTypeVar
{
explicit SingletonTypeVar(const SingletonVariant& variant)
: variant(variant)
{
}

explicit SingletonTypeVar(SingletonVariant&& variant)
: variant(std::move(variant))
{
}

// Default operator== is C++20.
bool operator==(const SingletonTypeVar& rhs) const
{
return variant == rhs.variant;
}

bool operator!=(const SingletonTypeVar& rhs) const
{
return !(*this == rhs);
}

SingletonVariant variant;
};

template<typename T>
const T* get(const SingletonTypeVar* stv)
{
if (stv)
return get_if<T>(&stv->variant);
else
return nullptr;
}

struct FunctionArgument
{
Name name;
Expand Down Expand Up @@ -332,8 +405,8 @@ struct LazyTypeVar

using ErrorTypeVar = Unifiable::Error;

using TypeVariant = Unifiable::Variant<TypeId, PrimitiveTypeVar, FunctionTypeVar, TableTypeVar, MetatableTypeVar, ClassTypeVar, AnyTypeVar,
UnionTypeVar, IntersectionTypeVar, LazyTypeVar>;
using TypeVariant = Unifiable::Variant<TypeId, PrimitiveTypeVar, SingletonTypeVar, FunctionTypeVar, TableTypeVar, MetatableTypeVar, ClassTypeVar,
AnyTypeVar, UnionTypeVar, IntersectionTypeVar, LazyTypeVar>;

struct TypeVar final
{
Expand Down Expand Up @@ -410,6 +483,9 @@ bool isGeneric(const TypeId ty);
// Checks if a type may be instantiated to one containing generic type binders
bool maybeGeneric(const TypeId ty);

// Checks if a type is of the form T1|...|Tn where one of the Ti is a singleton
bool maybeSingleton(TypeId ty);

struct SingletonTypes
{
const TypeId nilType;
Expand All @@ -418,16 +494,19 @@ struct SingletonTypes
const TypeId booleanType;
const TypeId threadType;
const TypeId anyType;
const TypeId errorType;
const TypeId optionalNumberType;

const TypePackId anyTypePack;
const TypePackId errorTypePack;

SingletonTypes();
SingletonTypes(const SingletonTypes&) = delete;
void operator=(const SingletonTypes&) = delete;

TypeId errorRecoveryType(TypeId guess);
TypePackId errorRecoveryTypePack(TypePackId guess);
TypeId errorRecoveryType();
TypePackId errorRecoveryTypePack();

private:
std::unique_ptr<struct TypeArena> arena;
TypeId makeStringMetatable();
Expand Down
2 changes: 2 additions & 0 deletions Analysis/include/Luau/Unifiable.h
Expand Up @@ -105,6 +105,8 @@ struct Generic

struct Error
{
// This constructor has to be public, since it's used in TypeVar and TypePack,
// but shouldn't be called directly. Please use errorRecoveryType() instead.
Error();

int index;
Expand Down
1 change: 1 addition & 0 deletions Analysis/include/Luau/Unifier.h
Expand Up @@ -65,6 +65,7 @@ struct Unifier
private:
void tryUnify_(TypeId superTy, TypeId subTy, bool isFunctionCall = false, bool isIntersection = false);
void tryUnifyPrimitives(TypeId superTy, TypeId subTy);
void tryUnifySingletons(TypeId superTy, TypeId subTy);
void tryUnifyFunctions(TypeId superTy, TypeId subTy, bool isFunctionCall = false);
void tryUnifyTables(TypeId left, TypeId right, bool isIntersection = false);
void DEPRECATED_tryUnifyTables(TypeId left, TypeId right, bool isIntersection = false);
Expand Down
33 changes: 22 additions & 11 deletions Analysis/src/Autocomplete.cpp
Expand Up @@ -14,6 +14,7 @@

LUAU_FASTFLAGVARIABLE(ElseElseIfCompletionImprovements, false);
LUAU_FASTFLAG(LuauIfElseExpressionAnalysisSupport)
LUAU_FASTFLAGVARIABLE(LuauAutocompleteAvoidMutation, false);

static const std::unordered_set<std::string> kStatementStartingKeywords = {
"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
Expand Down Expand Up @@ -198,11 +199,24 @@ static TypeCorrectKind checkTypeCorrectKind(const Module& module, TypeArena* typ
UnifierSharedState unifierState(&iceReporter);
Unifier unifier(typeArena, Mode::Strict, module.getModuleScope(), Location(), Variance::Covariant, unifierState);

unifier.tryUnify(expectedType, actualType);
if (FFlag::LuauAutocompleteAvoidMutation)
{
SeenTypes seenTypes;
SeenTypePacks seenTypePacks;
expectedType = clone(expectedType, *typeArena, seenTypes, seenTypePacks, nullptr);
actualType = clone(actualType, *typeArena, seenTypes, seenTypePacks, nullptr);

auto errors = unifier.canUnify(expectedType, actualType);
return errors.empty();
}
else
{
unifier.tryUnify(expectedType, actualType);

bool ok = unifier.errors.empty();
unifier.log.rollback();
return ok;
bool ok = unifier.errors.empty();
unifier.log.rollback();
return ok;
}
};

auto expr = node->asExpr();
Expand Down Expand Up @@ -1496,11 +1510,9 @@ AutocompleteResult autocomplete(Frontend& frontend, const ModuleName& moduleName
if (!sourceModule)
return {};

TypeChecker& typeChecker =
(frontend.options.typecheckTwice ? frontend.typeCheckerForAutocomplete : frontend.typeChecker);
ModulePtr module =
(frontend.options.typecheckTwice ? frontend.moduleResolverForAutocomplete.getModule(moduleName)
: frontend.moduleResolver.getModule(moduleName));
TypeChecker& typeChecker = (frontend.options.typecheckTwice ? frontend.typeCheckerForAutocomplete : frontend.typeChecker);
ModulePtr module = (frontend.options.typecheckTwice ? frontend.moduleResolverForAutocomplete.getModule(moduleName)
: frontend.moduleResolver.getModule(moduleName));

if (!module)
return {};
Expand All @@ -1527,8 +1539,7 @@ OwningAutocompleteResult autocompleteSource(Frontend& frontend, std::string_view
sourceModule->mode = Mode::Strict;
sourceModule->commentLocations = std::move(result.commentLocations);

TypeChecker& typeChecker =
(frontend.options.typecheckTwice ? frontend.typeCheckerForAutocomplete : frontend.typeChecker);
TypeChecker& typeChecker = (frontend.options.typecheckTwice ? frontend.typeCheckerForAutocomplete : frontend.typeChecker);

ModulePtr module = typeChecker.check(*sourceModule, Mode::Strict);

Expand Down
1 change: 1 addition & 0 deletions Analysis/src/EmbeddedBuiltinDefinitions.cpp
Expand Up @@ -153,6 +153,7 @@ declare function gcinfo(): number
wrap: <A..., R...>((A...) -> R...) -> any,
yield: <A..., R...>(A...) -> R...,
isyieldable: () -> boolean,
close: (thread) -> (boolean, any?)
}

declare table: {
Expand Down
8 changes: 4 additions & 4 deletions Analysis/src/Error.cpp
Expand Up @@ -180,13 +180,13 @@ struct ErrorConverter
switch (e.context)
{
case CountMismatch::Return:
return "Expected to return " + std::to_string(e.expected) + " value" + expectedS + ", but " +
std::to_string(e.actual) + " " + actualVerb + " returned here";
return "Expected to return " + std::to_string(e.expected) + " value" + expectedS + ", but " + std::to_string(e.actual) + " " +
actualVerb + " returned here";
case CountMismatch::Result:
// It is alright if right hand side produces more values than the
// left hand side accepts. In this context consider only the opposite case.
return "Function only returns " + std::to_string(e.expected) + " value" + expectedS + ". " +
std::to_string(e.actual) + " are required here";
return "Function only returns " + std::to_string(e.expected) + " value" + expectedS + ". " + std::to_string(e.actual) +
" are required here";
case CountMismatch::Arg:
if (FFlag::LuauTypeAliasPacks)
return "Argument count mismatch. Function " + wrongNumberOfArgsString(e.expected, e.actual);
Expand Down
4 changes: 1 addition & 3 deletions Analysis/src/Frontend.cpp
Expand Up @@ -22,7 +22,6 @@ LUAU_FASTFLAGVARIABLE(LuauResolveModuleNameWithoutACurrentModule, false)
LUAU_FASTFLAG(LuauTraceRequireLookupChild)
LUAU_FASTFLAGVARIABLE(LuauPersistDefinitionFileTypes, false)
LUAU_FASTFLAG(LuauNewRequireTrace2)
LUAU_FASTFLAGVARIABLE(LuauClearScopes, false)

namespace Luau
{
Expand Down Expand Up @@ -458,8 +457,7 @@ CheckResult Frontend::check(const ModuleName& name)
module->astTypes.clear();
module->astExpectedTypes.clear();
module->astOriginalCallTypes.clear();
if (FFlag::LuauClearScopes)
module->scopes.resize(1);
module->scopes.resize(1);
}

if (mode != Mode::NoCheck)
Expand Down