diff --git a/tslang/include/TypeScript/MLIRLogic/MLIRDeclarationPrinter.h b/tslang/include/TypeScript/MLIRLogic/MLIRDeclarationPrinter.h index 5a4cf46b7..a307cf45c 100644 --- a/tslang/include/TypeScript/MLIRLogic/MLIRDeclarationPrinter.h +++ b/tslang/include/TypeScript/MLIRLogic/MLIRDeclarationPrinter.h @@ -14,7 +14,11 @@ namespace typescript raw_ostream &os; public: - MLIRDeclarationPrinter(raw_ostream &os) : os(os) {}; + // see MLIRPrinter::getClassSpecialization + using ClassSpecializationFn = std::function &)>; + + MLIRDeclarationPrinter(raw_ostream &os, ClassSpecializationFn getClassSpecialization = {}) + : os(os), getClassSpecialization(std::move(getClassSpecialization)) {}; void printTypeDeclaration(StringRef, NamespaceInfo::TypePtr, mlir::Type); void printEnum(StringRef, NamespaceInfo::TypePtr, mlir::DictionaryAttr); @@ -25,6 +29,8 @@ namespace typescript void printGenericClass(NamespaceInfo::TypePtr, StringRef); protected: + ClassSpecializationFn getClassSpecialization; + void newline(); void printBeforeDeclaration(); void printAsFieldName(mlir::Attribute); diff --git a/tslang/include/TypeScript/MLIRLogic/MLIRPrinter.h b/tslang/include/TypeScript/MLIRLogic/MLIRPrinter.h index 4c32b201d..0f877d05a 100644 --- a/tslang/include/TypeScript/MLIRLogic/MLIRPrinter.h +++ b/tslang/include/TypeScript/MLIRLogic/MLIRPrinter.h @@ -35,6 +35,12 @@ class MLIRPrinter // the whole declaration. Off for diagnostics. bool quoteNonIdentifierFieldNames = false; + // a specialization of a generic class printed the way it is written, `Box`, rather + // than by its internal name `Box>`, which does not parse back. Gives + // the generic's name and the type arguments in order; the printer has no class registry, so + // declaration text (__decls) supplies it. Unset for diagnostics. + std::function &)> getClassSpecialization; + template void printFieldName(T &out, mlir::Attribute id) { @@ -357,6 +363,27 @@ class MLIRPrinter printType(out, t.getElementType()); }) .template Case([&](auto t) { + std::string genericName; + SmallVector typeArgs; + if (getClassSpecialization && getClassSpecialization(t, genericName, typeArgs)) + { + out << genericName.c_str() << "<"; + auto first = true; + for (auto typeArg : typeArgs) + { + if (!first) + { + out << ", "; + } + + first = false; + printType(out, typeArg); + } + + out << ">"; + return; + } + out << t.getName().getValue().str().c_str(); }) .template Case([&](auto t) { diff --git a/tslang/lib/TypeScript/DeclarationPrinter.cpp b/tslang/lib/TypeScript/DeclarationPrinter.cpp index d02d56b4a..0bfeb5bcf 100644 --- a/tslang/lib/TypeScript/DeclarationPrinter.cpp +++ b/tslang/lib/TypeScript/DeclarationPrinter.cpp @@ -27,6 +27,7 @@ namespace typescript MLIRPrinter mp{}; mp.printBoxedObjectTypes = true; mp.quoteNonIdentifierFieldNames = true; + mp.getClassSpecialization = getClassSpecialization; mp.printType(os, type); } diff --git a/tslang/lib/TypeScript/MLIRGenImpl.h b/tslang/lib/TypeScript/MLIRGenImpl.h index 034bc0acc..e697bac20 100644 --- a/tslang/lib/TypeScript/MLIRGenImpl.h +++ b/tslang/lib/TypeScript/MLIRGenImpl.h @@ -9983,6 +9983,46 @@ class MLIRGenImpl std::pair mlirGen(ClassLikeDeclaration classDeclarationAST, const GenContext &genContext); + // A type argument in a specialization's name (Box>), which becomes the name + // of its symbols. MLIR prints a class or interface as a symbol reference, `@Tree`, but an `@` + // in an ELF symbol name is read as its version (`name@VERSION`), and ld fails to link a + // shared library that exports it. The `@` before a symbol reference is left out; one inside a + // quoted string (a literal type) is written as MLIR's escape `\40`, so no two names meet. + static void appendTypeToSymbolName(std::string &name, mlir::Type type) + { + std::string printed; + llvm::raw_string_ostream s(printed); + s << type; + + auto quoted = false; + for (size_t i = 0; i < printed.size(); i++) + { + auto c = printed[i]; + if (quoted && c == '\\' && i + 1 < printed.size()) + { + name.push_back(c); + name.push_back(printed[++i]); + continue; + } + + if (c == '"') + { + quoted = !quoted; + } + else if (c == '@') + { + if (quoted) + { + name.append("\\40"); + } + + continue; + } + + name.push_back(c); + } + } + void appendSpecializedTypeNames(std::string &name, llvm::SmallVector &typeParams, const GenContext &genContext) { @@ -9998,8 +10038,7 @@ class MLIRGenImpl auto type = getResolveTypeParameter(typeParam->getName(), false, genContext); if (type) { - llvm::raw_string_ostream s(name); - s << type; + appendTypeToSymbolName(name, type); } else { @@ -10751,8 +10790,7 @@ class MLIRGenImpl auto type = getType(typeParam, genContext); if (type) { - llvm::raw_string_ostream s(name); - s << type; + appendTypeToSymbolName(name, type); } else { @@ -11879,7 +11917,7 @@ class MLIRGenImpl SmallVector out; llvm::raw_svector_ostream ss(out); - MLIRDeclarationPrinter dp(ss); + MLIRDeclarationPrinter dp(ss, classSpecializationNamer()); dp.printTypeDeclaration(name, elementNamespace, type); declExports << ss.str().str(); @@ -11899,7 +11937,7 @@ class MLIRGenImpl SmallVector out; llvm::raw_svector_ostream ss(out); - MLIRDeclarationPrinter dp(ss); + MLIRDeclarationPrinter dp(ss, classSpecializationNamer()); dp.print(interfaceInfo); declExports << ss.str().str(); @@ -11919,7 +11957,7 @@ class MLIRGenImpl SmallVector out; llvm::raw_svector_ostream ss(out); - MLIRDeclarationPrinter dp(ss); + MLIRDeclarationPrinter dp(ss, classSpecializationNamer()); dp.printEnum(name, elementNamespace, enumType.getValues()); declExports << ss.str().str(); @@ -11934,7 +11972,7 @@ class MLIRGenImpl SmallVector out; llvm::raw_svector_ostream ss(out); - MLIRDeclarationPrinter dp(ss); + MLIRDeclarationPrinter dp(ss, classSpecializationNamer()); dp.printVariableDeclaration(name, elementNamespace, type, isConst, dllName); declExports << ss.str().str(); @@ -11949,12 +11987,56 @@ class MLIRGenImpl SmallVector out; llvm::raw_svector_ostream ss(out); - MLIRDeclarationPrinter dp(ss); + MLIRDeclarationPrinter dp(ss, classSpecializationNamer()); dp.print(funcProto->getNameWithoutNamespace(), elementNamespace, funcProto->getFuncType(), dllName); declExports << ss.str().str(); } + // A specialization of a generic class - its generic and type arguments, in order. + GenericClassInfo::TypePtr getClassSpecializationOf(ClassInfo::TypePtr classInfo, SmallVectorImpl &typeArgs) + { + if (!classInfo || !classInfo->originClassType || classInfo->originClassType == classInfo->classType) + { + return nullptr; + } + + auto genericClassInfo = getGenericClassInfoByFullName(classInfo->originClassType.getName().getValue()); + if (!genericClassInfo) + { + return nullptr; + } + + for (auto &typeParam : genericClassInfo->typeParams) + { + auto found = classInfo->typeParamsWithArgs.find(typeParam->getName()); + if (found == classInfo->typeParamsWithArgs.end()) + { + return nullptr; + } + + typeArgs.push_back(found->getValue().second); + } + + return genericClassInfo; + } + + // see MLIRPrinter::getClassSpecialization + MLIRDeclarationPrinter::ClassSpecializationFn classSpecializationNamer() + { + return [this](mlir_ts::ClassType classType, std::string &genericName, SmallVectorImpl &typeArgs) { + auto genericClassInfo = getClassSpecializationOf(getClassInfoByFullName(classType.getName().getValue()), typeArgs); + if (!genericClassInfo) + { + typeArgs.clear(); + return false; + } + + genericName = genericClassInfo->fullName.str(); + return true; + }; + } + void addClassDeclarationToExport(ClassInfo::TypePtr newClassPtr) { if (isAddedToExport(newClassPtr->classType)) @@ -11965,11 +12047,33 @@ class MLIRGenImpl exportedTypes.insert(newClassPtr->classType); + // A specialization (Box, the type of an exported class's field) is not declared on + // its own: the importer makes it from the generic, as a use in its own source would, and + // names it `Box` (classSpecializationNamer). Declared, it came out as + // `class Box>`, which does not parse. What it needs is the generic + // - exported here when it is this module's own, even if not marked `export` - and its + // type arguments. + SmallVector typeArgs; + if (auto genericClassInfo = getClassSpecializationOf(newClassPtr, typeArgs)) + { + if (genericClassInfo->fileName == mainSourceFileName) + { + addGenericClassDeclarationToExport(genericClassInfo); + } + + for (auto typeArg : typeArgs) + { + addDependancyTypesToExport(typeArg); + } + + return; + } + addDependancyTypesToExport(newClassPtr->classType); SmallVector out; llvm::raw_svector_ostream ss(out); - MLIRDeclarationPrinter dp(ss); + MLIRDeclarationPrinter dp(ss, classSpecializationNamer()); dp.print(newClassPtr); declExports << ss.str().str(); @@ -12015,7 +12119,7 @@ class MLIRGenImpl SmallVector out; llvm::raw_svector_ostream ss(out); - MLIRDeclarationPrinter dp(ss); + MLIRDeclarationPrinter dp(ss, classSpecializationNamer()); dp.printGenericClass(genericFunctionInfo->elementNamespace, declText); genericDeclExports << ss.str().str(); @@ -12060,7 +12164,7 @@ class MLIRGenImpl SmallVector out; llvm::raw_svector_ostream ss(out); - MLIRDeclarationPrinter dp(ss); + MLIRDeclarationPrinter dp(ss, classSpecializationNamer()); dp.printGenericClass(genericClassInfo->elementNamespace, declText); genericDeclExports << ss.str().str(); @@ -12101,7 +12205,7 @@ class MLIRGenImpl SmallVector out; llvm::raw_svector_ostream ss(out); - MLIRDeclarationPrinter dp(ss); + MLIRDeclarationPrinter dp(ss, classSpecializationNamer()); dp.printGenericClass(genericInterfaceInfo->elementNamespace, declText); genericDeclExports << ss.str().str(); @@ -12131,7 +12235,7 @@ class MLIRGenImpl SmallVector out; llvm::raw_svector_ostream ss(out); - MLIRDeclarationPrinter dp(ss); + MLIRDeclarationPrinter dp(ss, classSpecializationNamer()); dp.printGenericClass(elementNamespace, declText); genericDeclExports << ss.str().str(); diff --git a/tslang/lib/TypeScript/MLIRGenModule.cpp b/tslang/lib/TypeScript/MLIRGenModule.cpp index fa9bb43b8..b1b1d4125 100644 --- a/tslang/lib/TypeScript/MLIRGenModule.cpp +++ b/tslang/lib/TypeScript/MLIRGenModule.cpp @@ -1343,6 +1343,14 @@ namespace mlirgen addGlobalConstructor(location, fullInitGlobalFuncName); } + // Generics first: a declaration elsewhere names their specializations (a field typed + // `Box`), and the symbols come sorted, which put __decls_ before + // __decls_generic_ - "generic type Box can't be found". Registering a generic + // only records it, so one naming a class declared after it is fine. + std::stable_partition(symbols.begin(), symbols.end(), [](StringRef symbol) { + return symbol.starts_with(std::string(SHARED_LIB_DECLARATIONS_2UNDERSCORE) + "_generic_"); + }); + // A library can hold several modules (test-runner -shared links them all into one), each // with its own __decls_. A module already in this module - imported as source, // or through another library - is not declared again; see mlirGenInclude. diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index dabe5728a..235bf450b 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -2471,9 +2471,10 @@ tslang_add_import_tests(import-bindings-namespace "${import_bindings_dir}" impor # A generic class specialized while its module was imported lost its members (#231). set(import_specialization_dir "${PROJECT_SOURCE_DIR}/test/tester/import-specialization") -# Static linking only: through a -shared library the importer cannot name Box yet ("generic type -# Box can't be found"), a separate gap. -tslang_add_test(NAME test-compile-import-specialization COMMAND test-runner "${import_specialization_dir}/import_specialization.ts" "${import_specialization_dir}/box_module.ts") +# Through a -shared library the specialization was declared by its internal name +# (`class Box>`), and before the generic it is made from ("generic type Box +# can't be found"). On Linux the name had an `@` (`@Tree`), which ld reads as a symbol version. +tslang_add_import_tests(import-specialization "${import_specialization_dir}" import_specialization box_module) # The same for a shared library: imported directly and through a source module - see # shared-import-reimport.cmake.