Skip to content

Commit

Permalink
Sync to upstream/release/553 (#751)
Browse files Browse the repository at this point in the history
* Autocomplete support for interpolated strings.
* Improved parse errors in various situations involving interpolated
strings.
  • Loading branch information
andyfriesen authored Nov 18, 2022
1 parent aa7c645 commit 95d9c6d
Show file tree
Hide file tree
Showing 12 changed files with 300 additions and 187 deletions.
19 changes: 18 additions & 1 deletion Analysis/include/Luau/Constraint.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,23 @@ struct HasPropConstraint
std::string prop;
};

// result ~ setProp subjectType ["prop", "prop2", ...] propType
//
// If the subject is a table or table-like thing that already has the named
// property chain, we unify propType with that existing property type.
//
// If the subject is a free table, we augment it in place.
//
// If the subject is an unsealed table, result is an augmented table that
// includes that new prop.
struct SetPropConstraint
{
TypeId resultType;
TypeId subjectType;
std::vector<std::string> path;
TypeId propType;
};

// result ~ if isSingleton D then ~D else unknown where D = discriminantType
struct SingletonOrTopTypeConstraint
{
Expand All @@ -141,7 +158,7 @@ struct SingletonOrTopTypeConstraint

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

struct Constraint
{
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 SetPropConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const SingletonOrTopTypeConstraint& c, NotNull<const Constraint> constraint);

// for a, ... in some_table do
Expand All @@ -120,6 +121,8 @@ struct ConstraintSolver
bool tryDispatchIterableFunction(
TypeId nextTy, TypeId tableTy, TypeId firstIndexTy, const IterableConstraint& c, NotNull<const Constraint> constraint, bool force);

std::optional<TypeId> lookupTableProp(TypeId subjectType, const std::string& propName);

void block(NotNull<const Constraint> target, NotNull<const Constraint> constraint);
/**
* Block a constraint on the resolution of a TypeVar.
Expand Down
124 changes: 32 additions & 92 deletions Analysis/src/ConstraintGraphBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "Luau/Scope.h"
#include "Luau/ToString.h"
#include "Luau/TypeUtils.h"
#include "Luau/TypeVar.h"

LUAU_FASTINT(LuauCheckRecursionLimit);
LUAU_FASTFLAG(DebugLuauLogSolverToJson);
Expand Down Expand Up @@ -1019,7 +1020,22 @@ InferencePack ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExprCa
args.push_back(check(scope, arg).ty);
}

// TODO self
if (call->self)
{
AstExprIndexName* indexExpr = call->func->as<AstExprIndexName>();
if (!indexExpr)
ice->ice("method call expression has no 'self'");

// The call to `check` we already did on `call->func` should have already produced a type for
// `indexExpr->expr`, so we can get it from `astTypes` to avoid exponential blow-up.
TypeId selfType = astTypes[indexExpr->expr];

// If we don't have a type for self, it means we had a code too complex error already.
if (selfType == nullptr)
selfType = singletonTypes->errorRecoveryType();

args.insert(args.begin(), selfType);
}

if (matchSetmetatable(*call))
{
Expand Down Expand Up @@ -1428,13 +1444,6 @@ TypePackId ConstraintGraphBuilder::checkLValues(const ScopePtr& scope, AstArray<
return arena->addTypePack(std::move(types));
}

static bool isUnsealedTable(TypeId ty)
{
ty = follow(ty);
const TableTypeVar* ttv = get<TableTypeVar>(ty);
return ttv && ttv->state == TableState::Unsealed;
};

/**
* If the expr is a dotted set of names, and if the root symbol refers to an
* unsealed table, return that table type, plus the indeces that follow as a
Expand Down Expand Up @@ -1468,80 +1477,6 @@ static std::optional<std::pair<Symbol, std::vector<const char*>>> extractDottedN
return std::nullopt;
}

/**
* Create a shallow copy of `ty` and its properties along `path`. Insert a new
* property (the last segment of `path`) into the tail table with the value `t`.
*
* On success, returns the new outermost table type. If the root table or any
* of its subkeys are not unsealed tables, the function fails and returns
* std::nullopt.
*
* TODO: Prove that we completely give up in the face of indexers and
* metatables.
*/
static std::optional<TypeId> updateTheTableType(NotNull<TypeArena> arena, TypeId ty, const std::vector<const char*>& path, TypeId replaceTy)
{
if (path.empty())
return std::nullopt;

// First walk the path and ensure that it's unsealed tables all the way
// to the end.
{
TypeId t = ty;
for (size_t i = 0; i < path.size() - 1; ++i)
{
if (!isUnsealedTable(t))
return std::nullopt;

const TableTypeVar* tbl = get<TableTypeVar>(t);
auto it = tbl->props.find(path[i]);
if (it == tbl->props.end())
return std::nullopt;

t = it->second.type;
}

// The last path segment should not be a property of the table at all.
// We are not changing property types. We are only admitting this one
// new property to be appended.
if (!isUnsealedTable(t))
return std::nullopt;
const TableTypeVar* tbl = get<TableTypeVar>(t);
auto it = tbl->props.find(path.back());
if (it != tbl->props.end())
return std::nullopt;
}

const TypeId res = shallowClone(ty, arena);
TypeId t = res;

for (size_t i = 0; i < path.size() - 1; ++i)
{
const std::string segment = path[i];

TableTypeVar* ttv = getMutable<TableTypeVar>(t);
LUAU_ASSERT(ttv);

auto propIt = ttv->props.find(segment);
if (propIt != ttv->props.end())
{
LUAU_ASSERT(isUnsealedTable(propIt->second.type));
t = shallowClone(follow(propIt->second.type), arena);
ttv->props[segment].type = t;
}
else
return std::nullopt;
}

TableTypeVar* ttv = getMutable<TableTypeVar>(t);
LUAU_ASSERT(ttv);

const std::string lastSegment = path.back();
LUAU_ASSERT(0 == ttv->props.count(lastSegment));
ttv->props[lastSegment] = Property{replaceTy};
return res;
}

/**
* This function is mostly about identifying properties that are being inserted into unsealed tables.
*
Expand All @@ -1559,31 +1494,36 @@ TypeId ConstraintGraphBuilder::checkLValue(const ScopePtr& scope, AstExpr* expr)
return checkLValue(scope, &synthetic);
}
}
else if (!expr->is<AstExprIndexName>())
return check(scope, expr).ty;

auto dottedPath = extractDottedName(expr);
if (!dottedPath)
return check(scope, expr).ty;
const auto [sym, segments] = std::move(*dottedPath);

if (!sym.local)
return check(scope, expr).ty;
LUAU_ASSERT(!segments.empty());

auto lookupResult = scope->lookupEx(sym);
if (!lookupResult)
return check(scope, expr).ty;
const auto [ty, symbolScope] = std::move(*lookupResult);
const auto [subjectType, symbolScope] = std::move(*lookupResult);

TypeId replaceTy = arena->freshType(scope.get());
TypeId propTy = freshType(scope);

std::optional<TypeId> updatedType = updateTheTableType(arena, ty, segments, replaceTy);
if (!updatedType)
return check(scope, expr).ty;
std::vector<std::string> segmentStrings(begin(segments), end(segments));

TypeId updatedType = arena->addType(BlockedTypeVar{});
addConstraint(scope, expr->location, SetPropConstraint{updatedType, subjectType, std::move(segmentStrings), propTy});

std::optional<DefId> def = dfg->getDef(sym);
LUAU_ASSERT(def);
symbolScope->bindings[sym].typeId = *updatedType;
symbolScope->dcrRefinements[*def] = *updatedType;
return replaceTy;
symbolScope->bindings[sym].typeId = updatedType;
symbolScope->dcrRefinements[*def] = updatedType;

astTypes[expr] = propTy;

return propTy;
}

Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprTable* expr, std::optional<TypeId> expectedType)
Expand Down
Loading

0 comments on commit 95d9c6d

Please sign in to comment.