Skip to content

Commit

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

* Fix a case where the stack wasn't completely cleaned up where
`debug.info` errored when passed `"f"` option and a thread.
* Fix a case of uninitialized field in `luaF_newproto`.

### New Type Solver

* When a local is captured in a function, don't add a new entry to the
`DfgScope::bindings` if the capture occurs within a loop.
* Fix a poor performance characteristic during unification by not trying
to simplify an intersection.
* Fix a case of multiple constraints mutating the same blocked type
causing incorrect inferences.
* Fix a case of assertion failure when overload resolution encounters a
return typepack mismatch.
* When refining a property of the top `table` type, we no longer signal
an unknown property error.
* Fix a misuse of free types when trying to infer the type of a
subscript expression.
* Fix a case of assertion failure when trying to resolve an overload
from `never`.

### Native Code Generation

* Fix dead store optimization issues caused by partial stores.

---

### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@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>

---------

Co-authored-by: Aaron Weiss <aaronweiss@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 15, 2024
1 parent a768311 commit d21b6fd
Show file tree
Hide file tree
Showing 54 changed files with 1,683 additions and 261 deletions.
24 changes: 23 additions & 1 deletion Analysis/include/Luau/Constraint.h
Expand Up @@ -157,6 +157,15 @@ struct HasPropConstraint
std::string prop;
ValueContext context;

// We want to track if this `HasPropConstraint` comes from a conditional.
// If it does, we're going to change the behavior of property look-up a bit.
// In particular, we're going to return `unknownType` for property lookups
// on `table` or inexact table types where the property is not present.
//
// This allows us to refine table types to have additional properties
// without reporting errors in typechecking on the property tests.
bool inConditional = false;

// HACK: We presently need types like true|false or string|"hello" when
// deciding whether a particular literal expression should have a singleton
// type. This boolean is set to true when extracting the property type of a
Expand Down Expand Up @@ -193,6 +202,19 @@ struct SetPropConstraint
TypeId propType;
};

// resultType ~ hasIndexer subjectType indexType
//
// If the subject type is a table or table-like thing that supports indexing,
// populate the type result with the result type of such an index operation.
//
// If the subject is not indexable, resultType is bound to errorType.
struct HasIndexerConstraint
{
TypeId resultType;
TypeId subjectType;
TypeId indexType;
};

// result ~ setIndexer subjectType indexType propType
//
// If the subject is a table or table-like thing that already has an indexer,
Expand Down Expand Up @@ -267,7 +289,7 @@ struct ReducePackConstraint

using ConstraintV = Variant<SubtypeConstraint, PackSubtypeConstraint, GeneralizationConstraint, InstantiationConstraint, IterableConstraint,
NameConstraint, TypeAliasExpansionConstraint, FunctionCallConstraint, FunctionCheckConstraint, PrimitiveTypeConstraint, HasPropConstraint,
SetPropConstraint, SetIndexerConstraint, SingletonOrTopTypeConstraint, UnpackConstraint, SetOpConstraint, ReduceConstraint, ReducePackConstraint,
SetPropConstraint, HasIndexerConstraint, SetIndexerConstraint, SingletonOrTopTypeConstraint, UnpackConstraint, SetOpConstraint, ReduceConstraint, ReducePackConstraint,
EqualityConstraint>;

struct Constraint
Expand Down
2 changes: 2 additions & 0 deletions Analysis/include/Luau/ConstraintGenerator.h
Expand Up @@ -71,6 +71,8 @@ struct ConstraintGenerator
// This is null when the CG is initially constructed.
Scope* rootScope;

TypeContext typeContext = TypeContext::Default;

