Skip to content

Commit

Permalink
Sync to upstream/release/604 (#1106)
Browse files Browse the repository at this point in the history
New Solver

* New algorithm for inferring the types of locals that have no
annotations. This
algorithm is very conservative by default, but is augmented with some
control
  flow awareness to handle most common scenarios.
* Fix bugs in type inference of tables
* Improve performance of by switching out standard C++ containers for
`DenseHashMap`
* Infrastructure to support clearer error messages in strict mode

Native Code Generation

* Fix a lowering issue with buffer.writeu8 and 0x80-0xff values: A
constant
  argument wasn't truncated to the target type range and that causes an
  assertion failure in `build.mov`.
* Store full lightuserdata value in loop iteration protocol lowering
* Add analysis to compute function bytecode distribution
* This includes a class to analyze the bytecode operator distribution
per
function and a CLI tool that produces a JSON report. See the new cmake
      target `Luau.Bytecode.CLI`

---------

Co-authored-by: Aaron Weiss <[email protected]>
Co-authored-by: Alexander McCord <[email protected]>
Co-authored-by: Andy Friesen <[email protected]>
Co-authored-by: Aviral Goel <[email protected]>
Co-authored-by: Lily Brown <[email protected]>
Co-authored-by: Vyacheslav Egorov <[email protected]>
  • Loading branch information
6 people authored Nov 17, 2023
1 parent 298cd70 commit 74c5320
Show file tree
Hide file tree
Showing 58 changed files with 1,734 additions and 371 deletions.
5 changes: 5 additions & 0 deletions Analysis/include/Luau/Constraint.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,11 @@ struct UnpackConstraint
{
TypePackId resultPack;
TypePackId sourcePack;

// UnpackConstraint is sometimes used to resolve the types of assignments.
// When this is the case, any LocalTypes in resultPack can have their
// domains extended by the corresponding type from sourcePack.
bool resultIsLValue = false;
};

// resultType ~ refine type mode discriminant
Expand Down
18 changes: 11 additions & 7 deletions Analysis/include/Luau/ConstraintGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,13 @@ struct ConstraintGenerator
TypeIds types;
};

// During constraint generation, we only populate the Scope::bindings
// property for annotated symbols. Unannotated symbols must be handled in a
// postprocessing step because we have not yet allocated the types that will
// be assigned to those unannotated symbols, so we queue them up here.
std::map<Symbol, InferredBinding> inferredBindings;
// Some locals have multiple type states. We wish for Scope::bindings to
// map each local name onto the union of every type that the local can have
// over its lifetime, so we use this map to accumulate the set of types it
// might have.
//
// See the functions recordInferredBinding and fillInInferredBindings.
DenseHashMap<Symbol, InferredBinding> inferredBindings{{}};

// Constraints that go straight to the solver.
std::vector<ConstraintPtr> constraints;
Expand Down Expand Up @@ -245,8 +247,6 @@ struct ConstraintGenerator
std::optional<TypeId> checkLValue(const ScopePtr& scope, AstExprIndexExpr* indexExpr, TypeId assignedTy);
TypeId updateProperty(const ScopePtr& scope, AstExpr* expr, TypeId assignedTy);

void updateLValueType(AstExpr* lvalue, TypeId ty);

