Skip to content

Commit

Permalink
Sync to upstream/release/550 (#723)
Browse files Browse the repository at this point in the history
* Support `["prop"]` syntax on class definitions in definition files.
(#704)
* Improve type checking performance for complex overloaded functions
* Fix rare cases of incorrect stack traces for out of memory errors at
runtime
  • Loading branch information
andyfriesen authored Oct 21, 2022
1 parent 12ee140 commit 5432486
Show file tree
Hide file tree
Showing 104 changed files with 4,200 additions and 2,256 deletions.
2 changes: 2 additions & 0 deletions Analysis/include/Luau/Clone.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once

#include <Luau/NotNull.h>
#include "Luau/TypeArena.h"
#include "Luau/TypeVar.h"

Expand All @@ -26,5 +27,6 @@ TypeId clone(TypeId tp, TypeArena& dest, CloneState& cloneState);
TypeFun clone(const TypeFun& typeFun, TypeArena& dest, CloneState& cloneState);

TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log, bool alwaysClone = false);
TypeId shallowClone(TypeId ty, NotNull<TypeArena> dest);

} // namespace Luau
17 changes: 12 additions & 5 deletions Analysis/include/Luau/Constraint.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
#pragma once

#include "Luau/Ast.h" // Used for some of the enumerations
#include "Luau/Def.h"
#include "Luau/NotNull.h"
#include "Luau/Variant.h"
#include "Luau/TypeVar.h"
#include "Luau/Variant.h"

#include <string>
#include <memory>
Expand Down Expand Up @@ -131,9 +132,15 @@ struct HasPropConstraint
std::string prop;
};

using ConstraintV =
Variant<SubtypeConstraint, PackSubtypeConstraint, GeneralizationConstraint, InstantiationConstraint, UnaryConstraint, BinaryConstraint,
IterableConstraint, NameConstraint, TypeAliasExpansionConstraint, FunctionCallConstraint, PrimitiveTypeConstraint, HasPropConstraint>;
struct RefinementConstraint
{
DefId def;
TypeId discriminantType;
};

using ConstraintV = Variant<SubtypeConstraint, PackSubtypeConstraint, GeneralizationConstraint, InstantiationConstraint, UnaryConstraint,
BinaryConstraint, IterableConstraint, NameConstraint, TypeAliasExpansionConstraint, FunctionCallConstraint, PrimitiveTypeConstraint,
HasPropConstraint, RefinementConstraint>;

