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
3 changes: 2 additions & 1 deletion tslang/include/TypeScript/MLIRLogic/MLIRDeclarationPrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ namespace typescript
void printVariableDeclaration(StringRef, NamespaceInfo::TypePtr, mlir::Type, bool);
void print(StringRef, NamespaceInfo::TypePtr, mlir_ts::FunctionType);
void print(ClassInfo::TypePtr);
void print(InterfaceInfo::TypePtr);
void print(InterfaceInfo::TypePtr);
void printGenericClass(NamespaceInfo::TypePtr, StringRef);

protected:
void newline();
Expand Down
14 changes: 14 additions & 0 deletions tslang/lib/TypeScript/DeclarationPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,20 @@ namespace typescript
printNamespaceEnd(classType->elementNamespace);
}

void MLIRDeclarationPrinter::printGenericClass(NamespaceInfo::TypePtr elementNamespace, StringRef rawDeclarationText)
{
// unlike print(ClassInfo::TypePtr) above, a generic class has no compiled body for
// any given instantiation to @dllimport against - each importing module
// instantiates it locally instead, so the caller passes the ORIGINAL source text
// (type parameters and method bodies intact) verbatim; this just supplies the
// matching namespace wrapper, deliberately WITHOUT printBeforeDeclaration()'s
// @dllimport marker.
printNamespaceBegin(elementNamespace);
os << rawDeclarationText;
newline();
printNamespaceEnd(elementNamespace);
}

void MLIRDeclarationPrinter::print(InterfaceInfo::TypePtr interfaceType)
{
printNamespaceBegin(interfaceType->elementNamespace);
Expand Down
42 changes: 41 additions & 1 deletion tslang/lib/TypeScript/MLIRGenClasses.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ namespace mlirgen
auto fullNamePtr = getFullNamespaceName(namePtr);
if (fullNameGenericClassesMap.count(fullNamePtr))
{
// already registered - but the registration itself typically happens during
// Stages::Discovering (before addGenericClassDeclarationToExport's
// isAddedToExport gate, which only actually emits once stage ==
// Stages::SourceGeneration, will do anything) - retry the export step alone
// using the existing GenericClassInfo rather than skipping it entirely.
if (getExportModifier(classDeclarationAST))
{
addGenericClassDeclarationToExport(fullNameGenericClassesMap.lookup(fullNamePtr));
}

return mlir::success();
}

Expand All @@ -40,6 +50,18 @@ namespace mlirgen
getGenericClassesMap().insert({namePtr, newGenericClassPtr});
fullNameGenericClassesMap.insert(fullNamePtr, newGenericClassPtr);

// support dynamic loading: a generic class is never instantiated in this module
// if nothing here uses it concretely, so mlirGen(ClassLikeDeclaration) never
// reaches its own addClassDeclarationToExport call below (that only runs for a
// SPECIALIZED instantiation, gated on genContext.typeParamsWithArgs being
// non-empty) - the bare template needs to be exported here instead, the one
// place every generic declaration passes through regardless of whether it is
// ever instantiated locally.
if (getExportModifier(classDeclarationAST))
{
addGenericClassDeclarationToExport(newGenericClassPtr);
}

return mlir::success();
}

Expand Down Expand Up @@ -71,9 +93,27 @@ namespace mlirgen
return {mlir::failure(), ""};
}

