Skip to content

Commit

Permalink
Sync to upstream/release/611 (#1160)
Browse files Browse the repository at this point in the history
# What's changed?

### Native Code Generation

* Fixed an UAF relating to reusing a hash key after a weak table has
undergone some GC.
* Fixed a bounds check on arm64 to allow access to the last byte of a
buffer.

### New Type Solver

* Type states now preserves error-suppression, i.e. `local x: any = 5`
and `x.foo` does not error.
* Made error-suppression logic in subtyping more accurate.
* Subtyping now knows how to reduce type families.
* Fixed function call overload resolution so that the return type
resolves to the correct overload.
* Fixed a case where we attempted to reduce irreducible type families a
few too many times, leading to duplicate errors.
* Type checker needs to type check annotations in function signatures to
be able to report errors relating to those annotations.
* Fixed an UAF from a pointer to stack-allocated data in Subtyping's
`explainReasonings`.

### Nonstrict Type Checker

* Fixed a crash when calling a checked function of the form `math.abs`
with an incorrect argument type.
* Fixed a crash when calling a checked function with a number of
arguments that did not exactly match the number of parameters required.

---

### Internal Contributors

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

---------

Co-authored-by: Aaron Weiss <[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]>
  • Loading branch information
8 people authored Feb 2, 2024
1 parent 974963a commit 67ce75e
Show file tree
Hide file tree
Showing 35 changed files with 1,263 additions and 566 deletions.
12 changes: 12 additions & 0 deletions Analysis/include/Luau/ConstraintGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,18 @@ struct ConstraintGenerator
*/
TypePackId freshTypePack(const ScopePtr& scope);

/**
* Allocate a new TypePack with the given head and tail.
*
* Avoids allocating 0-length type packs:
*
* If the head is non-empty, allocate and return a type pack with the given
* head and tail.
* If the head is empty and tail is non-empty, return *tail.
* If both the head and tail are empty, return an empty type pack.
*/
TypePackId addTypePack(std::vector<TypeId> head, std::optional<TypePackId> tail);

/**
* Fabricates a scope that is a child of another scope.
* @param node the lexical node that the scope belongs to.
Expand Down
3 changes: 1 addition & 2 deletions Analysis/include/Luau/ConstraintSolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,6 @@ struct ConstraintSolver
bool hasUnresolvedConstraints(TypeId ty);

private:

/** Helper used by tryDispatch(SubtypeConstraint) and
* tryDispatch(PackSubtypeConstraint)
*
Expand All @@ -265,7 +264,7 @@ struct ConstraintSolver
*
* If unification succeeds, unblock every type changed by the unification.
*/
template <typename TID>
template<typename TID>
bool tryUnify(NotNull<const Constraint> constraint, TID subTy, TID superTy);

/**
Expand Down
23 changes: 16 additions & 7 deletions Analysis/include/Luau/Error.h
Original file line number Diff line number Diff line change
Expand Up @@ -380,13 +380,22 @@ struct NonStrictFunctionDefinitionError
bool operator==(const NonStrictFunctionDefinitionError& 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, DynamicPropertyLookupOnClassesUnsafe, UninhabitedTypeFamily,
UninhabitedTypePackFamily, WhereClauseNeeded, PackWhereClauseNeeded, CheckedFunctionCallError, NonStrictFunctionDefinitionError>;
struct CheckedFunctionIncorrectArgs
{
std::string functionName;
size_t expected;
size_t actual;
bool operator==(const CheckedFunctionIncorrectArgs& 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, DynamicPropertyLookupOnClassesUnsafe, UninhabitedTypeFamily, UninhabitedTypePackFamily,
WhereClauseNeeded, PackWhereClauseNeeded, CheckedFunctionCallError, NonStrictFunctionDefinitionError, CheckedFunctionIncorrectArgs>;

struct TypeErrorSummary
{
Expand Down
70 changes: 70 additions & 0 deletions Analysis/include/Luau/OverloadResolution.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once

#include "Luau/Ast.h"
#include "Luau/InsertionOrderedMap.h"
#include "Luau/NotNull.h"
#include "Luau/TypeFwd.h"
#include "Luau/Location.h"
#include "Luau/Error.h"
#include "Luau/Subtyping.h"

namespace Luau
{

struct BuiltinTypes;
struct TypeArena;
struct Scope;
struct InternalErrorReporter;
struct TypeCheckLimits;
struct Subtyping;

class Normalizer;

struct OverloadResolver
{
enum Analysis
{
Ok,
TypeIsNotAFunction,
ArityMismatch,
OverloadIsNonviable, // Arguments were incompatible with the overloads parameters but were otherwise compatible by arity
};

OverloadResolver(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, NotNull<Normalizer> normalizer, NotNull<Scope> scope,
NotNull<InternalErrorReporter> reporter, NotNull<TypeCheckLimits> limits, Location callLocation);

NotNull<BuiltinTypes> builtinTypes;
NotNull<TypeArena> arena;
NotNull<Normalizer> normalizer;
NotNull<Scope> scope;
NotNull<InternalErrorReporter> ice;
NotNull<TypeCheckLimits> limits;
Subtyping subtyping;
Location callLoc;

// Resolver results
std::vector<TypeId> ok;
std::vector<TypeId> nonFunctions;
std::vector<std::pair<TypeId, ErrorVec>> arityMismatches;
std::vector<std::pair<TypeId, ErrorVec>> nonviableOverloads;
InsertionOrderedMap<TypeId, std::pair<OverloadResolver::Analysis, size_t>> resolution;


std::pair<OverloadResolver::Analysis, TypeId> selectOverload(TypeId ty, TypePackId args);
void resolve(TypeId fnTy, const TypePack* args, AstExpr* selfExpr, const std::vector<AstExpr*>* argExprs);

private:
std::optional<ErrorVec> testIsSubtype(const Location& location, TypeId subTy, TypeId superTy);
std::optional<ErrorVec> testIsSubtype(const Location& location, TypePackId subTy, TypePackId superTy);
std::pair<Analysis, ErrorVec> checkOverload(
TypeId fnTy, const TypePack* args, AstExpr* fnLoc, const std::vector<AstExpr*>* argExprs, bool callMetamethodOk = true);
static bool isLiteral(AstExpr* expr);
LUAU_NOINLINE
std::pair<Analysis, ErrorVec> checkOverload_(
TypeId fnTy, const FunctionType* fn, const TypePack* args, AstExpr* fnExpr, const std::vector<AstExpr*>* argExprs);
size_t indexof(Analysis analysis);
void add(Analysis analysis, TypeId ty, ErrorVec&& errors);
};

} // namespace Luau
2 changes: 2 additions & 0 deletions Analysis/include/Luau/Scope.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ struct Scope

TypeLevel level;

Location location; // the spanning location associated with this scope

std::unordered_map<Name, TypeFun> exportedTypeBindings;
std::unordered_map<Name, TypeFun> privateTypeBindings;
std::unordered_map<Name, Location> typeAliasLocations;
Expand Down
22 changes: 21 additions & 1 deletion Analysis/include/Luau/Subtyping.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include "Luau/TypeFwd.h"
#include "Luau/TypePairHash.h"
#include "Luau/TypePath.h"
#include "Luau/TypeFamily.h"
#include "Luau/TypeCheckLimits.h"
#include "Luau/DenseHash.h"

#include <vector>
Expand All @@ -24,6 +26,7 @@ struct NormalizedClassType;
struct NormalizedStringType;
struct NormalizedFunctionType;
struct TypeArena;
struct TypeCheckLimits;
struct Scope;
struct TableIndexer;

Expand Down Expand Up @@ -60,7 +63,6 @@ static const SubtypingReasoning kEmptyReasoning = SubtypingReasoning{TypePath::k
struct SubtypingResult
{
bool isSubtype = false;
bool isErrorSuppressing = false;
bool normalizationTooComplex = false;
bool isCacheable = true;

Expand Down Expand Up @@ -109,6 +111,7 @@ struct Subtyping
NotNull<InternalErrorReporter> iceReporter;

NotNull<Scope> scope;
TypeCheckLimits limits;

enum class Variance
{
Expand Down Expand Up @@ -199,13 +202,30 @@ struct Subtyping
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeIds& subTypes, const TypeIds& superTypes);

SubtypingResult isCovariantWith(SubtypingEnvironment& env, const VariadicTypePack* subVariadic, const VariadicTypePack* superVariadic);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeFamilyInstanceType* subFamilyInstance, const TypeId superTy);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeId subTy, const TypeFamilyInstanceType* superFamilyInstance);

bool bindGeneric(SubtypingEnvironment& env, TypeId subTp, TypeId superTp);
bool bindGeneric(SubtypingEnvironment& env, TypePackId subTp, TypePackId superTp);

template<typename T, typename Container>
TypeId makeAggregateType(const Container& container, TypeId orElse);

template<typename T>
T handleTypeFamilyReductionResult(const TypeFamilyInstanceType* tf)
{
TypeFamilyContext context{arena, builtinTypes, scope, normalizer, iceReporter, NotNull{&limits}};
TypeFamilyReductionResult<TypeId> result = tf->family->reducer(tf->typeArguments, tf->packArguments, NotNull{&context});
if (!result.blockedTypes.empty())
unexpected(result.blockedTypes[0]);
else if (!result.blockedPacks.empty())
unexpected(result.blockedPacks[0]);
else if (result.uninhabited || result.result == std::nullopt)
return builtinTypes->neverType;
return *result.result;
}

[[noreturn]] void unexpected(TypeId ty);
[[noreturn]] void unexpected(TypePackId tp);
};

Expand Down
32 changes: 28 additions & 4 deletions Analysis/include/Luau/TypeUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,33 @@ std::vector<TypeId> reduceUnion(const std::vector<TypeId>& types);
*/
TypeId stripNil(NotNull<BuiltinTypes> builtinTypes, TypeArena& arena, TypeId ty);

enum class ErrorSuppression
struct ErrorSuppression
{
Suppress,
DoNotSuppress,
NormalizationFailed
enum Value
{
Suppress,
DoNotSuppress,
NormalizationFailed,
};

ErrorSuppression() = default;
constexpr ErrorSuppression(Value enumValue) : value(enumValue) { }

constexpr operator Value() const { return value; }
explicit operator bool() const = delete;

ErrorSuppression orElse(const ErrorSuppression& other) const
{
switch (value)
{
case DoNotSuppress:
return other;
default:
return *this;
}
}
private:
Value value;
};

/**
Expand Down Expand Up @@ -118,6 +140,8 @@ struct TryPair
template<typename A, typename B, typename Ty>
TryPair<const A*, const B*> get2(Ty one, Ty two)
{
static_assert(std::is_pointer_v<Ty>, "argument must be a pointer type");

const A* a = get<A>(one);
const B* b = get<B>(two);
if (a && b)
Expand Down
63 changes: 56 additions & 7 deletions Analysis/src/ConstraintGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
ScopePtr scope = std::make_shared<Scope>(globalScope);
rootScope = scope.get();
scopes.emplace_back(block->location, scope);
rootScope->location = block->location;
module->astScopes[block] = NotNull{scope.get()};

rootScope->returnType = freshTypePack(scope);
Expand All @@ -192,10 +193,24 @@ TypePackId ConstraintGenerator::freshTypePack(const ScopePtr& scope)
return arena->addTypePack(TypePackVar{std::move(f)});
}

TypePackId ConstraintGenerator::addTypePack(std::vector<TypeId> head, std::optional<TypePackId> tail)
{
if (head.empty())
{
if (tail)
return *tail;
else
return builtinTypes->emptyTypePack;
}
else
return arena->addTypePack(TypePack{std::move(head), tail});
}

ScopePtr ConstraintGenerator::childScope(AstNode* node, const ScopePtr& parent)
{
auto scope = std::make_shared<Scope>(parent);
scopes.emplace_back(node->location, scope);
scope->location = node->location;

scope->returnType = parent->returnType;
scope->varargPack = parent->varargPack;
Expand Down Expand Up @@ -1278,7 +1293,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas
if (FunctionType* ftv = getMutable<FunctionType>(propTy))
{
ftv->argNames.insert(ftv->argNames.begin(), FunctionArgument{"self", {}});
ftv->argTypes = arena->addTypePack(TypePack{{classTy}, ftv->argTypes});
ftv->argTypes = addTypePack({classTy}, ftv->argTypes);

ftv->hasSelf = true;
}
Expand Down Expand Up @@ -1407,10 +1422,7 @@ InferencePack ConstraintGenerator::checkPack(
}
}

if (head.empty() && tail)
return InferencePack{*tail};
else
return InferencePack{arena->addTypePack(TypePack{std::move(head), tail})};
return InferencePack{addTypePack(std::move(head), tail)};
}

InferencePack ConstraintGenerator::checkPack(
Expand Down Expand Up @@ -1611,7 +1623,7 @@ InferencePack ConstraintGenerator::checkPack(const ScopePtr& scope, AstExprCall*

// TODO: How do expectedTypes play into this? Do they?
TypePackId rets = arena->addTypePack(BlockedTypePack{});
TypePackId argPack = arena->addTypePack(TypePack{args, argTail});
TypePackId argPack = addTypePack(std::move(args), argTail);
FunctionType ftv(TypeLevel{}, scope.get(), argPack, rets, std::nullopt, call->self);

NotNull<Constraint> fcc = addConstraint(scope, call->func->location,
Expand Down Expand Up @@ -2283,12 +2295,33 @@ std::optional<TypeId> ConstraintGenerator::checkLValue(const ScopePtr& scope, As
{
if (auto lt = getMutable<LocalType>(*ty))
++lt->blockCount;
else if (auto ut = getMutable<UnionType>(*ty))
{
for (TypeId optTy : ut->options)
if (auto lt = getMutable<LocalType>(optTy))
++lt->blockCount;
}
}
}
else
{
ty = arena->addType(LocalType{builtinTypes->neverType, /* blockCount */ 1, local->local->name.value});

