diff --git a/tslang/lib/TypeScript/MLIRGenFunctions.cpp b/tslang/lib/TypeScript/MLIRGenFunctions.cpp index 648d7a84a..8a113390e 100644 --- a/tslang/lib/TypeScript/MLIRGenFunctions.cpp +++ b/tslang/lib/TypeScript/MLIRGenFunctions.cpp @@ -307,6 +307,22 @@ namespace mlirgen LLVM_DEBUG(dbgs() << "\n!! binding param " << namePtr << " is type " << type << "\n"); } + // a set accessor's value without a type is of the type its get accessor returns + if (mth.isNoneType(type) && kind == SyntaxKind::SetAccessor && !typeParameter && !initializer) + { + auto [result, getAccessorType] = getPairedGetAccessorType(parametersContextAST, genContext); + if (mlir::failed(result)) + { + return {mlir::failure(), isGenericTypes, params}; + } + + if (getAccessorType) + { + type = getAccessorType; + isGenericTypes |= mth.isGenericType(type); + } + } + if (mth.isNoneType(type)) { if (!typeParameter && !initializer) @@ -373,6 +389,75 @@ namespace mlirgen return {mlir::success(), isGenericTypes, params}; } + // the type a class's set accessor takes when its parameter has none: what the get accessor of the + // same name returns. No type when the class has no such get accessor; a failure while the get + // accessor, declared after the set accessor, is not resolved yet (mlirGenClassMembers retries it). + std::tuple MLIRGenImpl::getPairedGetAccessorType( + SignatureDeclarationBase setAccessorAST, const GenContext &genContext) + { + auto parent = setAccessorAST->parent; + if (parent != SyntaxKind::ClassDeclaration && parent != SyntaxKind::ClassExpression) + { + return {mlir::success(), mlir::Type()}; + } + + auto [name, nameResult] = getNameForMethod(setAccessorAST, genContext); + if (!nameResult || name.empty()) + { + return {mlir::success(), mlir::Type()}; + } + + auto isStatic = hasModifier(setAccessorAST, SyntaxKind::StaticKeyword); + FunctionLikeDeclarationBase getAccessorAST; + for (auto &classMember : parent.as()->members) + { + if (classMember != SyntaxKind::GetAccessor || hasModifier(classMember, SyntaxKind::StaticKeyword) != isStatic) + { + continue; + } + + auto getAccessor = classMember.as(); + auto [getName, getNameResult] = getNameForMethod(getAccessor, genContext); + if (getNameResult && getName == name) + { + getAccessorAST = getAccessor; + break; + } + } + + if (!getAccessorAST) + { + return {mlir::success(), mlir::Type()}; + } + + if (getAccessorAST->type) + { + auto type = getType(getAccessorAST->type, genContext); + return {type ? mlir::success() : mlir::failure(), type}; + } + + // the type the get accessor's body gives it, once the get accessor is registered + if (genContext.thisClassType) + { + if (auto classInfo = getClassInfoByFullName(genContext.thisClassType.getName().getValue())) + { + auto accessorIndex = classInfo->getAccessorIndex(name); + if (accessorIndex >= 0) + { + auto &getFunc = classInfo->accessors[accessorIndex].get; + if (getFunc) + { + return {mlir::success(), getFunc.funcType.getNumResults() > 0 + ? getFunc.funcType.getResult(0) + : mlir::Type()}; + } + } + } + } + + return {mlir::failure(), mlir::Type()}; + } + std::tuple MLIRGenImpl::getNameOfFunction(SignatureDeclarationBase signatureDeclarationBaseAST, const GenContext &genContext) { diff --git a/tslang/lib/TypeScript/MLIRGenImpl.h b/tslang/lib/TypeScript/MLIRGenImpl.h index 8c48b7011..25f30d97c 100644 --- a/tslang/lib/TypeScript/MLIRGenImpl.h +++ b/tslang/lib/TypeScript/MLIRGenImpl.h @@ -3276,6 +3276,9 @@ class MLIRGenImpl std::tuple>> mlirGenParameters( SignatureDeclarationBase parametersContextAST, const GenContext &genContext); + std::tuple getPairedGetAccessorType(SignatureDeclarationBase setAccessorAST, + const GenContext &genContext); + std::tuple getNameOfFunction(SignatureDeclarationBase signatureDeclarationBaseAST, const GenContext &genContext); @@ -6781,6 +6784,30 @@ class MLIRGenImpl } }; + // a set accessor takes the value as its last parameter, whose type need not be the one its get + // accessor returns (the accessor's type) + auto setAccessorValueType = [&](mlir::Type setAccessorType, mlir::Type accessorType) -> mlir::Type { + if (!mth.isAnyFunctionType(setAccessorType)) + { + return accessorType; + } + + auto params = mth.getParamsFromFuncRef(setAccessorType); + return params.empty() ? accessorType : params.back(); + }; + + auto setAccessorSymbolValueType = [&](mlir::FlatSymbolRefAttr setAccessor, mlir::Type accessorType) -> mlir::Type { + if (setAccessor) + { + if (auto funcOp = theModule.lookupSymbol(setAccessor)) + { + return setAccessorValueType(funcOp.getFunctionType(), accessorType); + } + } + + return accessorType; + }; + // TODO: logic to support safe cast if (auto safeCastOp = leftExpressionValueBeforeCast.getDefiningOp()) { @@ -6898,7 +6925,7 @@ class MLIRGenImpl } else if (auto accessorOp = leftExpressionValueBeforeCast.getDefiningOp()) { - syncSavingValue(accessorOp.getType(0)); + syncSavingValue(setAccessorSymbolValueType(accessorOp.getSetAccessorAttr(), accessorOp.getType(0))); if (!savingValue) { return mlir::failure(); @@ -6913,7 +6940,7 @@ class MLIRGenImpl } else if (auto thisAccessorOp = leftExpressionValueBeforeCast.getDefiningOp()) { - syncSavingValue(thisAccessorOp.getType(0)); + syncSavingValue(setAccessorSymbolValueType(thisAccessorOp.getSetAccessorAttr(), thisAccessorOp.getType(0))); if (!savingValue) { return mlir::failure(); @@ -6928,7 +6955,7 @@ class MLIRGenImpl } else if (auto thisAccessorIndirectOp = leftExpressionValueBeforeCast.getDefiningOp()) { - syncSavingValue(thisAccessorIndirectOp.getType(0)); + syncSavingValue(setAccessorValueType(thisAccessorIndirectOp.getSetAccessor().getType(), thisAccessorIndirectOp.getType(0))); if (!savingValue) { return mlir::failure(); @@ -6945,7 +6972,7 @@ class MLIRGenImpl } else if (auto thisIndexAccessorOp = leftExpressionValueBeforeCast.getDefiningOp()) { - syncSavingValue(thisIndexAccessorOp.getType(0)); + syncSavingValue(setAccessorSymbolValueType(thisIndexAccessorOp.getSetAccessorAttr(), thisIndexAccessorOp.getType(0))); if (!savingValue) { return mlir::failure(); @@ -6960,7 +6987,7 @@ class MLIRGenImpl } else if (auto thisIndirectIndexAccessorOp = leftExpressionValueBeforeCast.getDefiningOp()) { - syncSavingValue(thisIndirectIndexAccessorOp.getType(0)); + syncSavingValue(setAccessorValueType(thisIndirectIndexAccessorOp.getSetAccessor().getType(), thisIndirectIndexAccessorOp.getType(0))); if (!savingValue) { return mlir::failure(); @@ -6975,7 +7002,7 @@ class MLIRGenImpl } else if (auto boundAccessorIndirectOp = leftExpressionValueBeforeCast.getDefiningOp()) { - syncSavingValue(boundAccessorIndirectOp.getType(0)); + syncSavingValue(setAccessorValueType(boundAccessorIndirectOp.getSetAccessor().getType(), boundAccessorIndirectOp.getType(0))); if (!savingValue) { return mlir::failure(); @@ -6991,7 +7018,7 @@ class MLIRGenImpl } else if (auto boundIndirectIndexAccessorOp = leftExpressionValueBeforeCast.getDefiningOp()) { - syncSavingValue(boundIndirectIndexAccessorOp.getType(0)); + syncSavingValue(setAccessorValueType(boundIndirectIndexAccessorOp.getSetAccessor().getType(), boundIndirectIndexAccessorOp.getType(0))); if (!savingValue) { return mlir::failure(); diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index 58dd6529e..895c8cd9d 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -609,6 +609,7 @@ tslang_add_test(NAME test-compile-00-class-accessor-super COMMAND test-runner "$ tslang_add_test(NAME test-compile-00-class-accessor-virtual COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_accessor_virtual.ts") tslang_add_test(NAME test-compile-00-class-accessor-implements-field COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_accessor_implements_field.ts") tslang_add_test(NAME test-compile-00-object-literal-contextual-lambda COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_literal_contextual_lambda.ts") +tslang_add_test(NAME test-compile-00-class-accessor-untyped-setter COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_accessor_untyped_setter.ts") tslang_add_test(NAME test-compile-00-class-super COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_super.ts") tslang_add_test(NAME test-compile-00-class-implicit-constructor COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_implicit_constructor.ts") tslang_add_test(NAME test-compile-00-class-super-static COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_super_static.ts") @@ -1224,6 +1225,7 @@ tslang_add_test(NAME test-jit-00-class-accessor-super COMMAND test-runner -jit " tslang_add_test(NAME test-jit-00-class-accessor-virtual COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_accessor_virtual.ts") tslang_add_test(NAME test-jit-00-class-accessor-implements-field COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_accessor_implements_field.ts") tslang_add_test(NAME test-jit-00-object-literal-contextual-lambda COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_literal_contextual_lambda.ts") +tslang_add_test(NAME test-jit-00-class-accessor-untyped-setter COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_accessor_untyped_setter.ts") tslang_add_test(NAME test-jit-00-class-super COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_super.ts") tslang_add_test(NAME test-jit-00-class-implicit-constructor COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_implicit_constructor.ts") tslang_add_test(NAME test-jit-00-class-super-static COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_super_static.ts") @@ -2178,6 +2180,7 @@ set(TSLANG_CORPUS 00class_accessor_virtual.ts 00class_accessor_implements_field.ts 00object_literal_contextual_lambda.ts + 00class_accessor_untyped_setter.ts 00class_accessor.ts 00class_accessor2.ts 00class_constr_fields.ts diff --git a/tslang/test/tester/tests/00class_accessor_untyped_setter.ts b/tslang/test/tester/tests/00class_accessor_untyped_setter.ts new file mode 100644 index 000000000..d5c67a88c --- /dev/null +++ b/tslang/test/tester/tests/00class_accessor_untyped_setter.ts @@ -0,0 +1,57 @@ +// a set accessor's parameter without a type takes the type its get accessor returns +class GetFirst { + f = 1; + get prop() { return this.f; } + set prop(v) { print("set", v); this.f = v; } +} + +// the get accessor after the set accessor +class SetFirst { + f = 1; + set prop(v) { this.f = v + 1; } + get prop() { return this.f; } +} + +// a get accessor with a written type +class Typed { + s = ""; + get prop(): string { return this.s; } + set prop(v) { this.s = v + "!"; } +} + +class Static { + static f = 1; + static get prop() { return Static.f; } + static set prop(v) { Static.f = v * 2; } +} + +// the value is given to a set accessor as the type it takes, not as the one its get accessor returns +class AnySetter { + f = 1; + get prop() { return this.f; } + set prop(v: any) { this.f = v; } +} + +function main() { + const c = new GetFirst(); + c.prop = 5; + print(c.prop); + assert(c.prop == 5); + + const s = new SetFirst(); + s.prop = 5; + assert(s.prop == 6); + + const t = new Typed(); + t.prop = "hi"; + assert(t.prop == "hi!"); + + Static.prop = 4; + assert(Static.prop == 8); + + const a = new AnySetter(); + a.prop = 7; + assert(a.prop == 7); + + print("done."); +}