mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 14:25:44 +08:00
74c532053f
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 <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
197 lines
7.7 KiB
C++
197 lines
7.7 KiB
C++
// 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/ControlFlow.h"
|
|
#include "Luau/DenseHash.h"
|
|
#include "Luau/Def.h"
|
|
#include "Luau/Symbol.h"
|
|
#include "Luau/TypedAllocator.h"
|
|
|
|
#include <unordered_map>
|
|
|
|
namespace Luau
|
|
{
|
|
|
|
struct RefinementKey
|
|
{
|
|
const RefinementKey* parent = nullptr;
|
|
DefId def;
|
|
std::optional<std::string> propName;
|
|
};
|
|
|
|
struct RefinementKeyArena
|
|
{
|
|
TypedAllocator<RefinementKey> allocator;
|
|
|
|
const RefinementKey* leaf(DefId def);
|
|
const RefinementKey* node(const RefinementKey* parent, DefId def, const std::string& propName);
|
|
};
|
|
|
|
struct DataFlowGraph
|
|
{
|
|
DataFlowGraph(DataFlowGraph&&) = default;
|
|
DataFlowGraph& operator=(DataFlowGraph&&) = default;
|
|
|
|
DefId getDef(const AstExpr* expr) const;
|
|
// Look up for the rvalue def for a compound assignment.
|
|
std::optional<DefId> getRValueDefForCompoundAssign(const AstExpr* expr) const;
|
|
|
|
DefId getDef(const AstLocal* local) const;
|
|
|
|
DefId getDef(const AstStatDeclareGlobal* global) const;
|
|
DefId getDef(const AstStatDeclareFunction* func) const;
|
|
|
|
const RefinementKey* getRefinementKey(const AstExpr* expr) const;
|
|
|
|
private:
|
|
DataFlowGraph() = default;
|
|
|
|
DataFlowGraph(const DataFlowGraph&) = delete;
|
|
DataFlowGraph& operator=(const DataFlowGraph&) = delete;
|
|
|
|
DefArena defArena;
|
|
RefinementKeyArena keyArena;
|
|
|
|
DenseHashMap<const AstExpr*, const Def*> astDefs{nullptr};
|
|
|
|
// Sometimes we don't have the AstExprLocal* but we have AstLocal*, and sometimes we need to extract that DefId.
|
|
DenseHashMap<const AstLocal*, const Def*> localDefs{nullptr};
|
|
|
|
// There's no AstStatDeclaration, and it feels useless to introduce it just to enforce an invariant in one place.
|
|
// All keys in this maps are really only statements that ambiently declares a symbol.
|
|
DenseHashMap<const AstStat*, const Def*> declaredDefs{nullptr};
|
|
|
|
// Compound assignments are in a weird situation where the local being assigned to is also being used at its
|
|
// previous type implicitly in an rvalue position. This map provides the previous binding.
|
|
DenseHashMap<const AstExpr*, const Def*> compoundAssignDefs{nullptr};
|
|
|
|
DenseHashMap<const AstExpr*, const RefinementKey*> astRefinementKeys{nullptr};
|
|
|
|
friend struct DataFlowGraphBuilder;
|
|
};
|
|
|
|
struct DfgScope
|
|
{
|
|
DfgScope* parent;
|
|
bool isLoopScope;
|
|
|
|
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;
|
|
|
|
void inherit(const DfgScope* childScope);
|
|
|
|
bool canUpdateDefinition(Symbol symbol) const;
|
|
bool canUpdateDefinition(DefId def, const std::string& key) const;
|
|
};
|
|
|
|
struct DataFlowResult
|
|
{
|
|
DefId def;
|
|
const RefinementKey* parent = nullptr;
|
|
};
|
|
|
|
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> defArena{&graph.defArena};
|
|
NotNull<RefinementKeyArena> keyArena{&graph.keyArena};
|
|
|
|
struct InternalErrorReporter* handle = nullptr;
|
|
DfgScope* moduleScope = nullptr;
|
|
|
|
std::vector<std::unique_ptr<DfgScope>> scopes;
|
|
|
|
DfgScope* childScope(DfgScope* scope, bool isLoopScope = false);
|
|
|
|
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);
|
|
|
|
ControlFlow visit(DfgScope* scope, AstStat* s);
|
|
ControlFlow visit(DfgScope* scope, AstStatIf* i);
|
|
ControlFlow visit(DfgScope* scope, AstStatWhile* w);
|
|
ControlFlow visit(DfgScope* scope, AstStatRepeat* r);
|
|
ControlFlow visit(DfgScope* scope, AstStatBreak* b);
|
|
ControlFlow visit(DfgScope* scope, AstStatContinue* c);
|
|
ControlFlow visit(DfgScope* scope, AstStatReturn* r);
|
|
ControlFlow visit(DfgScope* scope, AstStatExpr* e);
|
|
ControlFlow visit(DfgScope* scope, AstStatLocal* l);
|
|
ControlFlow visit(DfgScope* scope, AstStatFor* f);
|
|
ControlFlow visit(DfgScope* scope, AstStatForIn* f);
|
|
ControlFlow visit(DfgScope* scope, AstStatAssign* a);
|
|
ControlFlow visit(DfgScope* scope, AstStatCompoundAssign* c);
|
|
ControlFlow visit(DfgScope* scope, AstStatFunction* f);
|
|
ControlFlow visit(DfgScope* scope, AstStatLocalFunction* l);
|
|
ControlFlow visit(DfgScope* scope, AstStatTypeAlias* t);
|
|
ControlFlow visit(DfgScope* scope, AstStatDeclareGlobal* d);
|
|
ControlFlow visit(DfgScope* scope, AstStatDeclareFunction* d);
|
|
ControlFlow visit(DfgScope* scope, AstStatDeclareClass* d);
|
|
ControlFlow visit(DfgScope* scope, AstStatError* error);
|
|
|
|
DataFlowResult visitExpr(DfgScope* scope, AstExpr* e);
|
|
DataFlowResult visitExpr(DfgScope* scope, AstExprGroup* group);
|
|
DataFlowResult visitExpr(DfgScope* scope, AstExprLocal* l);
|
|
DataFlowResult visitExpr(DfgScope* scope, AstExprGlobal* g);
|
|
DataFlowResult visitExpr(DfgScope* scope, AstExprCall* c);
|
|
DataFlowResult visitExpr(DfgScope* scope, AstExprIndexName* i);
|
|
DataFlowResult visitExpr(DfgScope* scope, AstExprIndexExpr* i);
|
|
DataFlowResult visitExpr(DfgScope* scope, AstExprFunction* f);
|
|
DataFlowResult visitExpr(DfgScope* scope, AstExprTable* t);
|
|
DataFlowResult visitExpr(DfgScope* scope, AstExprUnary* u);
|
|
DataFlowResult visitExpr(DfgScope* scope, AstExprBinary* b);
|
|
DataFlowResult visitExpr(DfgScope* scope, AstExprTypeAssertion* t);
|
|
DataFlowResult visitExpr(DfgScope* scope, AstExprIfElse* i);
|
|
DataFlowResult visitExpr(DfgScope* scope, AstExprInterpString* i);
|
|
DataFlowResult visitExpr(DfgScope* scope, AstExprError* error);
|
|
|
|
void visitLValue(DfgScope* scope, AstExpr* e, DefId incomingDef, bool isCompoundAssignment = false);
|
|
void visitLValue(DfgScope* scope, AstExprLocal* l, DefId incomingDef, bool isCompoundAssignment);
|
|
void visitLValue(DfgScope* scope, AstExprGlobal* g, DefId incomingDef, bool isCompoundAssignment);
|
|
void visitLValue(DfgScope* scope, AstExprIndexName* i, DefId incomingDef);
|
|
void visitLValue(DfgScope* scope, AstExprIndexExpr* i, DefId incomingDef);
|
|
void visitLValue(DfgScope* scope, AstExprError* e, DefId incomingDef);
|
|
|
|
void visitType(DfgScope* scope, AstType* t);
|
|
void visitType(DfgScope* scope, AstTypeReference* r);
|
|
void visitType(DfgScope* scope, AstTypeTable* t);
|
|
void visitType(DfgScope* scope, AstTypeFunction* f);
|
|
void visitType(DfgScope* scope, AstTypeTypeof* t);
|
|
void visitType(DfgScope* scope, AstTypeUnion* u);
|
|
void visitType(DfgScope* scope, AstTypeIntersection* i);
|
|
void visitType(DfgScope* scope, AstTypeError* error);
|
|
|
|
void visitTypePack(DfgScope* scope, AstTypePack* p);
|
|
void visitTypePack(DfgScope* scope, AstTypePackExplicit* e);
|
|
void visitTypePack(DfgScope* scope, AstTypePackVariadic* v);
|
|
void visitTypePack(DfgScope* scope, AstTypePackGeneric* g);
|
|
|
|
void visitTypeList(DfgScope* scope, AstTypeList l);
|
|
|
|
void visitGenerics(DfgScope* scope, AstArray<AstGenericType> g);
|
|
void visitGenericPacks(DfgScope* scope, AstArray<AstGenericTypePack> g);
|
|
};
|
|
|
|
} // namespace Luau
|