Skip to content

Commit

Permalink
Sync to upstream/release/507-pre (#286)
Browse files Browse the repository at this point in the history
This doesn't contain all changes for 507 yet but we might want to do the
Luau 0.507 release a bit earlier to end the year sooner.

Changes:

- Type ascription (::) now permits casts between related types in both directions, allowing to refine or loosen the type (RFC #56)
- Fix type definition for tonumber to return number? since the input string isn't guaranteed to contain a valid number
- Fix type refinements for field access via []
- Many stability fixes for type checker
- Provide extra information in error messages for type mismatches in more cases
- Improve performance of type checking for large unions when union members are string literals
- Add coverage reporting support to Repl (--coverage command line argument) and lua_getcoverage C API
- Work around code signing issues during Makefile builds on macOS
- Improve performance of truthiness checks in some cases, particularly on Apple M1, resulting in 10-25% perf gains on qsort benchmark depending on the CPU/compiler
- Fix support for little-endian systems; IBM s390x here we go!
  • Loading branch information
zeux authored Dec 10, 2021
1 parent 88be067 commit f2e6a8f
Show file tree
Hide file tree
Showing 56 changed files with 1,694 additions and 638 deletions.
11 changes: 10 additions & 1 deletion Analysis/include/Luau/Error.h
Original file line number Diff line number Diff line change
Expand Up @@ -277,11 +277,20 @@ struct MissingUnionProperty
bool operator==(const MissingUnionProperty& rhs) const;
};

struct TypesAreUnrelated
{
TypeId left;
TypeId right;

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

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

struct TypeError
{
Expand Down
4 changes: 4 additions & 0 deletions Analysis/include/Luau/IostreamHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ std::ostream& operator<<(std::ostream& lhs, const IllegalRequire& error);
std::ostream& operator<<(std::ostream& lhs, const ModuleHasCyclicDependency& error);
std::ostream& operator<<(std::ostream& lhs, const DuplicateGenericParameter& error);
std::ostream& operator<<(std::ostream& lhs, const CannotInferBinaryOperation& error);
std::ostream& operator<<(std::ostream& lhs, const SwappedGenericTypeParameter& error);
std::ostream& operator<<(std::ostream& lhs, const OptionalValueAccess& error);
std::ostream& operator<<(std::ostream& lhs, const MissingUnionProperty& error);
std::ostream& operator<<(std::ostream& lhs, const TypesAreUnrelated& error);

std::ostream& operator<<(std::ostream& lhs, const TableState& tv);
std::ostream& operator<<(std::ostream& lhs, const TypeVar& tv);
Expand Down
4 changes: 2 additions & 2 deletions Analysis/include/Luau/ToString.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ std::string toStringNamedFunction(const std::string& prefix, const FunctionTypeV

// 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);
void dump(TypePackId ty);
std::string dump(TypeId ty);
std::string dump(TypePackId ty);

std::string generateName(size_t n);

Expand Down
7 changes: 4 additions & 3 deletions Analysis/include/Luau/TypeInfer.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,14 @@ struct TypeChecker

// Returns both the type of the lvalue and its binding (if the caller wants to mutate the binding).
// Note: the binding may be null.
// TODO: remove second return value with FFlagLuauUpdateFunctionNameBinding
std::pair<TypeId, TypeId*> checkLValueBinding(const ScopePtr& scope, const AstExpr& expr);
std::pair<TypeId, TypeId*> checkLValueBinding(const ScopePtr& scope, const AstExprLocal& expr);
std::pair<TypeId, TypeId*> checkLValueBinding(const ScopePtr& scope, const AstExprGlobal& expr);
std::pair<TypeId, TypeId*> checkLValueBinding(const ScopePtr& scope, const AstExprIndexName& expr);
std::pair<TypeId, TypeId*> checkLValueBinding(const ScopePtr& scope, const AstExprIndexExpr& expr);

TypeId checkFunctionName(const ScopePtr& scope, AstExpr& funName);
TypeId checkFunctionName(const ScopePtr& scope, AstExpr& funName, TypeLevel level);
std::pair<TypeId, ScopePtr> checkFunctionSignature(const ScopePtr& scope, int subLevel, const AstExprFunction& expr,
std::optional<Location> originalNameLoc, std::optional<TypeId> expectedType);
void checkFunctionBody(const ScopePtr& scope, TypeId type, const AstExprFunction& function);
Expand All @@ -174,7 +175,7 @@ struct TypeChecker
ExprResult<TypePackId> checkExprPack(const ScopePtr& scope, const AstExprCall& expr);
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,
TypePackId argPack, TypePack* args, const std::vector<Location>* argLocations, const ExprResult<TypePackId>& argListResult,
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);
Expand Down Expand Up @@ -277,7 +278,7 @@ struct TypeChecker
[[noreturn]] void ice(const std::string& message);

ScopePtr childFunctionScope(const ScopePtr& parent, const Location& location, int subLevel = 0);
ScopePtr childScope(const ScopePtr& parent, const Location& location, int subLevel = 0);
ScopePtr childScope(const ScopePtr& parent, const Location& location);

// Wrapper for merge(l, r, toUnion) but without the lambda junk.
void merge(RefinementMap& l, const RefinementMap& r);
Expand Down
8 changes: 4 additions & 4 deletions Analysis/include/Luau/TypeVar.h
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,7 @@ struct SingletonTypes
const TypePackId anyTypePack;

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

Expand All @@ -509,10 +510,12 @@ struct SingletonTypes

private:
std::unique_ptr<struct TypeArena> arena;
bool debugFreezeArena = false;

TypeId makeStringMetatable();
};

extern SingletonTypes singletonTypes;
SingletonTypes& getSingletonTypes();

void persist(TypeId ty);
void persist(TypePackId tp);
Expand All @@ -523,9 +526,6 @@ TypeLevel* getMutableLevel(TypeId ty);
const Property* lookupClassProp(const ClassTypeVar* cls, const Name& name);
bool isSubclass(const ClassTypeVar* cls, const ClassTypeVar* parent);

bool hasGeneric(TypeId ty);
bool hasGeneric(TypePackId tp);

TypeVar* asMutable(TypeId ty);

template<typename T>
Expand Down
11 changes: 10 additions & 1 deletion Analysis/include/Luau/Unifiable.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ struct TypeLevel
int level = 0;
int subLevel = 0;

// Returns true if the typelevel "this" is "bigger" than rhs
// Returns true if the level of "this" belongs to an equal or larger scope than that of rhs
bool subsumes(const TypeLevel& rhs) const
{
if (level < rhs.level)
Expand All @@ -38,6 +38,15 @@ struct TypeLevel
return false;
}

// Returns true if the level of "this" belongs to a larger (not equal) scope than that of rhs
bool subsumesStrict(const TypeLevel& rhs) const
{
if (level == rhs.level && subLevel == rhs.subLevel)
return false;
else
return subsumes(rhs);
}

TypeLevel incr() const
{
TypeLevel result;
Expand Down
3 changes: 3 additions & 0 deletions Analysis/include/Luau/Unifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ struct Unifier

[[noreturn]] void ice(const std::string& message, const Location& location);
[[noreturn]] void ice(const std::string& message);

// Available after regular type pack unification errors
std::optional<int> firstPackErrorPos;
};

} // namespace Luau
115 changes: 93 additions & 22 deletions Analysis/src/Autocomplete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
LUAU_FASTFLAG(LuauIfElseExpressionAnalysisSupport)
LUAU_FASTFLAGVARIABLE(LuauAutocompleteAvoidMutation, false);
LUAU_FASTFLAGVARIABLE(LuauAutocompletePreferToCallFunctions, false);
LUAU_FASTFLAGVARIABLE(LuauAutocompleteFirstArg, 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 @@ -190,7 +191,48 @@ static ParenthesesRecommendation getParenRecommendation(TypeId id, const std::ve
return ParenthesesRecommendation::None;
}

static TypeCorrectKind checkTypeCorrectKind(const Module& module, TypeArena* typeArena, AstNode* node, TypeId ty)
static std::optional<TypeId> findExpectedTypeAt(const Module& module, AstNode* node, Position position)
{
LUAU_ASSERT(FFlag::LuauAutocompleteFirstArg);

auto expr = node->asExpr();
if (!expr)
return std::nullopt;

// Extra care for first function call argument location
// When we don't have anything inside () yet, we also don't have an AST node to base our lookup
if (AstExprCall* exprCall = expr->as<AstExprCall>())
{
if (exprCall->args.size == 0 && exprCall->argLocation.contains(position))
{
auto it = module.astTypes.find(exprCall->func);

if (!it)
return std::nullopt;

const FunctionTypeVar* ftv = get<FunctionTypeVar>(follow(*it));

if (!ftv)
return std::nullopt;

auto [head, tail] = flatten(ftv->argTypes);
unsigned index = exprCall->self ? 1 : 0;

if (index < head.size())
return head[index];

return std::nullopt;
}
}

auto it = module.astExpectedTypes.find(expr);
if (!it)
return std::nullopt;

return *it;
}

static TypeCorrectKind checkTypeCorrectKind(const Module& module, TypeArena* typeArena, AstNode* node, Position position, TypeId ty)
{
ty = follow(ty);

Expand Down Expand Up @@ -220,15 +262,29 @@ static TypeCorrectKind checkTypeCorrectKind(const Module& module, TypeArena* typ
}
};

auto expr = node->asExpr();
if (!expr)
return TypeCorrectKind::None;
TypeId expectedType;

auto it = module.astExpectedTypes.find(expr);
if (!it)
return TypeCorrectKind::None;
if (FFlag::LuauAutocompleteFirstArg)
{
auto typeAtPosition = findExpectedTypeAt(module, node, position);

if (!typeAtPosition)
return TypeCorrectKind::None;

expectedType = follow(*typeAtPosition);
}
else
{
auto expr = node->asExpr();
if (!expr)
return TypeCorrectKind::None;

auto it = module.astExpectedTypes.find(expr);
if (!it)
return TypeCorrectKind::None;

TypeId expectedType = follow(*it);
expectedType = follow(*it);
}

if (FFlag::LuauAutocompletePreferToCallFunctions)
{
Expand Down Expand Up @@ -333,8 +389,8 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
if (result.count(name) == 0 && name != Parser::errorName)
{
Luau::TypeId type = Luau::follow(prop.type);
TypeCorrectKind typeCorrect =
indexType == PropIndexType::Key ? TypeCorrectKind::Correct : checkTypeCorrectKind(module, typeArena, nodes.back(), type);
TypeCorrectKind typeCorrect = indexType == PropIndexType::Key ? TypeCorrectKind::Correct
: checkTypeCorrectKind(module, typeArena, nodes.back(), {{}, {}}, type);
ParenthesesRecommendation parens =
indexType == PropIndexType::Key ? ParenthesesRecommendation::None : getParenRecommendation(type, nodes, typeCorrect);

Expand Down Expand Up @@ -692,17 +748,31 @@ std::optional<const T*> returnFirstNonnullOptionOfType(const UnionTypeVar* utv)
return ret;
}

static std::optional<bool> functionIsExpectedAt(const Module& module, AstNode* node)
static std::optional<bool> functionIsExpectedAt(const Module& module, AstNode* node, Position position)
{
auto expr = node->asExpr();
if (!expr)
return std::nullopt;
TypeId expectedType;

auto it = module.astExpectedTypes.find(expr);
if (!it)
return std::nullopt;
if (FFlag::LuauAutocompleteFirstArg)
{
auto typeAtPosition = findExpectedTypeAt(module, node, position);

TypeId expectedType = follow(*it);
if (!typeAtPosition)
return std::nullopt;

expectedType = follow(*typeAtPosition);
}
else
{
auto expr = node->asExpr();
if (!expr)
return std::nullopt;

auto it = module.astExpectedTypes.find(expr);
if (!it)
return std::nullopt;

expectedType = follow(*it);
}

if (get<FunctionTypeVar>(expectedType))
return true;
Expand Down Expand Up @@ -1171,7 +1241,7 @@ static void autocompleteExpression(const SourceModule& sourceModule, const Modul
std::string n = toString(name);
if (!result.count(n))
{
TypeCorrectKind typeCorrect = checkTypeCorrectKind(module, typeArena, node, binding.typeId);
TypeCorrectKind typeCorrect = checkTypeCorrectKind(module, typeArena, node, position, binding.typeId);

result[n] = {AutocompleteEntryKind::Binding, binding.typeId, binding.deprecated, false, typeCorrect, std::nullopt, std::nullopt,
binding.documentationSymbol, {}, getParenRecommendation(binding.typeId, ancestry, typeCorrect)};
Expand All @@ -1181,9 +1251,10 @@ static void autocompleteExpression(const SourceModule& sourceModule, const Modul
scope = scope->parent;
}

TypeCorrectKind correctForNil = checkTypeCorrectKind(module, typeArena, node, typeChecker.nilType);
TypeCorrectKind correctForBoolean = checkTypeCorrectKind(module, typeArena, node, typeChecker.booleanType);
TypeCorrectKind correctForFunction = functionIsExpectedAt(module, node).value_or(false) ? TypeCorrectKind::Correct : TypeCorrectKind::None;
TypeCorrectKind correctForNil = checkTypeCorrectKind(module, typeArena, node, position, typeChecker.nilType);
TypeCorrectKind correctForBoolean = checkTypeCorrectKind(module, typeArena, node, position, typeChecker.booleanType);
TypeCorrectKind correctForFunction =
functionIsExpectedAt(module, node, position).value_or(false) ? TypeCorrectKind::Correct : TypeCorrectKind::None;

if (FFlag::LuauIfElseExpressionAnalysisSupport)
result["if"] = {AutocompleteEntryKind::Keyword, std::nullopt, false, false};
Expand Down
9 changes: 6 additions & 3 deletions Analysis/src/BuiltinDefinitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,9 @@ void registerBuiltinTypes(TypeChecker& typeChecker)

TypeId genericK = arena.addType(GenericTypeVar{"K"});
TypeId genericV = arena.addType(GenericTypeVar{"V"});
TypeId mapOfKtoV = arena.addType(TableTypeVar{{}, TableIndexer(genericK, genericV), typeChecker.globalScope->level});
TypeId mapOfKtoV = arena.addType(TableTypeVar{{}, TableIndexer(genericK, genericV), typeChecker.globalScope->level, TableState::Generic});

std::optional<TypeId> stringMetatableTy = getMetatable(singletonTypes.stringType);
std::optional<TypeId> stringMetatableTy = getMetatable(getSingletonTypes().stringType);
LUAU_ASSERT(stringMetatableTy);
const TableTypeVar* stringMetatableTable = get<TableTypeVar>(follow(*stringMetatableTy));
LUAU_ASSERT(stringMetatableTable);
Expand Down Expand Up @@ -271,7 +271,10 @@ void registerBuiltinTypes(TypeChecker& typeChecker)
persist(pair.second.typeId);

if (TableTypeVar* ttv = getMutable<TableTypeVar>(pair.second.typeId))
ttv->name = toString(pair.first);
{
if (!ttv->name)
ttv->name = toString(pair.first);
}
}

attachMagicFunction(getGlobalBinding(typeChecker, "assert"), magicFunctionAssert);
Expand Down
12 changes: 10 additions & 2 deletions Analysis/src/EmbeddedBuiltinDefinitions.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/BuiltinDefinitions.h"

LUAU_FASTFLAGVARIABLE(LuauFixTonumberReturnType, false)

namespace Luau
{

Expand Down Expand Up @@ -113,7 +115,6 @@ declare function gcinfo(): number
declare function error<T>(message: T, level: number?)
declare function tostring<T>(value: T): string
declare function tonumber<T>(value: T, radix: number?): number
declare function rawequal<T1, T2>(a: T1, b: T2): boolean
declare function rawget<K, V>(tab: {[K]: V}, k: K): V
Expand Down Expand Up @@ -204,7 +205,14 @@ declare function gcinfo(): number

std::string getBuiltinDefinitionSource()
{
return kBuiltinDefinitionLuaSrc;
std::string result = kBuiltinDefinitionLuaSrc;

if (FFlag::LuauFixTonumberReturnType)
result += "declare function tonumber<T>(value: T, radix: number?): number?\n";
else
result += "declare function tonumber<T>(value: T, radix: number?): number\n";

return result;
}

} // namespace Luau
Loading

0 comments on commit f2e6a8f

Please sign in to comment.