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
2 changes: 2 additions & 0 deletions tslang/include/TypeScript/Defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@
#define CONSTRUCTOR_NAME "constructor"
#define CONSTRUCTOR_TEMPVAR_NAME ".ctor"
#define VTABLE_NAME ".vtbl"
// the vtable an object cast to an interface keeps in its own block (castTupleWithOwnVTableToInterface)
#define OWN_INTERFACE_VTABLE_FIELD_NAME ".ivtbl"
#define RTTI_NAME ".rtti"
#define SIZE_NAME ".size"
#define INSTANCEOF_NAME ".instanceOf"
Expand Down
12 changes: 12 additions & 0 deletions tslang/include/TypeScript/MLIRLogic/MLIRTypeHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -1383,6 +1383,18 @@ class MLIRTypeHelper
auto test = isa<mlir_ts::FunctionType>(foundField.type) && isa<mlir_ts::FunctionType>(fieldType)
? TestFunctionTypesMatchWithObjectMethods(location, foundField.type, fieldType).result == MatchResultType::Match
: stripLiteralType(fieldType) == stripLiteralType(foundField.type);

// a `?` member is read through the vtable as its T, and an optional<T> field (`v?: T` of a
// type literal) holds T first: the slot points at that value, and is patched to absent when
// the field has none (mlirGenCreateInterfaceVTableForObject)
if (!test && isConditional)
{
if (auto optionalType = dyn_cast<mlir_ts::OptionalType>(foundField.type))
{
test = stripLiteralType(fieldType) == stripLiteralType(optionalType.getElementType());
}
}

