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
9 changes: 9 additions & 0 deletions tslang/include/TypeScript/Defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,15 @@
#define SIZE_NAME ".size"
#define INSTANCEOF_NAME ".instanceOf"
#define INSTANCEOF_PARAM_NAME "rttiParam"
// Every interface vtable starts with the `.instanceOf` of what implements it, ahead of the
// interface's own members: a class's own `.instanceOf`, or, for an object literal, which is no
// instance of any class, INSTANCEOF_NONE_NAME, which answers false. That lets `i instanceof C`
// and `<C>anyHoldingAnInterface` ask an interface value what it is. An interface member's slot
// (InterfaceSymbolRefOp's index) is its index among the members, so the vtable slot is that plus
// INTERFACE_VTABLE_HEADER_SLOTS.
#define INTERFACE_VTABLE_INSTANCEOF_SLOT 0
#define INTERFACE_VTABLE_HEADER_SLOTS 1
#define INSTANCEOF_NONE_NAME ".instanceOf.none"
#define MAIN_ENTRY_NAME "main"
#define TS_NEST_ATTRIBUTE "ts.nest"
#define THIS_TEMPVAR_NAME ".this"
Expand Down
18 changes: 16 additions & 2 deletions tslang/include/TypeScript/MLIRLogic/MLIRGenStore.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define MLIR_TYPESCRIPT_MLIRGENSTORE_H_

#include "TypeScript/DOM.h"
#include "TypeScript/Defines.h"
#include "TypeScript/MLIRLogic/MLIRHelper.h"

#include "llvm/ADT/StringMap.h"
Expand Down Expand Up @@ -895,8 +896,21 @@ struct ClassInfo
base->getVirtualTable(vtable, methodSlots, interfaceSlots);
}

// TODO: we need to process .Rtti first
// TODO: then we need to process .instanceOf next
// `.instanceOf` is slot 0 of every class vtable: an object reached with no static type (an
// `any` being unboxed, `x instanceof C` on an opaque value) is asked through that slot
// (mlirGenInstanceOfOpaque). A root class puts it there ahead of its interfaces, and a
// derived class overrides it in place. Before this, a root class that implemented an
// interface kept the interface's vtable in slot 0, and `<B>anyValue` called it.
if (baseClasses.empty())
{
auto instanceOf = std::find_if(methods.begin(), methods.end(),
[](auto &method) { return method.name == INSTANCEOF_NAME; });
if (instanceOf != methods.end() && instanceOf->isVirtual && !methodSlots.contains(instanceOf->name))
{
methodSlots[instanceOf->name] = vtable.size();
vtable.push_back({*instanceOf, false});
}
}

// do vtable for current class
for (auto &implement : implements)
Expand Down
5 changes: 3 additions & 2 deletions tslang/lib/TypeScript/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5605,8 +5605,9 @@ struct InterfaceSymbolRefOpLowering : public TsLlvmPattern<mlir_ts::InterfaceSym
auto thisVal = rewriter.create<LLVM::ExtractValueOp>(loc, th.getPtrType(), transformed.getInterfaceVal(),
MLIRHelper::getStructIndex(rewriter, THIS_VALUE_INDEX));

auto methodOrFieldPtr =
rewriter.create<mlir_ts::VTableOffsetRefOp>(loc, th.getPtrType(), vtable, interfaceSymbolRefOp.getIndex());
// the vtable starts with the implementer's `.instanceOf` - see INTERFACE_VTABLE_HEADER_SLOTS
auto methodOrFieldPtr = rewriter.create<mlir_ts::VTableOffsetRefOp>(
loc, th.getPtrType(), vtable, interfaceSymbolRefOp.getIndex() + INTERFACE_VTABLE_HEADER_SLOTS);

