diff --git a/tslang/lib/TypeScript/MLIRGenGenerics.cpp b/tslang/lib/TypeScript/MLIRGenGenerics.cpp index f12be9b57..e09e857a4 100644 --- a/tslang/lib/TypeScript/MLIRGenGenerics.cpp +++ b/tslang/lib/TypeScript/MLIRGenGenerics.cpp @@ -158,6 +158,22 @@ namespace mlirgen return false; } + // `Ref` from a `Ref`. Without it a parameter `Ref`, or a tuple alias holding one + // (`Vec = [first: Ref | null, ...]`), inferred nothing, and T had to be given explicitly. + bool MLIRGenImpl::tryInferRef(mlir::Location location, mlir::Type templateType, mlir::Type concreteType, + StringMap &results, const GenContext &genContext) + { + auto tempRef = dyn_cast(templateType); + auto typeRef = dyn_cast(concreteType); + if (!tempRef || !typeRef) + { + return false; + } + + inferType(location, tempRef.getElementType(), typeRef.getElementType(), results, genContext); + return true; + } + bool MLIRGenImpl::tryInferTuple(mlir::Location location, mlir::Type templateType, mlir::Type concreteType, StringMap &results, const GenContext &genContext) { @@ -260,6 +276,36 @@ namespace mlirgen return true; } + // a member both unions have (`null` of `T | null` and `Item | null`) binds nothing: pair + // up the rest. By position alone, the members' order decided, and `T` could take `null`. + SmallVector tempRest; + SmallVector typeRest; + for (auto tempSubType : tempUnionType.getTypes()) + { + if (!llvm::is_contained(types, tempSubType)) + { + tempRest.push_back(tempSubType); + } + } + + for (auto subType : types) + { + if (!llvm::is_contained(tempUnionType.getTypes(), subType)) + { + typeRest.push_back(subType); + } + } + + if (tempRest.size() == typeRest.size()) + { + for (auto [index, tempSubType] : enumerate(tempRest)) + { + inferType(location, tempSubType, typeRest[index], results, genContext); + } + + return true; + } + for (auto [index, tempSubType] : enumerate(tempUnionType.getTypes())) { inferType(location, tempSubType, types[index], results, genContext); @@ -323,6 +369,12 @@ namespace mlirgen return; } + // Ref -> Ref + if (tryInferRef(location, templateType, concreteType, results, genContext)) + { + return; + } + // tuple -> tuple if (tryInferTuple(location, templateType, concreteType, results, genContext)) { diff --git a/tslang/lib/TypeScript/MLIRGenImpl.h b/tslang/lib/TypeScript/MLIRGenImpl.h index c4b942158..8ea0e268b 100644 --- a/tslang/lib/TypeScript/MLIRGenImpl.h +++ b/tslang/lib/TypeScript/MLIRGenImpl.h @@ -1241,6 +1241,9 @@ class MLIRGenImpl bool tryInferArray(mlir::Location location, mlir::Type templateType, mlir::Type concreteType, StringMap &results, const GenContext &genContext); + bool tryInferRef(mlir::Location location, mlir::Type templateType, mlir::Type concreteType, + StringMap &results, const GenContext &genContext); + // TODO: finish it template bool tryInferTupleFields(mlir::Location location, mlir_ts::TupleType tempTuple, T typeTuple, diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index 891d8b56e..8ba4f6814 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -488,6 +488,7 @@ tslang_add_test(NAME test-compile-00-rest-args-pack-owned COMMAND test-runner "$ tslang_add_test(NAME test-compile-00-switch-char-string-cases COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00switch_char_string_cases.ts") tslang_add_test(NAME test-compile-00-switch-default-fallthrough COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00switch_default_fallthrough.ts") tslang_add_test(NAME test-compile-00-generic-class-constructor-inference COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00generic_class_constructor_inference.ts") +tslang_add_test(NAME test-compile-00-generic-ref-inference COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00generic_ref_inference.ts") tslang_add_test(NAME test-compile-00-generator-done-value-zero COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00generator_done_value_zero.ts") tslang_add_test(NAME test-compile-00-any-equality-undefined COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00any_equality_undefined.ts") tslang_add_test(NAME test-compile-00-owned-generators COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00owned_generators.ts") @@ -1019,6 +1020,7 @@ tslang_add_test(NAME test-jit-00-rest-args-pack-owned COMMAND test-runner -jit " tslang_add_test(NAME test-jit-00-switch-char-string-cases COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00switch_char_string_cases.ts") tslang_add_test(NAME test-jit-00-switch-default-fallthrough COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00switch_default_fallthrough.ts") tslang_add_test(NAME test-jit-00-generic-class-constructor-inference COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00generic_class_constructor_inference.ts") +tslang_add_test(NAME test-jit-00-generic-ref-inference COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00generic_ref_inference.ts") tslang_add_test(NAME test-jit-00-generator-done-value-zero COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00generator_done_value_zero.ts") tslang_add_test(NAME test-jit-00-any-equality-undefined COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00any_equality_undefined.ts") tslang_add_test(NAME test-jit-00-owned-generators COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00owned_generators.ts") @@ -1905,6 +1907,7 @@ set(TSLANG_CORPUS 00generator_done_value_zero.ts 00generic_arguments_name_conflict.ts 00generic_class_constructor_inference.ts + 00generic_ref_inference.ts 00global_const_object_method.ts 00globals.ts 00globals2.ts diff --git a/tslang/test/tester/tests/00generic_ref_inference.ts b/tslang/test/tester/tests/00generic_ref_inference.ts new file mode 100644 index 000000000..81eac9db4 --- /dev/null +++ b/tslang/test/tester/tests/00generic_ref_inference.ts @@ -0,0 +1,39 @@ +// regression test: a generic function's T is inferred through Ref, also inside a generic tuple +// alias, and through `T | null`. +// +// bug found and fixed: inference had no case for Ref, so `count(v)` with `v: Vec` +// (`Vec = [first: Ref | null, last: Ref | null]`) failed with "not all types could be +// inferred". And a union was matched member by member in order, so `T | null` against +// `Item | null` could bind T to null. + +type Vec = [first: Ref | null, last: Ref | null]; +type Item = [id: s32, size: s32]; +type Nullable = [value: T | null]; + +function count(v: Vec): int { + return ((v.last - v.first) / sizeof()); +} + +function sizeOfRef(r: Ref): int { + return sizeof(); +} + +function sizeOfNullable(n: Nullable): int { + return sizeof(); +} + +function main() { + let items: Item[] = [[1, 2], [3, 4]]; + const first = Ref(items[0]); + const v: Vec = [first, >(first + 2 * sizeof())]; + + assert(count(v) == 2); + assert(count(v) == 2); + + assert(sizeOfRef(first) == sizeof()); + + const n: Nullable = [items[1]]; + assert(sizeOfNullable(n) == sizeof()); + + print("done."); +}