struct FunctionSignature
{
// The type of the function.
Expand Down Expand Up @@ -336,6 +336,10 @@ struct ConstraintGenerator
*/
void prepopulateGlobalScope(const ScopePtr& globalScope, AstStatBlock* program);

// Record the fact that a particular local has a particular type in at least
// one of its states.
void recordInferredBinding(AstLocal* local, TypeId ty);

void fillInInferredBindings(const ScopePtr& globalScope, AstStatBlock* block);

/** Given a function type annotation, return a vector describing the expected types of the calls to the function
Expand Down
15 changes: 12 additions & 3 deletions Analysis/include/Luau/DataFlowGraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,11 @@ struct DfgScope
DfgScope* parent;
bool isLoopScope;

DenseHashMap<Symbol, const Def*> bindings{Symbol{}};
DenseHashMap<const Def*, std::unordered_map<std::string, const Def*>> props{nullptr};
using Bindings = DenseHashMap<Symbol, const Def*>;
using Props = DenseHashMap<const Def*, std::unordered_map<std::string, const Def*>>;

Bindings bindings{Symbol{}};
Props props{nullptr};

std::optional<DefId> lookup(Symbol symbol) const;
std::optional<DefId> lookup(DefId def, const std::string& key) const;
Expand Down Expand Up @@ -115,7 +118,13 @@ struct DataFlowGraphBuilder
std::vector<std::unique_ptr<DfgScope>> scopes;

DfgScope* childScope(DfgScope* scope, bool isLoopScope = false);
void join(DfgScope* parent, DfgScope* a, DfgScope* b);

void join(DfgScope* p, DfgScope* a, DfgScope* b);
void joinBindings(DfgScope::Bindings& p, const DfgScope::Bindings& a, const DfgScope::Bindings& b);
void joinProps(DfgScope::Props& p, const DfgScope::Props& a, const DfgScope::Props& b);

DefId lookup(DfgScope* scope, Symbol symbol);
DefId lookup(DfgScope* scope, DefId def, const std::string& key);

ControlFlow visit(DfgScope* scope, AstStatBlock* b);
ControlFlow visitBlockWithoutChildScope(DfgScope* scope, AstStatBlock* b);
Expand Down
1 change: 1 addition & 0 deletions Analysis/include/Luau/Def.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ struct DefArena

DefId freshCell(bool subscripted = false);
DefId phi(DefId a, DefId b);
DefId phi(const std::vector<DefId>& defs);
};

} // namespace Luau
1 change: 1 addition & 0 deletions Analysis/include/Luau/Scope.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ struct Scope
void addBuiltinTypeBinding(const Name& name, const TypeFun& tyFun);

std::optional<TypeId> lookup(Symbol sym) const;
std::optional<TypeId> lookupUnrefinedType(DefId def) const;
std::optional<TypeId> lookup(DefId def) const;
std::optional<std::pair<TypeId, Scope*>> lookupEx(DefId def);
std::optional<std::pair<Binding*, Scope*>> lookupEx(Symbol sym);
Expand Down
68 changes: 67 additions & 1 deletion Analysis/include/Luau/Set.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@ template<typename T, typename Hash = SetHashDefault<T>>
class Set
{
private:
DenseHashMap<T, bool, Hash> mapping;
using Impl = DenseHashMap<T, bool, Hash>;
Impl mapping;
size_t entryCount = 0;

public:
class const_iterator;
using iterator = const_iterator;

Set(const T& empty_key)
: mapping{empty_key}
{
Expand Down Expand Up @@ -83,6 +87,16 @@ class Set
return count(element) != 0;
}

const_iterator begin() const
{
return const_iterator(mapping.begin(), mapping.end());
}

const_iterator end() const
{
return const_iterator(mapping.end(), mapping.end());
}

bool operator==(const Set<T>& there) const
{
// if the sets are unequal sizes, then they cannot possibly be equal.
Expand All @@ -100,6 +114,58 @@ class Set
// otherwise, we've proven the two equal!
return true;
}

class const_iterator
{
public:
const_iterator(typename Impl::const_iterator impl, typename Impl::const_iterator end)
: impl(impl)
, end(end)
{}

const T& operator*() const
{
return impl->first;
}

const T* operator->() const
{
return &impl->first;
}


bool operator==(const const_iterator& other) const
{
return impl == other.impl;
}

bool operator!=(const const_iterator& other) const
{
return impl != other.impl;
}


const_iterator& operator++()
{
do
{
impl++;
} while (impl != end && impl->second == false);
// keep iterating past pairs where the value is `false`

return *this;
}

const_iterator operator++(int)
{
const_iterator res = *this;
++*this;
return res;
}
private:
typename Impl::const_iterator impl;
typename Impl::const_iterator end;
};
};

} // namespace Luau
13 changes: 12 additions & 1 deletion Analysis/include/Luau/Subtyping.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,19 @@ struct TypeArena;
struct Scope;
struct TableIndexer;

enum class SubtypingVariance
{
// Used for an empty key. Should never appear in actual code.
Invalid,
Covariant,
Invariant,
};

struct SubtypingReasoning
{
Path subPath;
Path superPath;
SubtypingVariance variance = SubtypingVariance::Covariant;

bool operator==(const SubtypingReasoning& other) const;
};
Expand All @@ -49,7 +58,8 @@ struct SubtypingResult

/// The reason for isSubtype to be false. May not be present even if
/// isSubtype is false, depending on the input types.
DenseHashSet<SubtypingReasoning, SubtypingReasoningHash> reasoning{SubtypingReasoning{}};
DenseHashSet<SubtypingReasoning, SubtypingReasoningHash> reasoning{
SubtypingReasoning{TypePath::kEmpty, TypePath::kEmpty, SubtypingVariance::Invalid}};

SubtypingResult& andAlso(const SubtypingResult& other);
SubtypingResult& orElse(const SubtypingResult& other);
Expand All @@ -59,6 +69,7 @@ struct SubtypingResult
SubtypingResult& withBothPath(TypePath::Path path);
SubtypingResult& withSubPath(TypePath::Path path);
SubtypingResult& withSuperPath(TypePath::Path path);
SubtypingResult& withVariance(SubtypingVariance variance);

// Only negates the `isSubtype`.
static SubtypingResult negate(const SubtypingResult& result);
Expand Down
20 changes: 19 additions & 1 deletion Analysis/include/Luau/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,24 @@ struct FreeType
TypeId upperBound = nullptr;
};

/** A type that tracks the domain of a local variable.
*
* We consider each local's domain to be the union of all types assigned to it.
* We accomplish this with LocalType. Each time we dispatch an assignment to a
* local, we accumulate this union and decrement blockCount.
*
* When blockCount reaches 0, we can consider the LocalType to be "fully baked"
* and replace it with the union we've built.
*/
struct LocalType
{
TypeId domain;
int blockCount = 0;

// Used for debugging
std::string name;
};

struct GenericType
{
// By default, generics are global, with a synthetic name
Expand Down Expand Up @@ -623,7 +641,7 @@ struct NegationType
using ErrorType = Unifiable::Error;

using TypeVariant =
Unifiable::Variant<TypeId, FreeType, GenericType, PrimitiveType, BlockedType, PendingExpansionType, SingletonType, FunctionType, TableType,
Unifiable::Variant<TypeId, FreeType, LocalType, GenericType, PrimitiveType, BlockedType, PendingExpansionType, SingletonType, FunctionType, TableType,
MetatableType, ClassType, AnyType, UnionType, IntersectionType, LazyType, UnknownType, NeverType, NegationType, TypeFamilyInstanceType>;

struct Type final
Expand Down
9 changes: 9 additions & 0 deletions Analysis/include/Luau/VisitType.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ struct GenericTypeVisitor
{
return visit(ty);
}
virtual bool visit(TypeId ty, const LocalType& ftv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const GenericType& gtv)
{
return visit(ty);
Expand Down Expand Up @@ -241,6 +245,11 @@ struct GenericTypeVisitor
else
visit(ty, *ftv);
}
else if (auto lt = get<LocalType>(ty))
{
if (visit(ty, *lt))
traverse(lt->domain);
}
else if (auto gtv = get<GenericType>(ty))
visit(ty, *gtv);
else if (auto etv = get<ErrorType>(ty))
Expand Down
11 changes: 11 additions & 0 deletions Analysis/src/Clone.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,11 @@ class TypeCloner2
t->upperBound = shallowClone(t->upperBound);
}

void cloneChildren(LocalType* t)
{
t->domain = shallowClone(t->domain);
}

void cloneChildren(GenericType* t)
{
// TOOD: clone upper bounds.
Expand Down Expand Up @@ -504,6 +509,7 @@ struct TypeCloner
void defaultClone(const T& t);

void operator()(const FreeType& t);
void operator()(const LocalType& t);
void operator()(const GenericType& t);
void operator()(const BoundType& t);
void operator()(const ErrorType& t);
Expand Down Expand Up @@ -631,6 +637,11 @@ void TypeCloner::operator()(const FreeType& t)
defaultClone(t);
}

void TypeCloner::operator()(const LocalType& t)
{
defaultClone(t);
}

void TypeCloner::operator()(const GenericType& t)
{
defaultClone(t);
Expand Down
Loading

0 comments on commit 74c5320

Please sign in to comment.