Skip to content

Commit

Permalink
AST/Sema/SILGen: Implement tuple conversions
Browse files Browse the repository at this point in the history
TupleShuffleExpr could not express the full range of tuple conversions that
were accepted by the constraint solver; in particular, while it could re-order
elements or introduce and eliminate labels, it could not convert the tuple
element types to their supertypes.

This was the source of the annoying "cannot express tuple conversion"
diagnostic.

Replace TupleShuffleExpr with DestructureTupleExpr, which evaluates a
source expression of tuple type and binds its elements to OpaqueValueExprs.

The DestructureTupleExpr's result expression can then produce an arbitrary
value written in terms of these OpaqueValueExprs, as long as each
OpaqueValueExpr is used exactly once.

This is sufficient to express conversions such as (Int, Float) => (Int?, Any),
as well as the various cases that were already supported, such as
(x: Int, y: Float) => (y: Float, x: Int).

https://bugs.swift.org/browse/SR-2672, rdar://problem/12340004
  • Loading branch information
slavapestov committed Mar 27, 2019
1 parent ab81406 commit e2c9c52
Show file tree
Hide file tree
Showing 15 changed files with 295 additions and 357 deletions.
7 changes: 0 additions & 7 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -815,13 +815,6 @@ ERROR(precedence_group_redeclared,none,
NOTE(previous_precedence_group_decl,none,
"previous precedence group declaration here", ())

//------------------------------------------------------------------------------
// MARK: Type Check Coercions
//------------------------------------------------------------------------------

ERROR(tuple_conversion_not_expressible,none,
"cannot express tuple conversion %0 to %1", (Type, Type))

//------------------------------------------------------------------------------
// MARK: Expression Type Checking Errors
//------------------------------------------------------------------------------
Expand Down
61 changes: 38 additions & 23 deletions include/swift/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -290,10 +290,9 @@ class alignas(8) Expr {

SWIFT_INLINE_BITFIELD_EMPTY(ImplicitConversionExpr, Expr);

SWIFT_INLINE_BITFIELD_FULL(TupleShuffleExpr, ImplicitConversionExpr, 16,
/// This contains an entry for each element in the Expr type. Each element
/// specifies which index from the SubExpr that the destination element gets.
NumElementMappings : 16
SWIFT_INLINE_BITFIELD_FULL(DestructureTupleExpr, ImplicitConversionExpr, 16,
/// The number of elements in the tuple type being destructured.
NumElements : 16
);

SWIFT_INLINE_BITFIELD_FULL(ArgumentShuffleExpr, ImplicitConversionExpr, 2+16+16+16,
Expand Down Expand Up @@ -2960,37 +2959,53 @@ class UnevaluatedInstanceExpr : public ImplicitConversionExpr {
}
};

/// TupleShuffleExpr - This represents a permutation of a tuple value to a new
/// tuple type.
class TupleShuffleExpr final : public ImplicitConversionExpr,
private llvm::TrailingObjects<TupleShuffleExpr, unsigned> {
/// DestructureTupleExpr - Destructure a tuple value produced by a source
/// expression, binding the elements to OpaqueValueExprs, then evaluate the
/// result expression written in terms of the OpaqueValueExprs.
class DestructureTupleExpr final : public ImplicitConversionExpr,
private llvm::TrailingObjects<DestructureTupleExpr, OpaqueValueExpr *> {
friend TrailingObjects;

size_t numTrailingObjects(OverloadToken<unsigned>) const {
return Bits.TupleShuffleExpr.NumElementMappings;
size_t numTrailingObjects(OverloadToken<OpaqueValueExpr *>) const {
return Bits.DestructureTupleExpr.NumElements;
}

private:
TupleShuffleExpr(Expr *subExpr, ArrayRef<unsigned> elementMapping,
Type ty)
: ImplicitConversionExpr(ExprKind::TupleShuffle, subExpr, ty) {
Bits.TupleShuffleExpr.NumElementMappings = elementMapping.size();
std::uninitialized_copy(elementMapping.begin(), elementMapping.end(),
getTrailingObjects<unsigned>());
Expr *DstExpr;

DestructureTupleExpr(ArrayRef<OpaqueValueExpr *> destructuredElements,
Expr *srcExpr, Expr *dstExpr, Type ty)
: ImplicitConversionExpr(ExprKind::DestructureTuple, srcExpr, ty),
DstExpr(dstExpr) {
Bits.DestructureTupleExpr.NumElements = destructuredElements.size();
std::uninitialized_copy(destructuredElements.begin(),
destructuredElements.end(),
getTrailingObjects<OpaqueValueExpr *>());
}

public:
static TupleShuffleExpr *create(ASTContext &ctx, Expr *subExpr,
ArrayRef<unsigned> elementMapping,
Type ty);
/// Create a tuple destructuring. The type of srcExpr must be a tuple type,
/// and the number of elements must equal the size of destructureElements.
static DestructureTupleExpr *
create(ASTContext &ctx,
ArrayRef<OpaqueValueExpr *> destructuredElements,
Expr *srcExpr, Expr *dstExpr, Type ty);

ArrayRef<unsigned> getElementMapping() const {
return {getTrailingObjects<unsigned>(),
static_cast<size_t>(Bits.TupleShuffleExpr.NumElementMappings)};
ArrayRef<OpaqueValueExpr *> getDestructuredElements() const {
return {getTrailingObjects<OpaqueValueExpr *>(),
static_cast<size_t>(Bits.DestructureTupleExpr.NumElements)};
}

Expr *getResultExpr() const {
return DstExpr;
}

void setResultExpr(Expr *dstExpr) {
DstExpr = dstExpr;
}

static bool classof(const Expr *E) {
return E->getKind() == ExprKind::TupleShuffle;
return E->getKind() == ExprKind::DestructureTuple;
}
};

Expand Down
2 changes: 1 addition & 1 deletion include/swift/AST/ExprNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ ABSTRACT_EXPR(Apply, Expr)
EXPR_RANGE(Apply, Call, ConstructorRefCall)
ABSTRACT_EXPR(ImplicitConversion, Expr)
EXPR(Load, ImplicitConversionExpr)
EXPR(TupleShuffle, ImplicitConversionExpr)
EXPR(DestructureTuple, ImplicitConversionExpr)
EXPR(ArgumentShuffle, ImplicitConversionExpr)
EXPR(UnresolvedTypeConversion, ImplicitConversionExpr)
EXPR(FunctionConversion, ImplicitConversionExpr)
Expand Down
19 changes: 12 additions & 7 deletions lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2138,15 +2138,20 @@ class PrintExpr : public ExprVisitor<PrintExpr> {
printRec(E->getBase());
PrintWithColorRAII(OS, ParenthesisColor) << ')';
}
void visitTupleShuffleExpr(TupleShuffleExpr *E) {
printCommon(E, "tuple_shuffle_expr");
OS << " elements=[";
for (unsigned i = 0, e = E->getElementMapping().size(); i != e; ++i) {
if (i) OS << ", ";
OS << E->getElementMapping()[i];
void visitDestructureTupleExpr(DestructureTupleExpr *E) {
printCommon(E, "destructure_tuple_expr");
OS << " destructured=";
PrintWithColorRAII(OS, ParenthesisColor) << '(';
Indent += 2;
for (auto *elt : E->getDestructuredElements()) {
OS << "\n";
printRec(elt);
}
OS << "]\n";
Indent -= 2;
PrintWithColorRAII(OS, ParenthesisColor) << ")\n";
printRec(E->getSubExpr());
OS << "\n";
printRec(E->getResultExpr());
PrintWithColorRAII(OS, ParenthesisColor) << ')';
}
void visitArgumentShuffleExpr(ArgumentShuffleExpr *E) {
Expand Down
48 changes: 38 additions & 10 deletions lib/AST/ASTVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,26 @@ class Verifier : public ASTWalker {
OpaqueValues.erase(expr->getOpaqueValue());
}

// Register the OVEs in a DestructureTupleExpr.
bool shouldVerify(DestructureTupleExpr *expr) {
if (!shouldVerify(cast<Expr>(expr)))
return false;

for (auto *opaqueElt : expr->getDestructuredElements()) {
assert(!OpaqueValues.count(opaqueElt));
OpaqueValues[opaqueElt] = 0;
}

return true;
}

void cleanup(DestructureTupleExpr *expr) {
for (auto *opaqueElt : expr->getDestructuredElements()) {
assert(OpaqueValues.count(opaqueElt));
OpaqueValues.erase(opaqueElt);
}
}

// Keep a stack of the currently-live optional evaluations.
bool shouldVerify(OptionalEvaluationExpr *expr) {
if (!shouldVerify(cast<Expr>(expr)))
Expand Down Expand Up @@ -1988,27 +2008,35 @@ class Verifier : public ASTWalker {
verifyCheckedBase(E);
}

void verifyChecked(TupleShuffleExpr *E) {
PrettyStackTraceExpr debugStack(Ctx, "verifying TupleShuffleExpr", E);
void verifyChecked(DestructureTupleExpr *E) {
PrettyStackTraceExpr debugStack(Ctx, "verifying DestructureTupleExpr", E);

auto getSubElementType = [&](unsigned i) {
auto getInputElementType = [&](unsigned i) {
return (E->getSubExpr()->getType()->castTo<TupleType>()
->getElementType(i));
};

/// Retrieve the ith element type from the resulting tuple type.
auto getOuterElementType = [&](unsigned i) -> Type {
return E->getType()->castTo<TupleType>()->getElementType(i);
auto getOpaqueElementType = [&](unsigned i) -> Type {
return E->getDestructuredElements()[i]->getType();
};

for (unsigned i = 0, e = E->getElementMapping().size(); i != e; ++i) {
int subElem = E->getElementMapping()[i];
if (!getOuterElementType(i)->isEqual(getSubElementType(subElem))) {
Out << "Type mismatch in TupleShuffleExpr\n";
for (unsigned i = 0, e = E->getDestructuredElements().size(); i != e; ++i) {
Type inputType = getInputElementType(i);
Type opaqueType = getOpaqueElementType(i);
if (!inputType->isEqual(opaqueType)) {
Out << "Input type mismatch in DestructureTupleExpr\n";
inputType->dump(Out);
opaqueType->dump(Out);
abort();
}
}

if (!E->getResultExpr()->getType()->isEqual(E->getType())) {
Out << "Result type mismatch in DestructureTupleExpr\n";
E->getResultExpr()->getType()->dump(Out);
E->getType()->dump(Out);
}

verifyCheckedBase(E);
}

Expand Down
14 changes: 10 additions & 4 deletions lib/AST/ASTWalker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
// Note that semantic components will generally preserve the
// syntactic order of their children because doing something else
// could illegally change order of evaluation. This is why, for
// example, shuffling a TupleExpr creates a TupleShuffleExpr
// example, shuffling a TupleExpr creates a DestructureTupleExpr
// instead of just making a new TupleExpr with the elements in
// different order.
//
Expand Down Expand Up @@ -639,9 +639,15 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
return E;
}

Expr *visitTupleShuffleExpr(TupleShuffleExpr *E) {
if (Expr *E2 = doIt(E->getSubExpr())) {
E->setSubExpr(E2);
Expr *visitDestructureTupleExpr(DestructureTupleExpr *E) {
if (auto *src = doIt(E->getSubExpr())) {
E->setSubExpr(src);
} else {
return nullptr;
}

if (auto *dst = doIt(E->getResultExpr())) {
E->setResultExpr(dst);
} else {
return nullptr;
}
Expand Down
19 changes: 10 additions & 9 deletions lib/AST/Expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ ConcreteDeclRef Expr::getReferencedDecl() const {

PASS_THROUGH_REFERENCE(ConstructorRefCall, getFn);
PASS_THROUGH_REFERENCE(Load, getSubExpr);
NO_REFERENCE(TupleShuffle);
NO_REFERENCE(DestructureTuple);
NO_REFERENCE(ArgumentShuffle);
NO_REFERENCE(UnresolvedTypeConversion);
PASS_THROUGH_REFERENCE(FunctionConversion, getSubExpr);
Expand Down Expand Up @@ -637,7 +637,7 @@ bool Expr::canAppendPostfixExpression(bool appendingPostfixOperator) const {
return false;

case ExprKind::Load:
case ExprKind::TupleShuffle:
case ExprKind::DestructureTuple:
case ExprKind::ArgumentShuffle:
case ExprKind::UnresolvedTypeConversion:
case ExprKind::FunctionConversion:
Expand Down Expand Up @@ -1333,13 +1333,14 @@ CaptureListExpr *CaptureListExpr::create(ASTContext &ctx,
return ::new(mem) CaptureListExpr(captureList, closureBody);
}

TupleShuffleExpr *TupleShuffleExpr::create(ASTContext &ctx,
Expr *subExpr,
ArrayRef<unsigned> elementMapping,
Type ty) {
auto size = totalSizeToAlloc<unsigned>(elementMapping.size());
auto mem = ctx.Allocate(size, alignof(TupleShuffleExpr));
return ::new(mem) TupleShuffleExpr(subExpr, elementMapping, ty);
DestructureTupleExpr *
DestructureTupleExpr::create(ASTContext &ctx,
ArrayRef<OpaqueValueExpr *> destructuredElements,
Expr *srcExpr, Expr *dstExpr, Type ty) {
auto size = totalSizeToAlloc<OpaqueValueExpr *>(destructuredElements.size());
auto mem = ctx.Allocate(size, alignof(DestructureTupleExpr));
return ::new(mem) DestructureTupleExpr(destructuredElements,
srcExpr, dstExpr, ty);
}

ArgumentShuffleExpr *ArgumentShuffleExpr::create(ASTContext &ctx,
Expand Down
91 changes: 22 additions & 69 deletions lib/SILGen/SILGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ namespace {
RValue visitKeyPathApplicationExpr(KeyPathApplicationExpr *E, SGFContext C);
RValue visitDynamicSubscriptExpr(DynamicSubscriptExpr *E,
SGFContext C);
RValue visitTupleShuffleExpr(TupleShuffleExpr *E, SGFContext C);
RValue visitDestructureTupleExpr(DestructureTupleExpr *E, SGFContext C);
RValue visitArgumentShuffleExpr(ArgumentShuffleExpr *E, SGFContext C);
RValue visitDynamicTypeExpr(DynamicTypeExpr *E, SGFContext C);
RValue visitCaptureListExpr(CaptureListExpr *E, SGFContext C);
Expand Down Expand Up @@ -2151,81 +2151,34 @@ RValue SILGenFunction::emitApplyOfStoredPropertyInitializer(
subs, {}, calleeTypeInfo, ApplyOptions::None, C);
}

static void emitTupleShuffleExprInto(RValueEmitter &emitter,
TupleShuffleExpr *E,
Initialization *outerTupleInit) {
CanTupleType outerTuple = cast<TupleType>(E->getType()->getCanonicalType());
auto outerFields = outerTuple->getElements();
(void) outerFields;

// Decompose the initialization.
SmallVector<InitializationPtr, 4> outerInitsBuffer;
auto outerInits =
outerTupleInit->splitIntoTupleElements(emitter.SGF, RegularLocation(E),
outerTuple, outerInitsBuffer);
assert(outerInits.size() == outerFields.size() &&
"initialization size does not match tuple size?!");

// Map outer initializations into a tuple of inner initializations:
// - fill out the initialization elements with null
TupleInitialization innerTupleInit;

CanTupleType innerTuple =
cast<TupleType>(E->getSubExpr()->getType()->getCanonicalType());
innerTupleInit.SubInitializations.resize(innerTuple->getNumElements());
RValue RValueEmitter::visitDestructureTupleExpr(DestructureTupleExpr *E,
SGFContext C) {
// Emit the sub-expression tuple and destructure it into elements.
SmallVector<RValue, 4> elements;
visit(E->getSubExpr()).extractElements(elements);

// Map all the outer initializations to their appropriate targets.
for (unsigned outerIndex = 0; outerIndex != outerInits.size(); outerIndex++) {
auto innerMapping = E->getElementMapping()[outerIndex];
innerTupleInit.SubInitializations[innerMapping] =
std::move(outerInits[outerIndex]);
}
// Bind each element of the input tuple to its corresponding
// opaque value.
for (unsigned i = 0, e = E->getDestructuredElements().size();
i != e; ++i) {
auto *opaqueElt = E->getDestructuredElements()[i];
assert(!SGF.OpaqueValues.count(opaqueElt));

#ifndef NDEBUG
for (auto &innerInit : innerTupleInit.SubInitializations) {
assert(innerInit != nullptr && "didn't map all inner elements");
auto opaqueMV = std::move(elements[i]).getAsSingleValue(SGF, E);
SGF.OpaqueValues[opaqueElt] = opaqueMV;
}
#endif

// Emit the sub-expression into the tuple initialization we just built.
emitter.SGF.emitExprInto(E->getSubExpr(), &innerTupleInit);

outerTupleInit->finishInitialization(emitter.SGF);
}
// Emit the result expression written in terms of the above
// opaque values.
auto result = visit(E->getResultExpr(), C);

RValue RValueEmitter::visitTupleShuffleExpr(TupleShuffleExpr *E,
SGFContext C) {
// If we're emitting into an initialization, we can try shuffling the
// elements of the initialization.
if (Initialization *I = C.getEmitInto()) {
if (I->canSplitIntoTupleElements()) {
emitTupleShuffleExprInto(*this, E, I);
return RValue::forInContext();
}
// Clean up.
for (unsigned i = 0, e = E->getDestructuredElements().size();
i != e; ++i) {
auto *opaqueElt = E->getDestructuredElements()[i];
SGF.OpaqueValues.erase(opaqueElt);
}

// Emit the sub-expression tuple and destructure it into elements.
SmallVector<RValue, 4> elements;
visit(E->getSubExpr()).extractElements(elements);

// Prepare a new tuple to hold the shuffled result.
RValue result(E->getType()->getCanonicalType());

auto outerFields = E->getType()->castTo<TupleType>()->getElements();
auto shuffleIndexIterator = E->getElementMapping().begin();
auto shuffleIndexEnd = E->getElementMapping().end();
(void)shuffleIndexEnd;
for (auto &field : outerFields) {
(void) field;
assert(shuffleIndexIterator != shuffleIndexEnd &&
"ran out of shuffle indexes before running out of fields?!");
int shuffleIndex = *shuffleIndexIterator++;

// Map from a different tuple element.
result.addElement(
std::move(elements[shuffleIndex]).ensurePlusOne(SGF, E));
}

return result;
}

Expand Down
Loading

0 comments on commit e2c9c52

Please sign in to comment.