struct InferredBinding
{
Scope* scope;
Expand Down
8 changes: 6 additions & 2 deletions Analysis/include/Luau/ConstraintSolver.h
Expand Up @@ -131,6 +131,10 @@ struct ConstraintSolver
bool tryDispatch(const PrimitiveTypeConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const HasPropConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const SetPropConstraint& c, NotNull<const Constraint> constraint, bool force);

bool tryDispatchHasIndexer(int& recursionDepth, NotNull<const Constraint> constraint, TypeId subjectType, TypeId indexType, TypeId resultType);
bool tryDispatch(const HasIndexerConstraint& c, NotNull<const Constraint> constraint);

bool tryDispatch(const SetIndexerConstraint& c, NotNull<const Constraint> constraint, bool force);
bool tryDispatch(const SingletonOrTopTypeConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const UnpackConstraint& c, NotNull<const Constraint> constraint);
Expand All @@ -148,9 +152,9 @@ struct ConstraintSolver
TypeId nextTy, TypeId tableTy, TypeId firstIndexTy, const IterableConstraint& c, NotNull<const Constraint> constraint, bool force);

std::pair<std::vector<TypeId>, std::optional<TypeId>> lookupTableProp(
TypeId subjectType, const std::string& propName, ValueContext context, bool suppressSimplification = false);
TypeId subjectType, const std::string& propName, ValueContext context, bool inConditional = false, bool suppressSimplification = false);
std::pair<std::vector<TypeId>, std::optional<TypeId>> lookupTableProp(
TypeId subjectType, const std::string& propName, ValueContext context, bool suppressSimplification, DenseHashSet<TypeId>& seen);
TypeId subjectType, const std::string& propName, ValueContext context, bool inConditional, bool suppressSimplification, DenseHashSet<TypeId>& seen);

