Skip to content

Commit

Permalink
Sync to upstream/release/616 (#1184)
Browse files Browse the repository at this point in the history
# What's Changed

* Add a compiler hint to improve Luau memory allocation inlining

### New Type Solver

* Added a system for recommending explicit type annotations to users in
cases where we've inferred complex generic types with type families.
* Marked string library functions as `@checked` for use in new
non-strict mode.
* Fixed a bug with new non-strict mode where we would incorrectly report
arity mismatches when missing optional arguments.
* Implement an occurs check for unifications that would produce
self-recursive types.
* Fix bug where overload resolution would fail when applied to
non-overloaded functions.
* Fix bug that caused the subtyping to report an error whenever a
generic was instantiated in an invariant context.
* Fix crash caused by `SetPropConstraint` not blocking properly.

### Native Code Generation

* Implement optimization to eliminate dead stores
* Optimize vector ops for X64 when the source is computed (thanks,
@zeux!)
* Use more efficient lowering for UNM_* (thanks, @zeux!)

---

### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
  • Loading branch information
8 people committed Mar 9, 2024
1 parent 9323be6 commit ae459a0
Show file tree
Hide file tree
Showing 48 changed files with 2,371 additions and 283 deletions.
10 changes: 6 additions & 4 deletions Analysis/include/Luau/BuiltinDefinitions.h
Expand Up @@ -25,19 +25,21 @@ TypeId makeOption(NotNull<BuiltinTypes> builtinTypes, TypeArena& arena, TypeId t
/** Small utility function for building up type definitions from C++.
*/
TypeId makeFunction( // Monomorphic
TypeArena& arena, std::optional<TypeId> selfType, std::initializer_list<TypeId> paramTypes, std::initializer_list<TypeId> retTypes);
TypeArena& arena, std::optional<TypeId> selfType, std::initializer_list<TypeId> paramTypes, std::initializer_list<TypeId> retTypes,
bool checked = false);

TypeId makeFunction( // Polymorphic
TypeArena& arena, std::optional<TypeId> selfType, std::initializer_list<TypeId> generics, std::initializer_list<TypePackId> genericPacks,
std::initializer_list<TypeId> paramTypes, std::initializer_list<TypeId> retTypes);
std::initializer_list<TypeId> paramTypes, std::initializer_list<TypeId> retTypes, bool checked = false);

TypeId makeFunction( // Monomorphic
TypeArena& arena, std::optional<TypeId> selfType, std::initializer_list<TypeId> paramTypes, std::initializer_list<std::string> paramNames,
std::initializer_list<TypeId> retTypes);
std::initializer_list<TypeId> retTypes, bool checked = false);

TypeId makeFunction( // Polymorphic
TypeArena& arena, std::optional<TypeId> selfType, std::initializer_list<TypeId> generics, std::initializer_list<TypePackId> genericPacks,
std::initializer_list<TypeId> paramTypes, std::initializer_list<std::string> paramNames, std::initializer_list<TypeId> retTypes);
std::initializer_list<TypeId> paramTypes, std::initializer_list<std::string> paramNames, std::initializer_list<TypeId> retTypes,
bool checked = false);

void attachMagicFunction(TypeId ty, MagicFunction fn);
void attachDcrMagicFunction(TypeId ty, DcrMagicFunction fn);
Expand Down
24 changes: 16 additions & 8 deletions Analysis/include/Luau/Error.h
Expand Up @@ -341,6 +341,13 @@ struct UninhabitedTypeFamily
bool operator==(const UninhabitedTypeFamily& rhs) const;
};

struct ExplicitFunctionAnnotationRecommended
{
std::vector<std::pair<std::string, TypeId>> recommendedArgs;
TypeId recommendedReturn;
bool operator==(const ExplicitFunctionAnnotationRecommended& rhs) const;
};