if (!test)
{
LLVM_DEBUG(llvm::dbgs() << "field " << id << " not matching type: " << fieldType << " and "
Expand Down
124 changes: 114 additions & 10 deletions tslang/lib/TypeScript/MLIRGenCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,25 @@ namespace mlirgen
return mlir::success();
}

// the value of a field the source does not have, when the field's type admits undefined: `v?: T`
// is optional<T>, and `v?: any` stays any (any | undefined is any) - both read as undefined
mlir::Value MLIRGenImpl::absentFieldValue(mlir::Location location, mlir::Type fieldType, const GenContext &genContext)
{
if (isa<mlir_ts::OptionalType>(fieldType))
{
return builder.create<mlir_ts::OptionalUndefOp>(location, fieldType);
}

if (isa<mlir_ts::AnyType>(fieldType))
{
auto undefValue = builder.create<mlir_ts::UndefOp>(location, getUndefinedType());
auto result = cast(location, fieldType, undefValue, genContext);
return result.failed() ? mlir::Value() : V(result);
}

return mlir::Value();
}

ValueOrLogicalResult MLIRGenImpl::mapTupleToFields(mlir::Location location, SmallVector<mlir::Value> &values, mlir::Value value, mlir_ts::TupleType srcTupleType,
::llvm::ArrayRef<::mlir::typescript::FieldInfo> fields, bool filterSpecialCases, const GenContext &genContext, bool errorAsWarning)
{
Expand All @@ -137,12 +156,13 @@ namespace mlirgen
count ++;
if (fieldInfo.id == mlir::Attribute() || (index < srcTupleType.size() && srcTupleType.getFieldInfo(index).id == mlir::Attribute()))
{
if (index >= srcTupleType.size() && isa<mlir_ts::OptionalType>(fieldInfo.type))
if (index >= srcTupleType.size())
{
// add undefined value
auto undefVal = builder.create<mlir_ts::OptionalUndefOp>(location, fieldInfo.type);
values.push_back(undefVal);
continue;
if (auto undefVal = absentFieldValue(location, fieldInfo.type, genContext))
{
values.push_back(undefVal);
continue;
}
}

MLIRPropertyAccessCodeLogic cl(compileOptions, builder, location, value, builder.getI32IntegerAttr(index));
Expand All @@ -156,10 +176,8 @@ namespace mlirgen
auto fieldIndex = srcTupleType.getIndex(fieldInfo.id);
if (fieldIndex < 0)
{
if (isa<mlir_ts::OptionalType>(fieldInfo.type))
if (auto undefVal = absentFieldValue(location, fieldInfo.type, genContext))
{
// add undefined value
auto undefVal = builder.create<mlir_ts::OptionalUndefOp>(location, fieldInfo.type);
values.push_back(undefVal);
continue;
}
Expand Down Expand Up @@ -2302,6 +2320,14 @@ namespace mlirgen
emitWarning(location, "") << "Cloned object is used. Ensure all types are matching to interface: " << interfaceInfo->fullName;
}

// a `?` member held as optional<T> needs a vtable of this object's own (patchAbsentOptionalFieldSlots):
// it goes in a field after the object's own ones, so the block the interface holds frees it as well
if (!getOptionalFieldSlots(mlir::cast<mlir_ts::TupleType>(tupleType), interfaceInfo).empty())
{
return castTupleWithOwnVTableToInterface(location, inEffective, mlir::cast<mlir_ts::TupleType>(tupleType),
interfaceInfo, genContext);
}

// TODO: finish it, what to finish it? maybe optimization not to create extra object?
// convert Tuple to Object
auto objType = mlir_ts::ObjectType::get(tupleType);
Expand All @@ -2325,8 +2351,72 @@ namespace mlirgen
return castObjectToInterface(location, in, objType, interfaceInfo, genContext);
}

// the object is copied into a block with one more field, `.vtable`, after its own: the vtable patched for
// its `?` members (patchAbsentOptionalFieldSlots) is stored there, so it lives and dies with the block the
// interface holds. The object's own fields keep their offsets, which are what the vtable holds.
ValueOrLogicalResult MLIRGenImpl::castTupleWithOwnVTableToInterface(mlir::Location location, mlir::Value in,
mlir_ts::TupleType tupleType, InterfaceInfo::TypePtr interfaceInfo, const GenContext &genContext)
{
auto inEffective = in;
if (inEffective.getType() != tupleType)
{
CAST(inEffective, location, tupleType, inEffective, genContext);
}

// the vtable's type: the interface's slots typed by this object's fields
auto plainObjType = mlir_ts::ObjectType::get(tupleType);
auto vtableName = interfaceVTableNameForObject(plainObjType, interfaceInfo);
auto globalVTable = resolveFullNameIdentifier(location, vtableName, true, genContext);
if (!globalVTable)
{
if (mlir::failed(mlirGenObjectVirtualTableDefinitionForInterface(location, plainObjType, interfaceInfo, genContext)))
{
return mlir::failure();
}

globalVTable = resolveFullNameIdentifier(location, vtableName, true, genContext);
if (!globalVTable)
{
return mlir::failure();
}
}

auto vtableType = mlir::cast<mlir_ts::RefType>(globalVTable.getType()).getElementType();

SmallVector<mlir_ts::FieldInfo> fields(tupleType.getFields().begin(), tupleType.getFields().end());
fields.push_back({MLIRHelper::TupleFieldName(OWN_INTERFACE_VTABLE_FIELD_NAME, builder.getContext()), vtableType, false, mlir_ts::AccessLevel::Public});
auto withVTableType = getTupleType(fields);

SmallVector<mlir::Type> types;
for (auto &field : tupleType.getFields())
{
types.push_back(field.type);
}

// as in castTupleToInterface: the block owns what its fields hold
mlirGenRetainCaptured(location, mlir::ValueRange{inEffective});

auto parts = builder.create<mlir_ts::DeconstructTupleOp>(location, types, inEffective);
mlir::Value withVTable = builder.create<mlir_ts::UndefOp>(location, withVTableType);
for (auto [index, part] : llvm::enumerate(parts.getResults()))
{
withVTable = builder.create<mlir_ts::InsertPropertyOp>(location, withVTableType, part, withVTable,
MLIRHelper::getStructIndex(builder, index));
}

auto objType = mlir_ts::ObjectType::get(withVTableType);
auto valueAddr = builder.create<mlir_ts::NewOp>(location, mlir_ts::ValueRefType::get(withVTableType), builder.getBoolAttr(false));
builder.create<mlir_ts::StoreOp>(location, withVTable, valueAddr);
auto inCasted = builder.create<mlir_ts::CastOp>(location, objType, valueAddr);

auto vtableStorage = builder.create<mlir_ts::PropertyRefOp>(location, mlir_ts::RefType::get(vtableType), inCasted,
(int)fields.size() - 1);

return castObjectToInterface(location, inCasted, objType, interfaceInfo, genContext, vtableStorage);
}

ValueOrLogicalResult MLIRGenImpl::castObjectToInterface(mlir::Location location, mlir::Value in, mlir_ts::ObjectType objType,
InterfaceInfo::TypePtr interfaceInfo, const GenContext &genContext)
InterfaceInfo::TypePtr interfaceInfo, const GenContext &genContext, mlir::Value vtableStorage)
{
auto inEffective = in;
auto effectiveObjType = objType;
Expand Down Expand Up @@ -2364,7 +2454,21 @@ namespace mlirgen
}
}