if (auto boundFunc = dyn_cast<mlir_ts::BoundFunctionType>(interfaceSymbolRefOp.getType()))
{
Expand Down
104 changes: 94 additions & 10 deletions tslang/lib/TypeScript/MLIRGenCast.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Cast-family methods of MLIRGenImpl (see MLIRGenImpl.h).

#include "MLIRGenImpl.h"
#include "TypeScript/MLIRLogic/MLIRRTTIHelperVC.h"

namespace typescript
{
Expand Down Expand Up @@ -270,7 +271,66 @@ namespace mlirgen
return NewClassInstanceWithSettingFields(location, classType, fields, values, genContext);
}

ValueOrLogicalResult MLIRGenImpl::castFieldsToClass(mlir::Location location, mlir::Value value,
// `<C>i`, `i as C`, or narrowing by `i instanceof C`: the interface holds an object, and if that
// object is a C, the result is that very object - asked through slot 0 of the interface's
// vtable (INTERFACE_VTABLE_HEADER_SLOTS). If it is not (another class, or an object literal),
// there is no C to hand back, and the cast throws, as `<C>anyValue` does. It used to build a
// new C out of the interface's fields, which crashed the compiler for any class with a field
// the interface does not name - `class B implements I` with a field `x` was enough.
ValueOrLogicalResult MLIRGenImpl::castInterfaceToClass(mlir::Location location, mlir::Value value,
mlir_ts::ClassType classType, const GenContext &genContext)
{
// Where every field of the class is a field of the interface, the old conversion - a new
// C filled from the interface's fields - still answers for what is not a C, as before.
auto fields = mlir::cast<mlir_ts::ClassStorageType>(classType.getStorageType()).getFields();
auto interfaceInfo = getInterfaceInfoByFullName(mlir::cast<mlir_ts::InterfaceType>(value.getType()).getName().getValue());
auto fieldsInInterface = interfaceInfo && llvm::all_of(fields, [&](auto &field) {
auto name = dyn_cast_or_null<mlir::StringAttr>(field.id);
return (name && name.getValue().starts_with(".")) || interfaceInfo->findField(field.id);
});

auto classRefVal = builder.create<mlir_ts::ClassRefOp>(
location, classType, mlir::FlatSymbolRefAttr::get(builder.getContext(), classType.getName().getValue()));
auto isInstance = mlirGenInstanceOfInterface(location, value, classRefVal, genContext);
if (isInstance.failed_or_no_value())
{
return mlir::failure();
}

MLIRCodeLogicHelper mclh(builder, location, compileOptions);
return mclh.conditionalValue(
V(isInstance),
[&]() {
auto thisPtr = builder.create<mlir_ts::ExtractInterfaceThisOp>(location, getOpaqueType(), value);
return ValueOrLogicalResult(builder.create<mlir_ts::CastOp>(location, classType, thisPtr));
},
[&](mlir::Type) {
if (fieldsInInterface)
{
return castFieldsToClass(location, value, fields, classType, genContext);
}

auto message = builder.create<mlir_ts::ConstantOp>(location, getStringType(),
getStringAttr("Can't cast from interface"));
builder.create<mlir_ts::ThrowOp>(location, message);

MLIRRTTIHelperVC rtti(builder, theModule, compileOptions);
setCatchCopyThunkBuilder(rtti);
if (!rtti.setRTTIForType(location, message.getType(), [&](StringRef classFullName) {
return getClassInfoByFullName(classFullName);
}))
{
emitError(location, "Not supported type in throw");
return ValueOrLogicalResult(mlir::failure());
}

// not reached; the branch still needs a value of the class type
auto nullValue = builder.create<mlir_ts::NullOp>(location, getNullType());
return ValueOrLogicalResult(builder.create<mlir_ts::CastOp>(location, classType, nullValue));
});
}

ValueOrLogicalResult MLIRGenImpl::castFieldsToClass(mlir::Location location, mlir::Value value,
::llvm::ArrayRef<::mlir::typescript::FieldInfo> fields,
mlir_ts::ClassType classType, const GenContext &genContext, bool errorAsWarning)
{
Expand Down Expand Up @@ -1177,8 +1237,7 @@ namespace mlirgen
}
else if (auto classType = dyn_cast<mlir_ts::ClassType>(type))
{
fields = mlir::cast<mlir_ts::ClassStorageType>(classType.getStorageType()).getFields();
return castFieldsToClass(location, value, fields, classType, genContext);
return castInterfaceToClass(location, value, classType, genContext);
}
}

Expand Down Expand Up @@ -1691,7 +1750,7 @@ namespace mlirgen

StringMap<boolean> typeOfs;
SmallVector<mlir::Type> classInstances;
ss << S("function __unbox<T>(a: any) : T {\n");
ss << S("function __unbox<T>(a: any) : T {\nconst b = a;\n");
auto subType = type;
auto hasUnsupportedType = false;
mlir::TypeSwitch<mlir::Type>(subType)
Expand Down Expand Up @@ -1749,6 +1808,24 @@ namespace mlirgen
}

