Skip to content

Commit

Permalink
Sync to upstream/release/505 (#216)
Browse files Browse the repository at this point in the history
- Improve error recovery during type checking
- Initial (not fully complete) implementation for singleton types (RFC RFC: Singleton types #37)
- Implement a C-friendly interface for compiler (luacode.h)
- Remove C++ features from lua.h (removed default arguments from luau_load and lua_pushcfunction)
- Fix lua_breakpoint behavior when enabled=false
- Implement coroutine.close (RFC RFC: coroutine.close #88)

Note, this introduces small breaking changes in lua.h:

- luau_load env argument is now required, pass an extra 0
- lua_pushcfunction now must be called with 3 arguments; if you were calling it with 2 arguments, pass an extra NULL; if you were calling it with 4, use lua_pushcclosure.

These changes are necessary to make sure lua.h can be used from pure C - the future release will make it possible by adding an option to luaconf.h to change function name mangling to be C-compatible. We don't anticipate breaking the FFI interface in the future, but this change was necessary to restore C compatibility.

Closes #121
Fixes #213
  • Loading branch information
zeux authored Nov 19, 2021
1 parent 4265e58 commit 3f1508c
Show file tree
Hide file tree
Showing 62 changed files with 1,786 additions and 567 deletions.
11 changes: 10 additions & 1 deletion Analysis/include/Luau/Documentation.h
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Loading

0 comments on commit 3f1508c

Please sign in to comment.