struct Constraint
{
Expand All @@ -143,7 +150,7 @@ struct Constraint
Constraint& operator=(const Constraint&) = delete;

NotNull<Scope> scope;
Location location;
Location location; // TODO: Extract this out into only the constraints that needs a location. Not all constraints needs locations.
ConstraintV c;

std::vector<NotNull<Constraint>> dependencies;
Expand Down
31 changes: 21 additions & 10 deletions Analysis/include/Luau/ConstraintGraphBuilder.h
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details

#pragma once

#include <memory>
#include <vector>
#include <unordered_map>

#include "Luau/Ast.h"
#include "Luau/Constraint.h"
#include "Luau/DataFlowGraphBuilder.h"
#include "Luau/Module.h"
#include "Luau/ModuleResolver.h"
#include "Luau/NotNull.h"
#include "Luau/Symbol.h"
#include "Luau/TypeVar.h"
#include "Luau/Variant.h"

#include <memory>
#include <vector>
#include <unordered_map>

namespace Luau
{

Expand Down Expand Up @@ -48,6 +48,7 @@ struct ConstraintGraphBuilder
DenseHashMap<const AstTypePack*, TypePackId> astResolvedTypePacks{nullptr};
// Defining scopes for AST nodes.
DenseHashMap<const AstStatTypeAlias*, ScopePtr> astTypeAliasDefiningScopes{nullptr};
NotNull<const DataFlowGraph> dfg;

int recursionCount = 0;

Expand All @@ -63,7 +64,8 @@ struct ConstraintGraphBuilder
DcrLogger* logger;

ConstraintGraphBuilder(const ModuleName& moduleName, ModulePtr module, TypeArena* arena, NotNull<ModuleResolver> moduleResolver,
NotNull<SingletonTypes> singletonTypes, NotNull<InternalErrorReporter> ice, const ScopePtr& globalScope, DcrLogger* logger);
NotNull<SingletonTypes> singletonTypes, NotNull<InternalErrorReporter> ice, const ScopePtr& globalScope, DcrLogger* logger,
NotNull<DataFlowGraph> dfg);

/**
* Fabricates a new free type belonging to a given scope.
Expand All @@ -88,15 +90,17 @@ struct ConstraintGraphBuilder
* Adds a new constraint with no dependencies to a given scope.
* @param scope the scope to add the constraint to.
* @param cv the constraint variant to add.
* @return the pointer to the inserted constraint
*/
void addConstraint(const ScopePtr& scope, const Location& location, ConstraintV cv);
NotNull<Constraint> addConstraint(const ScopePtr& scope, const Location& location, ConstraintV cv);

/**
* Adds a constraint to a given scope.
* @param scope the scope to add the constraint to. Must not be null.
* @param c the constraint to add.
* @return the pointer to the inserted constraint
*/
void addConstraint(const ScopePtr& scope, std::unique_ptr<Constraint> c);
NotNull<Constraint> addConstraint(const ScopePtr& scope, std::unique_ptr<Constraint> c);

/**
* The entry point to the ConstraintGraphBuilder. This will construct a set
Expand Down Expand Up @@ -139,13 +143,20 @@ struct ConstraintGraphBuilder
*/
TypeId check(const ScopePtr& scope, AstExpr* expr, std::optional<TypeId> expectedType = {});

TypeId check(const ScopePtr& scope, AstExprTable* expr, std::optional<TypeId> expectedType);
TypeId check(const ScopePtr& scope, AstExprLocal* local);
TypeId check(const ScopePtr& scope, AstExprGlobal* global);
TypeId check(const ScopePtr& scope, AstExprIndexName* indexName);
TypeId check(const ScopePtr& scope, AstExprIndexExpr* indexExpr);
TypeId check(const ScopePtr& scope, AstExprUnary* unary);
TypeId check(const ScopePtr& scope, AstExprBinary* binary);
TypeId check_(const ScopePtr& scope, AstExprUnary* unary);
TypeId check(const ScopePtr& scope, AstExprBinary* binary, std::optional<TypeId> expectedType);
TypeId check(const ScopePtr& scope, AstExprIfElse* ifElse, std::optional<TypeId> expectedType);
TypeId check(const ScopePtr& scope, AstExprTypeAssertion* typeAssert);
TypeId check(const ScopePtr& scope, AstExprTable* expr, std::optional<TypeId> expectedType);

TypePackId checkLValues(const ScopePtr& scope, AstArray<AstExpr*> exprs);

TypeId checkLValue(const ScopePtr& scope, AstExpr* expr);

struct FunctionSignature
{
Expand Down
3 changes: 3 additions & 0 deletions Analysis/include/Luau/ConstraintSolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ struct ConstraintSolver
bool tryDispatch(const FunctionCallConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const PrimitiveTypeConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const HasPropConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const RefinementConstraint& c, NotNull<const Constraint> constraint);

// for a, ... in some_table do
// also handles __iter metamethod
Expand Down Expand Up @@ -215,6 +216,8 @@ struct ConstraintSolver
TypeId errorRecoveryType() const;
TypePackId errorRecoveryTypePack() const;

TypeId unionOfTypes(TypeId a, TypeId b, NotNull<Scope> scope, bool unifyFreeTypes);

ToStringOptions opts;
};

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

// Do not include LValue. It should never be used here.
#include "Luau/Ast.h"
#include "Luau/DenseHash.h"
#include "Luau/Def.h"
#include "Luau/Symbol.h"

#include <unordered_map>

namespace Luau
{

struct DataFlowGraph
{
DataFlowGraph(DataFlowGraph&&) = default;
DataFlowGraph& operator=(DataFlowGraph&&) = default;

// TODO: AstExprLocal, AstExprGlobal, and AstLocal* are guaranteed never to return nullopt.
// We leave them to return an optional as we build it out, but the end state is for them to return a non-optional DefId.
std::optional<DefId> getDef(const AstExpr* expr) const;
std::optional<DefId> getDef(const AstLocal* local) const;

/// Retrieve the Def that corresponds to the given Symbol.
///
/// We do not perform dataflow analysis on globals, so this function always
/// yields nullopt when passed a global Symbol.
std::optional<DefId> getDef(const Symbol& symbol) const;

private:
DataFlowGraph() = default;

DataFlowGraph(const DataFlowGraph&) = delete;
DataFlowGraph& operator=(const DataFlowGraph&) = delete;

DefArena arena;
DenseHashMap<const AstExpr*, const Def*> astDefs{nullptr};
DenseHashMap<const AstLocal*, const Def*> localDefs{nullptr};

friend struct DataFlowGraphBuilder;
};

struct DfgScope
{
DfgScope* parent;
DenseHashMap<Symbol, const Def*> bindings{Symbol{}};
};

struct ExpressionFlowGraph
{
std::optional<DefId> def;
};

// Currently unsound. We do not presently track the control flow of the program.
// Additionally, we do not presently track assignments.
struct DataFlowGraphBuilder
{
static DataFlowGraph build(AstStatBlock* root, NotNull<struct InternalErrorReporter> handle);

private:
DataFlowGraphBuilder() = default;

DataFlowGraphBuilder(const DataFlowGraphBuilder&) = delete;
DataFlowGraphBuilder& operator=(const DataFlowGraphBuilder&) = delete;

DataFlowGraph graph;
NotNull<DefArena> arena{&graph.arena};
struct InternalErrorReporter* handle;
std::vector<std::unique_ptr<DfgScope>> scopes;

DfgScope* childScope(DfgScope* scope);

std::optional<DefId> use(DfgScope* scope, Symbol symbol, AstExpr* e);

void visit(DfgScope* scope, AstStatBlock* b);
void visitBlockWithoutChildScope(DfgScope* scope, AstStatBlock* b);

// TODO: visit type aliases
void visit(DfgScope* scope, AstStat* s);
void visit(DfgScope* scope, AstStatIf* i);
void visit(DfgScope* scope, AstStatWhile* w);
void visit(DfgScope* scope, AstStatRepeat* r);
void visit(DfgScope* scope, AstStatBreak* b);
void visit(DfgScope* scope, AstStatContinue* c);
void visit(DfgScope* scope, AstStatReturn* r);
void visit(DfgScope* scope, AstStatExpr* e);
void visit(DfgScope* scope, AstStatLocal* l);
void visit(DfgScope* scope, AstStatFor* f);
void visit(DfgScope* scope, AstStatForIn* f);
void visit(DfgScope* scope, AstStatAssign* a);
void visit(DfgScope* scope, AstStatCompoundAssign* c);
void visit(DfgScope* scope, AstStatFunction* f);
void visit(DfgScope* scope, AstStatLocalFunction* l);

ExpressionFlowGraph visitExpr(DfgScope* scope, AstExpr* e);
ExpressionFlowGraph visitExpr(DfgScope* scope, AstExprLocal* l);
ExpressionFlowGraph visitExpr(DfgScope* scope, AstExprGlobal* g);
ExpressionFlowGraph visitExpr(DfgScope* scope, AstExprCall* c);
ExpressionFlowGraph visitExpr(DfgScope* scope, AstExprIndexName* i);
ExpressionFlowGraph visitExpr(DfgScope* scope, AstExprIndexExpr* i);
ExpressionFlowGraph visitExpr(DfgScope* scope, AstExprFunction* f);
ExpressionFlowGraph visitExpr(DfgScope* scope, AstExprTable* t);
ExpressionFlowGraph visitExpr(DfgScope* scope, AstExprUnary* u);
ExpressionFlowGraph visitExpr(DfgScope* scope, AstExprBinary* b);
ExpressionFlowGraph visitExpr(DfgScope* scope, AstExprTypeAssertion* t);
ExpressionFlowGraph visitExpr(DfgScope* scope, AstExprIfElse* i);
ExpressionFlowGraph visitExpr(DfgScope* scope, AstExprInterpString* i);

// TODO: visitLValue
// TODO: visitTypes (because of typeof which has access to values namespace, needs unreachable scope)
// TODO: visitTypePacks (because of typeof which has access to values namespace, needs unreachable scope)
};

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

#include "Luau/NotNull.h"
#include "Luau/TypedAllocator.h"
#include "Luau/Variant.h"

namespace Luau
{

using Def = Variant<struct Undefined, struct Phi>;

/**
* We statically approximate a value at runtime using a symbolic value, which we call a Def.
*
* DataFlowGraphBuilder will allocate these defs as a stand-in for some Luau values, and bind them to places that
* can hold a Luau value, and then observes how those defs will commute as it statically evaluate the program.
*
* It must also be noted that defs are a cyclic graph, so it is not safe to recursively traverse into it expecting it to terminate.
*/
using DefId = NotNull<const Def>;

/**
* A "single-object" value.
*
* Leaky implementation note: sometimes "multiple-object" values, but none of which were interesting enough to warrant creating a phi node instead.
* That can happen because there's no point in creating a phi node that points to either resultant in `if math.random() > 0.5 then 5 else "hello"`.
* This might become of utmost importance if we wanted to do some backward reasoning, e.g. if `5` is taken, then `cond` must be `truthy`.
*/
struct Undefined
{
};

/**
* A phi node is a union of defs.
*
* We need this because we're statically evaluating a program, and sometimes a place may be assigned with
* different defs, and when that happens, we need a special data type that merges in all the defs
* that will flow into that specific place. For example, consider this simple program:
*
* ```
* x-1
* if cond() then
* x-2 = 5
* else
* x-3 = "hello"
* end
* x-4 : {x-2, x-3}
* ```
*
* At x-4, we know for a fact statically that either `5` or `"hello"` can flow into the variable `x` after the branch, but
* we cannot make any definitive decisions about which one, so we just take in both.
*/
struct Phi
{
std::vector<DefId> operands;
};

template<typename T>
T* getMutable(DefId def)
{
return get_if<T>(def.get());
}

template<typename T>
const T* get(DefId def)
{
return getMutable<T>(def);
}

struct DefArena
{
TypedAllocator<Def> allocator;

DefId freshDef();
};

} // namespace Luau
2 changes: 2 additions & 0 deletions Analysis/include/Luau/LValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ struct TypeVar;
using TypeId = const TypeVar*;

struct Field;

// Deprecated. Do not use in new work.
using LValue = Variant<Symbol, Field>;

struct Field
Expand Down
32 changes: 32 additions & 0 deletions Analysis/include/Luau/Metamethods.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// 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 <unordered_map>

namespace Luau
{

static const std::unordered_map<AstExprBinary::Op, const char*> kBinaryOpMetamethods{
{AstExprBinary::Op::CompareEq, "__eq"},
{AstExprBinary::Op::CompareNe, "__eq"},
{AstExprBinary::Op::CompareGe, "__lt"},
{AstExprBinary::Op::CompareGt, "__le"},
{AstExprBinary::Op::CompareLe, "__le"},
{AstExprBinary::Op::CompareLt, "__lt"},
{AstExprBinary::Op::Add, "__add"},
{AstExprBinary::Op::Sub, "__sub"},
{AstExprBinary::Op::Mul, "__mul"},
{AstExprBinary::Op::Div, "__div"},
{AstExprBinary::Op::Pow, "__pow"},
{AstExprBinary::Op::Mod, "__mod"},
{AstExprBinary::Op::Concat, "__concat"},
};

static const std::unordered_map<AstExprUnary::Op, const char*> kUnaryOpMetamethods{
{AstExprUnary::Op::Minus, "__unm"},
{AstExprUnary::Op::Len, "__len"},
};

} // namespace Luau
Loading

0 comments on commit 5432486

Please sign in to comment.