ss << S(" }\n");

// An `any` holding an interface value holds an object behind it, which may be one
// of these classes: the interface's vtable answers (INTERFACE_VTABLE_HEADER_SLOTS).
// `b` is `a` before `typeof` narrowed it to the first word of the box.
if (!typeOfs.contains("interface"))
{
ss << S(" else if (typeof a == 'interface') { const i = <TYPE_ANY_INTERFACE_ALIAS>b;\n");
for (auto [index, _] : enumerate(classInstances))
{
ss << S("if (i instanceof TYPE_INST_ALIAS");
ss << index;
ss << S(") return <TYPE_INST_ALIAS");
ss << index;
ss << S(">i;\n");
}

ss << S(" }\n");
}
}
else
{
Expand Down Expand Up @@ -1830,17 +1907,24 @@ namespace mlirgen
funcCallGenContext.typeAliasMap.insert({"TYPE_INST_ALIAS" + std::to_string(index), instanceOfType});
}

// Any interface type will do to read an interface value back out of the box: they all
// lower to one layout, and nothing but the vtable and `this` is read from it. A private
// name of its own keeps it from being looked up as a declared interface.
funcCallGenContext.typeAliasMap.insert(
{"TYPE_ANY_INTERFACE_ALIAS",
mlir_ts::InterfaceType::get(builder.getContext(), mlir::FlatSymbolRefAttr::get(builder.getContext(), ".any_interface"))});

SmallVector<mlir::Value, 4> operands;
operands.push_back(value);

NodeFactory nf(NodeFactoryFlags::None);
return mlirGenCallExpression(
location,
funcResult,
{
nf.createTypeReferenceNode(nf.createIdentifier(S(".TYPE_ALIAS_T")).as<Node>()),
},
operands,
location,
funcResult,
{
nf.createTypeReferenceNode(nf.createIdentifier(S(".TYPE_ALIAS_T")).as<Node>()),
},
operands,
funcCallGenContext);
}

Expand Down
13 changes: 13 additions & 0 deletions tslang/lib/TypeScript/MLIRGenClasses.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1675,6 +1675,19 @@ genContext);
return result;
}

// slot 0: the class's own `.instanceOf` (INTERFACE_VTABLE_HEADER_SLOTS), ahead of the
// interface's members, so an interface value can be asked what class it holds. A class
// with no `.instanceOf` (no vtable of its own) is an instance of nothing it can answer for.
if (auto *instanceOf = newClassPtr->findMethod(INSTANCEOF_NAME))
{
virtualTable.insert(virtualTable.begin(), VirtualMethodOrFieldInfo(*instanceOf));
}
else
{
auto noInstanceOf = mlirGenNoInstanceOfFunction(location);
virtualTable.insert(virtualTable.begin(), VirtualMethodOrFieldInfo(noInstanceOf));
}

// a method implementing this interface can itself be owned by a dynamically imported
// class (not just a base of it) - such a slot can't be a link-time constant SymbolRefOp
// and must be resolved at runtime instead (see the dynamic-import handling in the method
Expand Down
54 changes: 47 additions & 7 deletions tslang/lib/TypeScript/MLIRGenImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -3031,6 +3031,7 @@ class MLIRGenImpl
int firstParam = 0, bool isPublic = false);

mlir::LogicalResult mlirGenCatchCopyThunk(mlir::Location location, StringRef name, mlir::Type source, mlir::Type target);
MethodInfo mlirGenNoInstanceOfFunction(mlir::Location location);

void setCatchCopyThunkBuilder(MLIRRTTIHelperVC &rtti);

Expand Down Expand Up @@ -4893,6 +4894,42 @@ class MLIRGenImpl
// generates nothing - `if (a instanceof C) return a;` in the generated `___unbox<C>` lost its
// test, so `<C>anyValue` compiled into a cast that always throws, with no error shown.
ValueOrLogicalResult mlirGenInstanceOfOpaque(mlir::Location location, mlir::Value thisPtrValue, mlir::Value classRefVal, const GenContext &genContext)
{
// an instance's first word is its class's vtable
auto vtablePtr = builder.create<mlir_ts::VTableOffsetRefOp>(location, getOpaqueType(),
thisPtrValue, 0 /*VTABLE index*/);
return mlirGenInstanceOfThroughVTable(location, vtablePtr, thisPtrValue, classRefVal, genContext);
}