auto result = mlirGenCreateInterfaceVTableForObject(location, inEffective, effectiveObjType, interfaceInfo, genContext);
// a boxed object literal's block is not made by this cast, so it has no room for a vtable of its own:
// its fields are copied into one that has (castTupleWithOwnVTableToInterface)
if (!vtableStorage)
{
if (auto storageTuple = dyn_cast<mlir_ts::TupleType>(effectiveObjType.getStorageType()))
{
if (!getOptionalFieldSlots(storageTuple, interfaceInfo).empty())
{
CAST_A(unboxed, location, storageTuple, inEffective, genContext);
return castTupleWithOwnVTableToInterface(location, unboxed, storageTuple, interfaceInfo, genContext);
}
}
}

auto result = mlirGenCreateInterfaceVTableForObject(location, inEffective, effectiveObjType, interfaceInfo, genContext, vtableStorage);
EXIT_IF_FAILED_OR_NO_VALUE(result)
auto createdInterfaceVTableForObject = V(result);

Expand Down
20 changes: 18 additions & 2 deletions tslang/lib/TypeScript/MLIRGenExpressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1583,8 +1583,24 @@ namespace mlirgen
llvm::SmallVector<mlir_ts::FieldInfo> destTupleFields;
if (mlir::succeeded(mth.getFields(oli.receiverType, destTupleFields)))
{
auto tupleType = getTupleType(destTupleFields);
return V(builder.create<mlir_ts::UndefOp>(location, tupleType));
// `{}` does not have an optional member: an interface's `?` member is left out (an absent
// member reads as undefined), and a tuple with a field that admits undefined is built by the
// cast of the empty literal below, which gives such a field undefined rather than undef
auto isInterface = isa<mlir_ts::InterfaceType>(oli.receiverType);
if (isInterface)
{
llvm::erase_if(destTupleFields, [](const mlir_ts::FieldInfo &fieldInfo) { return fieldInfo.isConditional; });
}

auto hasAbsentableField = !isInterface && llvm::any_of(destTupleFields, [](const mlir_ts::FieldInfo &fieldInfo) {
return isa<mlir_ts::OptionalType>(fieldInfo.type) || isa<mlir_ts::AnyType>(fieldInfo.type);
});

if (!hasAbsentableField)
{
auto tupleType = getTupleType(destTupleFields);
return V(builder.create<mlir_ts::UndefOp>(location, tupleType));
}
}
}
}
Expand Down
38 changes: 36 additions & 2 deletions tslang/lib/TypeScript/MLIRGenImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -9609,6 +9609,27 @@ class MLIRGenImpl
EXIT_IF_FAILED_OR_NO_VALUE(result)
auto tupleValue = V(result);

