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
96 changes: 96 additions & 0 deletions tslang/lib/TypeScript/MLIRGenExpressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1695,6 +1695,89 @@ namespace mlirgen
return createArrayFromArrayInfo(location, values, arrayInfo, genContext);
}

// A record literal whose receiver is a class (`const p: P = { x: i, items: [i] }`, a P
// parameter, a P result), built as the instance itself from its fields' values (#492). Not
// for a class with a base (its storage holds the base's fields elsewhere), nor for a literal
// missing one of the class's fields or holding one it has not, nor for a constant field other
// than a number, a string or a boolean: those go through the slot and castTupleToClass.
std::optional<ValueOrLogicalResult> MLIRGenImpl::mlirGenObjectLiteralAsClassInstance(mlir::Location location,
ObjectLiteralInfo &oli, const GenContext &genContext)
{
auto classType = dyn_cast_or_null<mlir_ts::ClassType>(oli.receiverType);
if (!classType)
{
return std::nullopt;
}

auto classInfo = getClassInfoByFullName(classType.getName().getValue());
if (!classInfo || !classInfo->baseClasses.empty())
{
return std::nullopt;
}

auto classFields = mlir::cast<mlir_ts::ClassStorageType>(classType.getStorageType()).getFields();
SmallVector<mlir::Value> values;
auto matched = 0;
for (auto &classField : classFields)
{
auto name = dyn_cast_or_null<mlir::StringAttr>(classField.id);
if (name && name.getValue().starts_with("."))
{
continue;
}

auto found = llvm::find_if(oli.fieldInfos, [&](auto &fieldInfo) { return fieldInfo.id == classField.id; });
if (found == oli.fieldInfos.end())
{
return std::nullopt;
}

auto index = std::distance(oli.fieldInfos.begin(), found);
auto toSet = llvm::find_if(oli.fieldsToSet, [&](auto &fieldToSet) { return fieldToSet.first == classField.id; });
if (toSet != oli.fieldsToSet.end())
{
values.push_back(toSet->second);
}
else if (isa<mlir::IntegerAttr, mlir::FloatAttr, mlir::StringAttr, mlir::BoolAttr>(oli.values[index]))
{
values.push_back(builder.create<mlir_ts::ConstantOp>(location, found->type, oli.values[index]));
}
else if (auto arrayAttr = dyn_cast<mlir::ArrayAttr>(oli.values[index]))
{
// a constant array (`items: []`, `items: [1, 2]`): a heap array of the instance's own,
// as in a literal built in a slot (#479)
auto arrayType = dyn_cast<mlir_ts::ArrayType>(found->type);
if (!arrayType)
{
return std::nullopt;
}

auto literal = builder.create<mlir_ts::ConstantOp>(
location, getConstArrayType(arrayType.getElementType(), arrayAttr.size()), arrayAttr);
auto copied = cast(location, arrayType, literal, genContext);
if (copied.failed_or_no_value())
{
return ValueOrLogicalResult(mlir::failure());
}

values.push_back(V(copied));
}
else
{
return std::nullopt;
}

matched++;
}

if (matched != static_cast<int>(oli.fieldInfos.size()))
{
return std::nullopt;
}

return NewClassInstanceWithSettingFields(location, classType, classFields, values, genContext);
}