struct UninhabitedTypePackFamily
{
TypePackId tp;
Expand Down Expand Up @@ -416,14 +423,15 @@ struct UnexpectedTypePackInSubtyping
bool operator==(const UnexpectedTypePackInSubtyping& 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,
PropertyAccessViolation, CheckedFunctionIncorrectArgs, UnexpectedTypeInSubtyping, UnexpectedTypePackInSubtyping>;
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, PropertyAccessViolation,
CheckedFunctionIncorrectArgs, UnexpectedTypeInSubtyping, UnexpectedTypePackInSubtyping, ExplicitFunctionAnnotationRecommended>;

struct TypeErrorSummary
{
Expand Down
3 changes: 3 additions & 0 deletions Analysis/include/Luau/Normalize.h
Expand Up @@ -307,6 +307,9 @@ struct NormalizedType
bool hasTables() const;
bool hasFunctions() const;
bool hasTyvars() const;

bool isFalsy() const;
bool isTruthy() const;
};


Expand Down
32 changes: 32 additions & 0 deletions Analysis/include/Luau/OverloadResolution.h
Expand Up @@ -67,4 +67,36 @@ struct OverloadResolver
void add(Analysis analysis, TypeId ty, ErrorVec&& errors);
};

struct SolveResult
{
enum OverloadCallResult {
Ok,
CodeTooComplex,
OccursCheckFailed,
NoMatchingOverload,
};

OverloadCallResult result;
std::optional<TypePackId> typePackId; // nullopt if result != Ok

TypeId overloadToUse = nullptr;
TypeId inferredTy = nullptr;
DenseHashMap<TypeId, std::vector<TypeId>> expandedFreeTypes{nullptr};
};

// Helper utility, presently used for binary operator type families.
//
// Given a function and a set of arguments, select a suitable overload.
SolveResult solveFunctionCall(
NotNull<TypeArena> arena,
NotNull<BuiltinTypes> builtinTypes,
NotNull<Normalizer> normalizer,
NotNull<InternalErrorReporter> iceReporter,
NotNull<TypeCheckLimits> limits,
NotNull<Scope> scope,
const Location& location,
TypeId fn,
TypePackId argsPack
);

} // namespace Luau
18 changes: 1 addition & 17 deletions Analysis/include/Luau/Subtyping.h
Expand Up @@ -219,23 +219,7 @@ struct Subtyping
template<typename T, typename Container>
TypeId makeAggregateType(const Container& container, TypeId orElse);


std::pair<TypeId, ErrorVec> handleTypeFamilyReductionResult(const TypeFamilyInstanceType* familyInstance)
{
TypeFamilyContext context{arena, builtinTypes, scope, normalizer, iceReporter, NotNull{&limits}};
TypeId family = arena->addType(*familyInstance);
std::string familyString = toString(family);
FamilyGraphReductionResult result = reduceFamilies(family, {}, context, true);
ErrorVec errors;
if (result.blockedTypes.size() != 0 || result.blockedPacks.size() != 0)
{
errors.push_back(TypeError{{}, UninhabitedTypeFamily{family}});
return {builtinTypes->neverType, errors};
}
if (result.reducedTypes.contains(family))
return {family, errors};
return {builtinTypes->neverType, errors};
}
std::pair<TypeId, ErrorVec> handleTypeFamilyReductionResult(const TypeFamilyInstanceType* familyInstance);

[[noreturn]] void unexpected(TypeId ty);
[[noreturn]] void unexpected(TypePackId tp);
Expand Down
2 changes: 2 additions & 0 deletions Analysis/include/Luau/TypeFamily.h
Expand Up @@ -57,6 +57,8 @@ struct TypeFamilyContext
, constraint(nullptr)
{
}

NotNull<Constraint> pushConstraint(ConstraintV&& c);
};
/// Represents a reduction result, which may have successfully reduced the type,
/// may have concretely failed to reduce the type, or may simply be stuck
Expand Down
81 changes: 81 additions & 0 deletions Analysis/include/Luau/TypeFamilyReductionGuesser.h
@@ -0,0 +1,81 @@
// 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/VecDeque.h"
#include "Luau/DenseHash.h"
#include "Luau/TypeFamily.h"
#include "Luau/Type.h"
#include "Luau/TypePack.h"
#include "Luau/TypeUtils.h"
#include "Luau/Normalize.h"
#include "Luau/TypeFwd.h"
#include "Luau/VisitType.h"
#include "Luau/NotNull.h"

