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
72 changes: 71 additions & 1 deletion tslang/lib/TypeScript/MLIRGenAccessCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1667,8 +1667,15 @@ namespace mlirgen

// value if true

auto innerFuncRef =
mlir::Value innerFuncRef =
builder.create<mlir_ts::ValueOp>(location, optFuncRef.getElementType(), actualFuncRefValue);
innerFuncRef = cppMethodAccessAgain(location, actualFuncRefValue, innerFuncRef, genContext);
if (!innerFuncRef)
{
builder.setInsertionPoint(ifOp);
ifOp.erase();
return mlir::failure();
}

auto result = mlirGenCallExpression(location, innerFuncRef, typeArguments, operands, genContext);
// a failed call (e.g. too few arguments) must fail the expression, not leave an
Expand Down Expand Up @@ -1983,6 +1990,69 @@ namespace mlirgen
return resolved;
}

// `obj?.method(...)`: the member access is made in a conditional of its own, and its callee comes
// to the call as the value of that conditional's optional result, which no longer says which
// method it is - resolveCppOverload kept the first overload (`mix(a, b)` took `mix(1, 2, 3)`), and
// the call wasn't a C++ call. A @cpp method is accessed again here, from the object the callee is
// bound to, so the call sees the member access itself. funcRefValue for any other callee; null,
// with an error, when the access fails.
mlir::Value MLIRGenImpl::cppMethodAccessAgain(mlir::Location location, mlir::Value optFuncRefValue,
mlir::Value funcRefValue, const GenContext &genContext)
{
auto ifOp = optFuncRefValue.getDefiningOp<mlir_ts::IfOp>();
if (!ifOp || ifOp.getThenRegion().empty())
{
return funcRefValue;
}

auto resultOp = dyn_cast<mlir_ts::ResultOp>(ifOp.getThenRegion().front().getTerminator());
if (!resultOp)
{
return funcRefValue;
}

auto optionalValueOp = resultOp.getOperand(mlir::cast<mlir::OpResult>(optFuncRefValue).getResultNumber())
.getDefiningOp<mlir_ts::OptionalValueOp>();
if (!optionalValueOp || !isCppVTableMethod(optionalValueOp.getIn()))
{
return funcRefValue;
}

mlir::Value thisValue;
StringRef identifier;
if (auto virtualRef = optionalValueOp.getIn().getDefiningOp<mlir_ts::ThisVirtualSymbolRefOp>())
{
thisValue = virtualRef.getThisVal();
identifier = virtualRef.getIdentifier();
}
else if (auto symbolRef = optionalValueOp.getIn().getDefiningOp<mlir_ts::ThisSymbolRefOp>())
{
thisValue = symbolRef.getThisVal();
identifier = symbolRef.getIdentifier();
}

// the object's own class, of which a method found in a base class took the object cast to
// that class: the overloads are those of the object's class (resolveCppOverload)
auto thisType = thisValue.getType();
if (auto castOp = thisValue.getDefiningOp<mlir_ts::CastOp>())
{
if (isa<mlir_ts::ClassType>(castOp.getIn().getType()))
{
thisType = castOp.getIn().getType();
}
}

auto objectValue = builder.create<mlir_ts::GetThisOp>(location, thisType, funcRefValue);
auto result = mlirGenPropertyAccessExpression(location, objectValue, cppOverloadedName(identifier.rsplit('.').second),
genContext);
if (result.failed_or_no_value())
{
return mlir::Value();
}

return V(result);
}

ValueOrLogicalResult MLIRGenImpl::NewClassInstanceOnStack(mlir::Location location, mlir_ts::ClassType classType,
SmallVector<mlir::Value, 4> &operands, const GenContext &genContext)
{
Expand Down
3 changes: 3 additions & 0 deletions tslang/lib/TypeScript/MLIRGenImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -11665,6 +11665,9 @@ class MLIRGenImpl
mlir::Value resolveCppOverload(mlir::Location location, mlir::Value funcRefValue, mlir::ArrayRef<mlir::Value> operands,
const GenContext &genContext);

mlir::Value cppMethodAccessAgain(mlir::Location location, mlir::Value optFuncRefValue, mlir::Value funcRefValue,
const GenContext &genContext);

bool isCppVirtualMethod(ClassInfo::TypePtr classInfo, ClassElement classMember, const GenContext &genContext);

bool isCppVTableImplementation(ClassLikeDeclaration classDeclarationAST, ClassInfo::TypePtr classInfo,
Expand Down
27 changes: 27 additions & 0 deletions tslang/test/tester/tests/00cpp_overloads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ class Canvas {
@slot(4) fill(shape: Shape): s32;
}

// overloads told apart by their number of parameters alone
@cpp
class Mixer {
@slot(1) mix(a: s32, b: s32): s32;
@slot(2) mix(a: s32, b: s32, c: s32): s32;
}

type ObjectData = [vtable: Opaque, value: s32];

function slot0(self: Opaque): s32 { return -1; }
Expand All @@ -51,6 +58,9 @@ function canvasDrawShape(self: Opaque, shape: Opaque): s32 { return 2000; }
function canvasFillSquare(self: Opaque, shape: Opaque): s32 { return 3000; }
function canvasFillShape(self: Opaque, shape: Opaque): s32 { return 4000; }

function mix2(self: Opaque, a: s32, b: s32): s32 { return 10 * a + b; }
function mix3(self: Opaque, a: s32, b: s32, c: s32): s32 { return 100 * a + 10 * b + c; }

function main() {
let vtable: Opaque[] = [<Opaque>slot0, <Opaque>slot1, <Opaque>slot2, <Opaque>slot3, <Opaque>slot4, <Opaque>slot5];
let data: ObjectData = [<Opaque>Ref(vtable[0]), 7];
Expand Down Expand Up @@ -85,5 +95,22 @@ function main() {
assert(canvas.fill(square) == 3000, "a Square: fill(Square) over fill(Shape)");
assert(canvas.fill(shape) == 4000, "a Shape: fill(Shape)");

let mixerVtable: Opaque[] = [<Opaque>slot0, <Opaque>mix2, <Opaque>mix3];
let mixerData: ObjectData = [<Opaque>Ref(mixerVtable[0]), 0];
const mixer = <Mixer><Opaque>Ref(mixerData);
assert(mixer.mix(1, 2) == 12, "two arguments: mix(s32, s32)");
assert(mixer.mix(1, 2, 3) == 123, "three arguments: mix(s32, s32, s32)");

// `?.`: the call picks among the overloads as a direct call does
const maybeMixer: Mixer | null = mixer;
assert(maybeMixer?.mix(4, 5) == 45, "?. with two arguments: mix(s32, s32)");
assert(maybeMixer?.mix(4, 5, 6) == 456, "?. with three arguments: mix(s32, s32, s32)");
const maybeDerived: Derived | null = derived;
assert(maybeDerived?.write(2.5) == 225, "?. on a derived class: its override");
assert(maybeDerived?.write("abcd") == 504, "?. on a derived class: its own overload");
assert(maybeDerived?.write(2, 3) == 306, "?. on a derived class: a base overload");
const noMixer: Mixer | null = null;
assert(noMixer?.mix(1, 2, 3) === undefined, "?. on null calls nothing");

print("done.");
}
Loading