// do not process specialized class second time;
if (isGenericClass && genContext.typeParamsWithArgs.size() > 0)
{
// a concrete instantiation of a generic class (e.g. Box<number>) reprocesses the
// SAME class declaration AST as the bare template (e.g. `export class Box<T>`),
// so mlirGenClassInfo's isExport = getExportModifier(classDeclarationAST) above
// would otherwise mark this LOCAL, per-instantiation specialization as exported
// too - wrong on two counts: (1) semantically, a specialization materialized by
// whichever module instantiates it is local to that module, not a re-export of
// it (an importer using M.Box<number> isn't re-exporting M.Box<number> further);
// (2) mechanically, a multi-type-param instantiation's mangled name contains a
// raw comma (e.g. M.Pair<!ts.number,!ts.string>..instanceOf), which is a
// metacharacter in the linker's `/EXPORT:name[,option]` directive syntax - lld
// rejects it outright ("invalid /export:") the moment anything in it is
// actually marked for export. The generic TEMPLATE's own declaration is already
// exported correctly and separately (see registerGenericClass /
// addGenericClassDeclarationToExport), which is the only export this class
// needs.
newClassPtr->isExport = false;
newClassPtr->isPublic = false;

// do not process specialized class second time;
// TODO: investigate why classType is provided already for class
if (testProcessingState(newClassPtr, ProcessingStages::Processing, genContext))
{
Expand Down
59 changes: 57 additions & 2 deletions tslang/lib/TypeScript/MLIRGenImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ class MLIRGenImpl
#endif

mlir::LogicalResult createDeclarationExportGlobalVar(const GenContext &genContext);
mlir::LogicalResult createGenericClassDeclarationExportGlobalVar(const GenContext &genContext);

bool isCodeStatment(SyntaxKind kind);

Expand Down Expand Up @@ -10308,20 +10309,65 @@ class MLIRGenImpl
{
// already added
return;
}
}

exportedTypes.insert(newClassPtr->classType);

addDependancyTypesToExport(newClassPtr->classType);

SmallVector<char> out;
llvm::raw_svector_ostream ss(out);
llvm::raw_svector_ostream ss(out);
MLIRDeclarationPrinter dp(ss);
dp.print(newClassPtr);

declExports << ss.str().str();
}

void addGenericClassDeclarationToExport(GenericClassInfo::TypePtr genericClassInfo)
{
if (isAddedToExport(genericClassInfo->classType))
{
// already added
return;
}

// A generic class re-declared while re-importing another module's embedded
// declarations (see mlirGenImportSharedLib) still carries its own `export` keyword
// verbatim (it is the original source, copied as-is), so it reaches this same
// function a second time from inside the IMPORTER's own compile - but at that
// point `sourceFile` is the ambient/outer file (e.g. the importer's own .ts), not
// the "partial" buffer parsePartialStatements actually parsed this declaration
// from, so classDeclaration's pos/_end (offsets into THAT buffer) do not correspond
// to sourceFile->text at all and can exceed its length. Re-exporting a
// re-declaration one level removed like this is also simply wrong (the importer
// isn't meant to re-export M's generics further) - bounds-check and skip rather
// than let getTextOfNodeFromSourceText's substr throw std::out_of_range.
auto declEnd = static_cast<size_t>(genericClassInfo->classDeclaration->_end);
if (declEnd > genericClassInfo->sourceFile->text.length())
{
return;
}

exportedTypes.insert(genericClassInfo->classType);

// unlike a concrete class (printed above as a signature-only, @dllimport-marked
// declaration - the real body is already compiled into this module's DLL), a
// generic class has no compiled body for any given instantiation: each importing
// module instantiates it locally, on demand, exactly like a same-file usage would.
// So the FULL original source - type parameters and method bodies intact, no
// @dllimport marker - must be re-exported verbatim for parsePartialStatements to
// recompile per instantiation in the importer.
auto declText = convertWideToUTF8(getTextOfNodeFromSourceText(
genericClassInfo->sourceFile->text, genericClassInfo->classDeclaration.as<Node>(), true));

SmallVector<char> out;
llvm::raw_svector_ostream ss(out);
MLIRDeclarationPrinter dp(ss);
dp.printGenericClass(genericClassInfo->elementNamespace, declText);

genericDeclExports << ss.str().str();
}

auto getNamespaceName() -> StringRef
{
return currentNamespace->name;
Expand Down Expand Up @@ -10990,6 +11036,15 @@ class MLIRGenImpl
bool declarationMode;

std::stringstream declExports;
// generic class declarations must be re-imported as plain (non-".d.ts") source: unlike
// declExports (parsed with a "partial.d.ts" filename, which makes the parser mark
// everything ambient/external regardless of any per-declaration @dllimport marker - see
// createDeclarationExportGlobalVar/mlirGenImportSharedLib), a generic class has no
// compiled body for any instantiation to link against - the importer instantiates it
// locally, and needs its real method bodies to actually be compilable, not treated as
// external stubs. Kept in a separate stream/global so it can be re-parsed with a plain
// ".ts" filename instead.
std::stringstream genericDeclExports;
mlir::SmallPtrSet<mlir::Type, 32> exportCheckedDependenciesTypes;
mlir::SmallPtrSet<mlir::Type, 32> exportedTypes;

Expand Down
69 changes: 63 additions & 6 deletions tslang/lib/TypeScript/MLIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,9 +334,49 @@ namespace mlirgen
llvm::sys::path::append(path, llvm::sys::path::filename(mainSourceFileName));
llvm::sys::path::replace_extension(path, ".d.ts");
return createDependencyDeclarationFile(path, declText);
#else
return success();
#endif
#else
return success();
#endif
}

