Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions tslang/include/TypeScript/Defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#define IDENTIFIER_ATTR_NAME "identifier"
#define BUILTIN_FUNC_ATTR_NAME "__builtin"
#define GENERIC_ATTR_NAME "__generic"
// instances of one generic function being made at once, each called from the one before (MLIRGenGenerics.cpp)
#define MAX_NESTED_GENERIC_INSTANCES 64
#define ATOMIC_ATTR_NAME "__atomic"
#define ORDERING_ATTR_NAME "__ordering"
#define SYNCSCOPE_ATTR_NAME "__syncscope"
Expand Down
11 changes: 11 additions & 0 deletions tslang/include/TypeScript/MLIRLogic/MLIRCodeLogic.h
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,17 @@ class MLIRCodeLogic
fieldIndex = intAttr.getValue().getSExtValue();;
notFound = fieldIndex < 0 || fieldIndex >= tupleType.size();
}
// `t["1"]`, as `for (const k in t)` gives it: the position of a field that has no name
else if (auto strAttr = dyn_cast<mlir::StringAttr>(fieldId))
{
unsigned position;
if (!strAttr.getValue().getAsInteger(10, position) && position < tupleType.size()
&& !isa_and_nonnull<mlir::StringAttr>(tupleType.getFieldInfo(position).id))
{
fieldIndex = position;
notFound = false;
}
}
}

if (notFound)
Expand Down
7 changes: 5 additions & 2 deletions tslang/include/TypeScript/MLIRLogic/MLIRGenStore.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "TypeScript/MLIRLogic/MLIRHelper.h"

#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSet.h"

#include <deque>
#include "llvm/Support/Debug.h"
Expand Down Expand Up @@ -44,8 +45,10 @@ struct GenericFunctionInfo
SourceFile sourceFile;
StringRef fileName;

bool processing;
bool processed;
// the instances being generated, by full name: a call of one of them from its own body (or from an
// instance it calls) is recursion and takes its type; any other instance can still be made then
llvm::StringSet<> processing;
bool processed = false;

// declared inside a function, whose locals its body may read; otherwise an instantiation sees no
// local of the function it is instantiated from (MLIRGenImpl::isolateGeneratedBodies)
Expand Down
6 changes: 6 additions & 0 deletions tslang/include/TypeScript/MLIRLogic/MLIRHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,11 @@ class MLIRHelper
});
}

// Set while one piece of source is generated more than once in one function - the body of a `for...in`
// over an object literal's fields, once for each field (MLIRGenImpl::mlirGen(ts::Block)): a name made
// from a location (an arrow function's, an object literal's type's) is then one for each copy.
static inline thread_local std::string anonymousNameCopy;

static std::string getAnonymousName(mlir::Location loc, const char *prefix, StringRef fullNamesapceName)
{
// auto calculate name
Expand All @@ -259,6 +264,7 @@ class MLIRHelper
ssName << fullNamesapceName.str() << ".";
ssName << prefix;
getAnonymousNameStep(ssName, loc);
ssName << anonymousNameCopy;
return ssName.str();
}

Expand Down
34 changes: 22 additions & 12 deletions tslang/lib/TypeScript/MLIRGenGenerics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1058,12 +1058,12 @@ namespace mlirgen
genericTypeGenContext.typeParamsWithArgs[typeParam->getName()] = std::make_pair(typeParam, getAnyType());
}