ValueOrLogicalResult MLIRGenImpl::mlirGen(ts::ObjectLiteralExpression objectLiteral, const GenContext &genContext)
{
auto location = loc(objectLiteral);
Expand Down Expand Up @@ -1835,6 +1918,19 @@ namespace mlirgen
(objectLiteral->internalFlags & InternalFlags::BoxAsObject) == InternalFlags::BoxAsObject ||
!oli.methodInfos.empty() || !oli.methodInfosWithCaptures.empty();

// A literal made into a class instance (`const p: P = { x: i, items: [i] }`) is the instance:
// each field's value is stored into a new P as it is, where it was stored into a slot the
// cast to P then read again - a read -mm=own takes for a borrow of the slot, which cannot
// be stored (#492). Only a literal whose fields are exactly the class's, and whose constant
// fields are plain values; any other goes through the slot and the cast (castTupleToClass).
if (!boxAsObject)
{
if (auto classInstance = mlirGenObjectLiteralAsClassInstance(location, oli, genContext))
{
return *classInstance;
}
}

// A literal built in a slot (a field known only at run time, or boxed) starts from the
// constant below, and an array kept in it would be a static header over constant data,
// which `push`, `pop` and element writes change (#479): such a field is set in the slot as a
Expand Down
5 changes: 4 additions & 1 deletion tslang/lib/TypeScript/MLIRGenImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -12169,9 +12169,12 @@ class MLIRGenImpl
ValueOrLogicalResult castTupleToTuple(mlir::Location location, mlir::Value value, mlir_ts::TupleType srcTupleType,
ArrayRef<mlir_ts::FieldInfo> fields, const GenContext &genContext, bool errorAsWarning = false);

ValueOrLogicalResult NewClassInstanceWithSettingFields(mlir::Location location, mlir_ts::ClassType classType,
ValueOrLogicalResult NewClassInstanceWithSettingFields(mlir::Location location, mlir_ts::ClassType classType,
ArrayRef<mlir_ts::FieldInfo> fields, ArrayRef<mlir::Value> values, const GenContext &genContext);

std::optional<ValueOrLogicalResult> mlirGenObjectLiteralAsClassInstance(mlir::Location location, ObjectLiteralInfo &oli,
const GenContext &genContext);

ValueOrLogicalResult castTupleToClass(mlir::Location location, mlir::Value value, mlir_ts::TupleType srcTupleType,
ArrayRef<mlir_ts::FieldInfo> fields, mlir_ts::ClassType classType, const GenContext &genContext, bool errorAsWarning = false);

Expand Down
3 changes: 3 additions & 0 deletions tslang/test/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ tslang_add_test(NAME test-compile-00-array-static-nested COMMAND test-runner "${
tslang_add_test(NAME test-compile-00-array-literal-copy COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_literal_copy.ts")
tslang_add_test(NAME test-compile-00-tuple-runtime-arrays COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00tuple_runtime_arrays.ts")
tslang_add_test(NAME test-compile-00-record-to-class COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00record_to_class.ts")
tslang_add_test(NAME test-compile-00-record-variable-to-class COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00record_variable_to_class.ts")
tslang_add_test(NAME test-compile-00-array-insert-functions COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_insert_functions.ts")
tslang_add_test(NAME test-compile-00-await-void COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00await_void.ts")
tslang_add_test(NAME test-compile-00-for-await-sequential COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00for_await_sequential.ts")
Expand Down Expand Up @@ -860,6 +861,7 @@ tslang_add_test(NAME test-jit-00-array-static-nested COMMAND test-runner -jit "$
tslang_add_test(NAME test-jit-00-array-literal-copy COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_literal_copy.ts")
tslang_add_test(NAME test-jit-00-tuple-runtime-arrays COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00tuple_runtime_arrays.ts")
tslang_add_test(NAME test-jit-00-record-to-class COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00record_to_class.ts")
tslang_add_test(NAME test-jit-00-record-variable-to-class COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00record_variable_to_class.ts")
tslang_add_test(NAME test-jit-00-array-insert-functions COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_insert_functions.ts")
tslang_add_test(NAME test-jit-00-await-void COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00await_void.ts")
tslang_add_test(NAME test-jit-00-for-await-sequential COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00for_await_sequential.ts")
Expand Down Expand Up @@ -1699,6 +1701,7 @@ set(TSLANG_CORPUS
00array_literal_copy.ts
00tuple_runtime_arrays.ts
00record_to_class.ts
00record_variable_to_class.ts
00array_insert_functions.ts
00await_void.ts
00for_await_sequential.ts
Expand Down
5 changes: 0 additions & 5 deletions tslang/test/tester/tests/00record_to_class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,6 @@ function main() {
e.items.push(i);
assert(e.items.length == 1 && e.items[0] == 7, "an empty array");

let n: number = 5;
let r = { x: n, items: [n, 2], name: "b" + n };
const q: P = r;
q.items.push(9);
assert(r.items.length == 3 && q.name == "b5", "a record variable, sharing its array");

assert(take({ x: 2, items: [i], name: "cc" + i }) == 4, "to a class parameter");
const m = make(i);
Expand Down
17 changes: 17 additions & 0 deletions tslang/test/tester/tests/00record_variable_to_class.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// A record variable assigned to a class type (#487): the instance is made from its fields and
// shares its arrays, which are references. Under -mm=own that is a second reference to the
// record's array, and an error; a record literal is not one (00record_to_class.ts, #492).
class P {
x: number;
items: number[];
name: string;
}

function main() {
let n: number = 5;
let r = { x: n, items: [n, 2], name: "b" + n };
const q: P = r;
q.items.push(9);
assert(r.items.length == 3 && q.name == "b5", "a record variable, sharing its array");
print("done.");
}
Loading