void block(NotNull<const Constraint> target, NotNull<const Constraint> constraint);
/**
Expand Down
16 changes: 12 additions & 4 deletions Analysis/include/Luau/Set.h
@@ -1,8 +1,12 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once

#include "Luau/Common.h"
#include "Luau/DenseHash.h"

LUAU_FASTFLAG(LuauFixSetIter)
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)

namespace Luau
{

Expand Down Expand Up @@ -124,10 +128,15 @@ class Set
using difference_type = ptrdiff_t;
using iterator_category = std::forward_iterator_tag;

const_iterator(typename Impl::const_iterator impl, typename Impl::const_iterator end)
: impl(impl)
, end(end)
const_iterator(typename Impl::const_iterator impl_, typename Impl::const_iterator end_)
: impl(impl_)
, end(end_)
{
if (FFlag::LuauFixSetIter || FFlag::DebugLuauDeferredConstraintResolution)
{
while (impl != end && impl->second == false)
++impl;
}
}

const T& operator*() const
Expand All @@ -140,7 +149,6 @@ class Set
return &impl->first;
}


bool operator==(const const_iterator& other) const
{
return impl == other.impl;
Expand Down
5 changes: 5 additions & 0 deletions Analysis/include/Luau/Type.h
Expand Up @@ -33,6 +33,7 @@ struct Scope;
using ScopePtr = std::shared_ptr<Scope>;

struct TypeFamily;
struct Constraint;

/**
* There are three kinds of type variables:
Expand Down Expand Up @@ -144,6 +145,10 @@ struct BlockedType
{
BlockedType();
int index;

// The constraint that is intended to unblock this type. Other constraints
// should block on this constraint if present.
Constraint* owner = nullptr;
};

struct PrimitiveType
Expand Down
31 changes: 31 additions & 0 deletions Analysis/include/Luau/TypeUtils.h
Expand Up @@ -22,6 +22,37 @@ enum class ValueContext
RValue
};

/// the current context of the type checker
enum class TypeContext
{
/// the default context
Default,
/// inside of a condition
Condition,
};

bool inConditional(const TypeContext& context);

// sets the given type context to `Condition` and restores it to its original
// value when the struct drops out of scope
struct InConditionalContext
{
TypeContext* typeContext;
TypeContext oldValue;

InConditionalContext(TypeContext* c)
: typeContext(c)
, oldValue(*c)
{
*typeContext = TypeContext::Condition;
}

~InConditionalContext()
{
*typeContext = oldValue;
}
};

using ScopePtr = std::shared_ptr<struct Scope>;

std::optional<TypeId> findMetatableEntry(
Expand Down
3 changes: 1 addition & 2 deletions Analysis/src/Autocomplete.cpp
Expand Up @@ -15,7 +15,6 @@

LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
LUAU_FASTFLAGVARIABLE(LuauAutocompleteStringLiteralBounds, false);
LUAU_FASTFLAGVARIABLE(LuauAutocompleteTableKeysNoInitialCharacter, false);

static const std::unordered_set<std::string> kStatementStartingKeywords = {
"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
Expand Down Expand Up @@ -1741,7 +1740,7 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
}
}
}
else if (AstExprTable* exprTable = node->as<AstExprTable>(); exprTable && FFlag::LuauAutocompleteTableKeysNoInitialCharacter)
else if (AstExprTable* exprTable = node->as<AstExprTable>())
{
AutocompleteEntryMap result;

Expand Down
66 changes: 45 additions & 21 deletions Analysis/src/ConstraintGenerator.cpp
Expand Up @@ -1175,7 +1175,10 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatCompoundAss

ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatIf* ifStatement)
{
RefinementId refinement = check(scope, ifStatement->condition, std::nullopt).refinement;
RefinementId refinement = [&](){
InConditionalContext flipper{&typeContext};
return check(scope, ifStatement->condition, std::nullopt).refinement;
}();

ScopePtr thenScope = childScope(ifStatement->thenbody, scope);
applyRefinements(thenScope, ifStatement->condition->location, refinement);
Expand Down Expand Up @@ -1910,7 +1913,7 @@ Inference ConstraintGenerator::checkIndexName(const ScopePtr& scope, const Refin
scope->rvalueRefinements[key->def] = result;
}

addConstraint(scope, indexee->location, HasPropConstraint{result, obj, std::move(index), ValueContext::RValue});
addConstraint(scope, indexee->location, HasPropConstraint{result, obj, std::move(index), ValueContext::RValue, inConditional(typeContext)});

if (key)
return Inference{result, refinementArena.proposition(key, builtinTypes->truthyType)};
Expand All @@ -1935,7 +1938,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprIndexExpr* in
TypeId obj = check(scope, indexExpr->expr).ty;
TypeId indexType = check(scope, indexExpr->index).ty;

TypeId result = freshType(scope);
TypeId result = arena->addType(BlockedType{});

const RefinementKey* key = dfg->getRefinementKey(indexExpr);
if (key)
Expand All @@ -1946,10 +1949,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprIndexExpr* in
scope->rvalueRefinements[key->def] = result;
}

TableIndexer indexer{indexType, result};
TypeId tableType = arena->addType(TableType{TableType::Props{}, TableIndexer{indexType, result}, TypeLevel{}, scope.get(), TableState::Free});

addConstraint(scope, indexExpr->expr->location, SubtypeConstraint{obj, tableType});
addConstraint(scope, indexExpr->expr->location, HasIndexerConstraint{result, obj, indexType});

if (key)
return Inference{result, refinementArena.proposition(key, builtinTypes->truthyType)};
Expand Down Expand Up @@ -2200,8 +2200,11 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprBinary* binar

Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprIfElse* ifElse, std::optional<TypeId> expectedType)
{
ScopePtr condScope = childScope(ifElse->condition, scope);
RefinementId refinement = check(condScope, ifElse->condition).refinement;
RefinementId refinement = [&](){
InConditionalContext flipper{&typeContext};
ScopePtr condScope = childScope(ifElse->condition, scope);
return check(condScope, ifElse->condition).refinement;
}();

ScopePtr thenScope = childScope(ifElse->trueExpr, scope);
applyRefinements(thenScope, ifElse->trueExpr->location, refinement);
Expand Down Expand Up @@ -2406,10 +2409,17 @@ std::optional<TypeId> ConstraintGenerator::checkLValue(const ScopePtr& scope, As

if (transform)
{
addConstraint(scope, local->location,
Constraint* owner = nullptr;
if (auto blocked = get<BlockedType>(*ty))
owner = blocked->owner;

auto unpackC = addConstraint(scope, local->location,
UnpackConstraint{arena->addTypePack({*ty}), arena->addTypePack({assignedTy}),
/*resultIsLValue*/ true});