namespace Luau
{

struct TypeFamilyReductionGuessResult
{
std::vector<std::pair<std::string, TypeId>> guessedFunctionAnnotations;
TypeId guessedReturnType;
bool shouldRecommendAnnotation = true;
};

// An Inference result for a type family is a list of types corresponding to the guessed argument types, followed by a type for the result
struct TypeFamilyInferenceResult
{
std::vector<TypeId> operandInference;
TypeId familyResultInference;
};

struct TypeFamilyReductionGuesser
{
// Tracks our hypothesis about what a type family reduces to
DenseHashMap<TypeId, TypeId> familyReducesTo{nullptr};
// Tracks our constraints on type family operands
DenseHashMap<TypeId, TypeId> substitutable{nullptr};
// List of instances to try progress
VecDeque<TypeId> toInfer;
DenseHashSet<TypeId> cyclicInstances{nullptr};

// Utilities
NotNull<BuiltinTypes> builtins;
NotNull<Normalizer> normalizer;

TypeFamilyReductionGuesser(NotNull<BuiltinTypes> builtins, NotNull<Normalizer> normalizer);

TypeFamilyReductionGuessResult guessTypeFamilyReductionForFunction(const AstExprFunction& expr, const FunctionType* ftv, TypeId retTy);

private:
std::optional<TypeId> guessType(TypeId arg);
void dumpGuesses();

bool isNumericBinopFamily(const TypeFamilyInstanceType& instance);
bool isComparisonFamily(const TypeFamilyInstanceType& instance);
bool isOrAndFamily(const TypeFamilyInstanceType& instance);
bool isNotFamily(const TypeFamilyInstanceType& instance);
bool isLenFamily(const TypeFamilyInstanceType& instance);
bool isUnaryMinus(const TypeFamilyInstanceType& instance);

// Operand is assignable if it looks like a cyclic family instance, or a generic type
bool operandIsAssignable(TypeId ty);
std::optional<TypeId> tryAssignOperandType(TypeId ty);

const NormalizedType* normalize(TypeId ty);
void step();
void infer();
bool done();

bool isFunctionGenericsSaturated(const FunctionType& ftv, DenseHashSet<TypeId>& instanceArgs);
void inferTypeFamilySubstitutions(TypeId ty, const TypeFamilyInstanceType* instance);
TypeFamilyInferenceResult inferNumericBinopFamily(const TypeFamilyInstanceType* instance);
TypeFamilyInferenceResult inferComparisonFamily(const TypeFamilyInstanceType* instance);
TypeFamilyInferenceResult inferOrAndFamily(const TypeFamilyInstanceType* instance);
TypeFamilyInferenceResult inferNotFamily(const TypeFamilyInstanceType* instance);
TypeFamilyInferenceResult inferLenFamily(const TypeFamilyInstanceType* instance);
TypeFamilyInferenceResult inferUnaryMinusFamily(const TypeFamilyInstanceType* instance);
};
} // namespace Luau
4 changes: 4 additions & 0 deletions Analysis/include/Luau/Unifier2.h
Expand Up @@ -86,6 +86,10 @@ struct Unifier2
*/
TypeId mkIntersection(TypeId left, TypeId right);

// Returns true if needle occurs within haystack already. ie if we bound
// needle to haystack, would a cyclic type result?
OccursCheckResult occursCheck(DenseHashSet<TypeId>& seen, TypeId needle, TypeId haystack);

// Returns true if needle occurs within haystack already. ie if we bound
// needle to haystack, would a cyclic TypePack result?
OccursCheckResult occursCheck(DenseHashSet<TypePackId>& seen, TypePackId needle, TypePackId haystack);
Expand Down

0 comments on commit ae459a0

Please sign in to comment.