if (functionGenericTypeInfo->processing)
auto [anyFullName, anyName] = getNameOfFunction(functionGenericTypeInfo->functionDeclaration, genericTypeGenContext);
if (functionGenericTypeInfo->processing.contains(anyFullName))
{
auto [fullName, funcName] = getNameOfFunction(functionGenericTypeInfo->functionDeclaration, genericTypeGenContext);
if (auto funcType = lookupFunctionTypeMap(fullName))
if (auto funcType = lookupFunctionTypeMap(anyFullName))
{
return {mlir::success(), funcType, fullName};
return {mlir::success(), funcType, anyFullName};
}

emitError(location) << "can't instantiate '" << name << "' with 'any' while it is being instantiated";
Expand All @@ -1073,10 +1073,10 @@ namespace mlirgen
mlir::OpBuilder::InsertionGuard guard(builder);
builder.setInsertionPointToStart(theModule.getBody());

functionGenericTypeInfo->processing = true;
functionGenericTypeInfo->processing.insert(anyFullName);
auto [result, funcOp, funcName, isGeneric] =
mlirGenFunctionLikeDeclaration(functionGenericTypeInfo->functionDeclaration, genericTypeGenContext);
functionGenericTypeInfo->processing = false;
functionGenericTypeInfo->processing.erase(anyFullName);
if (mlir::failed(result) || !funcOp)
{
return {mlir::failure(), mlir_ts::FunctionType(), ""};
Expand Down Expand Up @@ -1244,11 +1244,12 @@ namespace mlirgen

if (anyNamedGenericType == IsGeneric::False)
{
if (functionGenericTypeInfo->processing)
// a call of the instance being made, from its own body: recursion, which takes the type it
// has so far. A call of another instance (`f<number>` from `f<number[]>`) makes that one.
auto [fullName, name] =
getNameOfFunction(functionGenericTypeInfo->functionDeclaration, genericTypeGenContext);
if (functionGenericTypeInfo->processing.contains(fullName))
{
auto [fullName, name] =
getNameOfFunction(functionGenericTypeInfo->functionDeclaration, genericTypeGenContext);

auto funcType = lookupFunctionTypeMap(fullName);
if (funcType)
{
Expand All @@ -1263,11 +1264,20 @@ namespace mlirgen
return {mlir::failure(), mlir_ts::FunctionType(), ""};
}

// `f<T>` calling `f<T[]>` would make instances without end
if (functionGenericTypeInfo->processing.size() >= MAX_NESTED_GENERIC_INSTANCES)
{
emitError(location) << "can't instantiate '" << functionGenericTypeInfo->name << "': more than "
<< MAX_NESTED_GENERIC_INSTANCES
<< " of its instances are being made at once, one calling the next";
return {mlir::failure(), mlir_ts::FunctionType(), ""};
}

// create new instance of function with TypeArguments
functionGenericTypeInfo->processing = true;
functionGenericTypeInfo->processing.insert(fullName);
auto [result, funcOp, funcName, isGeneric] =
mlirGenFunctionLikeDeclaration(functionGenericTypeInfo->functionDeclaration, genericTypeGenContext);
functionGenericTypeInfo->processing = false;
functionGenericTypeInfo->processing.erase(fullName);
if (mlir::failed(result))
{
return {mlir::failure(), mlir_ts::FunctionType(), ""};
Expand Down
12 changes: 12 additions & 0 deletions tslang/lib/TypeScript/MLIRGenImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -5382,6 +5382,9 @@ class MLIRGenImpl

mlir::LogicalResult mlirGen(ForStatement forStatementAST, const GenContext &genContext);

// the keys `for...in` walks in an object literal's or a tuple's type (MLIRGenStatements.cpp)
static bool forInFieldKeys(mlir::Type type, SmallVector<std::string> &keys);

mlir::LogicalResult mlirGen(ForInStatement forInStatementAST, const GenContext &genContext);

mlir::LogicalResult mlirGenES3(ForOfStatement forOfStatementAST, mlir::Value exprValue,
Expand Down Expand Up @@ -6457,6 +6460,15 @@ class MLIRGenImpl

if (auto constantOp = leftExpressionValue.getDefiningOp<mlir_ts::ConstantOp>())
{
// every key `for...in` walks in an object literal or a tuple, the position of an unnamed field too
SmallVector<std::string> fieldKeys;
auto keyAttr = dyn_cast<mlir::StringAttr>(constantOp.getValue());
if (keyAttr && forInFieldKeys(rightExpressionValue.getType(), fieldKeys)
&& llvm::is_contained(fieldKeys, keyAttr.getValue().str()))
{
return mlirGenBooleanValue(loc(binaryExpressionAST->right), true);
}

auto hasField = !!mth.getFieldTypeByFieldName(rightExpressionValue.getType(), constantOp.getValue());
return mlirGenBooleanValue(loc(binaryExpressionAST->right), hasField);
}
Expand Down
165 changes: 165 additions & 0 deletions tslang/lib/TypeScript/MLIRGenStatements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,36 @@ namespace mlirgen

mlir::LogicalResult MLIRGenImpl::mlirGen(ts::Block blockAST, const GenContext &genContext, int skipStatements)
{
// one field's copy of a `for...in` body (mlirGen(ForInStatement)): its arrow functions and the like are its
// own, named for its key, which its first statement sets
if ((blockAST->internalFlags & InternalFlags::ForInFieldCopy) == InternalFlags::ForInFieldCopy)
{
auto copyName = std::string(".k");
auto keyStatement = blockAST->statements.front();
Expression keyLiteral = undefined;
if (keyStatement == SyntaxKind::VariableStatement)
{
keyLiteral = keyStatement.as<VariableStatement>()->declarationList->declarations.front()->initializer;
}
else if (keyStatement == SyntaxKind::ExpressionStatement)
{
keyLiteral = keyStatement.as<ExpressionStatement>()->expression.as<BinaryExpression>()->right;
}

if (keyLiteral == SyntaxKind::StringLiteral)
{
copyName += convertWideToUTF8(keyLiteral.as<ts::StringLiteral>()->text);
}

MLIRValueGuard<std::string> copyGuard(MLIRHelper::anonymousNameCopy);
MLIRHelper::anonymousNameCopy += copyName;

blockAST->internalFlags &= ~InternalFlags::ForInFieldCopy;
auto result = mlirGen(blockAST, genContext, skipStatements);
blockAST->internalFlags |= InternalFlags::ForInFieldCopy;
return result;
}

// A `using` here needs disposal on the unwind path too, and only a TryOp has a
// landing pad to run that from - see mlirGenBlockWithUnwindCleanup and
// blockDeclaresUsing. Most blocks qualify: a function's own body, an if/loop body, a
Expand Down Expand Up @@ -1013,6 +1043,77 @@ namespace mlirgen
return mlir::success();
}

// The keys `for...in` walks in a value of an object literal's or a tuple's type, in field order: a field's
// name, an unnamed field's position ("0", "1", ...) and an accessor's name once for its get_/set_ pair.
// False when the type has no fields known at compile time.
bool MLIRGenImpl::forInFieldKeys(mlir::Type type, SmallVector<std::string> &keys)
{
ArrayRef<mlir_ts::FieldInfo> fields;
if (auto tupleType = dyn_cast<mlir_ts::TupleType>(type))
{
fields = tupleType.getFields();
}
else if (auto constTupleType = dyn_cast<mlir_ts::ConstTupleType>(type))
{
fields = constTupleType.getFields();
}
else if (auto objectType = dyn_cast<mlir_ts::ObjectType>(type))
{
if (auto storageType = dyn_cast<mlir_ts::ObjectStorageType>(objectType.getStorageType()))
{
fields = storageType.getFields();
}
else if (auto storageTuple = dyn_cast<mlir_ts::TupleType>(objectType.getStorageType()))
{
fields = storageTuple.getFields();
}
else
{
return false;
}
}
else
{
return false;
}

for (auto [index, field] : llvm::enumerate(fields))
{
std::string key;
if (auto name = dyn_cast_or_null<mlir::StringAttr>(field.id))
{
key = name.getValue().str();
// an accessor is a function field named get_x / set_x; a value field may be named so too
auto isAccessor = isa<mlir_ts::FunctionType, mlir_ts::HybridFunctionType, mlir_ts::BoundFunctionType>(field.type)
&& key.size() > 4 && (StringRef(key).starts_with("get_") || StringRef(key).starts_with("set_"));
if (isAccessor)
{
key = key.substr(4);
}
}
else if (auto position = dyn_cast_or_null<mlir::IntegerAttr>(field.id))
{
key = std::to_string(position.getValue().getSExtValue());
}
else if (!field.id)
{
key = std::to_string(index);
}
else
{
// a symbol-named field (`[Symbol.iterator]`) is not walked by for...in
continue;
}

if (std::find(keys.begin(), keys.end(), key) == keys.end())
{
keys.push_back(key);
}
}

return true;
}

mlir::LogicalResult MLIRGenImpl::mlirGen(ForInStatement forInStatementAST, const GenContext &genContext)
{
SymbolTableScopeT varScope(symbolTable);
Expand All @@ -1021,6 +1122,70 @@ namespace mlirgen

NodeFactory nf(NodeFactoryFlags::None);

// An object literal or a tuple has no keys to find at run time: its fields are known from its type, so the
// loop is made for each of them, with the key a string literal, so that `obj[key]` in the body is that
// field, of its own type:
// for (let .i = 0; .i < N; .i++) { if (.i == 0) { const key = "a"; body } else if (.i == 1) { ... } }
// The loop is still a loop, so `break` and `continue` keep their meaning.
SmallVector<std::string> fieldKeys;
if (forInFieldKeys(evaluate(forInStatementAST->expression, genContext), fieldKeys))
{
auto _i = nf.createIdentifier(S(".i"));
NodeArray<VariableDeclaration> declarations;
declarations.push_back(nf.createVariableDeclaration(_i, undefined, undefined, nf.createNumericLiteral(S("0"))));

auto _o = nf.createIdentifier(S(".o"));
auto objectVar = nf.createVariableDeclaration(_o, undefined, undefined, forInStatementAST->expression);
objectVar->internalFlags |= InternalFlags::ForceConstRef;
declarations.push_back(objectVar);

auto initVars = nf.createVariableDeclarationList(declarations, NodeFlags::Let);

auto count = nf.createNumericLiteral(stows(std::to_string(fieldKeys.size())));
auto cond = nf.createBinaryExpression(_i, nf.createToken(SyntaxKind::LessThanToken), count);
auto incr = nf.createPrefixUnaryExpression(nf.createToken(SyntaxKind::PlusPlusToken), _i);

// the key: the loop's own `const key` / `let key`, or a variable declared before the loop
auto initializer = forInStatementAST->initializer;
auto keyStatement = [&](std::string key) -> ts::Statement {
auto keyLiteral = nf.createStringLiteral(stows(key));
if (initializer == SyntaxKind::VariableDeclarationList)
{
auto declList = initializer.as<VariableDeclarationList>();
auto declared = declList->declarations.front();
NodeArray<VariableDeclaration> keyDeclarations;
keyDeclarations.push_back(
nf.createVariableDeclaration(declared->name, undefined, declared->type, keyLiteral));
return nf.createVariableStatement(
undefined, nf.createVariableDeclarationList(keyDeclarations, declList->flags));
}

return nf.createExpressionStatement(nf.createBinaryExpression(
initializer.as<Expression>(), nf.createToken(SyntaxKind::EqualsToken), keyLiteral));
};

ts::Statement chain = undefined;
for (auto index = (int)fieldKeys.size() - 1; index >= 0; index--)
{
NodeArray<ts::Statement> statements;
statements.push_back(keyStatement(fieldKeys[index]));
statements.push_back(forInStatementAST->statement);
auto isThisField = nf.createBinaryExpression(_i, nf.createToken(SyntaxKind::EqualsEqualsEqualsToken),
nf.createNumericLiteral(stows(std::to_string(index))));
auto fieldBlock = nf.createBlock(statements);
fieldBlock->internalFlags |= InternalFlags::ForInFieldCopy;
chain = nf.createIfStatement(isThisField, fieldBlock, chain);
}

NodeArray<ts::Statement> body;
if (chain)
{
body.push_back(chain);
}

return mlirGen(nf.createForStatement(initVars, cond, incr, nf.createBlock(body)), genContext);
}

// init
NodeArray<VariableDeclaration> declarations;
auto _i = nf.createIdentifier(S(".i"));
Expand Down
14 changes: 14 additions & 0 deletions tslang/test/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2518,6 +2518,7 @@ set(TSLANG_CORPUS
00class_constructor_overloads.ts
00callback_result_ignored.ts
00generic_default_no_inference.ts
00generic_instance_from_instance.ts
00promise_type.ts
00class_deconst.ts
00class_def_constr.ts
Expand Down Expand Up @@ -2578,6 +2579,7 @@ set(TSLANG_CORPUS
00for_await_yield.ts
00for_await.ts
00for_in.ts
00for_in_fields.ts
00for_infinite_result.ts
00for_of.ts
00for.ts
Expand Down Expand Up @@ -4834,6 +4836,18 @@ tslang_add_test(NAME test-compile-own-00-const-interface-of-local COMMAND test-r
tslang_add_test(NAME test-jit-own-00-self-assign COMMAND test-runner -jit -mm=own "${PROJECT_SOURCE_DIR}/test/tester/tests/00self_assign.ts")
tslang_add_test(NAME test-compile-own-00-self-assign COMMAND test-runner -mm=own "${PROJECT_SOURCE_DIR}/test/tester/tests/00self_assign.ts")

# for...in over an object literal or a tuple walks the fields of its type, and an instance of a generic
# function makes other instances of it while it is being made (JSON.stringify of an object literal or of
# nested arrays). rc and none run them from TSLANG_CORPUS.
tslang_add_test(NAME test-compile-00-for-in-fields COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00for_in_fields.ts")
tslang_add_test(NAME test-compile-own-00-for-in-fields COMMAND test-runner -mm=own "${PROJECT_SOURCE_DIR}/test/tester/tests/00for_in_fields.ts")
tslang_add_test(NAME test-jit-00-for-in-fields COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00for_in_fields.ts")
tslang_add_test(NAME test-jit-own-00-for-in-fields COMMAND test-runner -jit -mm=own "${PROJECT_SOURCE_DIR}/test/tester/tests/00for_in_fields.ts")
tslang_add_test(NAME test-compile-00-generic-instance-from-instance COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00generic_instance_from_instance.ts")
tslang_add_test(NAME test-compile-own-00-generic-instance-from-instance COMMAND test-runner -mm=own "${PROJECT_SOURCE_DIR}/test/tester/tests/00generic_instance_from_instance.ts")
tslang_add_test(NAME test-jit-00-generic-instance-from-instance COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00generic_instance_from_instance.ts")
tslang_add_test(NAME test-jit-own-00-generic-instance-from-instance COMMAND test-runner -jit -mm=own "${PROJECT_SOURCE_DIR}/test/tester/tests/00generic_instance_from_instance.ts")

# Shared<T> (spec 23): counted handles. These are not in the own_test list, whose
# test-own-no-counting check forbids __tslang_inc_ref: a handle is counted under own by design.
# own_shared_user_named: a program's own class named Shared wins over the built-in (spec 23.1).
Expand Down
Loading
Loading