// `i instanceof C` for an interface value: its own vtable answers, since what it holds may be
// an object literal, which has no class vtable to ask (INTERFACE_VTABLE_HEADER_SLOTS). A null
// interface is an instance of nothing.
ValueOrLogicalResult mlirGenInstanceOfInterface(mlir::Location location, mlir::Value interfaceValue, mlir::Value classRefVal, const GenContext &genContext)
{
auto isSet = cast(location, getBooleanType(), interfaceValue, genContext);
if (isSet.failed_or_no_value())
{
return mlir::failure();
}

MLIRCodeLogicHelper mclh(builder, location, compileOptions);
return mclh.conditionalValue(
V(isSet),
[&]() {
auto vtable = builder.create<mlir_ts::ExtractInterfaceVTableOp>(location, getOpaqueType(), interfaceValue);
auto thisPtr = builder.create<mlir_ts::ExtractInterfaceThisOp>(location, getOpaqueType(), interfaceValue);
return mlirGenInstanceOfThroughVTable(location, vtable, thisPtr, classRefVal, genContext);
},
[&](mlir::Type) {
return ValueOrLogicalResult(
builder.create<mlir_ts::ConstantOp>(location, getBooleanType(), builder.getBoolAttr(false)));
});
}

// Asks `thisPtrValue` whether it is a `classRefVal` through slot 0 of `vtablePtr`: the class's
// `.instanceOf` for a class vtable, and the implementer's for an interface vtable.
ValueOrLogicalResult mlirGenInstanceOfThroughVTable(mlir::Location location, mlir::Value vtablePtr, mlir::Value thisPtrValue,
mlir::Value classRefVal, const GenContext &genContext)
{
auto classType = dyn_cast<mlir_ts::ClassType>(classRefVal.getType());
if (!classType)
Expand All @@ -4901,13 +4938,9 @@ class MLIRGenImpl
return mlir::failure();
}

// get VTable we can use VTableOffset
auto vtablePtr = builder.create<mlir_ts::VTableOffsetRefOp>(location, getOpaqueType(),
thisPtrValue, 0 /*VTABLE index*/);

// get InstanceOf method, this is 0 index in vtable
// `.instanceOf` is slot 0 of a class vtable and of an interface vtable alike
auto instanceOfPtr = builder.create<mlir_ts::VTableOffsetRefOp>(
location, getOpaqueType(), vtablePtr, 0 /*InstanceOf index*/);
location, getOpaqueType(), vtablePtr, INTERFACE_VTABLE_INSTANCEOF_SLOT);

auto classInfo = getClassInfoByFullName(classType.getName().getValue());

Expand Down Expand Up @@ -5038,6 +5071,11 @@ class MLIRGenImpl
{
return mlirGenInstanceOfOpaque(location, resultLeftValue, resultRightValue, genContext);
}

if (isa<mlir_ts::InterfaceType>(resultLeftfType))
{
return mlirGenInstanceOfInterface(location, resultLeftValue, resultRightValue, genContext);
}
}
#endif

Expand Down Expand Up @@ -11223,7 +11261,9 @@ class MLIRGenImpl
ValueOrLogicalResult castTupleToClass(mlir::Location location, mlir::Value value, mlir_ts::TupleType srcTupleType,
ArrayRef<mlir_ts::FieldInfo> fields, mlir_ts::ClassType classType, const GenContext &genContext, bool errorAsWarning = false);

ValueOrLogicalResult castFieldsToClass(mlir::Location location, mlir::Value value,
ValueOrLogicalResult castInterfaceToClass(mlir::Location location, mlir::Value value, mlir_ts::ClassType classType,
const GenContext &genContext);
ValueOrLogicalResult castFieldsToClass(mlir::Location location, mlir::Value value,
::llvm::ArrayRef<::mlir::typescript::FieldInfo> fields,
mlir_ts::ClassType classType, const GenContext &genContext, bool errorAsWarning = false);

Expand Down
Loading
Loading