// `...opt` of a `v?: T` field: spread T's fields - with their defaults when it has no value, as for
// a plain T field before `?` made it optional (`{...undefined}` adds no field, which a fixed
// literal type cannot express)
if (auto optType = dyn_cast<mlir_ts::OptionalType>(tupleValue.getType()))
{
auto hasValue = builder.create<mlir_ts::HasValueOp>(location, getBooleanType(), tupleValue);
auto ifOp = builder.create<mlir_ts::IfOp>(location, optType.getElementType(), hasValue, true);
{
mlir::OpBuilder::InsertionGuard guard(builder);
builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
auto value = builder.create<mlir_ts::ValueOp>(location, optType.getElementType(), tupleValue);
builder.create<mlir_ts::ResultOp>(location, mlir::ValueRange{value});

builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
auto defaultValue = builder.create<mlir_ts::DefaultOp>(location, optType.getElementType());
builder.create<mlir_ts::ResultOp>(location, mlir::ValueRange{defaultValue});
}

tupleValue = ifOp.getResult(0);
}

LLVM_DEBUG(llvm::dbgs() << "\n!! SpreadAssignment value: " << tupleValue << "\n";);

auto tupleFields = [&] (::llvm::ArrayRef<mlir_ts::FieldInfo> fields) -> mlir::LogicalResult {
Expand Down Expand Up @@ -10697,8 +10718,15 @@ class MLIRGenImpl
InterfaceInfo::TypePtr newInterfacePtr,
const GenContext &genContext);

ValueOrLogicalResult mlirGenCreateInterfaceVTableForObject(mlir::Location location, mlir::Value in,
mlir_ts::ObjectType objectType, InterfaceInfo::TypePtr newInterfacePtr, const GenContext &genContext);
ValueOrLogicalResult mlirGenCreateInterfaceVTableForObject(mlir::Location location, mlir::Value in,
mlir_ts::ObjectType objectType, InterfaceInfo::TypePtr newInterfacePtr, const GenContext &genContext,
mlir::Value vtableStorage = mlir::Value());

SmallVector<std::pair<int, int>> getOptionalFieldSlots(mlir_ts::TupleType storeType, InterfaceInfo::TypePtr newInterfacePtr);

mlir::Value patchAbsentOptionalFieldSlots(mlir::Location location, mlir::Value in, mlir_ts::ObjectType objectType,
InterfaceInfo::TypePtr newInterfacePtr, mlir::Value vtableRef, bool ownsVTable, mlir::Value vtableStorage,
const GenContext &genContext);

StringRef interfaceVTableNameForClass(ClassInfo::TypePtr newClassPtr, InterfaceInfo::TypePtr newInterfacePtr)
{
Expand Down Expand Up @@ -11552,6 +11580,8 @@ class MLIRGenImpl
ValueOrLogicalResult selectFieldsValues(mlir::Location location, SmallVector<mlir::Value> &values, mlir::Value value,
::llvm::ArrayRef<::mlir::typescript::FieldInfo> fields, bool filterSpecialCases, const GenContext &genContext, bool errorAsWarning = false);

mlir::Value absentFieldValue(mlir::Location location, mlir::Type fieldType, const GenContext &genContext);

// TODO: needs to unified with selectFieldsValues
ValueOrLogicalResult mapTupleToFields(mlir::Location location, SmallVector<mlir::Value> &values, mlir::Value value, mlir_ts::TupleType srcTupleType,
::llvm::ArrayRef<::mlir::typescript::FieldInfo> fields, bool filterSpecialCases, const GenContext &genContext, bool errorAsWarning = false);
Expand Down Expand Up @@ -11651,6 +11681,10 @@ class MLIRGenImpl
mlir_ts::InterfaceType interfaceType, const GenContext &genContext);

ValueOrLogicalResult castObjectToInterface(mlir::Location location, mlir::Value in, mlir_ts::ObjectType objType,
InterfaceInfo::TypePtr interfaceInfo, const GenContext &genContext,
mlir::Value vtableStorage = mlir::Value());

ValueOrLogicalResult castTupleWithOwnVTableToInterface(mlir::Location location, mlir::Value in, mlir_ts::TupleType tupleType,
InterfaceInfo::TypePtr interfaceInfo, const GenContext &genContext);

mlir_ts::CreateBoundFunctionOp createBoundMethodFromExtensionMethod(mlir::Location location, mlir_ts::CreateExtensionFunctionOp createExtentionFunction);
Expand Down
Loading
Loading