if (annotatedTy)
{
switch (shouldSuppressErrors(normalizer, *annotatedTy))
{
case ErrorSuppression::DoNotSuppress:
break;
case ErrorSuppression::Suppress:
ty = simplifyUnion(builtinTypes, arena, *ty, builtinTypes->errorType).result;
break;
case ErrorSuppression::NormalizationFailed:
reportError(local->local->annotation->location, NormalizationTooComplex{});
break;
}
}

scope->lvalueTypes[defId] = *ty;
}

Expand Down Expand Up @@ -2546,6 +2579,22 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr,

TypeId itemTy = check(scope, item.value, checkExpectedIndexResultType).ty;

// we should preserve error-suppressingness from the expected value type if we have one
if (expectedValueType)
{
switch (shouldSuppressErrors(normalizer, *expectedValueType))
{
case ErrorSuppression::DoNotSuppress:
break;
case ErrorSuppression::Suppress:
itemTy = simplifyUnion(builtinTypes, arena, itemTy, builtinTypes->errorType).result;
break;
case ErrorSuppression::NormalizationFailed:
reportError(item.value->location, NormalizationTooComplex{});
break;
}
}

if (isIndexedResultType && !pinnedIndexResultType)
pinnedIndexResultType = itemTy;

Expand Down Expand Up @@ -3071,7 +3120,7 @@ TypePackId ConstraintGenerator::resolveTypePack(const ScopePtr& scope, const Ast
tail = resolveTypePack(scope, list.tailType, inTypeArguments, replaceErrorWithFresh);
}

return arena->addTypePack(TypePack{head, tail});
return addTypePack(std::move(head), tail);
}

std::vector<std::pair<Name, GenericTypeDefinition>> ConstraintGenerator::createGenerics(
Expand Down
Loading

0 comments on commit 67ce75e

Please sign in to comment.