mlir::LogicalResult MLIRGenImpl::createGenericClassDeclarationExportGlobalVar(const GenContext &genContext)
{
if (!genericDeclExports.rdbuf()->in_avail() || !compileOptions.embedExportDeclarations)
{
return mlir::success();
}

auto declText = genericDeclExports.str();

LLVM_DEBUG(llvm::dbgs() << "\n!! export generic class declaration: \n" << declText << "\n";);

auto typeWithInit = [&](mlir::Location location, const GenContext &genContext) {
auto litValue = V(mlirGenStringValue(location, declText, true));
return std::make_tuple(litValue.getType(), litValue, TypeProvided::No);
};

auto loc = mlir::UnknownLoc::get(builder.getContext());

VariableClass varClass = VariableType::Var;
varClass.isExport = true;
varClass.isPublic = true;

// "generic" in the middle keeps the "__decls" prefix (so the existing
// symbol.starts_with(SHARED_LIB_DECLARATIONS_2UNDERSCORE) enumeration in
// mlirGenImportSharedLib still finds it) while staying distinguishable from the
// regular per-file "__decls_<file>_<hash>" global, so that call site can tell the
// two apart and parse each with the right file_d_ts flag.
std::string varName(SHARED_LIB_DECLARATIONS_2UNDERSCORE);
varName.append("_generic_");
varName.append(llvm::sys::path::stem(llvm::sys::path::filename(mainSourceFileName)));
varName.append("_");
varName.append(to_string(hash_value(mainSourceFileName)));

auto varNameRef = StringRef(varName).copy(stringAllocator);

registerVariable(loc, varNameRef, true, varClass, typeWithInit, genContext);

return mlir::success();
}

bool MLIRGenImpl::isCodeStatment(SyntaxKind kind)
Expand Down Expand Up @@ -649,6 +689,11 @@ namespace mlirgen
outputDiagnostics(postponedMessages, 1);
return mlir::failure();
}

if (mlir::failed(createGenericClassDeclarationExportGlobalVar(genContext))) {
outputDiagnostics(postponedMessages, 1);
return mlir::failure();
}
}

clearTempModule();
Expand Down Expand Up @@ -879,15 +924,27 @@ namespace mlirgen
LLVM_DEBUG(llvm::dbgs() << "\n!! Shared lib import: \n" << dataPtr << "\n";);

{
MLIRLocationGuard vgLoc(overwriteLoc);
MLIRLocationGuard vgLoc(overwriteLoc);
overwriteLoc = location;

// a generic class's declaration (see
// createGenericClassDeclarationExportGlobalVar) must NOT be parsed with
// the ".d.ts" filename convention used below for every other kind of
// declaration: that convention makes the parser mark everything ambient/
// external regardless of any per-declaration @dllimport marker (see the
// comment on parsePartialStatements's file_d_ts parameter), which would
// make the generic's instantiated specializations wrongly look like
// external stubs with no compilable body - they need to be treated as
// ordinary, fully-compilable local source instead.
auto isGenericClassDecl =
declSymbol.starts_with(std::string(SHARED_LIB_DECLARATIONS_2UNDERSCORE) + "_generic_");

auto importData = convertUTF8toWide(dataPtr);
if (mlir::failed(parsePartialStatements(importData, genContext, false, true)))
if (mlir::failed(parsePartialStatements(importData, genContext, false, !isGenericClassDecl)))
{
//assert(false);
return mlir::failure();
}
}
}
}
else
Expand Down
Loading
Loading