if (owner)
unpackC->dependencies.push_back(NotNull{owner});

recordInferredBinding(local->local, *ty);
}

Expand Down Expand Up @@ -2538,14 +2548,15 @@ TypeId ConstraintGenerator::updateProperty(const ScopePtr& scope, AstExpr* expr,

TypeId updatedType = arena->addType(BlockedType{});
auto setC = addConstraint(scope, expr->location, SetPropConstraint{updatedType, subjectType, std::move(segmentStrings), assignedTy});
getMutable<BlockedType>(updatedType)->owner = setC.get();

TypeId prevSegmentTy = updatedType;
for (size_t i = 0; i < segments.size(); ++i)
{
TypeId segmentTy = arena->addType(BlockedType{});
module->astTypes[exprs[i]] = segmentTy;
ValueContext ctx = i == segments.size() - 1 ? ValueContext::LValue : ValueContext::RValue;
auto hasC = addConstraint(scope, expr->location, HasPropConstraint{segmentTy, prevSegmentTy, segments[i], ctx});
auto hasC = addConstraint(scope, expr->location, HasPropConstraint{segmentTy, prevSegmentTy, segments[i], ctx, inConditional(typeContext)});
setC->dependencies.push_back(hasC);
prevSegmentTy = segmentTy;
}
Expand Down Expand Up @@ -2582,16 +2593,12 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr,

interiorTypes.back().push_back(ty);

auto createIndexer = [this, scope, ttv](const Location& location, TypeId currentIndexType, TypeId currentResultType) {
if (!ttv->indexer)
{
TypeId indexType = this->freshType(scope);
TypeId resultType = this->freshType(scope);
ttv->indexer = TableIndexer{indexType, resultType};
}
TypeIds indexKeyLowerBound;
TypeIds indexValueLowerBound;

addConstraint(scope, location, SubtypeConstraint{ttv->indexer->indexType, currentIndexType});
addConstraint(scope, location, SubtypeConstraint{ttv->indexer->indexResultType, currentResultType});
auto createIndexer = [&indexKeyLowerBound, &indexValueLowerBound](const Location& location, TypeId currentIndexType, TypeId currentResultType) {
indexKeyLowerBound.insert(follow(currentIndexType));
indexValueLowerBound.insert(follow(currentResultType));
};

std::optional<TypeId> annotatedKeyType;
Expand Down Expand Up @@ -2633,7 +2640,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr,
expectedValueType = arena->addType(BlockedType{});
addConstraint(scope, item.value->location,
HasPropConstraint{
*expectedValueType, *expectedType, stringKey->value.data, ValueContext::RValue, /*suppressSimplification*/ true});
*expectedValueType, *expectedType, stringKey->value.data, ValueContext::RValue, /*inConditional*/ inConditional(typeContext), /*suppressSimplification*/ true});
}
}
}
Expand Down Expand Up @@ -2705,6 +2712,23 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr,
}
}

if (!indexKeyLowerBound.empty())
{
LUAU_ASSERT(!indexValueLowerBound.empty());

TypeId indexKey = indexKeyLowerBound.size() == 1
? *indexKeyLowerBound.begin()
: arena->addType(UnionType{std::vector(indexKeyLowerBound.begin(), indexKeyLowerBound.end())})
;

TypeId indexValue = indexValueLowerBound.size() == 1
? *indexValueLowerBound.begin()
: arena->addType(UnionType{std::vector(indexValueLowerBound.begin(), indexValueLowerBound.end())})
;

ttv->indexer = TableIndexer{indexKey, indexValue};
}

return Inference{ty};
}

Expand Down

0 comments on commit d21b6fd

Please sign in to comment.