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
41 changes: 41 additions & 0 deletions tslang/lib/TypeScript/MLIRGenCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,11 @@ namespace mlirgen
return *result;
}

if (auto result = castConstArrayToArray(location, type, value, valueType, genContext))
{
return *result;
}

if (auto result = castToOptionalType(location, type, value, valueType, genContext))
{
return *result;
Expand Down Expand Up @@ -909,6 +914,42 @@ namespace mlirgen
return std::nullopt;
}

// A constant array keeps the element type its literal was built with: `const c = [1, 2]` is a
// const_array<si32>, and so is `[1, 2]` meeting a union such as `number[] | string`, where no single
// array type guides the literal. Lowering turns a const array into an array by copying its data as it
// is, so for another element type (`number[]`) the elements were read with the wrong layout - garbage,
// with only a warning. Such an array is built here from its elements, each cast to the element type.
std::optional<ValueOrLogicalResult> MLIRGenImpl::castConstArrayToArray(mlir::Location location, mlir::Type type, mlir::Value value, mlir::Type valueType, const GenContext &genContext)
{
auto constArrayType = dyn_cast<mlir_ts::ConstArrayType>(valueType);
auto arrayType = dyn_cast<mlir_ts::ArrayType>(type);
if (!constArrayType || !arrayType || constArrayType.getElementType() == arrayType.getElementType())
{
return std::nullopt;
}

auto constOp = value.getDefiningOp<mlir_ts::ConstantOp>();
auto elementAttrs = constOp ? dyn_cast<mlir::ArrayAttr>(constOp.getValue()) : mlir::ArrayAttr();
if (!elementAttrs || llvm::any_of(elementAttrs, [](mlir::Attribute attr) { return isa<mlir::ArrayAttr>(attr); }))
{
// not a literal, or nested arrays: left to the cast below
return std::nullopt;
}

SmallVector<mlir::Value> elements;
for (auto elementAttr : elementAttrs)
{
auto element = builder.create<mlir_ts::ConstantOp>(location, constArrayType.getElementType(), elementAttr);
CAST_A(castedElement, location, arrayType.getElementType(), element, genContext);
elements.push_back(castedElement);
}

// the data block about to be filled releases every element when it dies
mlirGenRetainCaptured(location, elements);

return V(builder.create<mlir_ts::CreateArrayOp>(location, arrayType, elements));
}

std::optional<ValueOrLogicalResult> MLIRGenImpl::castTupleLikeVariants(mlir::Location location, mlir::Type type, mlir::Value value, mlir::Type valueType, const GenContext &genContext)
{
// const tuple to tuple
Expand Down
3 changes: 3 additions & 0 deletions tslang/lib/TypeScript/MLIRGenImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -10912,6 +10912,9 @@ class MLIRGenImpl
// casts between tuple-like types (tuple, const tuple, class storage, interface fields)
std::optional<ValueOrLogicalResult> castTupleLikeVariants(mlir::Location location, mlir::Type type, mlir::Value value, mlir::Type valueType, const GenContext &genContext);

// constant array literal to an array of another element type, element by element
std::optional<ValueOrLogicalResult> castConstArrayToArray(mlir::Location location, mlir::Type type, mlir::Value value, mlir::Type valueType, const GenContext &genContext);

// optional
// TODO: it is in CastLogic as well, review usage and remove from here
// but if optional points to interface then it will not work
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 @@ -174,6 +174,7 @@ add_test(NAME test-compile-00-typeof-static-fold COMMAND test-runner "${PROJECT_
add_test(NAME test-compile-00-typeof-function-narrowing COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00typeof_function_narrowing.ts")
add_test(NAME test-compile-00-typeof-static-fold-conditions COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00typeof_static_fold_conditions.ts")
add_test(NAME test-compile-00-typeof-union-narrowing COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00typeof_union_narrowing.ts")
add_test(NAME test-compile-00-const-array-to-array-elements COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00const_array_to_array_elements.ts")
add_test(NAME test-compile-00-array-move-wide-elements COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_move_wide_elements.ts")
add_test(NAME test-compile-00-funcs-expression-generic COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_expression_generic.ts")
add_test(NAME test-compile-00-funcs-expression-iterator COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_expression_iterator.ts")
Expand Down Expand Up @@ -594,6 +595,7 @@ add_test(NAME test-jit-00-typeof-static-fold COMMAND test-runner -jit "${PROJECT
add_test(NAME test-jit-00-typeof-function-narrowing COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00typeof_function_narrowing.ts")
add_test(NAME test-jit-00-typeof-static-fold-conditions COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00typeof_static_fold_conditions.ts")
add_test(NAME test-jit-00-typeof-union-narrowing COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00typeof_union_narrowing.ts")
add_test(NAME test-jit-00-const-array-to-array-elements COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00const_array_to_array_elements.ts")
add_test(NAME test-jit-00-array-move-wide-elements COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_move_wide_elements.ts")
add_test(NAME test-jit-00-funcs-expression-generic COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_expression_generic.ts")
add_test(NAME test-jit-00-funcs-expression-iterator COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_expression_iterator.ts")
Expand Down Expand Up @@ -1322,6 +1324,7 @@ add_test(NAME test-jit-none-strings COMMAND test-runner -jit -mm=none "${PROJECT
set(TSLANG_CORPUS
00add_promotes_both_operands.ts
00conditional_owned_result.ts
00const_array_to_array_elements.ts
00owned_array_splice.ts
00alloc_in_catch.ts
00any_compare.ts
Expand Down
38 changes: 38 additions & 0 deletions tslang/test/tester/tests/00const_array_to_array_elements.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// An integer array literal that is not built for one array type keeps its `s32` elements: a const
// variable, or a literal assigned to a union like `number[] | string`. Casting it to `number[]` copied
// the elements as they were, so `number` elements were read out of `s32` data - garbage values and
// a garbage length, with only a warning.
function lengthOf(u: number[] | string) {
return typeof u === "array" ? u.length : -1;
}

function main() {
const c = [1, 2, 3];
let fromConst: number[] = c;
assert(fromConst.length == 3, "const variable: length");
assert(fromConst[2] == 3, "const variable: element");

let u: number[] | string = [1, 2];
if (typeof u === "array") {
assert(u.length == 2, "union initializer: length");
assert(u[1] == 2, "union initializer: element");
} else {
assert(false, "union initializer: wrong branch");
}

let v: number[] | string;
v = [4, 5, 6];
if (typeof v === "array") {
assert(v[0] + v[1] + v[2] == 15, "union assignment");
} else {
assert(false, "union assignment: wrong branch");
}

assert(lengthOf([7, 8]) == 2, "union parameter");

// the elements are real `number`s now: fractional arithmetic works on them
const halves: number[] = c;
assert(halves[0] / 2 == 0.5, "number arithmetic on converted element");

print("done.");
}
Loading